summaryrefslogtreecommitdiff
path: root/recipes/linux/linux-rp-2.6.24/tosa
diff options
context:
space:
mode:
authorDenys Dmytriyenko <denis@denix.org>2009-03-17 14:32:59 -0400
committerDenys Dmytriyenko <denis@denix.org>2009-03-17 14:32:59 -0400
commit709c4d66e0b107ca606941b988bad717c0b45d9b (patch)
tree37ee08b1eb308f3b2b6426d5793545c38396b838 /recipes/linux/linux-rp-2.6.24/tosa
parentfa6cd5a3b993f16c27de4ff82b42684516d433ba (diff)
rename packages/ to recipes/ per earlier agreement
See links below for more details: http://thread.gmane.org/gmane.comp.handhelds.openembedded/21326 http://thread.gmane.org/gmane.comp.handhelds.openembedded/21816 Signed-off-by: Denys Dmytriyenko <denis@denix.org> Acked-by: Mike Westerhof <mwester@dls.net> Acked-by: Philip Balister <philip@balister.org> Acked-by: Khem Raj <raj.khem@gmail.com> Acked-by: Marcin Juszkiewicz <hrw@openembedded.org> Acked-by: Koen Kooi <koen@openembedded.org> Acked-by: Frans Meulenbroeks <fransmeulenbroeks@gmail.com>
Diffstat (limited to 'recipes/linux/linux-rp-2.6.24/tosa')
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0001-Allow-runtime-registration-of-regions-of-memory-that.patch201
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0001-pxa2xx-ac97-switch-AC-unit-to-correct-state-before.patch56
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0002-Modify-dma_alloc_coherent-on-ARM-so-that-it-supports.patch260
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0003-Core-MFD-support.patch243
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0004-Add-support-for-tc6393xb-MFD-core.patch907
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0005-Add-support-for-tc6387xb-MFD-core.patch249
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0006-Add-support-for-t7l66xb-MFD-core.patch653
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0007-Common-headers-for-TMIO-MFD-subdevices.patch81
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0008-Nand-driver-for-TMIO-devices.patch608
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0009-FB-driver-for-TMIO-devices.patch1128
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0010-OHCI-driver-for-TMIO-devices.patch431
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch891
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0012-Tosa-keyboard-support.patch593
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0013-USB-gadget-pxa2xx_udc-supports-inverted-vbus.patch61
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0014-tosa_udc_use_gpio_vbus.patch.patch38
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0015-sharpsl-export-params.patch32
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0016-This-patch-fixes-the-pxa25x-clocks-definitions-to-ad.patch44
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0017-Convert-pxa2xx-UDC-to-use-debugfs.patch280
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0018-Fix-the-pxa2xx_udc-to-balance-calls-to-clk_enable-cl.patch225
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0019-pxa-remove-periodic-mode-emulation-support.patch128
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0020-Provide-dew-device-clock-backports-from-2.6.24-git.patch257
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0021-Add-an-empty-drivers-gpio-directory-for-gpiolib-infr.patch121
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0022-Provide-new-implementation-infrastructure-that-platf.patch746
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0023-This-adds-gpiolib-support-for-the-PXA-architecture.patch498
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0024-Update-Documentation-gpio.txt-primarily-to-include.patch238
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0025-Signed-off-by-Dmitry-Baryshkov-dbaryshkov-gmail.co.patch434
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0026-I-don-t-think-we-should-check-for-IRQs-when-determin.patch134
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0027-Add-LiMn-one-of-the-most-common-for-small-non-recha.patch59
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0028-Add-suspend-resume-wakeup-support-for-pda_power.patch72
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0029-Support-using-VOLTAGE_-properties-for-apm-calculati.patch163
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0030-Core-driver-for-WM97xx-touchscreens.patch1083
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0031-Add-chip-driver-for-WM9705-touchscreen.patch383
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0032-Add-chip-driver-for-WM9712-touchscreen.patch492
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0033-Add-chip-driver-for-WM9713-touchscreen.patch490
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0034-Driver-for-WM97xx-touchscreens-in-streaming-mode-on.patch329
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0035-Build-system-and-MAINTAINERS-entry-for-WM97xx-touchs.patch122
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0036-Set-id-to-1-for-wm97xx-subdevices.patch35
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0037-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch41
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0038-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch57
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch446
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0040-Clocklib-debugfs-support.patch108
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0041-From-80a359e60c2aec59ccf4fca0a7fd20495f82b1d2-Mon-Se.patch593
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0042-Use-correct-clock-for-IrDA-on-pxa.patch26
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0043-Use-clocklib-for-sa1100-sub-arch.patch153
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0044-fix-tmio_mmc-debug-compilation.patch26
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0045-Update-tmio_ohci.patch416
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0046-patch-tc6393xb-cleanup.patch66
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0047-tc6393xb-use-bitmasks-instead-of-bit-field-structs.patch412
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0048-tc6393xb-GPIO-support.patch225
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0049-platform-support-for-TMIO-on-tosa.patch373
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0050-tosa-update-for-tc6393xb-gpio.patch99
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0051-fix-sound-soc-pxa-tosa.c-to-new-gpio-api.patch86
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0052-tosa-platform-backlight-support.patch400
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0053-sound-soc-codecs-wm9712.c-28.patch56
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0054-sound-soc-codecs-wm9712.c-2.patch28
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0055-Add-GPIO_POWERON-to-the-list-of-devices-that-we-supp.patch30
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0056-Support-resetting-by-asserting-GPIO-pin.patch126
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0057-Clean-up-tosa-resetting.patch70
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0058-Fix-tosakbd-suspend.patch27
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0059-patch-tosa-wakeup-test.patch46
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0060-Add-support-for-power_supply-on-tosa.patch623
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0061-tosa-bat-unify.patch342
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0062-tosa-bat-fix-charging.patch78
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0063-patch-tosa-bat-jacket-detect.patch84
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0064-Export-modes-via-sysfs.patch27
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0065-wm97xx-core-fixes.patch49
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0066-tmiofb_probe-should-be-__devinit.patch26
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0067-modeswitching.patch225
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/0068-Preliminary-tosa-denoiser.patch239
-rw-r--r--recipes/linux/linux-rp-2.6.24/tosa/tosa-bl-fixup.diff103
70 files changed, 18471 insertions, 0 deletions
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0001-Allow-runtime-registration-of-regions-of-memory-that.patch b/recipes/linux/linux-rp-2.6.24/tosa/0001-Allow-runtime-registration-of-regions-of-memory-that.patch
new file mode 100644
index 0000000000..ba79b4a470
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0001-Allow-runtime-registration-of-regions-of-memory-that.patch
@@ -0,0 +1,201 @@
+From d48a09b301d9a460d5ce027433e8cb8872e7b5c3 Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Fri, 4 Jan 2008 18:26:38 +0000
+Subject: [PATCH 01/64] Allow runtime registration of regions of memory that require dma bouncing.
+
+---
+ arch/arm/common/Kconfig | 4 ++
+ arch/arm/common/dmabounce.c | 82 ++++++++++++++++++++++++++++++++++++-
+ arch/arm/common/sa1111.c | 2 +-
+ arch/arm/mach-ixp4xx/Kconfig | 1 +
+ arch/arm/mach-ixp4xx/common-pci.c | 2 +-
+ 5 files changed, 87 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
+index 3e07346..5f357fb 100644
+--- a/arch/arm/common/Kconfig
++++ b/arch/arm/common/Kconfig
+@@ -13,10 +13,14 @@ config ICST307
+ config SA1111
+ bool
+ select DMABOUNCE
++ select PLATFORM_DMABOUNCE
+
+ config DMABOUNCE
+ bool
+
++config PLATFORM_DMABOUNCE
++ bool
++
+ config TIMER_ACORN
+ bool
+
+diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c
+index 52fc6a8..ed80abe 100644
+--- a/arch/arm/common/dmabounce.c
++++ b/arch/arm/common/dmabounce.c
+@@ -16,6 +16,7 @@
+ *
+ * Copyright (C) 2002 Hewlett Packard Company.
+ * Copyright (C) 2004 MontaVista Software, Inc.
++ * Copyright (C) 2007 Dmitry Baryshkov <dbaryshkov@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+@@ -24,6 +25,7 @@
+
+ #include <linux/module.h>
+ #include <linux/init.h>
++#include <linux/rwsem.h>
+ #include <linux/slab.h>
+ #include <linux/device.h>
+ #include <linux/dma-mapping.h>
+@@ -80,6 +82,80 @@ struct dmabounce_device_info {
+ rwlock_t lock;
+ };
+
++struct dmabounce_check_entry {
++ struct list_head list;
++ dmabounce_check checker;
++ void *data;
++};
++
++static struct list_head checkers = LIST_HEAD_INIT(checkers);
++static rwlock_t checkers_lock = RW_LOCK_UNLOCKED;
++
++int
++dmabounce_register_checker(dmabounce_check function, void *data)
++{
++ unsigned long flags;
++ struct dmabounce_check_entry *entry =
++ kzalloc(sizeof(struct dmabounce_check_entry), GFP_ATOMIC);
++
++ if (!entry)
++ return ENOMEM;
++
++ INIT_LIST_HEAD(&entry->list);
++ entry->checker = function;
++ entry->data = data;
++
++ write_lock_irqsave(&checkers_lock, flags);
++ list_add(&entry->list, &checkers);
++ write_unlock_irqrestore(&checkers_lock, flags);
++
++ return 0;
++}
++
++void
++dmabounce_remove_checker(dmabounce_check function, void *data)
++{
++ unsigned long flags;
++ struct list_head *pos;
++
++ write_lock_irqsave(&checkers_lock, flags);
++ __list_for_each(pos, &checkers) {
++ struct dmabounce_check_entry *entry = container_of(pos,
++ struct dmabounce_check_entry, list);
++ if (entry->checker == function && entry->data == data) {
++ list_del(pos);
++ write_unlock_irqrestore(&checkers_lock, flags);
++ kfree(entry);
++ return;
++ }
++ }
++
++ write_unlock_irqrestore(&checkers_lock, flags);
++ printk(KERN_WARNING "dmabounce checker not found: %p\n", function);
++}
++
++static int dma_needs_bounce(struct device *dev, dma_addr_t dma, size_t size)
++{
++ unsigned long flags;
++ struct list_head *pos;
++
++ read_lock_irqsave(&checkers_lock, flags);
++ __list_for_each(pos, &checkers) {
++ struct dmabounce_check_entry *entry = container_of(pos,
++ struct dmabounce_check_entry, list);
++ if (entry->checker(dev, dma, size, entry->data)) {
++ read_unlock_irqrestore(&checkers_lock, flags);
++ return 1;
++ }
++ }
++
++ read_unlock_irqrestore(&checkers_lock, flags);
++#ifdef CONFIG_PLATFORM_DMABOUNCE
++ return platform_dma_needs_bounce(dev, dma, size);
++#else
++ return 0;
++#endif
++}
+ #ifdef STATS
+ static ssize_t dmabounce_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+@@ -239,7 +315,7 @@ map_single(struct device *dev, void *ptr, size_t size,
+ struct safe_buffer *buf;
+
+ buf = alloc_safe_buffer(device_info, ptr, size, dir);
+- if (buf == 0) {
++ if (buf == NULL) {
+ dev_err(dev, "%s: unable to map unsafe buffer %p!\n",
+ __func__, ptr);
+ return 0;
+@@ -643,7 +719,6 @@ dmabounce_unregister_dev(struct device *dev)
+ dev->bus_id, dev->bus->name);
+ }
+
+-
+ EXPORT_SYMBOL(dma_map_single);
+ EXPORT_SYMBOL(dma_unmap_single);
+ EXPORT_SYMBOL(dma_map_sg);
+@@ -653,6 +728,9 @@ EXPORT_SYMBOL(dma_sync_single_for_device);
+ EXPORT_SYMBOL(dma_sync_sg);
+ EXPORT_SYMBOL(dmabounce_register_dev);
+ EXPORT_SYMBOL(dmabounce_unregister_dev);
++EXPORT_SYMBOL(dmabounce_register_checker);
++EXPORT_SYMBOL(dmabounce_remove_checker);
++
+
+ MODULE_AUTHOR("Christopher Hoover <ch@hpl.hp.com>, Deepak Saxena <dsaxena@plexity.net>");
+ MODULE_DESCRIPTION("Special dma_{map/unmap/dma_sync}_* routines for systems with limited DMA windows");
+diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c
+index eb06d0b..3b8fbdd 100644
+--- a/arch/arm/common/sa1111.c
++++ b/arch/arm/common/sa1111.c
+@@ -778,7 +778,7 @@ static void __sa1111_remove(struct sa1111 *sachip)
+ * This should only get called for sa1111_device types due to the
+ * way we configure our device dma_masks.
+ */
+-int dma_needs_bounce(struct device *dev, dma_addr_t addr, size_t size)
++int platform_dma_needs_bounce(struct device *dev, dma_addr_t addr, size_t size)
+ {
+ /*
+ * Section 4.6 of the "Intel StrongARM SA-1111 Development Module
+diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig
+index 61b2dfc..5870371 100644
+--- a/arch/arm/mach-ixp4xx/Kconfig
++++ b/arch/arm/mach-ixp4xx/Kconfig
+@@ -161,6 +161,7 @@ comment "IXP4xx Options"
+ config DMABOUNCE
+ bool
+ default y
++ select PLATFORM_DMABOUNCE
+ depends on PCI
+
+ config IXP4XX_INDIRECT_PCI
+diff --git a/arch/arm/mach-ixp4xx/common-pci.c b/arch/arm/mach-ixp4xx/common-pci.c
+index bf04121..ac46492 100644
+--- a/arch/arm/mach-ixp4xx/common-pci.c
++++ b/arch/arm/mach-ixp4xx/common-pci.c
+@@ -336,7 +336,7 @@ static int ixp4xx_pci_platform_notify_remove(struct device *dev)
+ return 0;
+ }
+
+-int dma_needs_bounce(struct device *dev, dma_addr_t dma_addr, size_t size)
++int platform_dma_needs_bounce(struct device *dev, dma_addr_t dma_addr, size_t size)
+ {
+ return (dev->bus == &pci_bus_type ) && ((dma_addr + size) >= SZ_64M);
+ }
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0001-pxa2xx-ac97-switch-AC-unit-to-correct-state-before.patch b/recipes/linux/linux-rp-2.6.24/tosa/0001-pxa2xx-ac97-switch-AC-unit-to-correct-state-before.patch
new file mode 100644
index 0000000000..09f0cb946c
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0001-pxa2xx-ac97-switch-AC-unit-to-correct-state-before.patch
@@ -0,0 +1,56 @@
+From 688df15bb534519e0698cc8e4a4d9234afd32105 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 7 Nov 2008 15:50:39 +0300
+Subject: [PATCH] pxa2xx-ac97: switch AC unit to correct state before probing
+
+If AC97 unit is in partially enabled state, early request_irq can trigger
+IRQ storm or even full hang up. Workaround this by forcibly switching ACLINK off
+at the start of the probe.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ sound/soc/pxa/pxa2xx-ac97.c | 30 +++++++++++++++++-------------
+ 1 files changed, 17 insertions(+), 13 deletions(-)
+
+Index: linux-2.6.24/sound/soc/pxa/pxa2xx-ac97.c
+===================================================================
+--- linux-2.6.24.orig/sound/soc/pxa/pxa2xx-ac97.c 2008-01-25 01:58:37.000000000 +0300
++++ linux-2.6.24/sound/soc/pxa/pxa2xx-ac97.c 2008-11-15 20:02:45.396976363 +0300
+@@ -284,10 +284,6 @@ static int pxa2xx_ac97_probe(struct plat
+ {
+ int ret;
+
+- ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL);
+- if (ret < 0)
+- goto err;
+-
+ pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
+ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
+ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
+@@ -296,15 +292,23 @@ static int pxa2xx_ac97_probe(struct plat
+ /* Use GPIO 113 as AC97 Reset on Bulverde */
+ pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+ #endif
++ GCR = GCR_ACLINK_OFF;
++
+ pxa_set_cken(CKEN_AC97, 1);
++
++ ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL);
++ if (ret < 0)
++ goto err;
++
++
+ return 0;
+
+- err:
+- if (CKEN & (1 << CKEN_AC97)) {
++err:
++/* if (CKEN & (1 << CKEN_AC97)) {*/
+ GCR |= GCR_ACLINK_OFF;
+ free_irq(IRQ_AC97, NULL);
+ pxa_set_cken(CKEN_AC97, 0);
+- }
++/* }*/
+ return ret;
+ }
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0002-Modify-dma_alloc_coherent-on-ARM-so-that-it-supports.patch b/recipes/linux/linux-rp-2.6.24/tosa/0002-Modify-dma_alloc_coherent-on-ARM-so-that-it-supports.patch
new file mode 100644
index 0000000000..a562ef921b
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0002-Modify-dma_alloc_coherent-on-ARM-so-that-it-supports.patch
@@ -0,0 +1,260 @@
+From 8e95f90487d2fb46fd862744ddb34f47c30b0c5a Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Fri, 4 Jan 2008 18:27:50 +0000
+Subject: [PATCH 02/64] Modify dma_alloc_coherent on ARM so that it supports device local DMA.
+
+---
+ arch/arm/mm/consistent.c | 125 +++++++++++++++++++++++++++++++++++++++++
+ include/asm-arm/dma-mapping.h | 37 +++++++------
+ 2 files changed, 145 insertions(+), 17 deletions(-)
+
+diff --git a/arch/arm/mm/consistent.c b/arch/arm/mm/consistent.c
+index 333a82a..3da0f94 100644
+--- a/arch/arm/mm/consistent.c
++++ b/arch/arm/mm/consistent.c
+@@ -3,6 +3,8 @@
+ *
+ * Copyright (C) 2000-2004 Russell King
+ *
++ * Device local coherent memory support added by Ian Molton (spyro@f2s.com)
++ *
+ * 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.
+@@ -20,6 +22,7 @@
+
+ #include <asm/memory.h>
+ #include <asm/cacheflush.h>
++#include <asm/io.h>
+ #include <asm/tlbflush.h>
+ #include <asm/sizes.h>
+
+@@ -35,6 +38,13 @@
+ #define CONSISTENT_PTE_INDEX(x) (((unsigned long)(x) - CONSISTENT_BASE) >> PGDIR_SHIFT)
+ #define NUM_CONSISTENT_PTES (CONSISTENT_DMA_SIZE >> PGDIR_SHIFT)
+
++struct dma_coherent_mem {
++ void *virt_base;
++ u32 device_base;
++ int size;
++ int flags;
++ unsigned long *bitmap;
++};
+
+ /*
+ * These are the page tables (2MB each) covering uncached, DMA consistent allocations
+@@ -153,6 +163,13 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
+ unsigned long order;
+ u64 mask = ISA_DMA_THRESHOLD, limit;
+
++ /* Following is a work-around (a.k.a. hack) to prevent pages
++ * with __GFP_COMP being passed to split_page() which cannot
++ * handle them. The real problem is that this flag probably
++ * should be 0 on ARM as it is not supported on this
++ * platform--see CONFIG_HUGETLB_PAGE. */
++ gfp &= ~(__GFP_COMP);
++
+ if (!consistent_pte[0]) {
+ printk(KERN_ERR "%s: not initialised\n", __func__);
+ dump_stack();
+@@ -160,6 +177,26 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
+ }
+
+ if (dev) {
++
++ if (dev->dma_mem) {
++ unsigned long flags;
++ int pgnum;
++ void *ret;
++
++ spin_lock_irqsave(&consistent_lock, flags);
++ pgnum = bitmap_find_free_region(dev->dma_mem->bitmap,
++ dev->dma_mem->size,
++ get_order(size));
++ spin_unlock_irqrestore(&consistent_lock, flags);
++
++ if (pgnum >= 0) {
++ *handle = dev->dma_mem->device_base + (pgnum << PAGE_SHIFT);
++ ret = dev->dma_mem->virt_base + (pgnum << PAGE_SHIFT);
++ memset(ret, 0, size);
++ return ret;
++ }
++ }
++
+ mask = dev->coherent_dma_mask;
+
+ /*
+@@ -177,6 +214,9 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
+ mask, (unsigned long long)ISA_DMA_THRESHOLD);
+ goto no_page;
+ }
++
++ if (dev->dma_mem && dev->dma_mem->flags & DMA_MEMORY_EXCLUSIVE)
++ return NULL;
+ }
+
+ /*
+@@ -359,6 +399,8 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr
+ pte_t *ptep;
+ int idx;
+ u32 off;
++ struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
++ unsigned long order;
+
+ WARN_ON(irqs_disabled());
+
+@@ -368,6 +410,15 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr
+ }
+
+ size = PAGE_ALIGN(size);
++ order = get_order(size);
++
++ /* What if mem is valid and the range is not? */
++ if (mem && cpu_addr >= mem->virt_base && cpu_addr < (mem->virt_base + (mem->size << PAGE_SHIFT))) {
++ int page = (cpu_addr - mem->virt_base) >> PAGE_SHIFT;
++
++ bitmap_release_region(mem->bitmap, page, order);
++ return;
++ }
+
+ spin_lock_irqsave(&consistent_lock, flags);
+ c = vm_region_find(&consistent_head, (unsigned long)cpu_addr);
+@@ -437,6 +488,80 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr
+ }
+ EXPORT_SYMBOL(dma_free_coherent);
+
++int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
++ dma_addr_t device_addr, size_t size, int flags)
++{
++ void __iomem *mem_base;
++ int pages = size >> PAGE_SHIFT;
++ int bitmap_size = (pages + 31)/32;
++
++ if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0)
++ goto out;
++ if (!size)
++ goto out;
++ if (dev->dma_mem)
++ goto out;
++
++ /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */
++ mem_base = ioremap_nocache(bus_addr, size);
++ if (!mem_base)
++ goto out;
++
++ dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
++ if (!dev->dma_mem)
++ goto out;
++ memset(dev->dma_mem, 0, sizeof(struct dma_coherent_mem));
++ dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
++ if (!dev->dma_mem->bitmap)
++ goto free1_out;
++
++ dev->dma_mem->virt_base = mem_base;
++ dev->dma_mem->device_base = device_addr;
++ dev->dma_mem->size = pages;
++ dev->dma_mem->flags = flags;
++
++ if (flags & DMA_MEMORY_MAP)
++ return DMA_MEMORY_MAP;
++
++ return DMA_MEMORY_IO;
++
++ free1_out:
++ kfree(dev->dma_mem->bitmap);
++ out:
++ return 0;
++}
++EXPORT_SYMBOL(dma_declare_coherent_memory);
++
++void dma_release_declared_memory(struct device *dev)
++{
++ struct dma_coherent_mem *mem = dev->dma_mem;
++
++ if (!mem)
++ return;
++ dev->dma_mem = NULL;
++ kfree(mem->bitmap);
++ kfree(mem);
++}
++EXPORT_SYMBOL(dma_release_declared_memory);
++
++void *dma_mark_declared_memory_occupied(struct device *dev,
++ dma_addr_t device_addr, size_t size)
++{
++ struct dma_coherent_mem *mem = dev->dma_mem;
++ int pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
++ int pos, err;
++
++ if (!mem)
++ return ERR_PTR(-EINVAL);
++
++ pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
++ err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages));
++ if (err != 0)
++ return ERR_PTR(err);
++ return mem->virt_base + (pos << PAGE_SHIFT);
++}
++EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
++
+ /*
+ * Initialise the consistent memory allocation.
+ */
+diff --git a/include/asm-arm/dma-mapping.h b/include/asm-arm/dma-mapping.h
+index e99406a..f18ba05 100644
+--- a/include/asm-arm/dma-mapping.h
++++ b/include/asm-arm/dma-mapping.h
+@@ -7,6 +7,19 @@
+
+ #include <linux/scatterlist.h>
+
++#define ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY
++extern int
++dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
++ dma_addr_t device_addr, size_t size, int flags);
++
++extern void
++dma_release_declared_memory(struct device *dev);
++
++extern void *
++dma_mark_declared_memory_occupied(struct device *dev,
++ dma_addr_t device_addr, size_t size);
++
++
+ /*
+ * DMA-consistent mapping functions. These allocate/free a region of
+ * uncached, unwrite-buffered mapped memory space for use with DMA
+@@ -433,23 +446,13 @@ extern int dmabounce_register_dev(struct device *, unsigned long, unsigned long)
+ */
+ extern void dmabounce_unregister_dev(struct device *);
+
+-/**
+- * dma_needs_bounce
+- *
+- * @dev: valid struct device pointer
+- * @dma_handle: dma_handle of unbounced buffer
+- * @size: size of region being mapped
+- *
+- * Platforms that utilize the dmabounce mechanism must implement
+- * this function.
+- *
+- * The dmabounce routines call this function whenever a dma-mapping
+- * is requested to determine whether a given buffer needs to be bounced
+- * or not. The function must return 0 if the buffer is OK for
+- * DMA access and 1 if the buffer needs to be bounced.
+- *
+- */
+-extern int dma_needs_bounce(struct device*, dma_addr_t, size_t);
++typedef int (*dmabounce_check)(struct device *dev, dma_addr_t dma, size_t size, void *data);
++extern int dmabounce_register_checker(dmabounce_check, void *data);
++extern void dmabounce_remove_checker(dmabounce_check, void *data);
++#ifdef CONFIG_PLATFORM_DMABOUNCE
++extern int platform_dma_needs_bounce(struct device *dev, dma_addr_t dma, size_t size, void *data);
++#endif
++
+ #endif /* CONFIG_DMABOUNCE */
+
+ #endif /* __KERNEL__ */
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0003-Core-MFD-support.patch b/recipes/linux/linux-rp-2.6.24/tosa/0003-Core-MFD-support.patch
new file mode 100644
index 0000000000..d84a4f7835
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0003-Core-MFD-support.patch
@@ -0,0 +1,243 @@
+From a07910753f9965842b6647f0561db125b538f5ed Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Fri, 4 Jan 2008 18:32:44 +0000
+Subject: [PATCH 03/64] Core MFD support
+
+This patch provides a common subdevice registration system for MFD type
+chips, using platfrom device.
+
+It also provides a new resource type for IRQs such that a subdevices IRQ may
+be computed based on the MFD cores IRQ handler, since many MFDs provide an IRQ
+multiplex.
+---
+ drivers/mfd/Kconfig | 4 ++
+ drivers/mfd/Makefile | 2 +
+ drivers/mfd/mfd-core.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/ioport.h | 1 +
+ include/linux/mfd-core.h | 51 ++++++++++++++++++++
+ 5 files changed, 174 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/mfd/mfd-core.c
+ create mode 100644 include/linux/mfd-core.h
+
+diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
+index 2571619..1205c89 100644
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -5,6 +5,10 @@
+ menu "Multifunction device drivers"
+ depends on HAS_IOMEM
+
++config MFD_CORE
++ tristate
++ default n
++
+ config MFD_SM501
+ tristate "Support for Silicon Motion SM501"
+ ---help---
+diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
+index 5143209..6c20064 100644
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -4,6 +4,8 @@
+
+ obj-$(CONFIG_MFD_SM501) += sm501.o
+
++obj-$(CONFIG_MFD_CORE) += mfd-core.o
++
+ obj-$(CONFIG_MCP) += mcp-core.o
+ obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
+diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
+new file mode 100644
+index 0000000..88874e1
+--- /dev/null
++++ b/drivers/mfd/mfd-core.c
+@@ -0,0 +1,116 @@
++/*
++ * drivers/mfd/mfd-core.c
++ *
++ * core MFD support
++ * Copyright (c) 2006 Ian Molton
++ * Copyright (c) 2007 Dmitry Baryshkov
++ *
++ * 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/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/mfd-core.h>
++
++#define SIGNED_SHIFT(val, shift) ((shift) >= 0 ? \
++ ((val) << (shift)) : \
++ ((val) >> -(shift)))
++
++int mfd_add_devices(
++ struct platform_device *parent,
++ const struct mfd_cell *cells, int n_devs,
++ struct resource *mem,
++ int relative_addr_shift,
++ int irq_base)
++{
++ int i;
++
++ for (i = 0; i < n_devs; i++) {
++ struct resource *res = NULL;
++ const struct mfd_cell *cell = cells + i;
++ struct platform_device *pdev;
++ int ret = -ENOMEM;
++ int r;
++
++ pdev = platform_device_alloc(cell->name, -1);
++ if (!pdev)
++ goto fail_alloc;
++
++ pdev->dev.uevent_suppress = 0;
++ pdev->dev.parent = &parent->dev;
++
++ ret = platform_device_add_data(pdev, &cell, sizeof(struct mfd_cell *));
++ if (ret)
++ goto fail_device;
++
++ res = kzalloc(cell->num_resources * sizeof(struct resource),
++ GFP_KERNEL);
++ if (!res)
++ goto fail_device;
++
++ for (r = 0; r < cell->num_resources; r++) {
++ res[r].name = cell->resources[r].name;
++
++ /* Find out base to use */
++ if (cell->resources[r].flags & IORESOURCE_MEM) {
++ res[r].parent = mem;
++ res[r].start = mem->start +
++ SIGNED_SHIFT(cell->resources[r].start,
++ relative_addr_shift);
++ res[r].end = mem->start +
++ SIGNED_SHIFT(cell->resources[r].end,
++ relative_addr_shift);
++ } else if ((cell->resources[r].flags & IORESOURCE_IRQ) &&
++ (cell->resources[r].flags & IORESOURCE_IRQ_MFD_SUBDEVICE)) {
++ res[r].start = irq_base +
++ cell->resources[r].start;
++ res[r].end = irq_base +
++ cell->resources[r].end;
++ } else {
++ res[r].start = cell->resources[r].start;
++ res[r].end = cell->resources[r].end;
++ }
++
++ res[r].flags = cell->resources[r].flags;
++ }
++
++ ret = platform_device_add_resources(pdev,
++ res,
++ cell->num_resources);
++ kfree(res);
++
++ if (ret)
++ goto fail_device;
++
++ ret = platform_device_add(pdev);
++
++ if (ret) {
++ platform_device_del(pdev);
++fail_device:
++ platform_device_put(pdev);
++fail_alloc:
++ mfd_remove_devices(parent);
++ return ret;
++ }
++ }
++ return 0;
++}
++EXPORT_SYMBOL(mfd_add_devices);
++
++static int mfd_remove_devices_fn(struct device *dev, void *unused)
++{
++ platform_device_unregister(container_of(dev, struct platform_device, dev));
++ return 0;
++}
++
++void mfd_remove_devices(struct platform_device *parent)
++{
++ device_for_each_child(&parent->dev, NULL, mfd_remove_devices_fn);
++}
++EXPORT_SYMBOL(mfd_remove_devices);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov");
+diff --git a/include/linux/ioport.h b/include/linux/ioport.h
+index 6187a85..0348c71 100644
+--- a/include/linux/ioport.h
++++ b/include/linux/ioport.h
+@@ -56,6 +56,7 @@ struct resource_list {
+ #define IORESOURCE_IRQ_HIGHLEVEL (1<<2)
+ #define IORESOURCE_IRQ_LOWLEVEL (1<<3)
+ #define IORESOURCE_IRQ_SHAREABLE (1<<4)
++#define IORESOURCE_IRQ_MFD_SUBDEVICE (1<<5)
+
+ /* ISA PnP DMA specific bits (IORESOURCE_BITS) */
+ #define IORESOURCE_DMA_TYPE_MASK (3<<0)
+diff --git a/include/linux/mfd-core.h b/include/linux/mfd-core.h
+new file mode 100644
+index 0000000..0e9de78
+--- /dev/null
++++ b/include/linux/mfd-core.h
+@@ -0,0 +1,51 @@
++#ifndef MFD_CORE_H
++#define MFD_CORE_H
++/*
++ * drivers/mfd/mfd-core.h
++ *
++ * core MFD support
++ * Copyright (c) 2006 Ian Molton
++ * Copyright (c) 2007 Dmitry Baryshkov
++ *
++ * 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>
++
++struct mfd_cell {
++ const char *name;
++
++ int (*enable)(struct platform_device *dev);
++ int (*disable)(struct platform_device *dev);
++ int (*suspend)(struct platform_device *dev);
++ int (*resume)(struct platform_device *dev);
++
++ void *driver_data; /* data passed to drivers */
++
++ /*
++ * This resources can be specified relatievly to the parent device.
++ * For accessing device you should use resources from device
++ */
++ int num_resources;
++ const struct resource *resources;
++};
++
++static inline __maybe_unused struct mfd_cell *
++mfd_get_cell(struct platform_device *pdev)
++{
++ return *((struct mfd_cell **)(pdev->dev.platform_data));
++}
++
++extern int mfd_add_devices(
++ struct platform_device *parent,
++ const struct mfd_cell *cells, int n_devs,
++ struct resource *mem,
++ int relative_addr_shift,
++ int irq_base);
++
++extern void mfd_remove_devices(struct platform_device *parent);
++
++#endif
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0004-Add-support-for-tc6393xb-MFD-core.patch b/recipes/linux/linux-rp-2.6.24/tosa/0004-Add-support-for-tc6393xb-MFD-core.patch
new file mode 100644
index 0000000000..a78c0f37f3
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0004-Add-support-for-tc6393xb-MFD-core.patch
@@ -0,0 +1,907 @@
+From 3f56cac281fb407b7d8e574d18ee7d72aa7e7c28 Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Sat, 29 Dec 2007 15:02:30 +0000
+Subject: [PATCH 04/64] Add support for tc6393xb MFD core
+
+---
+ drivers/mfd/Kconfig | 6 +
+ drivers/mfd/Makefile | 2 +
+ drivers/mfd/tc6393xb.c | 740 ++++++++++++++++++++++++++++++++++++++++++
+ include/linux/mfd/tc6393xb.h | 108 ++++++
+ 4 files changed, 856 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/mfd/tc6393xb.c
+ create mode 100644 include/linux/mfd/tc6393xb.h
+
+diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
+index 1205c89..9903d0a 100644
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -9,6 +9,12 @@ config MFD_CORE
+ tristate
+ default n
+
++config MFD_TC6393XB
++ bool "Support Toshiba TC6393XB"
++ select MFD_CORE
++ help
++ Support for Toshiba Mobile IO Controller TC6393XB
++
+ config MFD_SM501
+ tristate "Support for Silicon Motion SM501"
+ ---help---
+diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
+index 6c20064..ffd342e 100644
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -6,6 +6,8 @@ obj-$(CONFIG_MFD_SM501) += sm501.o
+
+ obj-$(CONFIG_MFD_CORE) += mfd-core.o
+
++obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o
++
+ obj-$(CONFIG_MCP) += mcp-core.o
+ obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
+diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
+new file mode 100644
+index 0000000..9439f39
+--- /dev/null
++++ b/drivers/mfd/tc6393xb.c
+@@ -0,0 +1,740 @@
++/*
++ * Toshiba TC6393XB SoC support
++ *
++ * Copyright(c) 2005-2006 Chris Humbert
++ * Copyright(c) 2005 Dirk Opfer
++ * Copyright(c) 2005 Ian Molton <spyro@f2s.com>
++ * Copyright(c) 2007 Dmitry Baryshkov
++ *
++ * 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/kernel.h>
++#include <linux/module.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/platform_device.h>
++#include <linux/fb.h>
++#include <linux/mfd-core.h>
++#include <linux/mfd/tmio.h>
++#include <linux/mfd/tc6393xb.h>
++
++struct tc6393xb_scr {
++ u8 x00[8];
++ u8 revid; /* 0x08 Revision ID */
++ u8 x01[0x47];
++ u8 isr; /* 0x50 Interrupt Status */
++ u8 x02;
++ u8 imr; /* 0x52 Interrupt Mask */
++ u8 x03;
++ u8 irr; /* 0x54 Interrupt Routing */
++ u8 x04[0x0b];
++ u16 gper; /* 0x60 GP Enable */
++ u8 x05[2];
++ u16 gpi_sr[2]; /* 0x64 GPI Status */
++ u16 gpi_imr[2]; /* 0x68 GPI INT Mask */
++ u16 gpi_eder[2]; /* 0x6c GPI Edge Detect Enable */
++ u16 gpi_lir[4]; /* 0x70 GPI Level Invert */
++ u16 gpo_dsr[2]; /* 0x78 GPO Data Set */
++ u16 gpo_doecr[2]; /* 0x7c GPO Data OE Control */
++ u16 gp_iarcr[2]; /* 0x80 GP Internal Active Reg Control */
++ u16 gp_iarlcr[2]; /* 0x84 GP Internal Active Reg Level Con*/
++ u8 gpi_bcr[4]; /* 0x88 GPI Buffer Control */
++ u16 gpa_iarcr; /* 0x8c GPa Internal Active Reg Control */
++ u8 x06[2];
++ u16 gpa_iarlcr; /* 0x90 GPa Internal Active Reg Level Co*/
++ u8 x07[2];
++ u16 gpa_bcr; /* 0x94 GPa Buffer Control */
++ u8 x08[2];
++ u16 ccr; /* 0x98 Clock Control */
++ u16 pll2cr; /* 0x9a PLL2 Control */
++ u16 pll1cr[2]; /* 0x9c PLL1 Control */
++ u8 diarcr; /* 0xa0 Device Internal Active Reg Contr*/
++ u8 dbocr; /* 0xa1 Device Buffer Off Control */
++ u8 x09[0x3e];
++ u8 fer; /* 0xe0 Function Enable */
++ u8 x10[3];
++ u16 mcr; /* 0xe4 Mode Control */
++ u8 x11[0x14];
++ u8 config; /* 0xfc Configuration Control */
++ u8 x12[2];
++ u8 debug; /* 0xff Debug */
++} __attribute__ ((packed));
++
++/*--------------------------------------------------------------------------*/
++
++struct tc6393xb {
++ struct tc6393xb_scr __iomem *scr;
++
++ spinlock_t lock; /* protects RMW cycles */
++
++ struct {
++ union tc6393xb_scr_fer fer;
++ union tc6393xb_scr_ccr ccr;
++ u8 gpi_bcr[4];
++ } suspend_state;
++
++ struct resource rscr;
++ struct resource *iomem;
++ int irq;
++};
++
++/*--------------------------------------------------------------------------*/
++
++static int tc6393xb_mmc_enable(struct platform_device *mmc) {
++ struct platform_device *dev = to_platform_device(mmc->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_ccr ccr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++ ccr.raw = ioread16(&scr->ccr);
++ ccr.bits.ck32ken = 1;
++ iowrite16(ccr.raw, &scr->ccr);
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++static int tc6393xb_mmc_disable(struct platform_device *mmc) {
++ struct platform_device *dev = to_platform_device(mmc->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_ccr ccr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++ ccr.raw = ioread16(&scr->ccr);
++ ccr.bits.ck32ken = 0;
++ iowrite16(ccr.raw, &scr->ccr);
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++/*--------------------------------------------------------------------------*/
++
++static int tc6393xb_nand_disable(struct platform_device *nand)
++{
++ return 0;
++}
++
++static int tc6393xb_nand_enable(struct platform_device *nand)
++{
++ struct platform_device *dev = to_platform_device(nand->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ /* SMD buffer on */
++ dev_dbg(&dev->dev, "SMD buffer on\n");
++ iowrite8(0xff, scr->gpi_bcr + 1);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++int tc6393xb_lcd_set_power(struct platform_device *fb, bool on)
++{
++ struct platform_device *dev = to_platform_device(fb->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_fer fer;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ fer.raw = ioread8(&scr->fer);
++ fer.bits.slcden = on ? 1 : 0;
++ iowrite8(fer.raw, &scr->fer);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++EXPORT_SYMBOL(tc6393xb_lcd_set_power);
++
++int tc6393xb_lcd_mode(struct platform_device *fb_dev,
++ struct fb_videomode *mode) {
++ struct tc6393xb *tc6393xb =
++ platform_get_drvdata(to_platform_device(fb_dev->dev.parent));
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++
++ iowrite16(mode->pixclock, scr->pll1cr + 0);
++ iowrite16(mode->pixclock >> 16, scr->pll1cr + 1);
++
++ return 0;
++}
++EXPORT_SYMBOL(tc6393xb_lcd_mode);
++
++static int tc6393xb_ohci_disable(struct platform_device *ohci)
++{
++ struct platform_device *dev = to_platform_device(ohci->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_ccr ccr;
++ union tc6393xb_scr_fer fer;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ fer.raw = ioread8(&scr->fer);
++ fer.bits.usben = 0;
++ iowrite8(fer.raw, &scr->fer);
++
++ ccr.raw = ioread16(&scr->ccr);
++ ccr.bits.usbcken = 0;
++ iowrite16(ccr.raw, &scr->ccr);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++static int tc6393xb_ohci_enable(struct platform_device *ohci)
++{
++ struct platform_device *dev = to_platform_device(ohci->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_ccr ccr;
++ union tc6393xb_scr_fer fer;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ ccr.raw = ioread16(&scr->ccr);
++ ccr.bits.usbcken = 1;
++ iowrite16(ccr.raw, &scr->ccr);
++
++ fer.raw = ioread8(&scr->fer);
++ fer.bits.usben = 1;
++ iowrite8(fer.raw, &scr->fer);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++static int tc6393xb_fb_disable(struct platform_device *fb)
++{
++ struct platform_device *dev = to_platform_device(fb->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_ccr ccr;
++ union tc6393xb_scr_fer fer;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ /*
++ * FIXME: is this correct or it should be moved to other _disable?
++ */
++ fer.raw = ioread8(&scr->fer);
++ fer.bits.slcden = 0;
++/* fer.bits.lcdcven = 0; */
++ iowrite8(fer.raw, &scr->fer);
++
++ ccr.raw = ioread16(&scr->ccr);
++ ccr.bits.mclksel = disable;
++ iowrite16(ccr.raw, &scr->ccr);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++static int tc6393xb_fb_enable(struct platform_device *fb)
++{
++ struct platform_device *dev = to_platform_device(fb->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_ccr ccr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ ccr.raw = ioread16(&scr->ccr);
++ ccr.bits.mclksel = m48MHz;
++ iowrite16(ccr.raw, &scr->ccr);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++static int tc6393xb_fb_suspend(struct platform_device *fb)
++{
++ struct platform_device *dev = to_platform_device(fb->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_ccr ccr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ ccr.raw = ioread16(&scr->ccr);
++ ccr.bits.mclksel = disable;
++ iowrite16(ccr.raw, &scr->ccr);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++static int tc6393xb_fb_resume(struct platform_device *fb)
++{
++ struct platform_device *dev = to_platform_device(fb->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_ccr ccr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ ccr.raw = ioread16(&scr->ccr);
++ ccr.bits.mclksel = m48MHz;
++ iowrite16(ccr.raw, &scr->ccr);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++static struct resource tc6393xb_mmc_resources[] = {
++ {
++ .name = TMIO_MMC_CONTROL,
++ .start = 0x800,
++ .end = 0x9ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_MMC_CONFIG,
++ .start = 0x200,
++ .end = 0x2ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_MMC_IRQ,
++ .start = IRQ_TC6393_MMC,
++ .end = IRQ_TC6393_MMC,
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
++ },
++};
++
++const static struct resource tc6393xb_nand_resources[] = {
++ {
++ .name = TMIO_NAND_CONFIG,
++ .start = 0x0100,
++ .end = 0x01ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_NAND_CONTROL,
++ .start = 0x1000,
++ .end = 0x1007,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_NAND_IRQ,
++ .start = IRQ_TC6393_NAND,
++ .end = IRQ_TC6393_NAND,
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
++ },
++};
++
++const static struct resource tc6393xb_ohci_resources[] = {
++ {
++ .name = TMIO_OHCI_CONFIG,
++ .start = 0x0300,
++ .end = 0x03ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_OHCI_CONTROL,
++ .start = 0x3000,
++ .end = 0x31ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_OHCI_SRAM,
++ .start = 0x010000,
++ .end = 0x017fff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_OHCI_SRAM_ALIAS,
++ .start = 0x018000,
++ .end = 0x01ffff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_OHCI_IRQ,
++ .start = IRQ_TC6393_OHCI,
++ .end = IRQ_TC6393_OHCI,
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
++ },
++};
++
++const static struct resource tc6393xb_fb_resources[] = {
++ {
++ .name = TMIO_FB_CONFIG,
++ .start = 0x0500,
++ .end = 0x05ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_FB_CONTROL,
++ .start = 0x5000,
++ .end = 0x51ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_FB_VRAM,
++ .start = 0x100000,
++ .end = 0x1fffff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_FB_IRQ,
++ .start = IRQ_TC6393_FB,
++ .end = IRQ_TC6393_FB,
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
++ },
++};
++
++static struct mfd_cell tc6393xb_cells[] = {
++ {
++ .name = "tmio-nand",
++ .enable = tc6393xb_nand_enable,
++ .disable = tc6393xb_nand_disable,
++ .num_resources = ARRAY_SIZE(tc6393xb_nand_resources),
++ .resources = tc6393xb_nand_resources,
++ },
++ {
++ .name = "tmio-ohci",
++ .enable = tc6393xb_ohci_enable,
++ .disable = tc6393xb_ohci_disable,
++ .num_resources = ARRAY_SIZE(tc6393xb_ohci_resources),
++ .resources = tc6393xb_ohci_resources,
++ },
++ {
++ .name = "tmio-fb",
++ .enable = tc6393xb_fb_enable,
++ .disable = tc6393xb_fb_disable,
++ .suspend = tc6393xb_fb_suspend,
++ .resume = tc6393xb_fb_resume,
++ .num_resources = ARRAY_SIZE(tc6393xb_fb_resources),
++ .resources = tc6393xb_fb_resources,
++ },
++ {
++ .name = "tmio-mmc",
++ .enable = tc6393xb_mmc_enable,
++ .disable = tc6393xb_mmc_disable,
++ .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources),
++ .resources = tc6393xb_mmc_resources,
++ },
++};
++
++/*--------------------------------------------------------------------------*/
++
++static void
++tc6393xb_irq(unsigned int irq, struct irq_desc *desc)
++{
++ struct platform_device *dev = get_irq_chip_data(irq);
++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ unsigned int isr;
++ unsigned int i;
++
++ desc->chip->ack(irq);
++
++ while ((isr = ioread8(&scr->isr) & ~ioread8(&scr->imr)))
++ for (i = 0; i < TC6393XB_NR_IRQS; i++) {
++ if (isr & (1 << i))
++ desc_handle_irq(tcpd->irq_base + i,
++ irq_desc + tcpd->irq_base + i);
++ }
++}
++
++static void tc6393xb_irq_ack(unsigned int irq)
++{
++}
++
++static void tc6393xb_irq_mask(unsigned int irq)
++{
++ struct platform_device *dev = get_irq_chip_data(irq);
++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++ iowrite8(ioread8(&scr->imr) | (1 << (irq - tcpd->irq_base)),
++ &scr->imr);
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++}
++
++static void tc6393xb_irq_unmask(unsigned int irq)
++{
++ struct platform_device *dev = get_irq_chip_data(irq);
++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++ iowrite8(ioread8(&scr->imr) & ~(1 << (irq - tcpd->irq_base)),
++ &scr->imr);
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++}
++
++static struct irq_chip tc6393xb_chip = {
++ .name = "tc6393xb",
++ .ack = tc6393xb_irq_ack,
++ .mask = tc6393xb_irq_mask,
++ .unmask = tc6393xb_irq_unmask,
++};
++
++static void tc6393xb_attach_irq(struct platform_device *dev)
++{
++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ unsigned int irq;
++
++ for (
++ irq = tcpd->irq_base;
++ irq <= tcpd->irq_base + TC6393XB_NR_IRQS;
++ irq++) {
++ set_irq_chip(irq, &tc6393xb_chip);
++ set_irq_chip_data(irq, dev);
++ set_irq_handler(irq, handle_edge_irq);
++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
++ }
++
++ set_irq_type(tc6393xb->irq, IRQT_FALLING);
++ set_irq_chip_data(tc6393xb->irq, dev);
++ set_irq_chained_handler(tc6393xb->irq, tc6393xb_irq);
++}
++
++static void tc6393xb_detach_irq(struct platform_device *dev)
++{
++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ unsigned int irq;
++
++ set_irq_chained_handler(tc6393xb->irq, NULL);
++ set_irq_chip_data(tc6393xb->irq, NULL);
++
++ for (
++ irq = tcpd->irq_base;
++ irq <= tcpd->irq_base + TC6393XB_NR_IRQS;
++ irq++) {
++ set_irq_flags(irq, 0);
++ set_irq_chip(irq, NULL);
++ set_irq_chip_data(irq, NULL);
++ }
++}
++
++static int tc6393xb_hw_init(struct platform_device *dev, int resume)
++{
++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ int ret;
++ int i;
++
++ if (resume)
++ ret = tcpd->resume(dev);
++ else
++ ret = tcpd->enable(dev);
++ if (ret)
++ return ret;
++
++ iowrite8(resume ?
++ tc6393xb->suspend_state.fer.raw :
++ 0, &scr->fer);
++ iowrite16(tcpd->scr_pll2cr, &scr->pll2cr);
++ iowrite16(resume?
++ tc6393xb->suspend_state.ccr.raw :
++ tcpd->scr_ccr.raw, &scr->ccr);
++ iowrite16(tcpd->scr_mcr.raw, &scr->mcr);
++ iowrite16(tcpd->scr_gper, &scr->gper);
++ iowrite8(0, &scr->irr);
++ iowrite8(0xbf, &scr->imr);
++ iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr + 0);
++ iowrite16(tcpd->scr_gpo_dsr >> 16, scr->gpo_dsr + 1);
++ iowrite16(tcpd->scr_gpo_doecr, scr->gpo_doecr + 0);
++ iowrite16(tcpd->scr_gpo_doecr >> 16, scr->gpo_doecr + 1);
++
++ if (resume)
++ for (i = 0; i < 4; i++)
++ iowrite8(tc6393xb->suspend_state.gpi_bcr[i],
++ scr->gpi_bcr + i);
++
++ return 0;
++}
++
++static int __devinit tc6393xb_probe(struct platform_device *dev)
++{
++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
++ struct tc6393xb *tc6393xb;
++ struct resource *iomem;
++ struct resource *rscr;
++ int retval;
++
++ iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
++ if (!iomem)
++ return -EINVAL;
++
++ tc6393xb = kzalloc(sizeof *tc6393xb, GFP_KERNEL);
++ if (!tc6393xb) {
++ retval = -ENOMEM;
++ goto err_kzalloc;
++ }
++
++ spin_lock_init(&tc6393xb->lock);
++
++ platform_set_drvdata(dev, tc6393xb);
++ tc6393xb->iomem = iomem;
++ tc6393xb->irq = platform_get_irq(dev, 0);
++
++ rscr = &tc6393xb->rscr;
++ rscr->name = "tc6393xb-core";
++ rscr->start = iomem->start;
++ rscr->end = iomem->start + 0xff;
++ rscr->flags = IORESOURCE_MEM;
++
++ retval = request_resource(iomem, rscr);
++ if (retval)
++ goto err_request_scr;
++
++ tc6393xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1);
++ if (!tc6393xb->scr) {
++ retval = -ENOMEM;
++ goto err_ioremap;
++ }
++
++ retval = tc6393xb_hw_init(dev, 0);
++ if (retval)
++ goto err_hw_init;
++
++ printk(KERN_INFO "Toshiba tc6393xb revision %d at 0x%08lx, irq %d\n",
++ ioread8(&tc6393xb->scr->revid),
++ (unsigned long) iomem->start, tc6393xb->irq);
++
++ if (tc6393xb->irq)
++ tc6393xb_attach_irq(dev);
++
++ tc6393xb_cells[0].driver_data = tcpd->nand_data;
++ tc6393xb_cells[1].driver_data = NULL; /* tcpd->ohci_data; */
++ tc6393xb_cells[2].driver_data = tcpd->fb_data;
++
++ retval = mfd_add_devices(dev,
++ tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells),
++ iomem, 0, tcpd->irq_base);
++
++ if (retval == 0)
++ return 0;
++
++ if (tc6393xb->irq)
++ tc6393xb_detach_irq(dev);
++
++err_hw_init:
++ iounmap(tc6393xb->scr);
++err_ioremap:
++ release_resource(rscr);
++err_request_scr:
++ kfree(tc6393xb);
++err_kzalloc:
++ release_resource(iomem);
++ return retval;
++}
++
++static int __devexit tc6393xb_remove(struct platform_device *dev) {
++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ int ret;
++
++ if (tc6393xb->irq)
++ tc6393xb_detach_irq(dev);
++
++ ret = tcpd->disable(dev);
++
++ iounmap(tc6393xb->scr);
++ release_resource(&tc6393xb->rscr);
++ release_resource(tc6393xb->iomem);
++
++ mfd_remove_devices(dev);
++
++ platform_set_drvdata(dev, NULL);
++
++ kfree(tc6393xb);
++
++ return ret;
++}
++
++#ifdef CONFIG_PM
++static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ int i;
++
++
++ tc6393xb->suspend_state.ccr.raw = ioread16(&scr->ccr);
++ tc6393xb->suspend_state.fer.raw = ioread8(&scr->fer);
++ for (i = 0; i < 4; i++)
++ tc6393xb->suspend_state.gpi_bcr[i] =
++ ioread8(scr->gpi_bcr + i);
++
++ return tcpd->suspend(dev);
++}
++
++static int tc6393xb_resume(struct platform_device *dev)
++{
++ return tc6393xb_hw_init(dev, 1);
++}
++#else
++#define tc6393xb_suspend NULL
++#define tc6393xb_resume NULL
++#endif
++
++static struct platform_driver tc6393xb_driver = {
++ .probe = tc6393xb_probe,
++ .remove = __devexit_p(tc6393xb_remove),
++ .suspend = tc6393xb_suspend,
++ .resume = tc6393xb_resume,
++
++ .driver = {
++ .name = "tc6393xb",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init tc6393xb_init(void)
++{
++ return platform_driver_register(&tc6393xb_driver);
++}
++
++static void __exit tc6393xb_exit(void)
++{
++ platform_driver_unregister(&tc6393xb_driver);
++}
++
++module_init(tc6393xb_init);
++module_exit(tc6393xb_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov and Dirk Opfer");
++MODULE_DESCRIPTION("tc6393xb Toshiba Mobile IO Controller");
+diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h
+new file mode 100644
+index 0000000..e699294
+--- /dev/null
++++ b/include/linux/mfd/tc6393xb.h
+@@ -0,0 +1,108 @@
++/*
++ * Toshiba TC6393XB SoC support
++ *
++ * Copyright(c) 2005-2006 Chris Humbert
++ * Copyright(c) 2005 Dirk Opfer
++ * Copyright(c) 2005 Ian Molton <spyro@f2s.com>
++ * Copyright(c) 2007 Dmitry Baryshkov
++ *
++ * 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.
++ */
++
++#ifndef TC6393XB_H
++#define TC6393XB_H
++
++#include <linux/mfd-core.h>
++#include <linux/mfd/tmio.h>
++
++union tc6393xb_scr_fer {
++ u8 raw;
++struct {
++ unsigned usben:1; /* D0 USB enable */
++ unsigned lcdcven:1; /* D1 polysylicon TFT enable */
++ unsigned slcden:1; /* D2 SLCD enable */
++} __attribute__ ((packed)) bits;
++} __attribute__ ((packed));
++
++union tc6393xb_scr_ccr {
++ u16 raw;
++struct {
++ unsigned ck32ken:1; /* D0 SD host clock enable */
++ unsigned usbcken:1; /* D1 USB host clock enable */
++ unsigned x00:2;
++ unsigned sharp:1; /* D4 ??? set in Sharp's code */
++ unsigned x01:3;
++ enum { disable = 0,
++ m12MHz = 1,
++ m24MHz = 2,
++ m48MHz = 3,
++ } mclksel:3; /* D10-D8 LCD controller clock */
++ unsigned x02:1;
++ enum { h24MHz = 0,
++ h48MHz = 1,
++ } hclksel:2; /* D13-D12 host bus clock */
++ unsigned x03:2;
++} __attribute__ ((packed)) bits;
++} __attribute__ ((packed));
++
++enum pincontrol {
++ opendrain = 0,
++ tristate = 1,
++ pushpull = 2,
++ /* reserved = 3, */
++};
++
++union tc6393xb_scr_mcr {
++ u16 raw;
++struct {
++ enum pincontrol rdyst:2; /* D1-D0 HRDY control */
++ unsigned x00:1;
++ unsigned aren:1; /* D3 HRDY pull up resistance cut off */
++ enum pincontrol intst:2; /* D5-D4 #HINT control */
++ unsigned x01:1;
++ unsigned aien:1; /* D7 #HINT pull up resitance cut off */
++ unsigned x02:8;
++} __attribute__ ((packed)) bits;
++} __attribute__ ((packed));
++
++struct tc6393xb_platform_data {
++ u16 scr_pll2cr; /* PLL2 Control */
++ union tc6393xb_scr_ccr scr_ccr; /* Clock Control */
++ union tc6393xb_scr_mcr scr_mcr; /* Mode Control */
++ u16 scr_gper; /* GP Enable */
++ u32 scr_gpo_doecr; /* GPO Data OE Control */
++ u32 scr_gpo_dsr; /* GPO Data Set */
++
++ int (*enable)(struct platform_device *dev);
++ int (*disable)(struct platform_device *dev);
++ int (*suspend)(struct platform_device *dev);
++ int (*resume)(struct platform_device *dev);
++
++ int irq_base; /* a base for cascaded irq */
++
++ struct tmio_nand_data *nand_data;
++ struct tmio_fb_data *fb_data;
++};
++
++extern int tc6393xb_lcd_set_power(struct platform_device *fb_dev, bool on);
++extern int tc6393xb_lcd_mode(struct platform_device *fb_dev,
++ struct fb_videomode *mode);
++
++
++/*
++ * Relative to irq_base
++ */
++#define IRQ_TC6393_NAND 0
++#define IRQ_TC6393_MMC 1
++#define IRQ_TC6393_OHCI 2
++#define IRQ_TC6393_SERIAL 3
++#define IRQ_TC6393_FB 4
++
++#define TC6393XB_NR_IRQS 8
++
++#endif
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0005-Add-support-for-tc6387xb-MFD-core.patch b/recipes/linux/linux-rp-2.6.24/tosa/0005-Add-support-for-tc6387xb-MFD-core.patch
new file mode 100644
index 0000000000..7183e3af6d
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0005-Add-support-for-tc6387xb-MFD-core.patch
@@ -0,0 +1,249 @@
+From a6a6faf1dbb90c950fe55a1719720457bfb5830a Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Sun, 16 Dec 2007 02:19:49 +0000
+Subject: [PATCH 05/64] Add support for tc6387xb MFD core
+
+---
+ drivers/mfd/Kconfig | 6 ++
+ drivers/mfd/Makefile | 1 +
+ drivers/mfd/tc6387xb.c | 163 ++++++++++++++++++++++++++++++++++++++++++
+ include/linux/mfd/tc6387xb.h | 28 +++++++
+ 4 files changed, 198 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/mfd/tc6387xb.c
+ create mode 100644 include/linux/mfd/tc6387xb.h
+
+diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
+index 9903d0a..1575323 100644
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -9,6 +9,12 @@ config MFD_CORE
+ tristate
+ default n
+
++config MFD_TC6387XB
++ bool "Support Toshiba TC6387XB"
++ select MFD_CORE
++ help
++ Support for Toshiba Mobile IO Controller TC6387XB
++
+ config MFD_TC6393XB
+ bool "Support Toshiba TC6393XB"
+ select MFD_CORE
+diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
+index ffd342e..41b2190 100644
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -6,6 +6,7 @@ obj-$(CONFIG_MFD_SM501) += sm501.o
+
+ obj-$(CONFIG_MFD_CORE) += mfd-core.o
+
++obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o
+ obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o
+
+ obj-$(CONFIG_MCP) += mcp-core.o
+diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c
+new file mode 100644
+index 0000000..c81fca2
+--- /dev/null
++++ b/drivers/mfd/tc6387xb.c
+@@ -0,0 +1,163 @@
++/*
++ * 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.
++ *
++ * This file contains TC6387XB base support.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++
++#include <linux/mfd-core.h>
++#include <linux/mfd/tc6387xb.h>
++
++#ifdef CONFIG_PM
++static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct tc6387xb_platform_data *pdata = platform_get_drvdata(dev);
++
++ if (pdata && pdata->suspend)
++ pdata->suspend(dev);
++
++ return 0;
++}
++
++static int tc6387xb_resume(struct platform_device *dev)
++{
++ struct tc6387xb_platform_data *pdata = platform_get_drvdata(dev);
++
++ if (pdata && pdata->resume)
++ pdata->resume(dev);
++
++ return 0;
++}
++#else
++#define tc6387xb_suspend NULL
++#define tc6387xb_resume NULL
++#endif
++
++/*--------------------------------------------------------------------------*/
++
++static int tc6387xb_mmc_enable(struct platform_device *mmc) {
++ struct platform_device *dev = to_platform_device(mmc->dev.parent);
++ struct tc6387xb_platform_data *tc6387xb = dev->dev.platform_data;
++
++ if(tc6387xb->enable_mmc_clock)
++ tc6387xb->enable_mmc_clock(dev);
++
++ return 0;
++}
++
++static int tc6387xb_mmc_disable(struct platform_device *mmc) {
++ struct platform_device *dev = to_platform_device(mmc->dev.parent);
++ struct tc6387xb_platform_data *tc6387xb = dev->dev.platform_data;
++
++ if(tc6387xb->disable_mmc_clock)
++ tc6387xb->disable_mmc_clock(dev);
++
++ return 0;
++}
++
++
++/*--------------------------------------------------------------------------*/
++
++static struct resource tc6387xb_mmc_resources[] = {
++ {
++ .name = TMIO_MMC_CONTROL,
++ .start = 0x800,
++ .end = 0x9ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_MMC_CONFIG,
++ .start = 0x200,
++ .end = 0x2ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_MMC_IRQ,
++ .start = 0,
++ .end = 0,
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
++ },
++};
++
++static struct mfd_cell tc6387xb_cells[] = {
++ {
++ .name = "tmio-mmc",
++ .enable = tc6387xb_mmc_enable,
++ .disable = tc6387xb_mmc_disable,
++ .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources),
++ .resources = tc6387xb_mmc_resources,
++ },
++};
++
++static int tc6387xb_probe(struct platform_device *dev)
++{
++ struct tc6387xb_platform_data *data = platform_get_drvdata(dev);
++ struct resource *iomem;
++ int irq;
++
++ iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
++ if (!iomem)
++ return -EINVAL;
++
++ irq = platform_get_irq(dev, 0);
++
++ if(data && data->enable)
++ data->enable(dev);
++
++ printk(KERN_INFO "Toshiba tc6393xb initialised\n");
++
++ return mfd_add_devices(dev, tc6387xb_cells, ARRAY_SIZE(tc6387xb_cells),
++ iomem, 0, irq);
++}
++
++static int tc6387xb_remove(struct platform_device *dev)
++{
++ struct tc6387xb_platform_data *data = platform_get_drvdata(dev);
++
++ if(data && data->disable)
++ data->disable(dev);
++
++ return 0;
++}
++
++
++static struct platform_driver tc6387xb_platform_driver = {
++ .driver = {
++ .name = "tc6387xb",
++ },
++ .probe = tc6387xb_probe,
++ .remove = tc6387xb_remove,
++ .suspend = tc6387xb_suspend,
++ .resume = tc6387xb_resume,
++};
++
++
++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("GPLv2");
++MODULE_AUTHOR("Ian Molton");
+diff --git a/include/linux/mfd/tc6387xb.h b/include/linux/mfd/tc6387xb.h
+new file mode 100644
+index 0000000..496770b
+--- /dev/null
++++ b/include/linux/mfd/tc6387xb.h
+@@ -0,0 +1,28 @@
++/*
++ * linux/include/asm-arm/hardware/tc6387xb.h
++ *
++ * This file contains the definitions for the TC6393XB
++ *
++ * (C) Copyright 2005 Ian Molton <spyro@f2s.com>
++ *
++ * May be copied or modified under the terms of the GNU General Public
++ * License. See linux/COPYING for more information.
++ *
++ */
++#ifndef MFD_T7L66XB_H
++#define MFD_T7L66XB_H
++
++#include <linux/mfd-core.h>
++#include <linux/mfd/tmio.h>
++
++struct tc6387xb_platform_data
++{
++ int (*enable_mmc_clock)(struct platform_device *dev);
++ int (*disable_mmc_clock)(struct platform_device *dev);
++ int (*enable)(struct platform_device *dev);
++ int (*disable)(struct platform_device *dev);
++ int (*suspend)(struct platform_device *dev);
++ int (*resume)(struct platform_device *dev);
++};
++
++#endif
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0006-Add-support-for-t7l66xb-MFD-core.patch b/recipes/linux/linux-rp-2.6.24/tosa/0006-Add-support-for-t7l66xb-MFD-core.patch
new file mode 100644
index 0000000000..e7aff2455b
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0006-Add-support-for-t7l66xb-MFD-core.patch
@@ -0,0 +1,653 @@
+From 2e31fea352ca97988452f1f2c94809de2977ce40 Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Sat, 29 Dec 2007 15:08:52 +0000
+Subject: [PATCH 06/64] Add support for t7l66xb MFD core
+
+---
+ drivers/mfd/Kconfig | 6 +
+ drivers/mfd/Makefile | 1 +
+ drivers/mfd/t7l66xb.c | 550 +++++++++++++++++++++++++++++++++++++++++++
+ include/linux/mfd/t7l66xb.h | 45 ++++
+ 4 files changed, 602 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/mfd/t7l66xb.c
+ create mode 100644 include/linux/mfd/t7l66xb.h
+
+diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
+index 1575323..f79a969 100644
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -9,6 +9,12 @@ config MFD_CORE
+ tristate
+ default n
+
++config MFD_T7L66XB
++ bool "Support Toshiba T7L66XB"
++ select MFD_CORE
++ help
++ Support for Toshiba Mobile IO Controller T7L66XB
++
+ config MFD_TC6387XB
+ bool "Support Toshiba TC6387XB"
+ select MFD_CORE
+diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
+index 41b2190..b2037ae 100644
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -6,6 +6,7 @@ obj-$(CONFIG_MFD_SM501) += sm501.o
+
+ obj-$(CONFIG_MFD_CORE) += mfd-core.o
+
++obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o
+ obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o
+ obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o
+
+diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c
+new file mode 100644
+index 0000000..308776a
+--- /dev/null
++++ b/drivers/mfd/t7l66xb.c
+@@ -0,0 +1,550 @@
++/*
++ *
++ * Toshiba T7L66XB core mfd support
++ *
++ * Copyright (c) 2005 Ian Molton
++ * Copyright (c) 2007 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.
++ *
++ * T7L66 features:
++ *
++ * Supported in this driver:
++ * SD/MMC
++ * SM/NAND flash controller
++ * OHCI controller
++ *
++ * As yet not supported
++ * GPIO interface (on NAND pins)
++ * Serial interface
++ * TFT 'interface converter'
++ * PCMCIA interface logic
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/platform_device.h>
++#include <linux/fb.h>
++#include <linux/mfd-core.h>
++#include <linux/mfd/tmio.h>
++#include <linux/mfd/t7l66xb.h>
++
++union t7l66xb_dev_ctl {
++ u8 raw;
++struct {
++ unsigned usb_en:1; /* D0 USB enable */
++ unsigned mmc_en:1; /* D1 MMC enable */
++} __attribute__ ((packed));
++} __attribute__ ((packed));
++
++
++struct t7l66xb_scr {
++ u8 x00[8];
++ u8 revid; /* 0x08 Revision ID */
++ u8 x01[57];
++ u8 imr; /* 0x42 Interrupt Mask */
++ u8 x03[157];
++ union t7l66xb_dev_ctl dev_ctl; /* 0xe0 Device control */
++ u8 isr; /* 0xe1 Interrupt Status */
++ u8 x04[14];
++ u8 gpio_output_ctl; /* 0xf0 */
++ u8 gpio_output_status; /* 0xf1 */
++ u16 gpio_input_status; /* 0xf2 */
++ u8 x05[4];
++ u8 active_pullup_down_ctl; /* 0xf8 */
++ u8 x06[7];
++} __attribute__ ((packed));
++
++
++/*--------------------------------------------------------------------------*/
++
++struct t7l66xb
++{
++ struct t7l66xb_scr __iomem *scr;
++ spinlock_t lock;
++
++ struct resource rscr;
++ struct resource *iomem;
++ int irq;
++};
++
++/*--------------------------------------------------------------------------*/
++
++static int t7l66xb_ohci_enable(struct platform_device *ohci)
++{
++ struct platform_device *dev = to_platform_device(ohci->dev.parent);
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
++ unsigned long flags;
++ union t7l66xb_dev_ctl dev_ctl;
++
++ spin_lock_irqsave(&t7l66xb->lock, flags);
++
++ dev_ctl.raw = readb(&scr->dev_ctl);
++ dev_ctl.usb_en = 1;
++ writeb(dev_ctl.raw, &scr->dev_ctl);
++
++ spin_unlock_irqrestore(&t7l66xb->lock, flags);
++
++ return 0;
++}
++
++static int t7l66xb_ohci_disable(struct platform_device *ohci)
++{
++ struct platform_device *dev = to_platform_device(ohci->dev.parent);
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
++ unsigned long flags;
++ union t7l66xb_dev_ctl dev_ctl;
++
++ spin_lock_irqsave(&t7l66xb->lock, flags);
++
++ dev_ctl.raw = readb(&scr->dev_ctl);
++ dev_ctl.usb_en = 0;
++ writeb(dev_ctl.raw, &scr->dev_ctl);
++
++ spin_unlock_irqrestore(&t7l66xb->lock, flags);
++
++ return 0;
++}
++
++/*--------------------------------------------------------------------------*/
++
++static int t7l66xb_mmc_enable(struct platform_device *ohci)
++{
++ struct platform_device *dev = to_platform_device(ohci->dev.parent);
++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
++ unsigned long flags;
++ union t7l66xb_dev_ctl dev_ctl;
++
++ spin_lock_irqsave(&t7l66xb->lock, flags);
++
++ if(pdata->enable_clk32k)
++ pdata->enable_clk32k(dev);
++ dev_ctl.raw = readb(&scr->dev_ctl);
++ dev_ctl.mmc_en = 1;
++ writeb(dev_ctl.raw, &scr->dev_ctl);
++
++ spin_unlock_irqrestore(&t7l66xb->lock, flags);
++
++ return 0;
++}
++
++static int t7l66xb_mmc_disable(struct platform_device *ohci)
++{
++ struct platform_device *dev = to_platform_device(ohci->dev.parent);
++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
++ unsigned long flags;
++ union t7l66xb_dev_ctl dev_ctl;
++
++ spin_lock_irqsave(&t7l66xb->lock, flags);
++
++ dev_ctl.raw = readb(&scr->dev_ctl);
++ dev_ctl.mmc_en = 0;
++ writeb(dev_ctl.raw, &scr->dev_ctl);
++ if(pdata->disable_clk32k)
++ pdata->disable_clk32k(dev);
++
++ spin_unlock_irqrestore(&t7l66xb->lock, flags);
++
++ return 0;
++}
++
++/*--------------------------------------------------------------------------*/
++
++static int t7l66xb_nand_disable(struct platform_device *nand)
++{
++ struct platform_device *dev = to_platform_device(nand->dev.parent);
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
++ unsigned long flags;
++ union t7l66xb_dev_ctl dev_ctl;
++
++ spin_lock_irqsave(&t7l66xb->lock, flags);
++
++ dev_ctl.raw = readb(&scr->dev_ctl);
++// dev_ctl.nand_en = 0;
++ writeb(dev_ctl.raw, &scr->dev_ctl);
++
++ spin_unlock_irqrestore(&t7l66xb->lock, flags);
++
++ return 0;
++}
++
++static int t7l66xb_nand_enable(struct platform_device *nand)
++{
++ struct platform_device *dev = to_platform_device(nand->dev.parent);
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
++ unsigned long flags;
++ union t7l66xb_dev_ctl dev_ctl;
++
++ spin_lock_irqsave(&t7l66xb->lock, flags);
++
++ dev_ctl.raw = readb(&scr->dev_ctl);
++ // dev_ctl.nand_en = 1;
++ writeb(dev_ctl.raw, &scr->dev_ctl);
++
++ spin_unlock_irqrestore(&t7l66xb->lock, flags);
++
++ return 0;
++}
++
++/*--------------------------------------------------------------------------*/
++
++const static struct resource t7l66xb_mmc_resources[] = {
++ {
++ .name = TMIO_MMC_CONTROL,
++ .start = 0x800,
++ .end = 0x9ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_MMC_CONFIG,
++ .start = 0x200,
++ .end = 0x2ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_MMC_IRQ,
++ .start = IRQ_T7L66XB_MMC,
++ .end = IRQ_T7L66XB_MMC,
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
++ },
++};
++
++const static struct resource t7l66xb_ohci_resources[] = {
++ {
++ .name = TMIO_OHCI_CONFIG,
++ .start = 0x0300,
++ .end = 0x03ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_OHCI_CONTROL,
++ .start = 0xa00,
++ .end = 0xbff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_OHCI_SRAM,
++ .start = 0x01000,
++ .end = 0x02fff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_OHCI_IRQ,
++ .start = IRQ_T7L66XB_OHCI,
++ .end = IRQ_T7L66XB_OHCI,
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
++ },
++};
++
++const static struct resource t7l66xb_nand_resources[] = {
++ {
++ .name = TMIO_NAND_CONFIG,
++ .start = 0x0100,
++ .end = 0x01ff,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_NAND_CONTROL,
++ .start = 0xc00,
++ .end = 0xc07,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .name = TMIO_NAND_IRQ,
++ .start = IRQ_T7L66XB_NAND,
++ .end = IRQ_T7L66XB_NAND,
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
++ },
++};
++
++static struct mfd_cell t7l66xb_cells[] = {
++ {
++ .name = "tmio-mmc",
++ .enable = t7l66xb_mmc_enable,
++ .disable = t7l66xb_mmc_disable,
++ .num_resources = ARRAY_SIZE(t7l66xb_mmc_resources),
++ .resources = t7l66xb_mmc_resources,
++ },
++ {
++ .name = "tmio-ohci",
++ .enable = t7l66xb_ohci_enable,
++ .disable = t7l66xb_ohci_disable,
++ .num_resources = ARRAY_SIZE(t7l66xb_ohci_resources),
++ .resources = t7l66xb_ohci_resources,
++ },
++ {
++ .name = "tmio-nand",
++ .enable = t7l66xb_nand_enable,
++ .disable = t7l66xb_nand_disable,
++ .num_resources = ARRAY_SIZE(t7l66xb_nand_resources),
++ .resources = t7l66xb_nand_resources,
++ },
++};
++
++/*--------------------------------------------------------------------------*/
++
++/* Handle the T7L66XB interrupt mux */
++static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc)
++{
++ struct platform_device *dev = get_irq_chip_data(irq);
++ struct t7l66xb_platform_data *tcpd = dev->dev.platform_data;
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
++ unsigned int isr;
++ unsigned int i;
++
++ desc->chip->ack(irq);
++ while ((isr = readb(&scr->isr) & ~readb(&scr->imr)))
++ for (i = 0; i < T7L66XB_NR_IRQS; i++)
++ if (isr & (1 << i))
++ desc_handle_irq(tcpd->irq_base + i,
++ irq_desc + tcpd->irq_base + i);
++}
++
++static void t7l66xb_irq_mask(unsigned int irq)
++{
++ struct platform_device *dev = get_irq_chip_data(irq);
++ struct t7l66xb_platform_data *tcpd = dev->dev.platform_data;
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&t7l66xb->lock, flags);
++ iowrite8(ioread8(&scr->imr) | (1 << (irq - tcpd->irq_base)),
++ &scr->imr);
++ spin_unlock_irqrestore(&t7l66xb->lock, flags);
++}
++
++static void t7l66xb_irq_unmask(unsigned int irq)
++{
++ struct platform_device *dev = get_irq_chip_data(irq);
++ struct t7l66xb_platform_data *tcpd = dev->dev.platform_data;
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&t7l66xb->lock, flags);
++ iowrite8(ioread8(&scr->imr) & ~(1 << (irq - tcpd->irq_base)),
++ &scr->imr);
++ spin_unlock_irqrestore(&t7l66xb->lock, flags);
++}
++
++static struct irq_chip t7l66xb_chip = {
++ .name = "t7l66xb",
++ .ack = t7l66xb_irq_mask,
++ .mask = t7l66xb_irq_mask,
++ .unmask = t7l66xb_irq_unmask,
++};
++
++/*--------------------------------------------------------------------------*/
++
++/* Install the IRQ handler */
++static void t7l66xb_attach_irq(struct platform_device *dev)
++{
++ struct t7l66xb_platform_data *tcpd = dev->dev.platform_data;
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ unsigned int irq;
++
++ for (
++ irq = tcpd->irq_base;
++ irq <= tcpd->irq_base + T7L66XB_NR_IRQS;
++ irq++) {
++ set_irq_chip (irq, &t7l66xb_chip);
++ set_irq_chip_data (irq, dev);
++ set_irq_handler(irq, handle_level_irq);
++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
++ }
++
++ set_irq_type (t7l66xb->irq, IRQT_FALLING);
++ set_irq_chip_data (t7l66xb->irq, dev);
++ set_irq_chained_handler (t7l66xb->irq, t7l66xb_irq);
++}
++
++static void t7l66xb_detach_irq(struct platform_device *dev)
++{
++ struct t7l66xb_platform_data *tcpd = dev->dev.platform_data;
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ unsigned int irq;
++
++ set_irq_chained_handler(t7l66xb->irq, NULL);
++ set_irq_chip_data(t7l66xb->irq, NULL);
++
++ for (
++ irq = tcpd->irq_base;
++ irq <= tcpd->irq_base + T7L66XB_NR_IRQS;
++ irq++) {
++ set_irq_flags(irq, 0);
++ set_irq_chip(irq, NULL);
++ set_irq_chip_data(irq, NULL);
++ }
++}
++
++/*--------------------------------------------------------------------------*/
++
++#ifdef CONFIG_PM
++static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
++
++
++ if (pdata && pdata->suspend)
++ pdata->suspend(dev);
++
++ return 0;
++}
++
++static int t7l66xb_resume(struct platform_device *dev)
++{
++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
++
++ if (pdata && pdata->resume)
++ pdata->resume(dev);
++
++ return 0;
++}
++#else
++#define t7l66xb_suspend NULL
++#define t7l66xb_resume NULL
++#endif
++
++/*--------------------------------------------------------------------------*/
++
++static int t7l66xb_probe(struct platform_device *dev)
++{
++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
++ struct t7l66xb *t7l66xb;
++ struct resource *iomem;
++ struct resource *rscr;
++ int retval = -ENOMEM;
++
++ iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
++ if (!iomem)
++ return -EINVAL;
++
++ t7l66xb = kzalloc (sizeof *t7l66xb, GFP_KERNEL);
++ if (!t7l66xb)
++ goto err_kzalloc;
++
++ spin_lock_init(&t7l66xb->lock);
++
++ platform_set_drvdata(dev, t7l66xb);
++ t7l66xb->iomem = iomem;
++ t7l66xb->irq = platform_get_irq(dev, 0);
++
++ rscr = &t7l66xb->rscr;
++ rscr->name = "t7l66xb-core";
++ rscr->start = iomem->start;
++ rscr->end = iomem->start + 0xff;
++ rscr->flags = IORESOURCE_MEM;
++
++ if((retval = request_resource(iomem, rscr)))
++ goto err_request_scr;
++
++ t7l66xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1);
++ if (!t7l66xb->scr) {
++ retval = -ENOMEM;
++ goto err_ioremap;
++ }
++
++ if (pdata && pdata->enable)
++ pdata->enable(dev);
++
++ writeb(0xbf, &t7l66xb->scr->imr); /* Mask all interrupts */
++
++ printk(KERN_INFO "%s rev %d @ 0x%08lx, irq %d\n",
++ dev->name, readb(&t7l66xb->scr->revid),
++ (unsigned long)t7l66xb->scr, t7l66xb->irq);
++
++ if(t7l66xb->irq)
++ t7l66xb_attach_irq(dev);
++
++ t7l66xb_cells[2].driver_data = pdata->nand_data;
++
++ if(!(retval = mfd_add_devices(dev, t7l66xb_cells,
++ ARRAY_SIZE(t7l66xb_cells),
++ iomem, 0, pdata->irq_base)))
++ return 0;
++
++ if(t7l66xb->irq)
++ t7l66xb_detach_irq(dev);
++
++ iounmap(t7l66xb->scr);
++err_ioremap:
++ release_resource(rscr);
++err_request_scr:
++ kfree(t7l66xb);
++err_kzalloc:
++ release_resource(iomem);
++ return retval;
++}
++
++static int t7l66xb_remove(struct platform_device *dev)
++{
++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
++ int ret;
++
++ if (t7l66xb->irq)
++ t7l66xb_detach_irq(dev);
++
++ ret = pdata->disable(dev);
++
++ iounmap(t7l66xb->scr);
++ release_resource(&t7l66xb->rscr);
++ release_resource(t7l66xb->iomem);
++
++ mfd_remove_devices(dev);
++
++ platform_set_drvdata(dev, NULL);
++
++ kfree(t7l66xb);
++
++ return ret;
++
++}
++
++static struct platform_driver t7l66xb_platform_driver = {
++ .driver = {
++ .name = "t7l66xb",
++ .owner = THIS_MODULE,
++ },
++ .suspend = t7l66xb_suspend,
++ .resume = t7l66xb_resume,
++ .probe = t7l66xb_probe,
++ .remove = t7l66xb_remove,
++};
++
++/*--------------------------------------------------------------------------*/
++
++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("GPLv2");
++MODULE_AUTHOR("Ian Molton");
++
+diff --git a/include/linux/mfd/t7l66xb.h b/include/linux/mfd/t7l66xb.h
+new file mode 100644
+index 0000000..06b8de5
+--- /dev/null
++++ b/include/linux/mfd/t7l66xb.h
+@@ -0,0 +1,45 @@
++/*
++ * linux/include/asm-arm/hardware/t7l66xb.h
++ *
++ * This file contains the definitions for the T7L66XB
++ *
++ * (C) Copyright 2005 Ian Molton <spyro@f2s.com>
++ *
++ * 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.
++ *
++ */
++#ifndef _ASM_ARCH_T7L66XB_SOC
++#define _ASM_ARCH_T7L66XB_SOC
++
++#include <linux/mfd-core.h>
++#include <linux/mfd/tmio.h>
++
++
++struct t7l66xb_platform_data
++{
++ int (*enable_clk32k)(struct platform_device *dev);
++ int (*disable_clk32k)(struct platform_device *dev);
++
++ int (*enable)(struct platform_device *dev);
++ int (*disable)(struct platform_device *dev);
++ int (*suspend)(struct platform_device *dev);
++ int (*resume)(struct platform_device *dev);
++
++ int irq_base; /* a base for cascaded irq */
++
++ struct tmio_nand_data *nand_data;
++};
++
++
++#define T7L66XB_NAND_CNF_BASE (0x000100)
++#define T7L66XB_NAND_CTL_BASE (0x001000)
++
++#define IRQ_T7L66XB_NAND (3)
++#define IRQ_T7L66XB_MMC (1)
++#define IRQ_T7L66XB_OHCI (2)
++
++#define T7L66XB_NR_IRQS 8
++
++#endif
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0007-Common-headers-for-TMIO-MFD-subdevices.patch b/recipes/linux/linux-rp-2.6.24/tosa/0007-Common-headers-for-TMIO-MFD-subdevices.patch
new file mode 100644
index 0000000000..2f5f11400c
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0007-Common-headers-for-TMIO-MFD-subdevices.patch
@@ -0,0 +1,81 @@
+From d6e8b347dbcce9e0e8d2204b774c1c33cfcb483e Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Sat, 29 Dec 2007 15:27:43 +0000
+Subject: [PATCH 07/64] Common headers for TMIO MFD subdevices
+
+---
+ include/linux/mfd/tmio.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 files changed, 62 insertions(+), 0 deletions(-)
+ create mode 100644 include/linux/mfd/tmio.h
+
+diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
+new file mode 100644
+index 0000000..b42a4c3
+--- /dev/null
++++ b/include/linux/mfd/tmio.h
+@@ -0,0 +1,62 @@
++#ifndef MFD_TMIO_H
++#define MFD_TMIO_H
++
++#include <linux/io.h>
++#include <linux/platform_device.h>
++
++struct fb_videomode;
++
++/*
++ * data for the NAND controller
++ */
++struct tmio_nand_data {
++ struct nand_bbt_descr *badblock_pattern;
++ struct mtd_partition *partition;
++ unsigned int num_partitions;
++};
++
++struct tmio_fb_data {
++ int (*lcd_set_power)(struct platform_device *fb_dev,
++ bool on);
++ int (*lcd_mode)(struct platform_device *fb_dev,
++ struct fb_videomode *mode);
++ int num_modes;
++ struct fb_videomode *modes;
++};
++
++static u32 __maybe_unused tmio_ioread32(const void __iomem *addr)
++{
++ return ((u32) ioread16(addr)) | (((u32) ioread16(addr + 2)) << 16);
++}
++
++static u32 __maybe_unused tmio_iowrite32(u32 val, const void __iomem *addr)
++{
++ iowrite16(val, addr);
++ iowrite16(val >> 16, addr + 2);
++ return val;
++}
++
++#define FBIO_TMIO_ACC_WRITE 0x7C639300
++#define FBIO_TMIO_ACC_SYNC 0x7C639301
++
++#define TMIO_MMC_CONFIG "tmio-mmc-config"
++#define TMIO_MMC_CONTROL "tmio-mmc-control"
++#define TMIO_MMC_IRQ "tmio-mmc"
++
++#define TMIO_NAND_CONFIG "tmio-nand-config"
++#define TMIO_NAND_CONTROL "tmio-nand-control"
++#define TMIO_NAND_IRQ "tmio-nand"
++
++#define TMIO_FB_CONFIG "tmio-fb-config"
++#define TMIO_FB_CONTROL "tmio-fb-control"
++#define TMIO_FB_VRAM "tmio-fb-vram"
++#define TMIO_FB_IRQ "tmio-fb"
++
++#define TMIO_OHCI_CONFIG "tmio-ohci-config"
++#define TMIO_OHCI_CONTROL "tmio-ohci-control"
++#define TMIO_OHCI_SRAM "tmio-ohci-sram"
++#define TMIO_OHCI_SRAM_ALIAS "tmio-ohci-sram-alias"
++#define TMIO_OHCI_IRQ "tmio-ohci"
++
++#endif
++
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0008-Nand-driver-for-TMIO-devices.patch b/recipes/linux/linux-rp-2.6.24/tosa/0008-Nand-driver-for-TMIO-devices.patch
new file mode 100644
index 0000000000..48b8000ab7
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0008-Nand-driver-for-TMIO-devices.patch
@@ -0,0 +1,608 @@
+From 917b3997a39396f5f51418930de7b933ad053bad Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Sat, 29 Dec 2007 15:14:23 +0000
+Subject: [PATCH 08/64] Nand driver for TMIO devices
+
+---
+ drivers/mtd/nand/Kconfig | 7 +
+ drivers/mtd/nand/Makefile | 1 +
+ drivers/mtd/nand/tmio_nand.c | 557 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 565 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/mtd/nand/tmio_nand.c
+
+diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
+index 246d451..43e489a 100644
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -284,6 +284,13 @@ config MTD_NAND_CM_X270
+ depends on MTD_NAND && MACH_ARMCORE
+
+
++config MTD_NAND_TMIO
++ tristate "NAND Flash device on Toshiba Mobile IO Controller"
++ depends on MTD_NAND && MFD_CORE
++ help
++ Support for NAND flash connected to a Toshiba Mobile IO
++ Controller in some PDAs, including the Sharp SL6000x.
++
+ config MTD_NAND_NANDSIM
+ tristate "Support for NAND Flash Simulator"
+ depends on MTD_PARTITIONS
+diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
+index 3ad6c01..d839ebd 100644
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -27,6 +27,7 @@ obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
+ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
+ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
+ obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
++obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
+ obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
+ obj-$(CONFIG_MTD_ALAUDA) += alauda.o
+
+diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c
+new file mode 100644
+index 0000000..450b4ec
+--- /dev/null
++++ b/drivers/mtd/nand/tmio_nand.c
+@@ -0,0 +1,557 @@
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/mfd-core.h>
++#include <linux/mfd/tmio.h>
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++
++/*--------------------------------------------------------------------------*/
++
++/* tmio_nfcr.mode Register Command List */
++#define FCR_MODE_DATA 0x94 /* Data Data_Mode */
++#define FCR_MODE_COMMAND 0x95 /* Data Command_Mode */
++#define FCR_MODE_ADDRESS 0x96 /* Data Address_Mode */
++
++#define FCR_MODE_HWECC_CALC 0xB4 /* HW-ECC Data */
++#define FCR_MODE_HWECC_RESULT 0xD4 /* HW-ECC Calculation Result Read_Mode */
++#define FCR_MODE_HWECC_RESET 0xF4 /* HW-ECC Reset */
++
++#define FCR_MODE_POWER_ON 0x0C /* Power Supply ON to SSFDC card */
++#define FCR_MODE_POWER_OFF 0x08 /* Power Supply OFF to SSFDC card */
++
++#define FCR_MODE_LED_OFF 0x00 /* LED OFF */
++#define FCR_MODE_LED_ON 0x04 /* LED ON */
++
++#define FCR_MODE_EJECT_ON 0x68 /* Ejection Demand from Penguin is Advanced */
++#define FCR_MODE_EJECT_OFF 0x08 /* Ejection Demand from Penguin is Not Advanced */
++
++#define FCR_MODE_LOCK 0x6C /* Operates By Lock_Mode. Ejection Switch is Invalid */
++#define FCR_MODE_UNLOCK 0x0C /* Operates By UnLock_Mode.Ejection Switch is Effective */
++
++#define FCR_MODE_CONTROLLER_ID 0x40 /* Controller ID Read */
++#define FCR_MODE_STANDBY 0x00 /* SSFDC card Changes Standby State */
++
++#define FCR_MODE_WE 0x80
++#define FCR_MODE_ECC1 0x40
++#define FCR_MODE_ECC0 0x20
++#define FCR_MODE_CE 0x10
++#define FCR_MODE_PCNT1 0x08
++#define FCR_MODE_PCNT0 0x04
++#define FCR_MODE_ALE 0x02
++#define FCR_MODE_CLE 0x01
++
++#define FCR_STATUS_BUSY 0x80
++
++/*
++ *NAND Flash Host Controller Configuration Register
++ */
++struct tmio_nfhccr {
++ u8 x00[4];
++ u16 command; /* 0x04 Command */
++ u8 x01[0x0a];
++ u16 base[2]; /* 0x10 NAND Flash Control Reg Base Addr*/
++ u8 x02[0x29];
++ u8 intp; /* 0x3d Interrupt Pin */
++ u8 x03[0x0a];
++ u8 inte; /* 0x48 Interrupt Enable */
++ u8 x04;
++ u8 ec; /* 0x4a Event Control */
++ u8 x05;
++ u8 icc; /* 0x4c Internal Clock Control */
++ u8 x06[0x0e];
++ u8 eccc; /* 0x5b ECC Control */
++ u8 x07[4];
++ u8 nftc; /* 0x60 NAND Flash Transaction Control */
++ u8 nfm; /* 0x61 NAND Flash Monitor */
++ u8 nfpsc; /* 0x62 NAND Flash Power Supply Control */
++ u8 nfdc; /* 0x63 NAND Flash Detect Control */
++ u8 x08[0x9c];
++} __attribute__ ((packed));
++
++/*
++ *NAND Flash Control Register
++ */
++struct tmio_nfcr {
++union {
++ u8 u8; /* 0x00 Data Register */
++ u16 u16;
++ u32 u32;
++} __attribute__ ((packed));
++ u8 mode; /* 0x04 Mode Register */
++ u8 status; /* 0x05 Status Register */
++ u8 isr; /* 0x06 Interrupt Status Register */
++ u8 imr; /* 0x07 Interrupt Mask Register */
++} __attribute__ ((packed));
++
++struct tmio_nand {
++ struct mtd_info mtd;
++ struct nand_chip chip;
++
++ struct platform_device *dev;
++
++ struct tmio_nfhccr __iomem *ccr;
++ struct tmio_nfcr __iomem *fcr;
++
++ unsigned int irq;
++
++ /* for tmio_nand_read_byte */
++ u8 read;
++ unsigned read_good:1;
++};
++
++#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd)
++
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++static const char *part_probes[] = { "cmdlinepart", NULL };
++#endif
++
++/*--------------------------------------------------------------------------*/
++
++static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd,
++ unsigned int ctrl)
++{
++ struct tmio_nand *tmio = mtd_to_tmio(mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++ struct nand_chip *chip = mtd->priv;
++
++ if (ctrl & NAND_CTRL_CHANGE) {
++ u8 mode;
++
++ if (ctrl & NAND_NCE) {
++ mode = FCR_MODE_DATA;
++
++ if (ctrl & NAND_CLE)
++ mode |= FCR_MODE_CLE;
++ else
++ mode &= ~FCR_MODE_CLE;
++
++ if (ctrl & NAND_ALE)
++ mode |= FCR_MODE_ALE;
++ else
++ mode &= ~FCR_MODE_ALE;
++ } else {
++ mode = FCR_MODE_STANDBY;
++ }
++
++ iowrite8(mode, &fcr->mode);
++ tmio->read_good = 0;
++ }
++
++ if (cmd != NAND_CMD_NONE)
++ writeb(cmd, chip->IO_ADDR_W);
++}
++
++static int tmio_nand_dev_ready(struct mtd_info *mtd)
++{
++ struct tmio_nand *tmio = mtd_to_tmio(mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++
++ return !(ioread8(&fcr->status) & FCR_STATUS_BUSY);
++}
++
++static irqreturn_t tmio_irq(int irq, void *__dev)
++{
++ struct platform_device *dev = __dev;
++ struct tmio_nand *tmio = platform_get_drvdata(dev);
++ struct nand_chip *nand_chip = &tmio->chip;
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++
++ /* disable RDYREQ interrupt */
++ iowrite8(0x00, &fcr->imr);
++
++ if (unlikely(!waitqueue_active(&nand_chip->controller->wq)))
++ dev_warn(&dev->dev, "spurious interrupt\n");
++
++ wake_up(&nand_chip->controller->wq);
++ return IRQ_HANDLED;
++}
++
++/*
++ *The TMIO core has a RDYREQ interrupt on the posedge of #SMRB.
++ *This interrupt is normally disabled, but for long operations like
++ *erase and write, we enable it to wake us up. The irq handler
++ *disables the interrupt.
++ */
++static int
++tmio_nand_wait(struct mtd_info *mtd, struct nand_chip *nand_chip)
++{
++ struct tmio_nand *tmio = mtd_to_tmio(mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++ long timeout;
++
++ /* enable RDYREQ interrupt */
++ iowrite8(0x0f, &fcr->isr);
++ iowrite8(0x81, &fcr->imr);
++
++ timeout = wait_event_timeout(nand_chip->controller->wq, tmio_nand_dev_ready(mtd),
++ msecs_to_jiffies(nand_chip->state == FL_ERASING ? 400 : 20));
++
++ if (unlikely(!tmio_nand_dev_ready(mtd))) {
++ iowrite8(0x00, &fcr->imr);
++ dev_warn(&tmio->dev->dev, "still busy with %s after %d ms\n",
++ nand_chip->state == FL_ERASING ? "erase" : "program",
++ nand_chip->state == FL_ERASING ? 400 : 20);
++
++ } else if (unlikely(!timeout)) {
++ iowrite8(0x00, &fcr->imr);
++ dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n");
++ }
++
++ nand_chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
++ return nand_chip->read_byte(mtd);
++}
++
++/*
++ *The TMIO controller combines two 8-bit data bytes into one 16-bit
++ *word. This function separates them so nand_base.c works as expected,
++ *especially its NAND_CMD_READID routines.
++ *
++ *To prevent stale data from being read, tmio_nand_hwcontrol() clears
++ *tmio->read_good.
++ */
++static u_char tmio_nand_read_byte(struct mtd_info *mtd)
++{
++ struct tmio_nand *tmio = mtd_to_tmio(mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++ unsigned int data;
++
++ if (tmio->read_good--)
++ return tmio->read;
++
++ data = ioread16(&fcr->u16);
++ tmio->read = data >> 8;
++ return data;
++}
++
++/*
++ *The TMIO controller converts an 8-bit NAND interface to a 16-bit
++ *bus interface, so all data reads and writes must be 16-bit wide.
++ *Thus, we implement 16-bit versions of the read, write, and verify
++ *buffer functions.
++ */
++static void
++tmio_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ struct tmio_nand *tmio = mtd_to_tmio(mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++
++ iowrite16_rep(&fcr->u16, buf, len >> 1);
++}
++
++static void tmio_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
++{
++ struct tmio_nand *tmio = mtd_to_tmio(mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++
++ ioread16_rep(&fcr->u16, buf, len >> 1);
++}
++
++static int
++tmio_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ struct tmio_nand *tmio = mtd_to_tmio(mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++ u16 *p = (u16 *) buf;
++
++ for (len >>= 1; len; len--)
++ if (*(p++) != ioread16(&fcr->u16))
++ return -EFAULT;
++ return 0;
++}
++
++static void tmio_nand_enable_hwecc(struct mtd_info *mtd, int mode)
++{
++ struct tmio_nand *tmio = mtd_to_tmio(mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++
++ iowrite8(FCR_MODE_HWECC_RESET, &fcr->mode);
++ ioread8(&fcr->u8); /* dummy read */
++ iowrite8(FCR_MODE_HWECC_CALC, &fcr->mode);
++}
++
++static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
++ u_char *ecc_code)
++{
++ struct tmio_nand *tmio = mtd_to_tmio(mtd);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++ unsigned int ecc;
++
++ iowrite8(FCR_MODE_HWECC_RESULT, &fcr->mode);
++
++ ecc = ioread16(&fcr->u16);
++ ecc_code[1] = ecc; /* 000-255 LP7-0 */
++ ecc_code[0] = ecc >> 8; /* 000-255 LP15-8 */
++ ecc = ioread16(&fcr->u16);
++ ecc_code[2] = ecc; /* 000-255 CP5-0,11b */
++ ecc_code[4] = ecc >> 8; /* 256-511 LP7-0 */
++ ecc = ioread16(&fcr->u16);
++ ecc_code[3] = ecc; /* 256-511 LP15-8 */
++ ecc_code[5] = ecc >> 8; /* 256-511 CP5-0,11b */
++
++ iowrite8(FCR_MODE_DATA, &fcr->mode);
++ return 0;
++}
++
++static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ const struct resource *nfcr = NULL;
++ struct tmio_nfhccr __iomem *ccr = tmio->ccr;
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++ unsigned long base;
++ int i;
++
++ for (i = 0; i < cell->num_resources; i++)
++ if (!strcmp((cell->resources+i)->name, TMIO_NAND_CONTROL))
++ nfcr = &cell->resources[i];
++
++ if (nfcr == NULL)
++ return -ENOMEM;
++
++ if (!cell->enable) {
++ printk(KERN_ERR "null cell enable!");
++ return -EINVAL;
++ }
++
++ cell->enable(dev);
++
++ /* (4Ch) CLKRUN Enable 1st spcrunc */
++ iowrite8(0x81, &ccr->icc);
++
++ /* (10h)BaseAddress 0x1000 spba.spba2 */
++ base = nfcr->start;
++ iowrite16(base, ccr->base + 0);
++ iowrite16(base >> 16, ccr->base + 1);
++
++ /* (04h)Command Register I/O spcmd */
++ iowrite8(0x02, &ccr->command);
++
++ /* (62h) Power Supply Control ssmpwc */
++ /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */
++ iowrite8(0x02, &ccr->nfpsc);
++
++ /* (63h) Detect Control ssmdtc */
++ iowrite8(0x02, &ccr->nfdc);
++
++ /* Interrupt status register clear sintst */
++ iowrite8(0x0f, &fcr->isr);
++
++ /* After power supply, Media are reset smode */
++ iowrite8(FCR_MODE_POWER_ON, &fcr->mode);
++ iowrite8(FCR_MODE_COMMAND, &fcr->mode);
++ iowrite8(NAND_CMD_RESET, &fcr->u8);
++
++ /* Standby Mode smode */
++ iowrite8(FCR_MODE_STANDBY, &fcr->mode);
++
++ mdelay(5);
++
++ return 0;
++}
++
++static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct tmio_nfcr __iomem *fcr = tmio->fcr;
++
++ iowrite8(FCR_MODE_POWER_OFF, &fcr->mode);
++ cell->disable(dev);
++}
++
++static int tmio_probe(struct platform_device *dev)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct tmio_nand_data *data = cell->driver_data;
++ struct resource *ccr = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_NAND_CONFIG);
++ struct resource *fcr = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_NAND_CONTROL);
++ int irq = platform_get_irq(dev, 0);
++ struct tmio_nand *tmio;
++ struct mtd_info *mtd;
++ struct nand_chip *nand_chip;
++ struct mtd_partition *parts;
++ int nbparts = 0;
++ int retval;
++
++ if (data == NULL) {
++ dev_err(&dev->dev, "NULL platform data!\n");
++ return -EINVAL;
++ }
++
++ tmio = kzalloc(sizeof *tmio, GFP_KERNEL);
++ if (!tmio) {
++ retval = -ENOMEM;
++ goto err_kzalloc;
++ }
++
++ tmio->dev = dev;
++
++ platform_set_drvdata(dev, tmio);
++ mtd = &tmio->mtd;
++ nand_chip = &tmio->chip;
++ mtd->priv = nand_chip;
++ mtd->name = "tmio-nand";
++
++ tmio->ccr = ioremap(ccr->start, ccr->end - ccr->start + 1);
++ if (!tmio->ccr) {
++ retval = -EIO;
++ goto err_iomap_ccr;
++ }
++
++ tmio->fcr = ioremap(fcr->start, fcr->end - fcr->start + 1);
++ if (!tmio->fcr) {
++ retval = -EIO;
++ goto err_iomap_fcr;
++ }
++
++ retval = tmio_hw_init(dev, tmio);
++ if (retval)
++ goto err_hwinit;
++
++ /* Set address of NAND IO lines */
++ nand_chip->IO_ADDR_R = tmio->fcr;
++ nand_chip->IO_ADDR_W = tmio->fcr;
++
++ /* Set address of hardware control function */
++ nand_chip->cmd_ctrl = tmio_nand_hwcontrol;
++ nand_chip->dev_ready = tmio_nand_dev_ready;
++ nand_chip->read_byte = tmio_nand_read_byte;
++ nand_chip->write_buf = tmio_nand_write_buf;
++ nand_chip->read_buf = tmio_nand_read_buf;
++ nand_chip->verify_buf = tmio_nand_verify_buf;
++
++ /* set eccmode using hardware ECC */
++ nand_chip->ecc.mode = NAND_ECC_HW;
++ nand_chip->ecc.size = 512;
++ nand_chip->ecc.bytes = 6;
++ nand_chip->ecc.hwctl = tmio_nand_enable_hwecc;
++ nand_chip->ecc.calculate = tmio_nand_calculate_ecc;
++ nand_chip->ecc.correct = nand_correct_data;
++ nand_chip->badblock_pattern = data->badblock_pattern;
++
++ /* 15 us command delay time */
++ nand_chip->chip_delay = 15;
++
++ retval = request_irq(irq, &tmio_irq,
++ IRQF_DISABLED, dev->dev.bus_id, dev);
++ if (retval) {
++ dev_err(&dev->dev, "request_irq error %d\n", retval);
++ goto err_irq;
++ }
++
++ tmio->irq = irq;
++ nand_chip->waitfunc = tmio_nand_wait;
++
++ /* Scan to find existence of the device */
++ if (nand_scan(mtd, 1)) {
++ retval = -ENODEV;
++ goto err_scan;
++ }
++ /* Register the partitions */
++#ifdef CONFIG_MTD_PARTITIONS
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++ nbparts = parse_mtd_partitions(mtd, part_probes, &parts, 0);
++#endif
++ if (nbparts <= 0) {
++ parts = data->partition;
++ nbparts = data->num_partitions;
++ }
++
++ retval = add_mtd_partitions(mtd, parts, nbparts);
++#else
++ retval = add_mtd_device(mtd);
++#endif
++
++ if (!retval)
++ return retval;
++
++ nand_release(mtd);
++
++err_scan:
++ if (tmio->irq)
++ free_irq(tmio->irq, dev);
++err_irq:
++ tmio_hw_stop(dev, tmio);
++err_hwinit:
++ iounmap(tmio->fcr);
++err_iomap_fcr:
++ iounmap(tmio->ccr);
++err_iomap_ccr:
++ kfree(tmio);
++err_kzalloc:
++ return retval;
++}
++
++static int tmio_remove(struct platform_device *dev)
++{
++ struct tmio_nand *tmio = platform_get_drvdata(dev);
++
++ nand_release(&tmio->mtd);
++ if (tmio->irq)
++ free_irq(tmio->irq, tmio);
++ tmio_hw_stop(dev, tmio);
++ iounmap(tmio->fcr);
++ iounmap(tmio->ccr);
++ kfree(tmio);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int tmio_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++
++ if (cell->suspend)
++ cell->suspend(dev);
++
++ tmio_hw_stop(dev, platform_get_drvdata(dev));
++ return 0;
++}
++
++static int tmio_resume(struct platform_device *dev)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++
++ tmio_hw_init(dev, platform_get_drvdata(dev));
++
++ if (cell->resume)
++ cell->resume(dev);
++
++ return 0;
++}
++#endif
++
++static struct platform_driver tmio_driver = {
++ .driver.name = "tmio-nand",
++ .driver.owner = THIS_MODULE,
++ .probe = tmio_probe,
++ .remove = tmio_remove,
++#ifdef CONFIG_PM
++ .suspend = tmio_suspend,
++ .resume = tmio_resume,
++#endif
++};
++
++static int __init tmio_init(void)
++{
++ return platform_driver_register(&tmio_driver);
++}
++
++static void __exit tmio_exit(void)
++{
++ platform_driver_unregister(&tmio_driver);
++}
++
++module_init(tmio_init);
++module_exit(tmio_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Dirk Opfer, Chris Humbert, Dmitry Baryshkov");
++MODULE_DESCRIPTION("NAND flash driver on Toshiba Mobile IO controller");
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0009-FB-driver-for-TMIO-devices.patch b/recipes/linux/linux-rp-2.6.24/tosa/0009-FB-driver-for-TMIO-devices.patch
new file mode 100644
index 0000000000..5fc96f8973
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0009-FB-driver-for-TMIO-devices.patch
@@ -0,0 +1,1128 @@
+From 519d015892ab0a7cad1f6b26fcd38117171384ce Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Tue, 1 Jan 2008 21:22:23 +0000
+Subject: [PATCH 09/64] FB driver for TMIO devices
+
+---
+ drivers/video/Kconfig | 22 +
+ drivers/video/Makefile | 1 +
+ drivers/video/tmiofb.c | 1062 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1085 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/video/tmiofb.c
+
+diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
+index 5b3dbcf..6d0df58 100644
+--- a/drivers/video/Kconfig
++++ b/drivers/video/Kconfig
+@@ -1782,6 +1782,28 @@ config FB_W100
+
+ If unsure, say N.
+
++config FB_TMIO
++ tristate "Toshiba Mobice IO FrameBuffer support"
++ depends on FB && MFD_CORE
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ select FB_CFB_IMAGEBLIT
++ ---help---
++ Frame buffer driver for the Toshiba Mobile IO integrated as found
++ on the Sharp SL-6000 series
++
++ This driver is also available as a module ( = code which can be
++ inserted and removed from the running kernel whenever you want). The
++ module will be called tmiofb. If you want to compile it as a module,
++ say M here and read <file:Documentation/kbuild/modules.txt>.
++
++ If unsure, say N.
++
++config FB_TMIO_ACCELL
++ bool "tmiofb acceleration"
++ depends on FB_TMIO
++ default y
++
+ config FB_S3C2410
+ tristate "S3C2410 LCD framebuffer support"
+ depends on FB && ARCH_S3C2410
+diff --git a/drivers/video/Makefile b/drivers/video/Makefile
+index 83e02b3..74e9384 100644
+--- a/drivers/video/Makefile
++++ b/drivers/video/Makefile
+@@ -97,6 +97,7 @@ obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
+ obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o
+ obj-$(CONFIG_FB_PXA) += pxafb.o
+ obj-$(CONFIG_FB_W100) += w100fb.o
++obj-$(CONFIG_FB_TMIO) += tmiofb.o
+ obj-$(CONFIG_FB_AU1100) += au1100fb.o
+ obj-$(CONFIG_FB_AU1200) += au1200fb.o
+ obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o
+diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
+new file mode 100644
+index 0000000..6b963a1
+--- /dev/null
++++ b/drivers/video/tmiofb.c
+@@ -0,0 +1,1062 @@
++/*
++ * Frame Buffer Device for Toshiba Mobile IO(TMIO) controller
++ *
++ * Copyright(C) 2005-2006 Chris Humbert
++ * Copyright(C) 2005 Dirk Opfer
++ *
++ * Based on:
++ * drivers/video/w100fb.c
++ * code written by Sharp/Lineo for 2.4 kernels
++ *
++ * 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.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/mfd-core.h>
++#include <linux/mfd/tmio.h>
++#include <linux/fb.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++/* Why should fb driver call console functions? because acquire_console_sem() */
++#include <linux/console.h>
++#include <linux/uaccess.h>
++#include <linux/vmalloc.h>
++
++/*
++ * accelerator commands
++ */
++#define TMIOFB_ACC_CSADR(x) (0x00000000 | ((x) & 0x001ffffe))
++#define TMIOFB_ACC_CHPIX(x) (0x01000000 | ((x) & 0x000003ff))
++#define TMIOFB_ACC_CVPIX(x) (0x02000000 | ((x) & 0x000003ff))
++#define TMIOFB_ACC_PSADR(x) (0x03000000 | ((x) & 0x00fffffe))
++#define TMIOFB_ACC_PHPIX(x) (0x04000000 | ((x) & 0x000003ff))
++#define TMIOFB_ACC_PVPIX(x) (0x05000000 | ((x) & 0x000003ff))
++#define TMIOFB_ACC_PHOFS(x) (0x06000000 | ((x) & 0x000003ff))
++#define TMIOFB_ACC_PVOFS(x) (0x07000000 | ((x) & 0x000003ff))
++#define TMIOFB_ACC_POADR(x) (0x08000000 | ((x) & 0x00fffffe))
++#define TMIOFB_ACC_RSTR(x) (0x09000000 | ((x) & 0x000000ff))
++#define TMIOFB_ACC_TCLOR(x) (0x0A000000 | ((x) & 0x0000ffff))
++#define TMIOFB_ACC_FILL(x) (0x0B000000 | ((x) & 0x0000ffff))
++#define TMIOFB_ACC_DSADR(x) (0x0C000000 | ((x) & 0x00fffffe))
++#define TMIOFB_ACC_SSADR(x) (0x0D000000 | ((x) & 0x00fffffe))
++#define TMIOFB_ACC_DHPIX(x) (0x0E000000 | ((x) & 0x000003ff))
++#define TMIOFB_ACC_DVPIX(x) (0x0F000000 | ((x) & 0x000003ff))
++#define TMIOFB_ACC_SHPIX(x) (0x10000000 | ((x) & 0x000003ff))
++#define TMIOFB_ACC_SVPIX(x) (0x11000000 | ((x) & 0x000003ff))
++#define TMIOFB_ACC_LBINI(x) (0x12000000 | ((x) & 0x0000ffff))
++#define TMIOFB_ACC_LBK2(x) (0x13000000 | ((x) & 0x0000ffff))
++#define TMIOFB_ACC_SHBINI(x) (0x14000000 | ((x) & 0x0000ffff))
++#define TMIOFB_ACC_SHBK2(x) (0x15000000 | ((x) & 0x0000ffff))
++#define TMIOFB_ACC_SVBINI(x) (0x16000000 | ((x) & 0x0000ffff))
++#define TMIOFB_ACC_SVBK2(x) (0x17000000 | ((x) & 0x0000ffff))
++
++#define TMIOFB_ACC_CMGO 0x20000000
++#define TMIOFB_ACC_CMGO_CEND 0x00000001
++#define TMIOFB_ACC_CMGO_INT 0x00000002
++#define TMIOFB_ACC_CMGO_CMOD 0x00000010
++#define TMIOFB_ACC_CMGO_CDVRV 0x00000020
++#define TMIOFB_ACC_CMGO_CDHRV 0x00000040
++#define TMIOFB_ACC_CMGO_RUND 0x00008000
++#define TMIOFB_ACC_SCGO 0x21000000
++#define TMIOFB_ACC_SCGO_CEND 0x00000001
++#define TMIOFB_ACC_SCGO_INT 0x00000002
++#define TMIOFB_ACC_SCGO_ROP3 0x00000004
++#define TMIOFB_ACC_SCGO_TRNS 0x00000008
++#define TMIOFB_ACC_SCGO_DVRV 0x00000010
++#define TMIOFB_ACC_SCGO_DHRV 0x00000020
++#define TMIOFB_ACC_SCGO_SVRV 0x00000040
++#define TMIOFB_ACC_SCGO_SHRV 0x00000080
++#define TMIOFB_ACC_SCGO_DSTXY 0x00008000
++#define TMIOFB_ACC_SBGO 0x22000000
++#define TMIOFB_ACC_SBGO_CEND 0x00000001
++#define TMIOFB_ACC_SBGO_INT 0x00000002
++#define TMIOFB_ACC_SBGO_DVRV 0x00000010
++#define TMIOFB_ACC_SBGO_DHRV 0x00000020
++#define TMIOFB_ACC_SBGO_SVRV 0x00000040
++#define TMIOFB_ACC_SBGO_SHRV 0x00000080
++#define TMIOFB_ACC_SBGO_SBMD 0x00000100
++#define TMIOFB_ACC_FLGO 0x23000000
++#define TMIOFB_ACC_FLGO_CEND 0x00000001
++#define TMIOFB_ACC_FLGO_INT 0x00000002
++#define TMIOFB_ACC_FLGO_ROP3 0x00000004
++#define TMIOFB_ACC_LDGO 0x24000000
++#define TMIOFB_ACC_LDGO_CEND 0x00000001
++#define TMIOFB_ACC_LDGO_INT 0x00000002
++#define TMIOFB_ACC_LDGO_ROP3 0x00000004
++#define TMIOFB_ACC_LDGO_ENDPX 0x00000008
++#define TMIOFB_ACC_LDGO_LVRV 0x00000010
++#define TMIOFB_ACC_LDGO_LHRV 0x00000020
++#define TMIOFB_ACC_LDGO_LDMOD 0x00000040
++
++/* a FIFO is always allocated, even if acceleration is not used */
++#define TMIOFB_FIFO_SIZE 512
++
++/*
++ * LCD Host Controller Configuration Register
++ *
++ * This iomem area supports only 16-bit IO.
++ */
++struct tmio_lhccr {
++ u16 x00[2];
++ u16 cmd; /* 0x04 Command */
++ u16 x01;
++ u16 revid; /* 0x08 Revision ID */
++ u16 x02[3];
++ u16 basel; /* 0x10 LCD Control Reg Base Addr Low */
++ u16 baseh; /* 0x12 LCD Control Reg Base Addr High */
++ u16 x03[0x16];
++ u16 ugcc; /* 0x40 Unified Gated Clock Control */
++ u16 gcc; /* 0x42 Gated Clock Control */
++ u16 x04[6];
++ u16 usc; /* 0x50 Unified Software Clear */
++ u16 x05[7];
++ u16 vramrtc; /* 0x60 VRAM Timing Control */
++ /* 0x61 VRAM Refresh Control */
++ u16 vramsac; /* 0x62 VRAM Access Control */
++ /* 0x63 VRAM Status */
++ u16 vrambc; /* 0x64 VRAM Block Control */
++ u16 x06[0x4d];
++};
++
++/*
++ * LCD Control Register
++ *
++ * This iomem area supports only 16-bit IO.
++ */
++struct tmio_lcr {
++ u16 uis; /* 0x000 Unified Interrupt Status */
++ u16 x00[3];
++ u16 vhpn; /* 0x008 VRAM Horizontal Pixel Number */
++ u16 cfsal; /* 0x00a Command FIFO Start Address Low */
++ u16 cfsah; /* 0x00c Command FIFO Start Address High */
++ u16 cfs; /* 0x00e Command FIFO Size */
++ u16 cfws; /* 0x010 Command FIFO Writeable Size */
++ u16 bbie; /* 0x012 BitBLT Interrupt Enable */
++ u16 bbisc; /* 0x014 BitBLT Interrupt Status and Clear */
++ u16 ccs; /* 0x016 Command Count Status */
++ u16 bbes; /* 0x018 BitBLT Execution Status */
++ u16 x01;
++ u16 cmdl; /* 0x01c Command Low */
++ u16 cmdh; /* 0x01e Command High */
++ u16 x02;
++ u16 cfc; /* 0x022 Command FIFO Clear */
++ u16 ccifc; /* 0x024 CMOS Camera IF Control */
++ u16 hwt; /* 0x026 Hardware Test */
++ u16 x03[0x6c];
++ u16 lcdccrc;/* 0x100 LCDC Clock and Reset Control */
++ u16 lcdcc; /* 0x102 LCDC Control */
++ u16 lcdcopc;/* 0x104 LCDC Output Pin Control */
++ u16 x04;
++ u16 lcdis; /* 0x108 LCD Interrupt Status */
++ u16 lcdim; /* 0x10a LCD Interrupt Mask */
++ u16 lcdie; /* 0x10c LCD Interrupt Enable */
++ u16 x05[10];
++ u16 gdsal; /* 0x122 Graphics Display Start Address Low */
++ u16 gdsah; /* 0x124 Graphics Display Start Address High */
++ u16 x06[2];
++ u16 vhpcl; /* 0x12a VRAM Horizontal Pixel Count Low */
++ u16 vhpch; /* 0x12c VRAM Horizontal Pixel Count High */
++ u16 gm; /* 0x12e Graphic Mode(VRAM access enable) */
++ u16 x07[8];
++ u16 ht; /* 0x140 Horizontal Total */
++ u16 hds; /* 0x142 Horizontal Display Start */
++ u16 hss; /* 0x144 H-Sync Start */
++ u16 hse; /* 0x146 H-Sync End */
++ u16 x08[2];
++ u16 hnp; /* 0x14c Horizontal Number of Pixels */
++ u16 x09;
++ u16 vt; /* 0x150 Vertical Total */
++ u16 vds; /* 0x152 Vertical Display Start */
++ u16 vss; /* 0x154 V-Sync Start */
++ u16 vse; /* 0x156 V-Sync End */
++ u16 x0a[4];
++ u16 cdln; /* 0x160 Current Display Line Number */
++ u16 iln; /* 0x162 Interrupt Line Number */
++ u16 sp; /* 0x164 Sync Polarity */
++ u16 misc; /* 0x166 MISC(RGB565 mode) */
++ u16 x0b;
++ u16 vihss; /* 0x16a Video Interface H-Sync Start */
++ u16 vivs; /* 0x16c Video Interface Vertical Start */
++ u16 vive; /* 0x16e Video Interface Vertical End */
++ u16 vivss; /* 0x170 Video Interface V-Sync Start */
++ u16 x0c[6];
++ u16 vccis; /* 0x17e Video / CMOS Camera Interface Select */
++ u16 vidwsal;/* 0x180 VI Data Write Start Address Low */
++ u16 vidwsah;/* 0x182 VI Data Write Start Address High */
++ u16 vidrsal;/* 0x184 VI Data Read Start Address Low */
++ u16 vidrsah;/* 0x186 VI Data Read Start Address High */
++ u16 vipddst;/* 0x188 VI Picture Data Display Start Timing */
++ u16 vipddet;/* 0x186 VI Picture Data Display End Timing */
++ u16 vie; /* 0x18c Video Interface Enable */
++ u16 vcs; /* 0x18e Video/Camera Select */
++ u16 x0d[2];
++ u16 vphwc; /* 0x194 Video Picture Horizontal Wait Count */
++ u16 vphs; /* 0x196 Video Picture Horizontal Size */
++ u16 vpvwc; /* 0x198 Video Picture Vertical Wait Count */
++ u16 vpvs; /* 0x19a Video Picture Vertical Size */
++ u16 x0e[2];
++ u16 plhpix; /* 0x1a0 PLHPIX */
++ u16 xs; /* 0x1a2 XStart */
++ u16 xckhw; /* 0x1a4 XCK High Width */
++ u16 x0f;
++ u16 sths; /* 0x1a8 STH Start */
++ u16 vt2; /* 0x1aa Vertical Total */
++ u16 ycksw; /* 0x1ac YCK Start Wait */
++ u16 ysts; /* 0x1ae YST Start */
++ u16 ppols; /* 0x1b0 #PPOL Start */
++ u16 precw; /* 0x1b2 PREC Width */
++ u16 vclkhw; /* 0x1b4 VCLK High Width */
++ u16 oc; /* 0x1b6 Output Control */
++ u16 x10[0x24];
++};
++static char *mode_option __devinitdata;
++
++struct tmiofb_par {
++ u32 pseudo_palette[16];
++
++#ifdef CONFIG_FB_TMIO_ACCELL
++ wait_queue_head_t wait_acc;
++ bool use_polling;
++#endif
++
++ struct tmio_lhccr __iomem *ccr;
++ struct tmio_lcr __iomem *lcr;
++ void __iomem *vram;
++};
++
++/*--------------------------------------------------------------------------*/
++
++static irqreturn_t tmiofb_irq(int irq, void *__info);
++
++/*--------------------------------------------------------------------------*/
++
++
++/*
++ * Turns off the LCD controller and LCD host controller.
++ */
++static int tmiofb_hw_stop(struct platform_device *dev)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct tmio_fb_data *data = cell->driver_data;
++ struct fb_info *info = platform_get_drvdata(dev);
++ struct tmiofb_par *par = info->par;
++ struct tmio_lhccr __iomem *ccr = par->ccr;
++ struct tmio_lcr __iomem *lcr = par->lcr;
++
++ iowrite16(0, &ccr->ugcc);
++ iowrite16(0, &lcr->gm);
++ data->lcd_set_power(dev, 0);
++ iowrite16(0x0010, &lcr->lcdccrc);
++
++ return 0;
++}
++
++/*
++ * Initializes the LCD host controller.
++ */
++static int tmiofb_hw_init(struct platform_device *dev)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct tmio_fb_data *data = cell->driver_data;
++ struct fb_info *info = platform_get_drvdata(dev);
++ struct tmiofb_par *par = info->par;
++ struct tmio_lhccr __iomem *ccr = par->ccr;
++ struct tmio_lcr __iomem *lcr = par->lcr;
++ const struct resource *nlcr = NULL;
++ const struct resource *vram = NULL;
++ unsigned long base;
++ int i;
++
++ for (i = 0; i < cell->num_resources; i++) {
++ if (!strcmp((cell->resources+i)->name, TMIO_FB_CONTROL))
++ nlcr = &cell->resources[i];
++ if (!strcmp((cell->resources+i)->name, TMIO_FB_VRAM))
++ vram = &cell->resources[i];
++ }
++
++ if (nlcr == NULL || vram == NULL)
++ return -EINVAL;
++
++ base = nlcr->start;
++
++ if (info->mode == NULL) {
++ printk(KERN_ERR "tmio-fb: null info->mode\n");
++ info->mode = data->modes;
++ }
++
++ data->lcd_mode(dev, info->mode);
++
++ iowrite16(0x003a, &ccr->ugcc);
++ iowrite16(0x003a, &ccr->gcc);
++ iowrite16(0x3f00, &ccr->usc);
++
++ data->lcd_set_power(dev, 1);
++ mdelay(2);
++
++ iowrite16(0x0000, &ccr->usc);
++ iowrite16(base >> 16, &ccr->baseh);
++ iowrite16(base, &ccr->basel);
++ iowrite16(0x0002, &ccr->cmd); /* base address enable */
++ iowrite16(0x40a8, &ccr->vramrtc); /* VRAMRC, VRAMTC */
++ iowrite16(0x0018, &ccr->vramsac); /* VRAMSTS, VRAMAC */
++ iowrite16(0x0002, &ccr->vrambc);
++ mdelay(2);
++ iowrite16(0x000b, &ccr->vrambc);
++
++ base = vram->start + info->screen_size;
++ iowrite16(base >> 16, &lcr->cfsah);
++ iowrite16(base, &lcr->cfsal);
++ iowrite16(TMIOFB_FIFO_SIZE - 1, &lcr->cfs);
++ iowrite16(1, &lcr->cfc);
++ iowrite16(1, &lcr->bbie);
++ iowrite16(0, &lcr->cfws);
++
++ return 0;
++}
++
++/*
++ * Sets the LCD controller's output resolution and pixel clock
++ */
++static void tmiofb_hw_mode(struct platform_device *dev)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct tmio_fb_data *data = cell->driver_data;
++ struct fb_info *info = platform_get_drvdata(dev);
++ struct fb_videomode *mode = info->mode;
++ struct tmiofb_par *par = info->par;
++ struct tmio_lcr __iomem *lcr = par->lcr;
++ unsigned int i;
++
++ iowrite16(0, &lcr->gm);
++ data->lcd_set_power(dev, 0);
++ iowrite16(0x0010, &lcr->lcdccrc);
++ data->lcd_mode(dev, mode);
++ data->lcd_set_power(dev, 1);
++
++ iowrite16(i = mode->xres * 2, &lcr->vhpn);
++ iowrite16(0, &lcr->gdsah);
++ iowrite16(0, &lcr->gdsal);
++ iowrite16(i >> 16, &lcr->vhpch);
++ iowrite16(i, &lcr->vhpcl);
++ iowrite16(i = 0, &lcr->hss);
++ iowrite16(i += mode->hsync_len, &lcr->hse);
++ iowrite16(i += mode->left_margin, &lcr->hds);
++ iowrite16(i += mode->xres + mode->right_margin, &lcr->ht);
++ iowrite16(mode->xres, &lcr->hnp);
++ iowrite16(i = 0, &lcr->vss);
++ iowrite16(i += mode->vsync_len, &lcr->vse);
++ iowrite16(i += mode->upper_margin, &lcr->vds);
++ iowrite16(i += mode->yres, &lcr->iln);
++ iowrite16(i += mode->lower_margin, &lcr->vt);
++ iowrite16(3, /* RGB565 mode */ &lcr->misc);
++ iowrite16(1, /* VRAM enable */ &lcr->gm);
++ iowrite16(0x4007, &lcr->lcdcc);
++ iowrite16(3, /* sync polarity */ &lcr->sp);
++
++ iowrite16(0x0010, &lcr->lcdccrc);
++ mdelay(5);
++ iowrite16(0x0014, &lcr->lcdccrc); /* STOP_CKP */
++ mdelay(5);
++ iowrite16(0x0015, &lcr->lcdccrc); /* STOP_CKP | SOFT_RESET */
++ iowrite16(0xfffa, &lcr->vcs);
++}
++
++/*--------------------------------------------------------------------------*/
++
++#ifdef CONFIG_FB_TMIO_ACCELL
++static int __must_check
++tmiofb_acc_wait(struct fb_info *info, unsigned int ccs)
++{
++ struct tmiofb_par *par = info->par;
++ struct tmio_lcr __iomem *lcr = par->lcr;
++ if (in_atomic() || par->use_polling) {
++ int i = 0;
++ while (ioread16(&lcr->ccs) > ccs) {
++ udelay(1);
++ i++;
++ if (i > 10000) {
++ printk(KERN_ERR "tmiofb: timeout waiting for %d\n", ccs);
++ return -ETIMEDOUT;
++ }
++ tmiofb_irq(-1, info);
++ }
++ } else {
++ if (!wait_event_interruptible_timeout(par->wait_acc,
++ ioread16(&par->lcr->ccs) <= ccs, 1000)) {
++ printk(KERN_ERR "tmiofb: timeout waiting for %d\n", ccs);
++ return -ETIMEDOUT;
++ }
++ }
++
++ return 0;
++}
++
++/*
++ * Writes an accelerator command to the accelerator's FIFO.
++ */
++static int
++tmiofb_acc_write(struct fb_info *info, const u32 *cmd, unsigned int count)
++{
++ struct tmiofb_par *par = info->par;
++ struct tmio_lcr __iomem *lcr = par->lcr;
++ int ret;
++
++ ret = tmiofb_acc_wait(info, TMIOFB_FIFO_SIZE - count);
++ if (ret)
++ return ret;
++
++ for (; count; count--, cmd++) {
++ iowrite16(*cmd >> 16, &lcr->cmdh);
++ iowrite16(*cmd, &lcr->cmdl);
++ }
++
++ return ret;
++}
++
++/*
++ * Wait for the accelerator to finish its operations before writing
++ * to the framebuffer for consistent display output.
++ */
++static int tmiofb_sync(struct fb_info *fbi)
++{
++ struct tmiofb_par *par = fbi->par;
++
++ int ret;
++ int i = 0;
++
++ ret = tmiofb_acc_wait(fbi, 0);
++
++ while (ioread16(&par->lcr->bbes) & 2) { /* blit active */
++ udelay(1);
++ i++ ;
++ if (i > 10000) {
++ printk(KERN_ERR "timeout waiting for blit to end!\n");
++ return -ETIMEDOUT;
++ }
++ }
++
++ return ret;
++}
++
++static void
++tmiofb_fillrect(struct fb_info *fbi, const struct fb_fillrect *rect)
++{
++ const u32 cmd [] = {
++ TMIOFB_ACC_DSADR((rect->dy * fbi->mode->xres + rect->dx) * 2),
++ TMIOFB_ACC_DHPIX(rect->width - 1),
++ TMIOFB_ACC_DVPIX(rect->height - 1),
++ TMIOFB_ACC_FILL(rect->color),
++ TMIOFB_ACC_FLGO,
++ };
++
++ if (fbi->state != FBINFO_STATE_RUNNING ||
++ fbi->flags & FBINFO_HWACCEL_DISABLED) {
++ cfb_fillrect(fbi, rect);
++ return;
++ }
++
++ tmiofb_acc_write(fbi, cmd, ARRAY_SIZE(cmd));
++}
++
++static void
++tmiofb_copyarea(struct fb_info *fbi, const struct fb_copyarea *area)
++{
++ const u32 cmd [] = {
++ TMIOFB_ACC_DSADR((area->dy * fbi->mode->xres + area->dx) * 2),
++ TMIOFB_ACC_DHPIX(area->width - 1),
++ TMIOFB_ACC_DVPIX(area->height - 1),
++ TMIOFB_ACC_SSADR((area->sy * fbi->mode->xres + area->sx) * 2),
++ TMIOFB_ACC_SCGO,
++ };
++
++ if (fbi->state != FBINFO_STATE_RUNNING ||
++ fbi->flags & FBINFO_HWACCEL_DISABLED) {
++ cfb_copyarea(fbi, area);
++ return;
++ }
++
++ tmiofb_acc_write(fbi, cmd, ARRAY_SIZE(cmd));
++}
++#endif
++
++static void tmiofb_clearscreen(struct fb_info *info)
++{
++ const struct fb_fillrect rect = {
++ .dx = 0,
++ .dy = 0,
++ .width = info->mode->xres,
++ .height = info->mode->yres,
++ .color = 0,
++ };
++
++ info->fbops->fb_fillrect(info, &rect);
++}
++
++static int tmiofb_vblank(struct fb_info *fbi, struct fb_vblank *vblank)
++{
++ struct tmiofb_par *par = fbi->par;
++ struct fb_videomode *mode = fbi->mode;
++ unsigned int vcount = ioread16(&par->lcr->cdln);
++ unsigned int vds = mode->vsync_len + mode->upper_margin;
++
++ vblank->vcount = vcount;
++ vblank->flags = FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_VCOUNT
++ | FB_VBLANK_HAVE_VSYNC;
++
++ if (vcount < mode->vsync_len)
++ vblank->flags |= FB_VBLANK_VSYNCING;
++
++ if (vcount < vds || vcount > vds + mode->yres)
++ vblank->flags |= FB_VBLANK_VBLANKING;
++
++ return 0;
++}
++
++
++static int tmiofb_ioctl(struct fb_info *fbi,
++ unsigned int cmd, unsigned long arg)
++{
++ switch (cmd) {
++ case FBIOGET_VBLANK: {
++ struct fb_vblank vblank = {0};
++ void __user *argp = (void __user *) arg;
++
++ tmiofb_vblank(fbi, &vblank);
++ if (copy_to_user(argp, &vblank, sizeof vblank))
++ return -EFAULT;
++ return 0;
++ }
++
++#ifdef CONFIG_FB_TMIO_ACCELL
++ case FBIO_TMIO_ACC_SYNC:
++ tmiofb_sync(fbi);
++ return 0;
++
++ case FBIO_TMIO_ACC_WRITE: {
++ u32 __user *argp = (void __user *) arg;
++ u32 len;
++ u32 acc [16];
++
++ if (copy_from_user(&len, argp, sizeof(u32)))
++ return -EFAULT;
++ if (len > ARRAY_SIZE(acc))
++ return -EINVAL;
++ if (copy_from_user(acc, argp + 1, sizeof(u32) * len))
++ return -EFAULT;
++
++ return tmiofb_acc_write(fbi, acc, len);
++ }
++#endif
++ }
++
++ return -EINVAL;
++}
++
++/*--------------------------------------------------------------------------*/
++
++/* Select the smallest mode that allows the desired resolution to be
++ * displayed. If desired, the x and y parameters can be rounded up to
++ * match the selected mode.
++ */
++static struct fb_videomode*
++tmiofb_find_mode(struct fb_info *info, struct fb_var_screeninfo *var)
++{
++ struct mfd_cell *cell = mfd_get_cell(to_platform_device(info->device));
++ struct tmio_fb_data *data = cell->driver_data;
++ struct fb_videomode *best = NULL;
++ int i;
++
++ for (i = 0; i < data->num_modes; i++) {
++ struct fb_videomode *mode = data->modes + i;
++
++ if (mode->xres >= var->xres && mode->yres >= var->yres
++ && (!best || (mode->xres < best->xres
++ && mode->yres < best->yres)))
++ best = mode;
++ }
++
++ return best;
++}
++
++static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
++{
++
++ struct fb_videomode *mode;
++
++ mode = tmiofb_find_mode(info, var);
++ if (!mode || var->bits_per_pixel > 16)
++ return -EINVAL;
++
++ fb_videomode_to_var(var, mode);
++
++ var->xres_virtual = mode->xres;
++ var->yres_virtual = info->screen_size / (mode->xres * 2);
++ var->xoffset = 0;
++ var->yoffset = 0;
++ var->bits_per_pixel = 16;
++ var->grayscale = 0;
++ var->red.offset = 11; var->red.length = 5;
++ var->green.offset = 5; var->green.length = 6;
++ var->blue.offset = 0; var->blue.length = 5;
++ var->transp.offset = 0; var->transp.length = 0;
++ var->nonstd = 0;
++ var->height = 82; /* mm */
++ var->width = 60; /* mm */
++ var->rotate = 0;
++ return 0;
++}
++
++static int tmiofb_set_par(struct fb_info *info)
++{
++/* struct fb_var_screeninfo *var = &info->var;
++ struct fb_videomode *mode;
++
++ mode = tmiofb_find_mode(info, var);
++ if (!mode)
++ return -EINVAL;
++
++ if (info->mode == mode)
++ return 0;
++
++ info->mode = mode; */
++ info->fix.line_length = info->mode->xres * 2;
++
++ tmiofb_hw_mode(to_platform_device(info->device));
++ tmiofb_clearscreen(info);
++ return 0;
++}
++
++static int tmiofb_setcolreg(unsigned regno, unsigned red, unsigned green,
++ unsigned blue, unsigned transp,
++ struct fb_info *info)
++{
++ struct tmiofb_par *par = info->par;
++
++ if (regno < ARRAY_SIZE(par->pseudo_palette)) {
++ par->pseudo_palette [regno] =
++ ((red & 0xf800)) |
++ ((green & 0xfc00) >> 5) |
++ ((blue & 0xf800) >> 11);
++ return 0;
++ }
++
++ return 1;
++}
++
++static struct fb_ops tmiofb_ops = {
++ .owner = THIS_MODULE,
++
++ .fb_ioctl = tmiofb_ioctl,
++ .fb_check_var = tmiofb_check_var,
++ .fb_set_par = tmiofb_set_par,
++ .fb_setcolreg = tmiofb_setcolreg,
++ .fb_imageblit = cfb_imageblit,
++#ifdef CONFIG_FB_TMIO_ACCELL
++ .fb_sync = tmiofb_sync,
++ .fb_fillrect = tmiofb_fillrect,
++ .fb_copyarea = tmiofb_copyarea,
++#else
++ .fb_fillrect = cfb_fillrect,
++ .fb_copyarea = cfb_copyarea,
++#endif
++};
++
++/*--------------------------------------------------------------------------*/
++
++/*
++ * reasons for an interrupt:
++ * uis bbisc lcdis
++ * 0100 0001 accelerator command completed
++ * 2000 0001 vsync start
++ * 2000 0002 display start
++ * 2000 0004 line number match(0x1ff mask???)
++ */
++static irqreturn_t tmiofb_irq(int irq, void *__info)
++{
++ struct fb_info *info = __info;
++ struct tmiofb_par *par = info->par;
++ struct tmio_lcr __iomem *lcr = par->lcr;
++ unsigned int bbisc = ioread16(&lcr->bbisc);
++
++
++ if (unlikely(par->use_polling && irq != -1)) {
++ printk(KERN_INFO "tmiofb: switching to waitq\n");
++ par->use_polling = false;
++ }
++
++ iowrite16(bbisc, &lcr->bbisc);
++
++#ifdef CONFIG_FB_TMIO_ACCELL
++ if (bbisc & 1)
++ wake_up(&par->wait_acc);
++#endif
++
++ return IRQ_HANDLED;
++}
++
++static int tmiofb_probe(struct platform_device *dev)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct tmio_fb_data *data = cell->driver_data;
++ struct resource *ccr = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_FB_CONFIG);
++ struct resource *lcr = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_FB_CONTROL);
++ struct resource *vram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_FB_VRAM);
++ int irq = platform_get_irq(dev, 0);
++ struct fb_info *info;
++ struct tmiofb_par *par;
++ int retval;
++
++ if (data == NULL) {
++ dev_err(&dev->dev, "NULL platform data!\n");
++ return -EINVAL;
++ }
++
++ info = framebuffer_alloc(sizeof(struct tmiofb_par), &dev->dev);
++
++ if (!info) {
++ retval = -ENOMEM;
++ goto err_framebuffer_alloc;
++ }
++
++ par = info->par;
++ platform_set_drvdata(dev, info);
++
++#ifdef CONFIG_FB_TMIO_ACCELL
++ init_waitqueue_head(&par->wait_acc);
++
++ par->use_polling = true;
++
++ info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA
++ | FBINFO_HWACCEL_FILLRECT;
++#else
++ info->flags = FBINFO_DEFAULT;
++#endif
++
++ info->fbops = &tmiofb_ops;
++
++ strcpy(info->fix.id, "tmio-fb");
++ info->fix.smem_start = vram->start;
++ info->fix.smem_len = vram->end - vram->start + 1;
++ info->fix.type = FB_TYPE_PACKED_PIXELS;
++ info->fix.visual = FB_VISUAL_TRUECOLOR;
++ info->fix.mmio_start = lcr->start;
++ info->fix.mmio_len = lcr->end - lcr->start + 1;
++ info->fix.accel = FB_ACCEL_NONE;
++ info->screen_size = info->fix.smem_len - (4 * TMIOFB_FIFO_SIZE);
++ info->pseudo_palette = par->pseudo_palette;
++
++ par->ccr = ioremap(ccr->start, ccr->end - ccr->start + 1);
++ if (!par->ccr) {
++ retval = -ENOMEM;
++ goto err_ioremap_ccr;
++ }
++
++ par->lcr = ioremap(info->fix.mmio_start, info->fix.mmio_len);
++ if (!par->lcr) {
++ retval = -ENOMEM;
++ goto err_ioremap_lcr;
++ }
++
++ par->vram = ioremap(info->fix.smem_start, info->fix.smem_len);
++ if (!par->vram) {
++ retval = -ENOMEM;
++ goto err_ioremap_vram;
++ }
++ info->screen_base = par->vram;
++
++ retval = request_irq(irq, &tmiofb_irq, IRQF_DISABLED,
++ dev->dev.bus_id, info);
++
++ if (retval)
++ goto err_request_irq;
++
++ retval = fb_find_mode(&info->var, info, mode_option,
++ data->modes, data->num_modes,
++ data->modes, 16);
++ if (!retval) {
++ retval = -EINVAL;
++ goto err_find_mode;
++ }
++
++ retval = cell->enable(dev);
++ if (retval)
++ goto err_enable;
++
++ retval = tmiofb_hw_init(dev);
++ if (retval)
++ goto err_hw_init;
++
++/* retval = tmiofb_set_par(info);
++ if (retval)
++ goto err_set_par;*/
++
++ retval = register_framebuffer(info);
++ if (retval < 0)
++ goto err_register_framebuffer;
++
++ printk(KERN_INFO "fb%d: %s frame buffer device\n",
++ info->node, info->fix.id);
++
++ return 0;
++
++err_register_framebuffer:
++/*err_set_par:*/
++ tmiofb_hw_stop(dev);
++err_hw_init:
++ cell->disable(dev);
++err_enable:
++err_find_mode:
++ free_irq(irq, info);
++err_request_irq:
++ iounmap(par->vram);
++err_ioremap_vram:
++ iounmap(par->lcr);
++err_ioremap_lcr:
++ iounmap(par->ccr);
++err_ioremap_ccr:
++ platform_set_drvdata(dev, NULL);
++ framebuffer_release(info);
++err_framebuffer_alloc:
++ return retval;
++}
++
++static int __devexit tmiofb_remove(struct platform_device *dev)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct fb_info *info = platform_get_drvdata(dev);
++ int irq = platform_get_irq(dev, 0);
++ struct tmiofb_par *par;
++
++ if (info) {
++ par = info->par;
++ unregister_framebuffer(info);
++
++ tmiofb_hw_stop(dev);
++
++ cell->disable(dev);
++
++ free_irq(irq, info);
++
++ iounmap(par->vram);
++ iounmap(par->lcr);
++ iounmap(par->ccr);
++
++ framebuffer_release(info);
++ platform_set_drvdata(dev, NULL);
++ }
++
++ return 0;
++}
++
++#if 0
++static void tmiofb_dump_regs(struct platform_device *dev)
++{
++ struct fb_info *info = platform_get_drvdata(dev);
++ struct tmiofb_par *par = info->par;
++ struct tmio_lhccr __iomem *ccr = par->ccr;
++ struct tmio_lcr __iomem *lcr = par->lcr;
++
++ printk("lhccr:\n");
++#define CCR_PR(n) printk("\t" #n " = \t%04x\n", ioread16(&ccr->n));
++ CCR_PR(cmd);
++ CCR_PR(revid);
++ CCR_PR(basel);
++ CCR_PR(baseh);
++ CCR_PR(ugcc);
++ CCR_PR(gcc);
++ CCR_PR(usc);
++ CCR_PR(vramrtc);
++ CCR_PR(vramsac);
++ CCR_PR(vrambc);
++#undef CCR_PR
++
++ printk("lcr: \n");
++#define LCR_PR(n) printk("\t" #n " = \t%04x\n", ioread16(&lcr->n));
++ LCR_PR(uis);
++ LCR_PR(vhpn);
++ LCR_PR(cfsal);
++ LCR_PR(cfsah);
++ LCR_PR(cfs);
++ LCR_PR(cfws);
++ LCR_PR(bbie);
++ LCR_PR(bbisc);
++ LCR_PR(ccs);
++ LCR_PR(bbes);
++ LCR_PR(cmdl);
++ LCR_PR(cmdh);
++ LCR_PR(cfc);
++ LCR_PR(ccifc);
++ LCR_PR(hwt);
++ LCR_PR(lcdccrc);
++ LCR_PR(lcdcc);
++ LCR_PR(lcdcopc);
++ LCR_PR(lcdis);
++ LCR_PR(lcdim);
++ LCR_PR(lcdie);
++ LCR_PR(gdsal);
++ LCR_PR(gdsah);
++ LCR_PR(vhpcl);
++ LCR_PR(vhpch);
++ LCR_PR(gm);
++ LCR_PR(ht);
++ LCR_PR(hds);
++ LCR_PR(hss);
++ LCR_PR(hse);
++ LCR_PR(hnp);
++ LCR_PR(vt);
++ LCR_PR(vds);
++ LCR_PR(vss);
++ LCR_PR(vse);
++ LCR_PR(cdln);
++ LCR_PR(iln);
++ LCR_PR(sp);
++ LCR_PR(misc);
++ LCR_PR(vihss);
++ LCR_PR(vivs);
++ LCR_PR(vive);
++ LCR_PR(vivss);
++ LCR_PR(vccis);
++ LCR_PR(vidwsal);
++ LCR_PR(vidwsah);
++ LCR_PR(vidrsal);
++ LCR_PR(vidrsah);
++ LCR_PR(vipddst);
++ LCR_PR(vipddet);
++ LCR_PR(vie);
++ LCR_PR(vcs);
++ LCR_PR(vphwc);
++ LCR_PR(vphs);
++ LCR_PR(vpvwc);
++ LCR_PR(vpvs);
++ LCR_PR(plhpix);
++ LCR_PR(xs);
++ LCR_PR(xckhw);
++ LCR_PR(sths);
++ LCR_PR(vt2);
++ LCR_PR(ycksw);
++ LCR_PR(ysts);
++ LCR_PR(ppols);
++ LCR_PR(precw);
++ LCR_PR(vclkhw);
++ LCR_PR(oc);
++#undef LCR_PR
++}
++#endif
++
++#ifdef CONFIG_PM
++static int tmiofb_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct fb_info *info = platform_get_drvdata(dev);
++ struct tmiofb_par *par = info->par;
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ int retval = 0;
++
++ acquire_console_sem();
++
++ fb_set_suspend(info, 1);
++
++ if (info->fbops->fb_sync)
++ info->fbops->fb_sync(info);
++
++
++ printk(KERN_INFO "tmiofb: switching to polling\n");
++ par->use_polling = true;
++ tmiofb_hw_stop(dev);
++
++ if (cell->suspend)
++ retval = cell->suspend(dev);
++
++ release_console_sem();
++
++ return retval;
++}
++
++static int tmiofb_resume(struct platform_device *dev)
++{
++ struct fb_info *info = platform_get_drvdata(dev);
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ int retval;
++
++ acquire_console_sem();
++
++ if (cell->resume) {
++ retval = cell->resume(dev);
++ if (retval)
++ return retval;
++ }
++
++ tmiofb_irq(-1, info);
++
++ tmiofb_hw_init(dev);
++
++ tmiofb_hw_mode(dev);
++
++ fb_set_suspend(info, 0);
++ release_console_sem();
++ return 0;
++}
++#endif
++
++static struct platform_driver tmiofb_driver = {
++ .driver.name = "tmio-fb",
++ .driver.owner = THIS_MODULE,
++ .probe = tmiofb_probe,
++ .remove = __devexit_p(tmiofb_remove),
++#ifdef CONFIG_PM
++ .suspend = tmiofb_suspend,
++ .resume = tmiofb_resume,
++#endif
++};
++
++/*--------------------------------------------------------------------------*/
++
++#ifndef MODULE
++static void __init tmiofb_setup(char *options)
++{
++ char *this_opt;
++
++ if (!options || !*options)
++ return;
++
++ while ((this_opt = strsep(&options, ",")) != NULL) {
++ if (!*this_opt) continue;
++ /*
++ * FIXME
++ */
++ }
++}
++#endif
++
++static int __init tmiofb_init(void)
++{
++#ifndef MODULE
++ char *option = NULL;
++
++ if (fb_get_options("tmiofb", &option))
++ return -ENODEV;
++ tmiofb_setup(option);
++#endif
++ return platform_driver_register(&tmiofb_driver);
++}
++
++static void __exit tmiofb_cleanup(void)
++{
++ platform_driver_unregister(&tmiofb_driver);
++}
++
++module_init(tmiofb_init);
++module_exit(tmiofb_cleanup);
++
++MODULE_DESCRIPTION("TMIO framebuffer driver");
++MODULE_AUTHOR("Chris Humbert, Dirk Opfer, Dmitry Baryshkov");
++MODULE_LICENSE("GPL");
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0010-OHCI-driver-for-TMIO-devices.patch b/recipes/linux/linux-rp-2.6.24/tosa/0010-OHCI-driver-for-TMIO-devices.patch
new file mode 100644
index 0000000000..f358c069d0
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0010-OHCI-driver-for-TMIO-devices.patch
@@ -0,0 +1,431 @@
+From e5f06830bc8d3ef4792c9c0569825d0347b39852 Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Fri, 4 Jan 2008 18:43:31 +0000
+Subject: [PATCH 10/64] OHCI driver for TMIO devices
+
+---
+ drivers/usb/Kconfig | 1 +
+ drivers/usb/host/Kconfig | 1 +
+ drivers/usb/host/ohci-hcd.c | 5 +
+ drivers/usb/host/ohci-tmio.c | 369 ++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 376 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/usb/host/ohci-tmio.c
+
+diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
+index 7580aa5..8912042 100644
+--- a/drivers/usb/Kconfig
++++ b/drivers/usb/Kconfig
+@@ -36,6 +36,7 @@ config USB_ARCH_HAS_OHCI
+ default y if ARCH_EP93XX
+ default y if ARCH_AT91
+ default y if ARCH_PNX4008
++ default y if MFD_TC6393XB
+ # PPC:
+ default y if STB03xxx
+ default y if PPC_MPC52xx
+diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
+index 49a91c5..5ae3589 100644
+--- a/drivers/usb/host/Kconfig
++++ b/drivers/usb/host/Kconfig
+@@ -101,6 +101,7 @@ config USB_OHCI_HCD
+ depends on USB && USB_ARCH_HAS_OHCI
+ select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
+ select I2C if ARCH_PNX4008
++ select DMABOUNCE if MFD_TC6393XB
+ ---help---
+ The Open Host Controller Interface (OHCI) is a standard for accessing
+ USB 1.1 host controller hardware. It does more in hardware than Intel's
+diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
+index ecfe800..77abf3e 100644
+--- a/drivers/usb/host/ohci-hcd.c
++++ b/drivers/usb/host/ohci-hcd.c
+@@ -1043,6 +1043,11 @@ MODULE_LICENSE ("GPL");
+ #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver
+ #endif
+
++#ifdef CONFIG_MFD_TC6393XB
++#include "ohci-tmio.c"
++#define PLATFORM_DRIVER ohci_hcd_tmio_driver
++#endif
++
+ #ifdef CONFIG_USB_OHCI_HCD_SSB
+ #include "ohci-ssb.c"
+ #define SSB_OHCI_DRIVER ssb_ohci_driver
+diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c
+new file mode 100644
+index 0000000..be609f3
+--- /dev/null
++++ b/drivers/usb/host/ohci-tmio.c
+@@ -0,0 +1,369 @@
++/*
++ * OHCI HCD(Host Controller Driver) for USB.
++ *
++ *(C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
++ *(C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
++ *(C) Copyright 2002 Hewlett-Packard Company
++ *
++ * Bus glue for Toshiba Mobile IO(TMIO) Controller's OHCI core
++ *(C) Copyright 2005 Chris Humbert <mahadri-usb@drigon.com>
++ *
++ * This is known to work with the following variants:
++ * TC6393XB revision 3 (32kB SRAM)
++ *
++ * The TMIO's OHCI core DMAs through a small internal buffer that
++ * is directly addressable by the CPU. dma_declare_coherent_memory
++ * and DMA bounce buffers allow the higher-level OHCI host driver to
++ * work. However, the dma API doesn't handle dma mapping failures
++ * well(dma_sg_map() is a prime example), so it is unusable.
++ *
++ * This HC pretends be a PIO-ish controller and uses the kernel's
++ * generic allocator for the entire SRAM. Using the USB core's
++ * usb_operations, we provide hcd_buffer_alloc/free. Using the OHCI's
++ * ohci_ops, we provide memory management for OHCI's TDs and EDs. We
++ * internally queue a URB's TDs until enough dma memory is available
++ * to enqueue them with the HC.
++ *
++ * Written from sparse documentation from Toshiba and Sharp's driver
++ * for the 2.4 kernel,
++ * usb-ohci-tc6393.c(C) Copyright 2004 Lineo Solutions, Inc.
++ *
++ * 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/fs.h>
++#include <linux/mount.h>
++#include <linux/pagemap.h>
++#include <linux/init.h>
++#include <linux/namei.h>
++#include <linux/sched.h>*/
++#include <linux/platform_device.h>
++#include <linux/mfd-core.h>
++#include <linux/mfd/tmio.h>
++#include <linux/dma-mapping.h>
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * USB Host Controller Configuration Register
++ */
++struct tmio_uhccr {
++ u8 x00[8];
++ u8 revid; /* 0x08 Revision ID */
++ u8 x01[7];
++ u16 basel; /* 0x10 USB Control Register Base Address Low */
++ u16 baseh; /* 0x12 USB Control Register Base Address High */
++ u8 x02[0x2c];
++ u8 ilme; /* 0x40 Internal Local Memory Enable */
++ u8 x03[0x0b];
++ u16 pm; /* 0x4c Power Management */
++ u8 x04[2];
++ u8 intc; /* 0x50 INT Control */
++ u8 x05[3];
++ u16 lmw1l; /* 0x54 Local Memory Window 1 LMADRS Low */
++ u16 lmw1h; /* 0x56 Local Memory Window 1 LMADRS High */
++ u16 lmw1bl; /* 0x58 Local Memory Window 1 Base Address Low */
++ u16 lmw1bh; /* 0x5A Local Memory Window 1 Base Address High */
++ u16 lmw2l; /* 0x5C Local Memory Window 2 LMADRS Low */
++ u16 lmw2h; /* 0x5E Local Memory Window 2 LMADRS High */
++ u16 lmw2bl; /* 0x60 Local Memory Window 2 Base Address Low */
++ u16 lmw2bh; /* 0x62 Local Memory Window 2 Base Address High */
++ u8 x06[0x98];
++ u8 misc; /* 0xFC MISC */
++ u8 x07[3];
++} __attribute__((packed));
++
++#define UHCCR_PM_GKEN 0x0001
++#define UHCCR_PM_CKRNEN 0x0002
++#define UHCCR_PM_USBPW1 0x0004
++#define UHCCR_PM_USBPW2 0x0008
++#define UHCCR_PM_PMEE 0x0100
++#define UHCCR_PM_PMES 0x8000
++
++/*-------------------------------------------------------------------------*/
++
++struct tmio_hcd {
++ struct tmio_uhccr __iomem *ccr;
++};
++
++#define hcd_to_tmio(hcd) ((struct tmio_hcd *)(hcd_to_ohci(hcd) + 1))
++#define ohci_to_tmio(ohci) ((struct tmio_hcd *)(ohci + 1))
++
++/*-------------------------------------------------------------------------*/
++
++static void tmio_stop_hc(struct platform_device *dev)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct usb_hcd *hcd = platform_get_drvdata(dev);
++ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
++ struct tmio_uhccr __iomem *ccr = tmio->ccr;
++ u16 pm;
++
++ pm = UHCCR_PM_GKEN | UHCCR_PM_CKRNEN | UHCCR_PM_USBPW1 | UHCCR_PM_USBPW2;
++ iowrite8(0, &ccr->intc);
++ iowrite8(0, &ccr->ilme);
++ iowrite16(0, &ccr->basel);
++ iowrite16(0, &ccr->baseh);
++ iowrite16(pm, &ccr->pm);
++
++ cell->disable(dev);
++}
++
++static void tmio_start_hc(struct platform_device *dev)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct usb_hcd *hcd = platform_get_drvdata(dev);
++ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
++ struct tmio_uhccr __iomem *ccr = tmio->ccr;
++ u16 pm;
++ unsigned long base = hcd->rsrc_start;
++
++ pm = UHCCR_PM_CKRNEN | UHCCR_PM_GKEN | UHCCR_PM_PMEE | UHCCR_PM_PMES;
++ cell->enable(dev);
++
++ iowrite16(pm, &ccr->pm);
++ iowrite16(base, &ccr->basel);
++ iowrite16(base >> 16, &ccr->baseh);
++ iowrite8(1, &ccr->ilme);
++ iowrite8(2, &ccr->intc);
++
++ dev_info(&dev->dev, "revision %d @ 0x%08llx, irq %d\n",
++ ioread8(&ccr->revid), hcd->rsrc_start, hcd->irq);
++}
++
++static int usb_hcd_tmio_probe(const struct hc_driver *driver,
++ struct platform_device *dev)
++{
++ struct resource *config = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_CONFIG);
++ struct resource *regs = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_CONTROL);
++ struct resource *sram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_SRAM);
++ int irq = platform_get_irq(dev, 0);
++ struct tmio_hcd *tmio;
++ struct ohci_hcd *ohci;
++ struct usb_hcd *hcd;
++ int retval;
++
++ if (usb_disabled())
++ return -ENODEV;
++
++ hcd = usb_create_hcd(driver, &dev->dev, dev->dev.bus_id);
++ if (!hcd) {
++ retval = -ENOMEM;
++ goto err_usb_create_hcd;
++ }
++
++ hcd->rsrc_start = regs->start;
++ hcd->rsrc_len = regs->end - regs->start + 1;
++
++ tmio = hcd_to_tmio(hcd);
++
++ tmio->ccr = ioremap(config->start, config->end - config->start + 1);
++ if (!tmio->ccr) {
++ retval = -ENOMEM;
++ goto err_ioremap_ccr;
++ }
++
++ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
++ if (!hcd->regs) {
++ retval = -ENOMEM;
++ goto err_ioremap_regs;
++ }
++
++ if (dma_declare_coherent_memory(&dev->dev, sram->start,
++ sram->start,
++ sram->end - sram->start + 1,
++ DMA_MEMORY_MAP) != DMA_MEMORY_MAP) {
++ retval = -EBUSY;
++ goto err_dma_declare;
++ }
++
++ retval = dmabounce_register_dev(&dev->dev, 512, 4096);
++ if (retval)
++ goto err_dmabounce_register_dev;
++
++ tmio_start_hc(dev);
++ ohci = hcd_to_ohci(hcd);
++ ohci_hcd_init(ohci);
++
++ retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
++
++ if (retval == 0)
++ return retval;
++
++ tmio_stop_hc(dev);
++
++ dmabounce_unregister_dev(&dev->dev);
++err_dmabounce_register_dev:
++ dma_release_declared_memory(&dev->dev);
++err_dma_declare:
++ iounmap(hcd->regs);
++err_ioremap_regs:
++ iounmap(tmio->ccr);
++err_ioremap_ccr:
++ usb_put_hcd(hcd);
++err_usb_create_hcd:
++
++ return retval;
++}
++
++static void usb_hcd_tmio_remove(struct usb_hcd *hcd, struct platform_device *dev)
++{
++ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
++
++ usb_remove_hcd(hcd);
++ tmio_stop_hc(dev);
++ dmabounce_unregister_dev(&dev->dev);
++ dma_release_declared_memory(&dev->dev);
++ iounmap(hcd->regs);
++ iounmap(tmio->ccr);
++ usb_put_hcd(hcd);
++}
++
++static int __devinit
++ohci_tmio_start(struct usb_hcd *hcd)
++{
++ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
++ int retval;
++
++ if ((retval = ohci_init(ohci)) < 0)
++ return retval;
++
++ if ((retval = ohci_run(ohci)) < 0) {
++ err("can't start %s", hcd->self.bus_name);
++ ohci_stop(hcd);
++ return retval;
++ }
++
++ return 0;
++}
++
++static const struct hc_driver ohci_tmio_hc_driver = {
++ .description = hcd_name,
++ .product_desc = "TMIO OHCI USB Host Controller",
++ .hcd_priv_size = sizeof(struct ohci_hcd) + sizeof (struct tmio_hcd),
++
++ /* generic hardware linkage */
++ .irq = ohci_irq,
++ .flags = HCD_USB11 | HCD_MEMORY,
++
++ /* basic lifecycle operations */
++ .start = ohci_tmio_start,
++ .stop = ohci_stop,
++ .shutdown = ohci_shutdown,
++
++ /* managing i/o requests and associated device resources */
++ .urb_enqueue = ohci_urb_enqueue,
++ .urb_dequeue = ohci_urb_dequeue,
++ .endpoint_disable = ohci_endpoint_disable,
++
++ /* scheduling support */
++ .get_frame_number = ohci_get_frame,
++
++ /* root hub support */
++ .hub_status_data = ohci_hub_status_data,
++ .hub_control = ohci_hub_control,
++ .hub_irq_enable = ohci_rhsc_enable,
++#ifdef CONFIG_PM
++ .bus_suspend = ohci_bus_suspend,
++ .bus_resume = ohci_bus_resume,
++#endif
++ .start_port_reset = ohci_start_port_reset,
++};
++
++/*-------------------------------------------------------------------------*/
++static struct platform_driver ohci_hcd_tmio_driver;
++
++static int
++tmio_dmabounce_check(struct device *dev, dma_addr_t dma, size_t size, void *data)
++{
++ struct resource *sram = data;
++#ifdef DEBUG
++ printk(KERN_ERR "tmio_dmabounce_check: %08x %d\n", dma, size);
++#endif
++
++ if (dev->driver != &ohci_hcd_tmio_driver.driver)
++ return 0;
++
++ if (sram->start <= dma && dma + size <= sram->end)
++ return 0;
++
++ return 1;
++}
++
++static u64 dma_mask = DMA_32BIT_MASK;
++
++static int ohci_hcd_tmio_drv_probe(struct platform_device *dev)
++{
++ struct resource *sram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_SRAM);
++
++ dev->dev.dma_mask = &dma_mask;
++ dev->dev.coherent_dma_mask = DMA_32BIT_MASK;
++
++ dmabounce_register_checker(tmio_dmabounce_check, sram);
++
++ return usb_hcd_tmio_probe(&ohci_tmio_hc_driver, dev);
++}
++
++static int ohci_hcd_tmio_drv_remove(struct platform_device *dev)
++{
++ struct usb_hcd *hcd = platform_get_drvdata(dev);
++ struct resource *sram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_SRAM);
++
++ usb_hcd_tmio_remove(hcd, dev);
++
++ platform_set_drvdata(dev, NULL);
++
++ dmabounce_remove_checker(tmio_dmabounce_check, sram);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct usb_hcd *hcd = platform_get_drvdata(dev);
++ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
++
++ if (time_before(jiffies, ohci->next_statechange))
++ msleep(5);
++ ohci->next_statechange = jiffies;
++
++ tmio_stop_hc(dev);
++ hcd->state = HC_STATE_SUSPENDED;
++ dev->dev.power.power_state = PMSG_SUSPEND;
++
++ return 0;
++}
++
++static int ohci_hcd_tmio_drv_resume(struct platform_device *dev)
++{
++ struct usb_hcd *hcd = platform_get_drvdata(dev);
++ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
++
++ if (time_before(jiffies, ohci->next_statechange))
++ msleep(5);
++ ohci->next_statechange = jiffies;
++
++ tmio_start_hc(dev);
++
++ dev->dev.power.power_state = PMSG_ON;
++ usb_hcd_resume_root_hub(hcd);
++
++ return 0;
++}
++#endif
++
++static struct platform_driver ohci_hcd_tmio_driver = {
++ .probe = ohci_hcd_tmio_drv_probe,
++ .remove = ohci_hcd_tmio_drv_remove,
++ .shutdown = usb_hcd_platform_shutdown,
++#ifdef CONFIG_PM
++ .suspend = ohci_hcd_tmio_drv_suspend,
++ .resume = ohci_hcd_tmio_drv_resume,
++#endif
++ .driver = {
++ .name = "tmio-ohci",
++ },
++};
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch b/recipes/linux/linux-rp-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch
new file mode 100644
index 0000000000..6ff752d1ff
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch
@@ -0,0 +1,891 @@
+From b358a64c1fdd1eb80da57f919c893d910db95e37 Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Sat, 29 Dec 2007 15:26:19 +0000
+Subject: [PATCH 11/64] MMC driver for TMIO devices
+
+---
+ drivers/mmc/host/Kconfig | 6 +
+ drivers/mmc/host/Makefile | 1 +
+ drivers/mmc/host/tmio_mmc.c | 633 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/mmc/host/tmio_mmc.h | 205 ++++++++++++++
+ 4 files changed, 845 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/mmc/host/tmio_mmc.c
+ create mode 100644 drivers/mmc/host/tmio_mmc.h
+
+diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
+index 5fef678..f8f9b7e 100644
+--- a/drivers/mmc/host/Kconfig
++++ b/drivers/mmc/host/Kconfig
+@@ -130,3 +130,9 @@ config MMC_SPI
+
+ If unsure, or if your system has no SPI master driver, say N.
+
++config MMC_TMIO
++ tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
++ depends on MMC
++ help
++ This provides support for the SD/MMC cell found in TC6393XB,
++ T7L66XB and also ipaq ASIC3
+diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
+index 3877c87..7ac956b 100644
+--- a/drivers/mmc/host/Makefile
++++ b/drivers/mmc/host/Makefile
+@@ -17,4 +17,5 @@ obj-$(CONFIG_MMC_OMAP) += omap.o
+ obj-$(CONFIG_MMC_AT91) += at91_mci.o
+ obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
+ obj-$(CONFIG_MMC_SPI) += mmc_spi.o
++obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
+
+diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
+new file mode 100644
+index 0000000..735c386
+--- /dev/null
++++ b/drivers/mmc/host/tmio_mmc.c
+@@ -0,0 +1,633 @@
++/*
++ * linux/drivers/mmc/tmio_mmc.c
++ *
++ * Copyright (C) 2004 Ian Molton
++ * Copyright (C) 2007 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.
++ *
++ * Driver for the MMC / SD / SDIO cell found in:
++ *
++ * TC6393XB TC6391XB TC6387XB T7L66XB
++ *
++ * This driver draws mainly on scattered spec sheets, Reverse engineering
++ * of the toshiba e800 SD driver and some parts of the 2.4 ASIC3 driver (4 bit
++ * support). (Further 4 bit support from a later datasheet).
++ *
++ * TODO:
++ * Investigate using a workqueue for PIO transfers
++ * Eliminate FIXMEs
++ * SDIO support
++ * Better Power management
++ * Handle MMC errors better
++ * double buffer support
++ *
++ */
++#include <linux/module.h>
++#include <linux/irq.h>
++#include <linux/device.h>
++#include <linux/delay.h>
++#include <linux/mmc/mmc.h>
++#include <linux/mmc/host.h>
++#include <linux/mfd-core.h>
++#include <linux/mfd/tmio.h>
++
++#include "tmio_mmc.h"
++
++/*
++ * Fixme - documentation conflicts on what the clock values are for the
++ * various dividers.
++ * One document I have says that its a divisor of a 24MHz clock, another 33.
++ * This probably depends on HCLK for a given platform, so we may need to
++ * require HCLK be passed to us from the MFD core.
++ *
++ */
++
++static void tmio_mmc_set_clock (struct tmio_mmc_host *host, int new_clock) {
++ struct tmio_mmc_cnf __iomem *cnf = host->cnf;
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++ u32 clk = 0, clock;
++
++ if (new_clock) {
++ for(clock = 46875, clk = 0x100; new_clock >= (clock<<1); ){
++ clock <<= 1;
++ clk >>= 1;
++ }
++ if(clk & 0x1)
++ clk = 0x20000;
++
++ clk >>= 2;
++ if(clk & 0x8000) /* For full speed we disable the divider. */
++ writeb(0, &cnf->sd_clk_mode);
++ else
++ writeb(1, &cnf->sd_clk_mode);
++ clk |= 0x100;
++ }
++
++ writew(clk, &ctl->sd_card_clk_ctl);
++}
++
++static void tmio_mmc_clk_stop (struct tmio_mmc_host *host) {
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++
++ writew(0x0000, &ctl->clk_and_wait_ctl);
++ msleep(10);
++ writew(readw(&ctl->sd_card_clk_ctl) & ~0x0100, &ctl->sd_card_clk_ctl);
++ msleep(10);
++}
++
++static void tmio_mmc_clk_start (struct tmio_mmc_host *host) {
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++
++ writew(readw(&ctl->sd_card_clk_ctl) | 0x0100, &ctl->sd_card_clk_ctl);
++ msleep(10);
++ writew(0x0100, &ctl->clk_and_wait_ctl);
++ msleep(10);
++}
++
++static void reset(struct tmio_mmc_host *host) {
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++
++ /* FIXME - should we set stop clock reg here */
++ writew(0x0000, &ctl->reset_sd);
++ writew(0x0000, &ctl->reset_sdio);
++ msleep(10);
++ writew(0x0001, &ctl->reset_sd);
++ writew(0x0001, &ctl->reset_sdio);
++ msleep(10);
++}
++
++static void
++tmio_mmc_finish_request(struct tmio_mmc_host *host)
++{
++ struct mmc_request *mrq = host->mrq;
++
++ host->mrq = NULL;
++ host->cmd = NULL;
++ host->data = NULL;
++
++ mmc_request_done(host->mmc, mrq);
++}
++
++/* These are the bitmasks the tmio chip requires to implement the MMC response
++ * types. Note that R1 and R6 are the same in this scheme. */
++#define APP_CMD 0x0040
++#define RESP_NONE 0x0300
++#define RESP_R1 0x0400
++#define RESP_R1B 0x0500
++#define RESP_R2 0x0600
++#define RESP_R3 0x0700
++#define DATA_PRESENT 0x0800
++#define TRANSFER_READ 0x1000
++#define TRANSFER_MULTI 0x2000
++#define SECURITY_CMD 0x4000
++
++static void
++tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd)
++{
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++ struct mmc_data *data = host->data;
++ int c = cmd->opcode;
++
++ if(cmd->opcode == MMC_STOP_TRANSMISSION) {
++ writew(0x001, &ctl->stop_internal_action);
++ return;
++ }
++
++ switch(mmc_resp_type(cmd)) {
++ case MMC_RSP_NONE: c |= RESP_NONE; break;
++ case MMC_RSP_R1: c |= RESP_R1; break;
++ case MMC_RSP_R1B: c |= RESP_R1B; break;
++ case MMC_RSP_R2: c |= RESP_R2; break;
++ case MMC_RSP_R3: c |= RESP_R3; break;
++ default:
++ DBG("Unknown response type %d\n", mmc_resp_type(cmd));
++ }
++
++ host->cmd = cmd;
++
++/* FIXME - this seems to be ok comented out but the spec suggest this bit should
++ * be set when issuing app commands.
++ * if(cmd->flags & MMC_FLAG_ACMD)
++ * c |= APP_CMD;
++ */
++ if(data) {
++ c |= DATA_PRESENT;
++ if(data->blocks > 1) {
++ writew(0x100, &ctl->stop_internal_action);
++ c |= TRANSFER_MULTI;
++ }
++ if(data->flags & MMC_DATA_READ)
++ c |= TRANSFER_READ;
++ }
++
++ enable_mmc_irqs(ctl, TMIO_MASK_CMD);
++
++ /* Fire off the command */
++ tmio_iowrite32(cmd->arg, ctl->arg_reg);
++ writew(c, &ctl->sd_cmd);
++}
++
++/* This chip always returns (at least?) as much data as you ask for.
++ * Im unsure what happens if you ask for less than a block. This should be
++ * looked into to ensure that a funny length read doesnt hose the controller.
++ *
++ * FIXME - this chip cannot do 1 and 2 byte data requests in 4 bit mode
++ */
++static inline void tmio_mmc_pio_irq(struct tmio_mmc_host *host) {
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++ struct mmc_data *data = host->data;
++ unsigned short *buf;
++ unsigned int count;
++ unsigned long flags;
++
++ if(!data){
++ DBG("Spurious PIO IRQ\n");
++ return;
++ }
++
++ buf = (unsigned short *)(tmio_mmc_kmap_atomic(host, &flags) +
++ host->sg_off);
++
++ /* Ensure we dont read more than one block. The chip will interrupt us
++ * When the next block is available.
++ * FIXME - this is probably not true now IRQ handling is fixed
++ */
++ count = host->sg_ptr->length - host->sg_off;
++ if(count > data->blksz)
++ count = data->blksz;
++
++ DBG("count: %08x offset: %08x flags %08x\n",
++ count, host->sg_off, data->flags);
++
++ /* Transfer the data */
++ if(data->flags & MMC_DATA_READ)
++ readsw(&ctl->sd_data_port[0], buf, count >> 1);
++ else
++ writesw(&ctl->sd_data_port[0], buf, count >> 1);
++
++ host->sg_off += count;
++
++ tmio_mmc_kunmap_atomic(host, &flags);
++
++ if(host->sg_off == host->sg_ptr->length)
++ tmio_mmc_next_sg(host);
++
++ return;
++}
++
++static inline void tmio_mmc_data_irq(struct tmio_mmc_host *host) {
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++ struct mmc_data *data = host->data;
++
++ host->data = NULL;
++
++ if(!data){
++ DBG("Spurious data end IRQ\n");
++ return;
++ }
++
++ /* FIXME - return correct transfer count on errors */
++ if (!data->error)
++ data->bytes_xfered = data->blocks * data->blksz;
++ else
++ data->bytes_xfered = 0;
++
++ DBG("Completed data request\n");
++
++ /*FIXME - other drivers allow an optional stop command of any given type
++ * which we dont do, as the chip can auto generate them.
++ * Perhaps we can be smarter about when to use auto CMD12 and
++ * only issue the auto request when we know this is the desired
++ * stop command, allowing fallback to the stop command the
++ * upper layers expect. For now, we do what works.
++ */
++
++ writew(0x000, &ctl->stop_internal_action);
++
++ if(data->flags & MMC_DATA_READ)
++ disable_mmc_irqs(ctl, TMIO_MASK_READOP);
++ else
++ disable_mmc_irqs(ctl, TMIO_MASK_WRITEOP);
++
++ tmio_mmc_finish_request(host);
++}
++
++static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat) {
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++ struct mmc_command *cmd = host->cmd;
++
++ if(!host->cmd) {
++ DBG("Spurious CMD irq\n");
++ return;
++ }
++
++ host->cmd = NULL;
++
++ /* This controller is sicker than the PXA one. not only do we need to
++ * drop the top 8 bits of the first response word, we also need to
++ * modify the order of the response for short response command types.
++ */
++
++ /* FIXME - this works but readl is wrong and will break on asic3... */
++ cmd->resp[3] = tmio_ioread32(&ctl->response[0]);
++ cmd->resp[2] = tmio_ioread32(&ctl->response[2]);
++ cmd->resp[1] = tmio_ioread32(&ctl->response[4]);
++ cmd->resp[0] = tmio_ioread32(&ctl->response[6]);
++
++ if(cmd->flags & MMC_RSP_136) {
++ cmd->resp[0] = (cmd->resp[0] <<8) | (cmd->resp[1] >>24);
++ cmd->resp[1] = (cmd->resp[1] <<8) | (cmd->resp[2] >>24);
++ cmd->resp[2] = (cmd->resp[2] <<8) | (cmd->resp[3] >>24);
++ cmd->resp[3] <<= 8;
++ }
++ else if(cmd->flags & MMC_RSP_R3) {
++ cmd->resp[0] = cmd->resp[3];
++ }
++
++ if (stat & TMIO_STAT_CMDTIMEOUT)
++ cmd->error = -ETIMEDOUT;
++ else if (stat & TMIO_STAT_CRCFAIL && cmd->flags & MMC_RSP_CRC)
++ cmd->error = -EILSEQ;
++
++ /* If there is data to handle we enable data IRQs here, and
++ * we will ultimatley finish the request in the data_end handler.
++ * If theres no data or we encountered an error, finish now.
++ */
++ if(host->data && !cmd->error){
++ if(host->data->flags & MMC_DATA_READ)
++ enable_mmc_irqs(ctl, TMIO_MASK_READOP);
++ else
++ enable_mmc_irqs(ctl, TMIO_MASK_WRITEOP);
++ }
++ else {
++ tmio_mmc_finish_request(host);
++ }
++
++ return;
++}
++
++
++static irqreturn_t tmio_mmc_irq(int irq, void *devid)
++{
++ struct tmio_mmc_host *host = devid;
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++ unsigned int ireg, irq_mask, status;
++
++ DBG("MMC IRQ begin\n");
++
++ status = tmio_ioread32(ctl->status);
++ irq_mask = tmio_ioread32(ctl->irq_mask);
++ ireg = status & TMIO_MASK_IRQ & ~irq_mask;
++
++#ifdef CONFIG_MMC_DEBUG
++ debug_status(status);
++ debug_status(ireg);
++#endif
++ if (!ireg) {
++ disable_mmc_irqs(ctl, status & ~irq_mask);
++#ifdef CONFIG_MMC_DEBUG
++ WARN("tmio_mmc: Spurious MMC irq, disabling! 0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg);
++ debug_status(status);
++#endif
++ goto out;
++ }
++
++ while (ireg) {
++ /* Card insert / remove attempts */
++ if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)){
++ ack_mmc_irqs(ctl, TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE);
++ mmc_detect_change(host->mmc,0);
++ }
++
++ /* CRC and other errors */
++/* if (ireg & TMIO_STAT_ERR_IRQ)
++ * handled |= tmio_error_irq(host, irq, stat);
++ */
++
++ /* Command completion */
++ if (ireg & TMIO_MASK_CMD) {
++ tmio_mmc_cmd_irq(host, status);
++ ack_mmc_irqs(ctl, TMIO_MASK_CMD);
++ }
++
++ /* Data transfer */
++ if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) {
++ ack_mmc_irqs(ctl, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ);
++ tmio_mmc_pio_irq(host);
++ }
++
++ /* Data transfer completion */
++ if (ireg & TMIO_STAT_DATAEND) {
++ tmio_mmc_data_irq(host);
++ ack_mmc_irqs(ctl, TMIO_STAT_DATAEND);
++ }
++
++ /* Check status - keep going until we've handled it all */
++ status = tmio_ioread32(ctl->status);
++ irq_mask = tmio_ioread32(ctl->irq_mask);
++ ireg = status & TMIO_MASK_IRQ & ~irq_mask;
++
++#ifdef CONFIG_MMC_DEBUG
++ DBG("Status at end of loop: %08x\n", status);
++ debug_status(status);
++#endif
++ }
++ DBG("MMC IRQ end\n");
++
++out:
++ return IRQ_HANDLED;
++}
++
++static void tmio_mmc_start_data(struct tmio_mmc_host *host, struct mmc_data *data)
++{
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++
++ DBG("setup data transfer: blocksize %08x nr_blocks %d\n",
++ data->blksz, data->blocks);
++
++ tmio_mmc_init_sg(host, data);
++ host->data = data;
++
++ /* Set transfer length / blocksize */
++ writew(data->blksz, &ctl->sd_xfer_len);
++ writew(data->blocks, &ctl->xfer_blk_count);
++}
++
++/* Process requests from the MMC layer */
++static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
++{
++ struct tmio_mmc_host *host = mmc_priv(mmc);
++
++ WARN_ON(host->mrq != NULL);
++
++ host->mrq = mrq;
++
++ /* If we're performing a data request we need to setup some
++ extra information */
++ if (mrq->data)
++ tmio_mmc_start_data(host, mrq->data);
++
++ tmio_mmc_start_command(host, mrq->cmd);
++}
++
++/* Set MMC clock / power.
++ * Note: This controller uses a simple divider scheme therefore it cannot
++ * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as
++ * MMC wont run that fast, it has to be clocked at 12MHz which is the next
++ * slowest setting.
++ */
++static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++ struct tmio_mmc_host *host = mmc_priv(mmc);
++ struct tmio_mmc_cnf __iomem *cnf = host->cnf;
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++
++ if(ios->clock)
++ tmio_mmc_set_clock (host, ios->clock);
++
++ /* Power sequence - OFF -> ON -> UP */
++ switch (ios->power_mode) {
++ case MMC_POWER_OFF:
++ writeb(0x00, &cnf->pwr_ctl[1]); /* power down SD bus */
++ tmio_mmc_clk_stop(host);
++ break;
++ case MMC_POWER_ON:
++ writeb(0x02, &cnf->pwr_ctl[1]); /* power up SD bus */
++ break;
++ case MMC_POWER_UP:
++ tmio_mmc_clk_start(host); /* start bus clock */
++ break;
++ }
++
++ switch (ios->bus_width) {
++ case MMC_BUS_WIDTH_1:
++ writew(0x80e0, &ctl->sd_mem_card_opt);
++ break;
++ case MMC_BUS_WIDTH_4:
++ writew(0x00e0, &ctl->sd_mem_card_opt);
++ break;
++ }
++
++ /* Potentially we may need a 140us pause here. FIXME */
++ udelay(140);
++}
++
++static int tmio_mmc_get_ro(struct mmc_host *mmc) {
++ struct tmio_mmc_host *host = mmc_priv(mmc);
++ struct tmio_mmc_ctl __iomem *ctl = host->ctl;
++
++ return (readw(&ctl->status[0]) & TMIO_STAT_WRPROTECT)?0:1;
++}
++
++static struct mmc_host_ops tmio_mmc_ops = {
++ .request = tmio_mmc_request,
++ .set_ios = tmio_mmc_set_ios,
++ .get_ro = tmio_mmc_get_ro,
++};
++
++static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state) {
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct mmc_host *mmc = platform_get_drvdata(dev);
++ int ret;
++
++ ret = mmc_suspend_host(mmc, state);
++
++ /* Tell MFD core it can disable us now.*/
++ if(!ret && cell->disable)
++ cell->disable(dev);
++
++ return ret;
++}
++
++static int tmio_mmc_resume(struct platform_device *dev) {
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct mmc_host *mmc = platform_get_drvdata(dev);
++ struct tmio_mmc_host *host = mmc_priv(mmc);
++ struct tmio_mmc_cnf __iomem *cnf = host->cnf;
++
++ /* Enable the MMC/SD Control registers */
++ writew(SDCREN, &cnf->cmd);
++ writel(dev->resource[0].start & 0xfffe, &cnf->ctl_base);
++
++ /* Tell the MFD core we are ready to be enabled */
++ if(cell->enable)
++ cell->enable(dev);
++
++ mmc_resume_host(mmc);
++
++ return 0;
++}
++
++static int __devinit tmio_mmc_probe(struct platform_device *dev)
++{
++ struct mfd_cell *cell = mfd_get_cell(dev);
++ struct tmio_mmc_cnf __iomem *cnf;
++ struct tmio_mmc_ctl __iomem *ctl;
++ struct tmio_mmc_host *host;
++ struct mmc_host *mmc;
++ int ret = -ENOMEM;
++
++ mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &dev->dev);
++ if (!mmc) {
++ goto out;
++ }
++
++ host = mmc_priv(mmc);
++ host->mmc = mmc;
++ platform_set_drvdata(dev, mmc); /* Used so we can de-init safely. */
++
++ host->cnf = cnf = ioremap((unsigned long)dev->resource[1].start,
++ (unsigned long)dev->resource[1].end -
++ (unsigned long)dev->resource[1].start);
++ if(!host->cnf)
++ goto host_free;
++
++ host->ctl = ctl = ioremap((unsigned long)dev->resource[0].start,
++ (unsigned long)dev->resource[0].end -
++ (unsigned long)dev->resource[0].start);
++ if (!host->ctl) {
++ goto unmap_cnf;
++ }
++
++ mmc->ops = &tmio_mmc_ops;
++ mmc->caps = MMC_CAP_4_BIT_DATA;
++ mmc->f_min = 46875;
++ mmc->f_max = 24000000;
++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
++
++ /* Enable the MMC/SD Control registers */
++ writew(SDCREN, &cnf->cmd);
++ writel(dev->resource[0].start & 0xfffe, &cnf->ctl_base);
++
++ /* Tell the MFD core we are ready to be enabled */
++ if(cell->enable)
++ cell->enable(dev);
++
++ writeb(0x01,&cnf->pwr_ctl[2]); /* Disable SD power during suspend */
++ writeb(0x1f, &cnf->stop_clk_ctl); /* Route clock to SDIO??? FIXME */
++ writeb(0x0, &cnf->pwr_ctl[1]); /* Power down SD bus*/
++ tmio_mmc_clk_stop(host); /* Stop bus clock */
++ reset(host); /* Reset MMC HC */
++
++ host->irq = (unsigned long)dev->resource[2].start;
++ ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED, "tmio-mmc", host);
++ if (ret){
++ ret = -ENODEV;
++ DBG("Failed to allocate IRQ.\n");
++ goto unmap_ctl;
++ }
++ set_irq_type(host->irq, IRQT_FALLING);
++
++ mmc_add_host(mmc);
++
++ printk(KERN_INFO "%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc),
++ (unsigned long)host->ctl, host->irq);
++
++ /* Lets unmask the IRQs we want to know about */
++ disable_mmc_irqs(ctl, TMIO_MASK_ALL);
++ enable_mmc_irqs(ctl, TMIO_MASK_IRQ);
++
++ return 0;
++
++unmap_ctl:
++ iounmap(host->ctl);
++unmap_cnf:
++ iounmap(host->cnf);
++host_free:
++ mmc_free_host(mmc);
++out:
++ return ret;
++}
++
++static int __devexit tmio_mmc_remove(struct platform_device *dev)
++{
++ struct mmc_host *mmc = platform_get_drvdata(dev);
++
++ platform_set_drvdata(dev, NULL);
++
++ if (mmc) {
++ struct tmio_mmc_host *host = mmc_priv(mmc);
++ mmc_remove_host(mmc);
++ free_irq(host->irq, host);
++ /* FIXME - we might want to consider stopping the chip here. */
++ iounmap(host->ctl);
++ iounmap(host->cnf);
++ mmc_free_host(mmc); /* FIXME - why does this call hang ? */
++ }
++ return 0;
++}
++
++/* ------------------- device registration ----------------------- */
++
++static struct platform_driver tmio_mmc_driver = {
++ .driver = {
++ .name = "tmio-mmc",
++ },
++ .probe = tmio_mmc_probe,
++ .remove = __devexit_p(tmio_mmc_remove),
++#ifdef CONFIG_PM
++ .suspend = tmio_mmc_suspend,
++ .resume = tmio_mmc_resume,
++#endif
++};
++
++
++static int __init tmio_mmc_init(void)
++{
++ return platform_driver_register (&tmio_mmc_driver);
++}
++
++static void __exit tmio_mmc_exit(void)
++{
++ platform_driver_unregister (&tmio_mmc_driver);
++}
++
++module_init(tmio_mmc_init);
++module_exit(tmio_mmc_exit);
++
++MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver");
++MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
++MODULE_LICENSE("GPLv2");
+diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
+new file mode 100644
+index 0000000..d4d9f8f
+--- /dev/null
++++ b/drivers/mmc/host/tmio_mmc.h
+@@ -0,0 +1,205 @@
++/* Definitons for use with the tmio_mmc.c
++ *
++ * (c) 2005 Ian Molton <spyro@f2s.com>
++ * (c) 2007 Ian Molton <spyro@f2s.com>
++ *
++ * 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_mmc_cnf {
++ u8 x00[4];
++ u16 cmd;
++ u8 x01[10];
++ u32 ctl_base;
++ u8 x02[41];
++ u8 int_pin;
++ u8 x03[2];
++ u8 stop_clk_ctl;
++ u8 gclk_ctl; /* Gated Clock Control */
++ u8 sd_clk_mode; /* 0x42 */
++ u8 x04;
++ u16 pin_status;
++ u8 x05[2];
++ u8 pwr_ctl[3];
++ u8 x06;
++ u8 card_detect_mode;
++ u8 x07[3];
++ u8 sd_slot;
++ u8 x08[159];
++ u8 ext_gclk_ctl_1; /* Extended Gated Clock Control 1 */
++ u8 ext_gclk_ctl_2; /* Extended Gated Clock Control 2 */
++ u8 x09[7];
++ u8 ext_gclk_ctl_3; /* Extended Gated Clock Control 3 */
++ u8 sd_led_en_1;
++ u8 x10[3];
++ u8 sd_led_en_2;
++ u8 x11;
++} __attribute__ ((packed));
++
++#define SDCREN 0x2 /* Enable access to MMC CTL regs. (flag in COMMAND_REG)*/
++
++struct tmio_mmc_ctl {
++ u16 sd_cmd;
++ u16 x00;
++ u16 arg_reg[2];
++ u16 stop_internal_action;
++ u16 xfer_blk_count;
++ u16 response[8];
++ u16 status[2];
++ u16 irq_mask[2];
++ u16 sd_card_clk_ctl;
++ u16 sd_xfer_len;
++ u16 sd_mem_card_opt;
++ u16 x01;
++ u16 sd_error_detail_status[2];
++ u16 sd_data_port[2];
++ u16 transaction_ctl;
++ u16 x02[85];
++ u16 reset_sd;
++ u16 x03[15];
++ u16 sdio_regs[28];
++ u16 clk_and_wait_ctl;
++ u16 x04[83];
++ u16 reset_sdio;
++ u16 x05[15];
++} __attribute__ ((packed));
++
++/* Definitions for values the CTRL_STATUS register can take. */
++#define TMIO_STAT_CMDRESPEND 0x00000001
++#define TMIO_STAT_DATAEND 0x00000004
++#define TMIO_STAT_CARD_REMOVE 0x00000008
++#define TMIO_STAT_CARD_INSERT 0x00000010
++#define TMIO_STAT_SIGSTATE 0x00000020
++#define TMIO_STAT_WRPROTECT 0x00000080
++#define TMIO_STAT_CARD_REMOVE_A 0x00000100
++#define TMIO_STAT_CARD_INSERT_A 0x00000200
++#define TMIO_STAT_SIGSTATE_A 0x00000400
++#define TMIO_STAT_CMD_IDX_ERR 0x00010000
++#define TMIO_STAT_CRCFAIL 0x00020000
++#define TMIO_STAT_STOPBIT_ERR 0x00040000
++#define TMIO_STAT_DATATIMEOUT 0x00080000
++#define TMIO_STAT_RXOVERFLOW 0x00100000
++#define TMIO_STAT_TXUNDERRUN 0x00200000
++#define TMIO_STAT_CMDTIMEOUT 0x00400000
++#define TMIO_STAT_RXRDY 0x01000000
++#define TMIO_STAT_TXRQ 0x02000000
++#define TMIO_STAT_ILL_FUNC 0x20000000
++#define TMIO_STAT_CMD_BUSY 0x40000000
++#define TMIO_STAT_ILL_ACCESS 0x80000000
++
++/* Define some IRQ masks */
++/* This is the mask used at reset by the chip */
++#define TMIO_MASK_ALL 0x837f031d
++#define TMIO_MASK_READOP (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND | \
++ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
++#define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND | \
++ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
++#define TMIO_MASK_CMD (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT | \
++ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
++#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD)
++
++#define enable_mmc_irqs(ctl, i) \
++ do { \
++ u32 mask;\
++ mask = tmio_ioread32((ctl)->irq_mask); \
++ mask &= ~((i) & TMIO_MASK_IRQ); \
++ tmio_iowrite32(mask, (ctl)->irq_mask); \
++ } while (0)
++
++#define disable_mmc_irqs(ctl, i) \
++ do { \
++ u32 mask;\
++ mask = tmio_ioread32((ctl)->irq_mask); \
++ mask |= ((i) & TMIO_MASK_IRQ); \
++ tmio_iowrite32(mask, (ctl)->irq_mask); \
++ } while (0)
++
++#define ack_mmc_irqs(ctl, i) \
++ do { \
++ u32 mask;\
++ mask = tmio_ioread32((ctl)->status); \
++ mask &= ~((i) & TMIO_MASK_IRQ); \
++ tmio_iowrite32(mask, (ctl)->status); \
++ } while (0)
++
++
++struct tmio_mmc_host {
++ struct tmio_mmc_cnf __iomem *cnf;
++ struct tmio_mmc_ctl __iomem *ctl;
++ struct mmc_command *cmd;
++ struct mmc_request *mrq;
++ struct mmc_data *data;
++ struct mmc_host *mmc;
++ int irq;
++
++ /* pio related stuff */
++ struct scatterlist *sg_ptr;
++ unsigned int sg_len;
++ unsigned int sg_off;
++};
++
++#include <linux/scatterlist.h>
++#include <linux/blkdev.h>
++
++static inline void tmio_mmc_init_sg(struct tmio_mmc_host *host, struct mmc_data *data)
++{
++ host->sg_len = data->sg_len;
++ host->sg_ptr = data->sg;
++ host->sg_off = 0;
++}
++
++static inline int tmio_mmc_next_sg(struct tmio_mmc_host *host)
++{
++ host->sg_ptr++;
++ host->sg_off = 0;
++ return --host->sg_len;
++}
++
++static inline char *tmio_mmc_kmap_atomic(struct tmio_mmc_host *host, unsigned long *flags)
++{
++ struct scatterlist *sg = host->sg_ptr;
++
++ local_irq_save(*flags);
++ return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
++}
++
++static inline void tmio_mmc_kunmap_atomic(struct tmio_mmc_host *host, unsigned long *flags)
++{
++ kunmap_atomic(sg_page(host->sg_ptr), KM_BIO_SRC_IRQ);
++ local_irq_restore(*flags);
++}
++
++#ifdef CONFIG_MMC_DEBUG
++#define DBG(args...) printk(args)
++
++void debug_status(u32 status){
++ printk("status: %08x = ", status);
++ if(status & TMIO_STAT_CARD_REMOVE) printk("Card_removed ");
++ if(status & TMIO_STAT_CARD_INSERT) printk("Card_insert ");
++ if(status & TMIO_STAT_SIGSTATE) printk("Sigstate ");
++ if(status & TMIO_STAT_WRPROTECT) printk("Write_protect ");
++ if(status & TMIO_STAT_CARD_REMOVE_A) printk("Card_remove_A ");
++ if(status & TMIO_STAT_CARD_INSERT_A) printk("Card_insert_A ");
++ if(status & TMIO_STAT_SIGSTATE_A) printk("Sigstate_A ");
++ if(status & TMIO_STAT_CMD_IDX_ERR) printk("Cmd_IDX_Err ");
++ if(status & TMIO_STAT_STOPBIT_ERR) printk("Stopbit_ERR ");
++ if(status & TMIO_STAT_ILL_FUNC) printk("ILLEGAL_FUNC ");
++ if(status & TMIO_STAT_CMD_BUSY) printk("CMD_BUSY ");
++ if(status & TMIO_STAT_CMDRESPEND) printk("Response_end ");
++ if(status & TMIO_STAT_DATAEND) printk("Data_end ");
++ if(status & TMIO_STAT_CRCFAIL) printk("CRC_failure ");
++ if(status & TMIO_STAT_DATATIMEOUT) printk("Data_timeout ");
++ if(status & TMIO_STAT_CMDTIMEOUT) printk("Command_timeout ");
++ if(status & TMIO_STAT_RXOVERFLOW) printk("RX_OVF ");
++ if(status & TMIO_STAT_TXUNDERRUN) printk("TX_UND ");
++ if(status & TMIO_STAT_RXRDY) printk("RX_rdy ");
++ if(status & TMIO_STAT_TXRQ) printk("TX_req ");
++ if(status & TMIO_STAT_ILL_ACCESS) printk("ILLEGAL_ACCESS ");
++ printk("\n");
++}
++#else
++#define DBG(fmt,args...) do { } while (0)
++#endif
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0012-Tosa-keyboard-support.patch b/recipes/linux/linux-rp-2.6.24/tosa/0012-Tosa-keyboard-support.patch
new file mode 100644
index 0000000000..0fa10ebd4c
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0012-Tosa-keyboard-support.patch
@@ -0,0 +1,593 @@
+From 6d377e8f80ce421e6842ac5f42081345fbc70002 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 01:27:41 +0300
+Subject: [PATCH 12/64] Tosa keyboard support
+
+Support keyboard on tosa (Sharp Zaurus SL-6000x).
+Largely based on patches by Dirk Opfer.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/tosa.c | 43 ++++
+ drivers/input/keyboard/Kconfig | 21 ++
+ drivers/input/keyboard/Makefile | 1 +
+ drivers/input/keyboard/tosakbd.c | 415 ++++++++++++++++++++++++++++++++++++++
+ include/asm-arm/arch-pxa/tosa.h | 30 +++
+ 5 files changed, 510 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/input/keyboard/tosakbd.c
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index 240fd04..e7e0f52 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -21,6 +21,8 @@
+ #include <linux/mmc/host.h>
+ #include <linux/pm.h>
+ #include <linux/delay.h>
++#include <linux/gpio_keys.h>
++#include <linux/input.h>
+
+ #include <asm/setup.h>
+ #include <asm/memory.h>
+@@ -253,6 +255,46 @@ static struct platform_device tosakbd_device = {
+ .id = -1,
+ };
+
++static struct gpio_keys_button tosa_gpio_keys[] = {
++ {
++ .type = EV_PWR,
++ .code = KEY_SUSPEND,
++ .gpio = TOSA_GPIO_ON_KEY,
++ .desc = "On key",
++ .wakeup = 1,
++ .active_low = 1,
++ },
++ {
++ .type = EV_KEY,
++ .code = TOSA_KEY_RECORD,
++ .gpio = TOSA_GPIO_RECORD_BTN,
++ .desc = "Record Button",
++ .wakeup = 1,
++ .active_low = 1,
++ },
++ {
++ .type = EV_KEY,
++ .code = TOSA_KEY_SYNC,
++ .gpio = TOSA_GPIO_SYNC,
++ .desc = "Sync Button",
++ .wakeup = 1,
++ .active_low = 1,
++ },
++};
++
++static struct gpio_keys_platform_data tosa_gpio_keys_platform_data = {
++ .buttons = tosa_gpio_keys,
++ .nbuttons = ARRAY_SIZE(tosa_gpio_keys),
++};
++
++static struct platform_device tosa_gpio_keys_device = {
++ .name = "gpio-keys",
++ .id = -1,
++ .dev = {
++ .platform_data = &tosa_gpio_keys_platform_data,
++ },
++};
++
+ /*
+ * Tosa LEDs
+ */
+@@ -265,6 +307,7 @@ static struct platform_device *devices[] __initdata = {
+ &tosascoop_device,
+ &tosascoop_jc_device,
+ &tosakbd_device,
++ &tosa_gpio_keys_device,
+ &tosaled_device,
+ };
+
+diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
+index 086d58c..0c32762 100644
+--- a/drivers/input/keyboard/Kconfig
++++ b/drivers/input/keyboard/Kconfig
+@@ -154,6 +154,27 @@ config KEYBOARD_SPITZ
+ To compile this driver as a module, choose M here: the
+ module will be called spitzkbd.
+
++config KEYBOARD_TOSA
++ tristate "Tosa keyboard"
++ depends on MACH_TOSA
++ default y
++ help
++ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa)
++
++ To compile this driver as a module, choose M here: the
++ module will be called tosakbd.
++
++config KEYBOARD_TOSA_USE_EXT_KEYCODES
++ bool "Tosa keyboard: use extended keycodes"
++ depends on KEYBOARD_TOSA
++ default n
++ help
++ Say Y here to enable the tosa keyboard driver to generate extended
++ (>= 127) keycodes. Be aware, that they can't be correctly interpreted
++ by either console keyboard driver or by Kdrive keybd driver.
++
++ Say Y only if you know, what you are doing!
++
+ config KEYBOARD_AMIGA
+ tristate "Amiga keyboard"
+ depends on AMIGA
+diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
+index e97455f..6caa065 100644
+--- a/drivers/input/keyboard/Makefile
++++ b/drivers/input/keyboard/Makefile
+@@ -15,6 +15,7 @@ obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+ obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
+ obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
+ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
++obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
+ obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
+ obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+ obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
+diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c
+new file mode 100644
+index 0000000..3884d1e
+--- /dev/null
++++ b/drivers/input/keyboard/tosakbd.c
+@@ -0,0 +1,415 @@
++/*
++ * Keyboard driver for Sharp Tosa models (SL-6000x)
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ * Copyright (c) 2007 Dmitry Baryshkov
++ *
++ * Based on xtkbd.c/locomkbd.c/corgikbd.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/kernel.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++
++#include <asm/arch/gpio.h>
++#include <asm/arch/tosa.h>
++
++#define KB_ROWMASK(r) (1 << (r))
++#define SCANCODE(r, c) (((r)<<4) + (c) + 1)
++#define NR_SCANCODES SCANCODE(TOSA_KEY_SENSE_NUM - 1, TOSA_KEY_STROBE_NUM - 1) + 1
++
++#define SCAN_INTERVAL (HZ/10)
++
++#define KB_DISCHARGE_DELAY 10
++#define KB_ACTIVATE_DELAY 10
++
++static unsigned int tosakbd_keycode[NR_SCANCODES] = {
++0,
++0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P,
++0, 0, 0, 0, 0, 0, 0, 0,
++KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA,
++0, 0, 0, 0, 0, 0, 0, 0,
++KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT,
++0, 0, 0, 0, 0, 0, 0, 0,
++KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESSBOOK, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK,
++KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, 0,
++KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDAR, TOSA_KEY_HOMEPAGE, KEY_LEFTCTRL, TOSA_KEY_LIGHT,
++0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0,
++KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0,
++0, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0,
++KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT,
++0, 0, 0,
++};
++
++struct tosakbd {
++ unsigned int keycode[ARRAY_SIZE(tosakbd_keycode)];
++ struct input_dev *input;
++
++ spinlock_t lock; /* protect kbd scanning */
++ struct timer_list timer;
++};
++
++
++/* Helper functions for reading the keyboard matrix
++ * Note: We should really be using pxa_gpio_mode to alter GPDR but it
++ * requires a function call per GPIO bit which is excessive
++ * when we need to access 12 bits at once, multiple times.
++ * These functions must be called within local_irq_save()/local_irq_restore()
++ * or similar.
++ */
++#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT)
++
++static inline void tosakbd_discharge_all(void)
++{
++ /* STROBE All HiZ */
++ GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT;
++ GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT;
++ GPCR2 = TOSA_GPIO_LOW_STROBE_BIT;
++ GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT;
++}
++
++static inline void tosakbd_activate_all(void)
++{
++ /* STROBE ALL -> High */
++ GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT;
++ GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT;
++ GPSR2 = TOSA_GPIO_LOW_STROBE_BIT;
++ GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT;
++
++ udelay(KB_DISCHARGE_DELAY);
++
++ /* STATE CLEAR */
++ GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT;
++}
++
++static inline void tosakbd_activate_col(int col)
++{
++ if (col <= 5) {
++ /* STROBE col -> High, not col -> HiZ */
++ GPSR1 = TOSA_GPIO_STROBE_BIT(col);
++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ } else {
++ /* STROBE col -> High, not col -> HiZ */
++ GPSR2 = TOSA_GPIO_STROBE_BIT(col);
++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ }
++}
++
++static inline void tosakbd_reset_col(int col)
++{
++ if (col <= 5) {
++ /* STROBE col -> Low */
++ GPCR1 = TOSA_GPIO_STROBE_BIT(col);
++ /* STROBE col -> out, not col -> HiZ */
++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ } else {
++ /* STROBE col -> Low */
++ GPCR2 = TOSA_GPIO_STROBE_BIT(col);
++ /* STROBE col -> out, not col -> HiZ */
++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
++ }
++}
++/*
++ * The tosa keyboard only generates interrupts when a key is pressed.
++ * So when a key is pressed, we enable a timer. This timer scans the
++ * keyboard, and this is how we detect when the key is released.
++ */
++
++/* Scan the hardware keyboard and push any changes up through the input layer */
++static void tosakbd_scankeyboard(struct platform_device *dev)
++{
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++ unsigned int row, col, rowd;
++ unsigned long flags;
++ unsigned int num_pressed = 0;
++
++ spin_lock_irqsave(&tosakbd->lock, flags);
++
++ for (col = 0; col < TOSA_KEY_STROBE_NUM; col++) {
++ /*
++ * Discharge the output driver capacitatance
++ * in the keyboard matrix. (Yes it is significant..)
++ */
++ tosakbd_discharge_all();
++ udelay(KB_DISCHARGE_DELAY);
++
++ tosakbd_activate_col(col);
++ udelay(KB_ACTIVATE_DELAY);
++
++ rowd = GET_ROWS_STATUS(col);
++
++ for (row = 0; row < TOSA_KEY_SENSE_NUM; row++) {
++ unsigned int scancode, pressed;
++ scancode = SCANCODE(row, col);
++ pressed = rowd & KB_ROWMASK(row);
++
++ if (pressed && !tosakbd->keycode[scancode])
++ dev_warn(&dev->dev,
++ "unhandled scancode: 0x%02x\n",
++ scancode);
++
++ input_report_key(tosakbd->input,
++ tosakbd->keycode[scancode],
++ pressed);
++ if (pressed)
++ num_pressed++;
++ }
++
++ tosakbd_reset_col(col);
++ }
++
++ tosakbd_activate_all();
++
++ input_sync(tosakbd->input);
++
++ /* if any keys are pressed, enable the timer */
++ if (num_pressed)
++ mod_timer(&tosakbd->timer, jiffies + SCAN_INTERVAL);
++
++ spin_unlock_irqrestore(&tosakbd->lock, flags);
++}
++
++/*
++ * tosa keyboard interrupt handler.
++ */
++static irqreturn_t tosakbd_interrupt(int irq, void *__dev)
++{
++ struct platform_device *dev = __dev;
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ if (!timer_pending(&tosakbd->timer)) {
++ /** wait chattering delay **/
++ udelay(20);
++ tosakbd_scankeyboard(dev);
++ }
++
++ return IRQ_HANDLED;
++}
++
++/*
++ * tosa timer checking for released keys
++ */
++static void tosakbd_timer_callback(unsigned long __dev)
++{
++ struct platform_device *dev = (struct platform_device *)__dev;
++ tosakbd_scankeyboard(dev);
++}
++
++#ifdef CONFIG_PM
++static int tosakbd_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ del_timer_sync(&tosakbd->timer);
++
++ return 0;
++}
++
++static int tosakbd_resume(struct platform_device *dev)
++{
++ tosakbd_scankeyboard(dev);
++
++ return 0;
++}
++#else
++#define tosakbd_suspend NULL
++#define tosakbd_resume NULL
++#endif
++
++static int __devinit tosakbd_probe(struct platform_device *pdev) {
++
++ int i;
++ struct tosakbd *tosakbd;
++ struct input_dev *input_dev;
++ int error;
++
++ tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL);
++ if (!tosakbd)
++ return -ENOMEM;
++
++ input_dev = input_allocate_device();
++ if (!input_dev) {
++ kfree(tosakbd);
++ return -ENOMEM;
++ }
++
++ platform_set_drvdata(pdev, tosakbd);
++
++ spin_lock_init(&tosakbd->lock);
++
++ /* Init Keyboard rescan timer */
++ init_timer(&tosakbd->timer);
++ tosakbd->timer.function = tosakbd_timer_callback;
++ tosakbd->timer.data = (unsigned long) pdev;
++
++ tosakbd->input = input_dev;
++
++ input_set_drvdata(input_dev, tosakbd);
++ input_dev->name = "Tosa Keyboard";
++ input_dev->phys = "tosakbd/input0";
++ input_dev->dev.parent = &pdev->dev;
++
++ input_dev->id.bustype = BUS_HOST;
++ input_dev->id.vendor = 0x0001;
++ input_dev->id.product = 0x0001;
++ input_dev->id.version = 0x0100;
++
++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
++ input_dev->keycode = tosakbd->keycode;
++ input_dev->keycodesize = sizeof(unsigned int);
++ input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode);
++
++ memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd_keycode));
++
++ for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++)
++ __set_bit(tosakbd->keycode[i], input_dev->keybit);
++ clear_bit(0, input_dev->keybit);
++
++ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) {
++ int gpio = TOSA_GPIO_KEY_SENSE(i);
++ int irq;
++ error = gpio_request(gpio, "tosakbd");
++ if (error < 0) {
++ printk(KERN_ERR "tosakbd: failed to request GPIO %d, "
++ " error %d\n", gpio, error);
++ goto fail;
++ }
++
++ error = gpio_direction_input(TOSA_GPIO_KEY_SENSE(i));
++ if (error < 0) {
++ printk(KERN_ERR "tosakbd: failed to configure input"
++ " direction for GPIO %d, error %d\n",
++ gpio, error);
++ gpio_free(gpio);
++ goto fail;
++ }
++
++ irq = gpio_to_irq(gpio);
++ if (irq < 0) {
++ error = irq;
++ printk(KERN_ERR "gpio-keys: Unable to get irq number"
++ " for GPIO %d, error %d\n",
++ gpio, error);
++ gpio_free(gpio);
++ goto fail;
++ }
++
++ error = request_irq(irq, tosakbd_interrupt,
++ IRQF_DISABLED | IRQF_TRIGGER_RISING,
++ "tosakbd", pdev);
++
++ if (error) {
++ printk("tosakbd: Can't get IRQ: %d: error %d!\n",
++ irq, error);
++ gpio_free(gpio);
++ goto fail;
++ }
++ }
++
++ /* Set Strobe lines as outputs - set high */
++ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) {
++ int gpio = TOSA_GPIO_KEY_STROBE(i);
++ error = gpio_request(gpio, "tosakbd");
++ if (error < 0) {
++ printk(KERN_ERR "tosakbd: failed to request GPIO %d, "
++ " error %d\n", gpio, error);
++ goto fail2;
++ }
++
++ error = gpio_direction_output(gpio, 1);
++ if (error < 0) {
++ printk(KERN_ERR "tosakbd: failed to configure input"
++ " direction for GPIO %d, error %d\n",
++ gpio, error);
++ gpio_free(gpio);
++ goto fail;
++ }
++
++ }
++
++ error = input_register_device(input_dev);
++ if (error) {
++ printk(KERN_ERR "tosakbd: Unable to register input device, "
++ "error: %d\n", error);
++ goto fail;
++ }
++
++ printk(KERN_INFO "input: Tosa Keyboard Registered\n");
++
++ return 0;
++
++fail2:
++ while (--i >= 0)
++ gpio_free(TOSA_GPIO_KEY_STROBE(i));
++
++ i = TOSA_KEY_SENSE_NUM;
++fail:
++ while (--i >= 0) {
++ free_irq(gpio_to_irq(TOSA_GPIO_KEY_SENSE(i)), pdev);
++ gpio_free(TOSA_GPIO_KEY_SENSE(i));
++ }
++
++ platform_set_drvdata(pdev, NULL);
++ input_free_device(input_dev);
++ kfree(tosakbd);
++
++ return error;
++}
++
++static int __devexit tosakbd_remove(struct platform_device *dev) {
++
++ int i;
++ struct tosakbd *tosakbd = platform_get_drvdata(dev);
++
++ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++)
++ gpio_free(TOSA_GPIO_KEY_STROBE(i));
++
++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) {
++ free_irq(gpio_to_irq(TOSA_GPIO_KEY_SENSE(i)), dev);
++ gpio_free(TOSA_GPIO_KEY_SENSE(i));
++ }
++
++ del_timer_sync(&tosakbd->timer);
++
++ input_unregister_device(tosakbd->input);
++
++ kfree(tosakbd);
++
++ return 0;
++}
++
++static struct platform_driver tosakbd_driver = {
++ .probe = tosakbd_probe,
++ .remove = __devexit_p(tosakbd_remove),
++ .suspend = tosakbd_suspend,
++ .resume = tosakbd_resume,
++ .driver = {
++ .name = "tosa-keyboard",
++ },
++};
++
++static int __devinit tosakbd_init(void)
++{
++ return platform_driver_register(&tosakbd_driver);
++}
++
++static void __exit tosakbd_exit(void)
++{
++ platform_driver_unregister(&tosakbd_driver);
++}
++
++module_init(tosakbd_init);
++module_exit(tosakbd_exit);
++
++MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>");
++MODULE_DESCRIPTION("Tosa Keyboard Driver");
++MODULE_LICENSE("GPL v2");
+diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h
+index c3364a2..c05e4fa 100644
+--- a/include/asm-arm/arch-pxa/tosa.h
++++ b/include/asm-arm/arch-pxa/tosa.h
+@@ -163,4 +163,34 @@
+
+ extern struct platform_device tosascoop_jc_device;
+ extern struct platform_device tosascoop_device;
++
++#define TOSA_KEY_SYNC KEY_102ND /* ??? */
++
++
++#ifndef CONFIG_KEYBOARD_TOSA_USE_EXT_KEYCODES
++#define TOSA_KEY_RECORD KEY_YEN
++#define TOSA_KEY_ADDRESSBOOK KEY_KATAKANA
++#define TOSA_KEY_CANCEL KEY_ESC
++#define TOSA_KEY_CENTER KEY_HIRAGANA
++#define TOSA_KEY_OK KEY_HENKAN
++#define TOSA_KEY_CALENDAR KEY_KATAKANAHIRAGANA
++#define TOSA_KEY_HOMEPAGE KEY_HANGEUL
++#define TOSA_KEY_LIGHT KEY_MUHENKAN
++#define TOSA_KEY_MENU KEY_HANJA
++#define TOSA_KEY_FN KEY_RIGHTALT
++#define TOSA_KEY_MAIL KEY_ZENKAKUHANKAKU
++#else
++#define TOSA_KEY_RECORD KEY_RECORD
++#define TOSA_KEY_ADDRESSBOOK KEY_ADDRESSBOOK
++#define TOSA_KEY_CANCEL KEY_CANCEL
++#define TOSA_KEY_CENTER KEY_SELECT /* ??? */
++#define TOSA_KEY_OK KEY_OK
++#define TOSA_KEY_CALENDAR KEY_CALENDAR
++#define TOSA_KEY_HOMEPAGE KEY_HOMEPAGE
++#define TOSA_KEY_LIGHT KEY_KBDILLUMTOGGLE
++#define TOSA_KEY_MENU KEY_MENU
++#define TOSA_KEY_FN KEY_FN
++#define TOSA_KEY_MAIL KEY_MAIL
++#endif
++
+ #endif /* _ASM_ARCH_TOSA_H_ */
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0013-USB-gadget-pxa2xx_udc-supports-inverted-vbus.patch b/recipes/linux/linux-rp-2.6.24/tosa/0013-USB-gadget-pxa2xx_udc-supports-inverted-vbus.patch
new file mode 100644
index 0000000000..082a2c72b8
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0013-USB-gadget-pxa2xx_udc-supports-inverted-vbus.patch
@@ -0,0 +1,61 @@
+From 18c1a92a09faf75ebdac7ac471c741a6622cf3e2 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 01:27:49 +0300
+Subject: [PATCH 13/64] USB: gadget: pxa2xx_udc supports inverted vbus
+
+Some boards (like e.g. Tosa) invert the VBUS-detection signal:
+it's low when a host is supplying VBUS, and high otherwise.
+Allow specifying whether gpio_vbus value is inverted.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/usb/gadget/pxa2xx_udc.c | 9 +++++++--
+ include/asm-arm/mach/udc_pxa2xx.h | 2 ++
+ 2 files changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
+index 3173b39..4f7d4ef 100644
+--- a/drivers/usb/gadget/pxa2xx_udc.c
++++ b/drivers/usb/gadget/pxa2xx_udc.c
+@@ -127,8 +127,10 @@ static int is_vbus_present(void)
+ {
+ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+
+- if (mach->gpio_vbus)
+- return gpio_get_value(mach->gpio_vbus);
++ if (mach->gpio_vbus) {
++ int value = gpio_get_value(mach->gpio_vbus);
++ return mach->gpio_vbus_inverted ? !value : value;
++ }
+ if (mach->udc_is_connected)
+ return mach->udc_is_connected();
+ return 1;
+@@ -1397,6 +1399,9 @@ static irqreturn_t udc_vbus_irq(int irq, void *_dev)
+ struct pxa2xx_udc *dev = _dev;
+ int vbus = gpio_get_value(dev->mach->gpio_vbus);
+
++ if (dev->mach->gpio_vbus_inverted)
++ vbus = !vbus;
++
+ pxa2xx_udc_vbus_session(&dev->gadget, vbus);
+ return IRQ_HANDLED;
+ }
+diff --git a/include/asm-arm/mach/udc_pxa2xx.h b/include/asm-arm/mach/udc_pxa2xx.h
+index ff0a957..f191e14 100644
+--- a/include/asm-arm/mach/udc_pxa2xx.h
++++ b/include/asm-arm/mach/udc_pxa2xx.h
+@@ -19,7 +19,9 @@ struct pxa2xx_udc_mach_info {
+ * with on-chip GPIOs not Lubbock's wierd hardware, can have a sane
+ * VBUS IRQ and omit the methods above. Store the GPIO number
+ * here; for GPIO 0, also mask in one of the pxa_gpio_mode() bits.
++ * Note that sometimes the signals go through inverters...
+ */
++ bool gpio_vbus_inverted;
+ u16 gpio_vbus; /* high == vbus present */
+ u16 gpio_pullup; /* high == pullup activated */
+ };
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0014-tosa_udc_use_gpio_vbus.patch.patch b/recipes/linux/linux-rp-2.6.24/tosa/0014-tosa_udc_use_gpio_vbus.patch.patch
new file mode 100644
index 0000000000..98783efea0
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0014-tosa_udc_use_gpio_vbus.patch.patch
@@ -0,0 +1,38 @@
+From 932ff38b17c7847c43e2bad01b510b64c27f9810 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 01:27:59 +0300
+Subject: [PATCH 14/64] tosa_udc_use_gpio_vbus.patch
+
+Use gpio_vbus instead of udc_is_connected for udc on tosa.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>
+---
+ arch/arm/mach-pxa/tosa.c | 9 ++-------
+ 1 files changed, 2 insertions(+), 7 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index e7e0f52..5268e94 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -159,15 +159,10 @@ static void tosa_udc_command(int cmd)
+ }
+ }
+
+-static int tosa_udc_is_connected(void)
+-{
+- return ((GPLR(TOSA_GPIO_USB_IN) & GPIO_bit(TOSA_GPIO_USB_IN)) == 0);
+-}
+-
+-
+ static struct pxa2xx_udc_mach_info udc_info __initdata = {
+ .udc_command = tosa_udc_command,
+- .udc_is_connected = tosa_udc_is_connected,
++ .gpio_vbus = TOSA_GPIO_USB_IN,
++ .gpio_vbus_inverted = 1,
+ };
+
+ /*
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0015-sharpsl-export-params.patch b/recipes/linux/linux-rp-2.6.24/tosa/0015-sharpsl-export-params.patch
new file mode 100644
index 0000000000..f8e57e8306
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0015-sharpsl-export-params.patch
@@ -0,0 +1,32 @@
+From bba216220d17d1091413e82c9924ac5614402c05 Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Wed, 9 Jan 2008 01:28:06 +0300
+Subject: [PATCH 15/64] sharpsl export params
+
+---
+ arch/arm/common/sharpsl_param.c | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/arch/arm/common/sharpsl_param.c b/arch/arm/common/sharpsl_param.c
+index aad4d94..d56c932 100644
+--- a/arch/arm/common/sharpsl_param.c
++++ b/arch/arm/common/sharpsl_param.c
+@@ -12,6 +12,7 @@
+ */
+
+ #include <linux/kernel.h>
++#include <linux/module.h>
+ #include <linux/string.h>
+ #include <asm/mach/sharpsl_param.h>
+
+@@ -36,6 +37,7 @@
+ #define PHAD_MAGIC MAGIC_CHG('P','H','A','D')
+
+ struct sharpsl_param_info sharpsl_param;
++EXPORT_SYMBOL(sharpsl_param);
+
+ void sharpsl_save_param(void)
+ {
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0016-This-patch-fixes-the-pxa25x-clocks-definitions-to-ad.patch b/recipes/linux/linux-rp-2.6.24/tosa/0016-This-patch-fixes-the-pxa25x-clocks-definitions-to-ad.patch
new file mode 100644
index 0000000000..d73de0698c
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0016-This-patch-fixes-the-pxa25x-clocks-definitions-to-ad.patch
@@ -0,0 +1,44 @@
+From 0fe7b491b70efafbd41185f8e95a3eada65984a1 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Mon, 28 Jan 2008 01:49:28 +0300
+Subject: [PATCH 16/64] This patch fixes the pxa25x clocks definitions to add hwuart.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/pxa25x.c | 9 ++++++++-
+ 1 files changed, 8 insertions(+), 1 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c
+index 9732d5d..006a6e0 100644
+--- a/arch/arm/mach-pxa/pxa25x.c
++++ b/arch/arm/mach-pxa/pxa25x.c
+@@ -111,11 +111,14 @@ static const struct clkops clk_pxa25x_lcd_ops = {
+ * 95.842MHz -> MMC 19.169MHz, I2C 31.949MHz, FICP 47.923MHz, USB 47.923MHz
+ * 147.456MHz -> UART 14.7456MHz, AC97 12.288MHz, I2S 5.672MHz (allegedly)
+ */
++static struct clk pxa25x_hwuart_clk =
++ INIT_CKEN("UARTCLK", HWUART, 14745600, 1, &pxa_device_hwuart.dev)
++;
++
+ static struct clk pxa25x_clks[] = {
+ INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_ops, &pxa_device_fb.dev),
+ INIT_CKEN("UARTCLK", FFUART, 14745600, 1, &pxa_device_ffuart.dev),
+ INIT_CKEN("UARTCLK", BTUART, 14745600, 1, &pxa_device_btuart.dev),
+- INIT_CKEN("UARTCLK", BTUART, 14745600, 1, &pxa_device_btuart.dev),
+ INIT_CKEN("UARTCLK", STUART, 14745600, 1, NULL),
+ INIT_CKEN("UDCCLK", USB, 47923000, 5, &pxa_device_udc.dev),
+ INIT_CKEN("MMCCLK", MMC, 19169000, 0, &pxa_device_mci.dev),
+@@ -303,6 +306,10 @@ static int __init pxa25x_init(void)
+ {
+ int ret = 0;
+
++ /* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */
++ if (cpu_is_pxa25x())
++ clks_register(&pxa25x_hwuart_clk, 1);
++
+ if (cpu_is_pxa21x() || cpu_is_pxa25x()) {
+ clks_register(pxa25x_clks, ARRAY_SIZE(pxa25x_clks));
+
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0017-Convert-pxa2xx-UDC-to-use-debugfs.patch b/recipes/linux/linux-rp-2.6.24/tosa/0017-Convert-pxa2xx-UDC-to-use-debugfs.patch
new file mode 100644
index 0000000000..5163361da3
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0017-Convert-pxa2xx-UDC-to-use-debugfs.patch
@@ -0,0 +1,280 @@
+From 71857e8f6c4a8d2d3eac3037f02e0c30c6fdb37e Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 01:43:28 +0300
+Subject: [PATCH 17/64] Convert pxa2xx UDC to use debugfs
+
+Use debugfs instead of /proc/driver/udc
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/usb/gadget/pxa2xx_udc.c | 100 +++++++++++++++++----------------------
+ drivers/usb/gadget/pxa2xx_udc.h | 10 +++-
+ 2 files changed, 51 insertions(+), 59 deletions(-)
+
+diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
+index 4f7d4ef..2900556 100644
+--- a/drivers/usb/gadget/pxa2xx_udc.c
++++ b/drivers/usb/gadget/pxa2xx_udc.c
+@@ -38,13 +38,14 @@
+ #include <linux/timer.h>
+ #include <linux/list.h>
+ #include <linux/interrupt.h>
+-#include <linux/proc_fs.h>
+ #include <linux/mm.h>
+ #include <linux/platform_device.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/irq.h>
+ #include <linux/clk.h>
+ #include <linux/err.h>
++#include <linux/seq_file.h>
++#include <linux/debugfs.h>
+
+ #include <asm/byteorder.h>
+ #include <asm/dma.h>
+@@ -993,45 +994,36 @@ static const struct usb_gadget_ops pxa2xx_udc_ops = {
+
+ /*-------------------------------------------------------------------------*/
+
+-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+-
+-static const char proc_node_name [] = "driver/udc";
++#ifdef CONFIG_USB_GADGET_DEBUG_FS
+
++static struct pxa2xx_udc memory;
+ static int
+-udc_proc_read(char *page, char **start, off_t off, int count,
+- int *eof, void *_dev)
++udc_seq_show(struct seq_file *m, void *d)
+ {
+- char *buf = page;
+- struct pxa2xx_udc *dev = _dev;
+- char *next = buf;
+- unsigned size = count;
++ struct pxa2xx_udc *dev = m->private;
+ unsigned long flags;
+- int i, t;
++ int i;
+ u32 tmp;
+
+- if (off != 0)
+- return 0;
++
++ BUG_ON(dev == NULL);
+
+ local_irq_save(flags);
+
+ /* basic device status */
+- t = scnprintf(next, size, DRIVER_DESC "\n"
++ seq_printf(m, DRIVER_DESC "\n"
+ "%s version: %s\nGadget driver: %s\nHost %s\n\n",
+ driver_name, DRIVER_VERSION SIZE_STR "(pio)",
+ dev->driver ? dev->driver->driver.name : "(none)",
+ is_vbus_present() ? "full speed" : "disconnected");
+- size -= t;
+- next += t;
+
+ /* registers for device and ep0 */
+- t = scnprintf(next, size,
++ seq_printf(m,
+ "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
+ UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
+- size -= t;
+- next += t;
+
+ tmp = UDCCR;
+- t = scnprintf(next, size,
++ seq_printf(m,
+ "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp,
+ (tmp & UDCCR_REM) ? " rem" : "",
+ (tmp & UDCCR_RSTIR) ? " rstir" : "",
+@@ -1041,11 +1033,9 @@ udc_proc_read(char *page, char **start, off_t off, int count,
+ (tmp & UDCCR_RSM) ? " rsm" : "",
+ (tmp & UDCCR_UDA) ? " uda" : "",
+ (tmp & UDCCR_UDE) ? " ude" : "");
+- size -= t;
+- next += t;
+
+ tmp = UDCCS0;
+- t = scnprintf(next, size,
++ seq_printf(m,
+ "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp,
+ (tmp & UDCCS0_SA) ? " sa" : "",
+ (tmp & UDCCS0_RNE) ? " rne" : "",
+@@ -1055,28 +1045,22 @@ udc_proc_read(char *page, char **start, off_t off, int count,
+ (tmp & UDCCS0_FTF) ? " ftf" : "",
+ (tmp & UDCCS0_IPR) ? " ipr" : "",
+ (tmp & UDCCS0_OPR) ? " opr" : "");
+- size -= t;
+- next += t;
+
+ if (dev->has_cfr) {
+ tmp = UDCCFR;
+- t = scnprintf(next, size,
++ seq_printf(m,
+ "udccfr %02X =%s%s\n", tmp,
+ (tmp & UDCCFR_AREN) ? " aren" : "",
+ (tmp & UDCCFR_ACM) ? " acm" : "");
+- size -= t;
+- next += t;
+ }
+
+ if (!is_vbus_present() || !dev->driver)
+ goto done;
+
+- t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
++ seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
+ dev->stats.write.bytes, dev->stats.write.ops,
+ dev->stats.read.bytes, dev->stats.read.ops,
+ dev->stats.irqs);
+- size -= t;
+- next += t;
+
+ /* dump endpoint queues */
+ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+@@ -1090,55 +1074,57 @@ udc_proc_read(char *page, char **start, off_t off, int count,
+ if (!d)
+ continue;
+ tmp = *dev->ep [i].reg_udccs;
+- t = scnprintf(next, size,
++ seq_printf(m,
+ "%s max %d %s udccs %02x irqs %lu\n",
+ ep->ep.name, le16_to_cpu (d->wMaxPacketSize),
+ "pio", tmp, ep->pio_irqs);
+ /* TODO translate all five groups of udccs bits! */
+
+ } else /* ep0 should only have one transfer queued */
+- t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n",
++ seq_printf(m, "ep0 max 16 pio irqs %lu\n",
+ ep->pio_irqs);
+- if (t <= 0 || t > size)
+- goto done;
+- size -= t;
+- next += t;
+
+ if (list_empty(&ep->queue)) {
+- t = scnprintf(next, size, "\t(nothing queued)\n");
+- if (t <= 0 || t > size)
+- goto done;
+- size -= t;
+- next += t;
++ seq_printf(m, "\t(nothing queued)\n");
+ continue;
+ }
+ list_for_each_entry(req, &ep->queue, queue) {
+- t = scnprintf(next, size,
++ seq_printf(m,
+ "\treq %p len %d/%d buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+- if (t <= 0 || t > size)
+- goto done;
+- size -= t;
+- next += t;
+ }
+ }
+
+ done:
+ local_irq_restore(flags);
+- *eof = 1;
+- return count - size;
++ return 0;
+ }
+
+-#define create_proc_files() \
+- create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
+-#define remove_proc_files() \
+- remove_proc_entry(proc_node_name, NULL)
++static int
++udc_debugfs_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, udc_seq_show, inode->i_private);
++}
++
++static const struct file_operations debug_fops = {
++ .open = udc_debugfs_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++ .owner = THIS_MODULE,
++};
++
++#define create_debug_files(dev) \
++ dev->debugfs_udc = debugfs_create_file(dev->gadget.name, S_IRUGO, \
++ NULL, dev, &debug_fops)
++#define remove_debug_files(dev) \
++ if (dev->debugfs_udc) debugfs_remove(dev->debugfs_udc)
+
+ #else /* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+-#define create_proc_files() do {} while (0)
+-#define remove_proc_files() do {} while (0)
++#define create_debug_files(dev) do {} while (0)
++#define remove_debug_files(dev) do {} while (0)
+
+ #endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+@@ -2245,7 +2231,7 @@ lubbock_fail0:
+ goto err_vbus_irq;
+ }
+ }
+- create_proc_files();
++ create_debug_files(dev);
+
+ return 0;
+
+@@ -2282,7 +2268,7 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
+ return -EBUSY;
+
+ udc_disable(dev);
+- remove_proc_files();
++ remove_debug_files(dev);
+
+ if (dev->got_irq) {
+ free_irq(platform_get_irq(pdev, 0), dev);
+diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h
+index 1db46d7..c08b1a2 100644
+--- a/drivers/usb/gadget/pxa2xx_udc.h
++++ b/drivers/usb/gadget/pxa2xx_udc.h
+@@ -129,6 +129,10 @@ struct pxa2xx_udc {
+ struct pxa2xx_udc_mach_info *mach;
+ u64 dma_mask;
+ struct pxa2xx_ep ep [PXA_UDC_NUM_ENDPOINTS];
++
++#ifdef CONFIG_USB_GADGET_DEBUG_FS
++ struct dentry *debugfs_udc;
++#endif
+ };
+
+ /*-------------------------------------------------------------------------*/
+@@ -153,6 +157,8 @@ static struct pxa2xx_udc *the_controller;
+
+ #ifdef DEBUG
+
++static int is_vbus_present(void);
++
+ static const char *state_name[] = {
+ "EP0_IDLE",
+ "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
+@@ -207,8 +213,7 @@ dump_state(struct pxa2xx_udc *dev)
+ unsigned i;
+
+ DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
+- //is_usb_connected() ? "host " : "disconnected",
+- "host ",
++ is_vbus_present() ? "host " : "disconnected",
+ state_name[dev->ep0state],
+ UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
+ dump_udccr("udccr");
+@@ -224,7 +230,7 @@ dump_state(struct pxa2xx_udc *dev)
+ } else
+ DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
+
+- //if (!is_usb_connected())
+- // return;
++ if (!is_vbus_present())
++ return;
+
+ dump_udccs0 ("udccs0");
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0018-Fix-the-pxa2xx_udc-to-balance-calls-to-clk_enable-cl.patch b/recipes/linux/linux-rp-2.6.24/tosa/0018-Fix-the-pxa2xx_udc-to-balance-calls-to-clk_enable-cl.patch
new file mode 100644
index 0000000000..7bf4ad02d6
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0018-Fix-the-pxa2xx_udc-to-balance-calls-to-clk_enable-cl.patch
@@ -0,0 +1,225 @@
+From b9a0fdbf333b461682d5da8b9aaa42f4de91ffcf Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Sun, 10 Feb 2008 03:29:17 +0300
+Subject: [PATCH 18/64] Fix the pxa2xx_udc to balance calls to clk_enable/clk_disable
+
+Signed-off-by: Dmitry Baryshkov dbaryshkov@gmail.com
+---
+ drivers/usb/gadget/pxa2xx_udc.c | 84 +++++++++++++++++++++++----------------
+ drivers/usb/gadget/pxa2xx_udc.h | 6 ++-
+ 2 files changed, 54 insertions(+), 36 deletions(-)
+
+diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
+index 2900556..8e32d07 100644
+--- a/drivers/usb/gadget/pxa2xx_udc.c
++++ b/drivers/usb/gadget/pxa2xx_udc.c
+@@ -680,7 +680,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+
+ /* kickstart this i/o queue? */
+ if (list_empty(&ep->queue) && !ep->stopped) {
+- if (ep->desc == 0 /* ep0 */) {
++ if (ep->desc == NULL /* ep0 */) {
+ unsigned length = _req->length;
+
+ switch (dev->ep0state) {
+@@ -734,7 +734,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+ }
+
+ /* pio or dma irq handler advances the queue. */
+- if (likely (req != 0))
++ if (likely (req != NULL))
+ list_add_tail(&req->queue, &ep->queue);
+ local_irq_restore(flags);
+
+@@ -934,20 +934,35 @@ static void udc_disable(struct pxa2xx_udc *);
+ /* We disable the UDC -- and its 48 MHz clock -- whenever it's not
+ * in active use.
+ */
+-static int pullup(struct pxa2xx_udc *udc, int is_active)
++static int pullup(struct pxa2xx_udc *udc)
+ {
+- is_active = is_active && udc->vbus && udc->pullup;
++ int is_active = udc->vbus && udc->pullup && ! udc->suspended;
+ DMSG("%s\n", is_active ? "active" : "inactive");
+- if (is_active)
+- udc_enable(udc);
+- else {
+- if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+- DMSG("disconnect %s\n", udc->driver
+- ? udc->driver->driver.name
+- : "(no driver)");
+- stop_activity(udc, udc->driver);
++ if (is_active) {
++ if (!udc->active) {
++ udc->active = 1;
++#ifdef CONFIG_ARCH_PXA
++ /* Enable clock for USB device */
++ clk_enable(udc->clk);
++#endif
++ udc_enable(udc);
+ }
+- udc_disable(udc);
++ } else {
++ if (udc->active) {
++ if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
++ DMSG("disconnect %s\n", udc->driver
++ ? udc->driver->driver.name
++ : "(no driver)");
++ stop_activity(udc, udc->driver);
++ }
++ udc_disable(udc);
++#ifdef CONFIG_ARCH_PXA
++ /* Disable clock for USB device */
++ clk_disable(udc->clk);
++#endif
++ udc->active = 0;
++ }
++
+ }
+ return 0;
+ }
+@@ -958,9 +973,9 @@ static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+ struct pxa2xx_udc *udc;
+
+ udc = container_of(_gadget, struct pxa2xx_udc, gadget);
+- udc->vbus = is_active = (is_active != 0);
++ udc->vbus = (is_active != 0);
+ DMSG("vbus %s\n", is_active ? "supplied" : "inactive");
+- pullup(udc, is_active);
++ pullup(udc);
+ return 0;
+ }
+
+@@ -975,9 +990,8 @@ static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active)
+ if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
+ return -EOPNOTSUPP;
+
+- is_active = (is_active != 0);
+- udc->pullup = is_active;
+- pullup(udc, is_active);
++ udc->pullup = (is_active != 0);
++ pullup(udc);
+ return 0;
+ }
+
+@@ -998,7 +1012,7 @@ static const struct usb_gadget_ops pxa2xx_udc_ops = {
+
+ static struct pxa2xx_udc memory;
+ static int
+-udc_seq_show(struct seq_file *m, void *d)
++udc_seq_show(struct seq_file *m, void *_d)
+ {
+ struct pxa2xx_udc *dev = m->private;
+ unsigned long flags;
+@@ -1145,11 +1159,6 @@ static void udc_disable(struct pxa2xx_udc *dev)
+
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+
+-#ifdef CONFIG_ARCH_PXA
+- /* Disable clock for USB device */
+- clk_disable(dev->clk);
+-#endif
+-
+ ep0_idle (dev);
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ }
+@@ -1190,11 +1199,6 @@ static void udc_enable (struct pxa2xx_udc *dev)
+ {
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+
+-#ifdef CONFIG_ARCH_PXA
+- /* Enable clock for USB device */
+- clk_enable(dev->clk);
+-#endif
+-
+ /* try to clear these bits before we enable the udc */
+ udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR);
+
+@@ -1285,7 +1289,7 @@ fail:
+ * for set_configuration as well as eventual disconnect.
+ */
+ DMSG("registered gadget driver '%s'\n", driver->driver.name);
+- pullup(dev, 1);
++ pullup(dev);
+ dump_state(dev);
+ return 0;
+ }
+@@ -1328,7 +1332,8 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+ return -EINVAL;
+
+ local_irq_disable();
+- pullup(dev, 0);
++ dev->pullup = 0;
++ pullup(dev);
+ stop_activity(dev, driver);
+ local_irq_enable();
+
+@@ -2267,7 +2272,9 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
+ if (dev->driver)
+ return -EBUSY;
+
+- udc_disable(dev);
++ dev->pullup = 0;
++ pullup(dev);
++
+ remove_debug_files(dev);
+
+ if (dev->got_irq) {
+@@ -2315,10 +2322,15 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
+ static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state)
+ {
+ struct pxa2xx_udc *udc = platform_get_drvdata(dev);
++ unsigned long flags;
+
+ if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
+ WARN("USB host won't detect disconnect!\n");
+- pullup(udc, 0);
++ udc->suspended = 1;
++
++ local_irq_save(flags);
++ pullup(udc);
++ local_irq_restore(flags);
+
+ return 0;
+ }
+@@ -2326,8 +2338,12 @@ static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state)
+ static int pxa2xx_udc_resume(struct platform_device *dev)
+ {
+ struct pxa2xx_udc *udc = platform_get_drvdata(dev);
++ unsigned long flags;
+
+- pullup(udc, 1);
++ udc->suspended = 0;
++ local_irq_save(flags);
++ pullup(udc);
++ local_irq_restore(flags);
+
+ return 0;
+ }
+diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h
+index c08b1a2..93586b2 100644
+--- a/drivers/usb/gadget/pxa2xx_udc.h
++++ b/drivers/usb/gadget/pxa2xx_udc.h
+@@ -119,7 +119,9 @@ struct pxa2xx_udc {
+ has_cfr : 1,
+ req_pending : 1,
+ req_std : 1,
+- req_config : 1;
++ req_config : 1,
++ suspended : 1,
++ active : 1;
+
+ #define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
+ struct timer_list timer;
+@@ -239,7 +241,7 @@ dump_state(struct pxa2xx_udc *dev)
+ dev->stats.read.bytes, dev->stats.read.ops);
+
+ for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+- if (dev->ep [i].desc == 0)
++ if (dev->ep [i].desc == NULL)
+ continue;
+ DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs);
+ }
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0019-pxa-remove-periodic-mode-emulation-support.patch b/recipes/linux/linux-rp-2.6.24/tosa/0019-pxa-remove-periodic-mode-emulation-support.patch
new file mode 100644
index 0000000000..4b4107d655
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0019-pxa-remove-periodic-mode-emulation-support.patch
@@ -0,0 +1,128 @@
+From bda65817167cce5294e1d84670f36815262ed550 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk@dyn-67.arm.linux.org.uk>
+Date: Sun, 3 Feb 2008 21:58:12 +0300
+Subject: [PATCH 19/64] pxa: remove periodic mode emulation support
+
+Apparantly, the generic time subsystem can accurately emulate periodic
+mode via the one-shot support code, so we don't need our own periodic
+emulation code anymore. Just ensure that we build support for one shot
+into the generic time subsystem.
+
+Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
+---
+ arch/arm/Kconfig | 1 +
+ arch/arm/mach-pxa/time.c | 61 ++++++----------------------------------------
+ 2 files changed, 9 insertions(+), 53 deletions(-)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index a04f507..1be7182 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -345,6 +345,7 @@ config ARCH_PXA
+ select GENERIC_GPIO
+ select GENERIC_TIME
+ select GENERIC_CLOCKEVENTS
++ select TICK_ONESHOT
+ help
+ Support for Intel/Marvell's PXA2xx/PXA3xx processor line.
+
+diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c
+index fbfa192..3c4abbf 100644
+--- a/arch/arm/mach-pxa/time.c
++++ b/arch/arm/mach-pxa/time.c
+@@ -59,55 +59,17 @@ unsigned long long sched_clock(void)
+ }
+
+
++#define MIN_OSCR_DELTA 16
++
+ static irqreturn_t
+ pxa_ost0_interrupt(int irq, void *dev_id)
+ {
+- int next_match;
+ struct clock_event_device *c = dev_id;
+
+- if (c->mode == CLOCK_EVT_MODE_ONESHOT) {
+- /* Disarm the compare/match, signal the event. */
+- OIER &= ~OIER_E0;
+- OSSR = OSSR_M0;
+- c->event_handler(c);
+- } else if (c->mode == CLOCK_EVT_MODE_PERIODIC) {
+- /* Call the event handler as many times as necessary
+- * to recover missed events, if any (if we update
+- * OSMR0 and OSCR0 is still ahead of us, we've missed
+- * the event). As we're dealing with that, re-arm the
+- * compare/match for the next event.
+- *
+- * HACK ALERT:
+- *
+- * There's a latency between the instruction that
+- * writes to OSMR0 and the actual commit to the
+- * physical hardware, because the CPU doesn't (have
+- * to) run at bus speed, there's a write buffer
+- * between the CPU and the bus, etc. etc. So if the
+- * target OSCR0 is "very close", to the OSMR0 load
+- * value, the update to OSMR0 might not get to the
+- * hardware in time and we'll miss that interrupt.
+- *
+- * To be safe, if the new OSMR0 is "very close" to the
+- * target OSCR0 value, we call the event_handler as
+- * though the event actually happened. According to
+- * Nico's comment in the previous version of this
+- * code, experience has shown that 6 OSCR ticks is
+- * "very close" but he went with 8. We will use 16,
+- * based on the results of testing on PXA270.
+- *
+- * To be doubly sure, we also tell clkevt via
+- * clockevents_register_device() not to ask for
+- * anything that might put us "very close".
+- */
+-#define MIN_OSCR_DELTA 16
+- do {
+- OSSR = OSSR_M0;
+- next_match = (OSMR0 += LATCH);
+- c->event_handler(c);
+- } while (((signed long)(next_match - OSCR) <= MIN_OSCR_DELTA)
+- && (c->mode == CLOCK_EVT_MODE_PERIODIC));
+- }
++ /* Disarm the compare/match, signal the event. */
++ OIER &= ~OIER_E0;
++ OSSR = OSSR_M0;
++ c->event_handler(c);
+
+ return IRQ_HANDLED;
+ }
+@@ -133,14 +95,6 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
+ unsigned long irqflags;
+
+ switch (mode) {
+- case CLOCK_EVT_MODE_PERIODIC:
+- raw_local_irq_save(irqflags);
+- OSSR = OSSR_M0;
+- OIER |= OIER_E0;
+- OSMR0 = OSCR + LATCH;
+- raw_local_irq_restore(irqflags);
+- break;
+-
+ case CLOCK_EVT_MODE_ONESHOT:
+ raw_local_irq_save(irqflags);
+ OIER &= ~OIER_E0;
+@@ -158,13 +112,14 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
+ break;
+
+ case CLOCK_EVT_MODE_RESUME:
++ case CLOCK_EVT_MODE_PERIODIC:
+ break;
+ }
+ }
+
+ static struct clock_event_device ckevt_pxa_osmr0 = {
+ .name = "osmr0",
+- .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
++ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .shift = 32,
+ .rating = 200,
+ .cpumask = CPU_MASK_CPU0,
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0020-Provide-dew-device-clock-backports-from-2.6.24-git.patch b/recipes/linux/linux-rp-2.6.24/tosa/0020-Provide-dew-device-clock-backports-from-2.6.24-git.patch
new file mode 100644
index 0000000000..0a42bc5855
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0020-Provide-dew-device-clock-backports-from-2.6.24-git.patch
@@ -0,0 +1,257 @@
+From ee8ca5742e0000dd2369ef4d328c2c1117276a3b Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Mon, 4 Feb 2008 02:56:28 +0300
+Subject: [PATCH 20/64] Provide dew device/clock backports from 2.6.24-git
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/Kconfig | 1 +
+ arch/arm/mach-pxa/devices.h | 12 ++++++++++++
+ arch/arm/mach-pxa/pxa25x.c | 18 ++++++++++++------
+ arch/arm/mach-pxa/pxa27x.c | 22 ++++++++++++++++------
+ arch/arm/mach-pxa/pxa3xx.c | 30 ++++++++++++++++++++++++++++++
+ kernel/Makefile | 1 +
+ 6 files changed, 72 insertions(+), 12 deletions(-)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 1be7182..10faf9c 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -367,6 +367,7 @@ config ARCH_SA1100
+ select ARCH_DISCONTIGMEM_ENABLE
+ select ARCH_MTD_XIP
+ select GENERIC_GPIO
++ select GENERIC_TIME
+ help
+ Support for StrongARM 11x0 based boards.
+
+diff --git a/arch/arm/mach-pxa/devices.h b/arch/arm/mach-pxa/devices.h
+index 94c8d5c..96c7c89 100644
+--- a/arch/arm/mach-pxa/devices.h
++++ b/arch/arm/mach-pxa/devices.h
+@@ -1,4 +1,6 @@
+ extern struct platform_device pxa_device_mci;
++extern struct platform_device pxa3xx_device_mci2;
++extern struct platform_device pxa3xx_device_mci3;
+ extern struct platform_device pxa_device_udc;
+ extern struct platform_device pxa_device_fb;
+ extern struct platform_device pxa_device_ffuart;
+@@ -12,3 +14,13 @@ extern struct platform_device pxa_device_rtc;
+
+ extern struct platform_device pxa27x_device_i2c_power;
+ extern struct platform_device pxa27x_device_ohci;
++
++extern struct platform_device pxa25x_device_ssp;
++extern struct platform_device pxa25x_device_nssp;
++extern struct platform_device pxa25x_device_assp;
++extern struct platform_device pxa27x_device_ssp1;
++extern struct platform_device pxa27x_device_ssp2;
++extern struct platform_device pxa27x_device_ssp3;
++extern struct platform_device pxa3xx_device_ssp4;
++
++void __init pxa_register_device(struct platform_device *dev, void *data);
+diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c
+index 006a6e0..5988d99 100644
+--- a/arch/arm/mach-pxa/pxa25x.c
++++ b/arch/arm/mach-pxa/pxa25x.c
+@@ -123,12 +123,15 @@ static struct clk pxa25x_clks[] = {
+ INIT_CKEN("UDCCLK", USB, 47923000, 5, &pxa_device_udc.dev),
+ INIT_CKEN("MMCCLK", MMC, 19169000, 0, &pxa_device_mci.dev),
+ INIT_CKEN("I2CCLK", I2C, 31949000, 0, &pxa_device_i2c.dev),
++
++ INIT_CKEN("SSPCLK", SSP, 3686400, 0, &pxa25x_device_ssp.dev),
++ INIT_CKEN("SSPCLK", NSSP, 3686400, 0, &pxa25x_device_nssp.dev),
++ INIT_CKEN("SSPCLK", ASSP, 3686400, 0, &pxa25x_device_assp.dev),
++
+ /*
+ INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL),
+ INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL),
+- INIT_CKEN("SSPCLK", SSP, 3686400, 0, NULL),
+ INIT_CKEN("I2SCLK", I2S, 14745600, 0, NULL),
+- INIT_CKEN("NSSPCLK", NSSP, 3686400, 0, NULL),
+ */
+ INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL),
+ };
+@@ -216,8 +219,6 @@ static void pxa25x_cpu_pm_restore(unsigned long *sleep_save)
+
+ static void pxa25x_cpu_pm_enter(suspend_state_t state)
+ {
+- CKEN = 0;
+-
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ /* set resume return address */
+@@ -239,6 +240,8 @@ static void __init pxa25x_init_pm(void)
+ {
+ pxa_cpu_pm_fns = &pxa25x_cpu_pm_fns;
+ }
++#else
++static inline void pxa25x_init_pm(void) {}
+ #endif
+
+ /* PXA25x: supports wakeup from GPIO0..GPIO15 and RTC alarm
+@@ -300,6 +303,9 @@ static struct platform_device *pxa25x_devices[] __initdata = {
+ &pxa_device_i2s,
+ &pxa_device_ficp,
+ &pxa_device_rtc,
++ &pxa25x_device_ssp,
++ &pxa25x_device_nssp,
++ &pxa25x_device_assp,
+ };
+
+ static int __init pxa25x_init(void)
+@@ -315,9 +321,9 @@ static int __init pxa25x_init(void)
+
+ if ((ret = pxa_init_dma(16)))
+ return ret;
+-#ifdef CONFIG_PM
++
+ pxa25x_init_pm();
+-#endif
++
+ ret = platform_add_devices(pxa25x_devices,
+ ARRAY_SIZE(pxa25x_devices));
+ }
+diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c
+index 8e126e6..30ca4fd 100644
+--- a/arch/arm/mach-pxa/pxa27x.c
++++ b/arch/arm/mach-pxa/pxa27x.c
+@@ -150,11 +150,12 @@ static struct clk pxa27x_clks[] = {
+ INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0, &pxa27x_device_i2c_power.dev),
+ INIT_CKEN("KBDCLK", KEYPAD, 32768, 0, NULL),
+
++ INIT_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev),
++ INIT_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev),
++ INIT_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev),
++
+ /*
+ INIT_CKEN("PWMCLK", PWM0, 13000000, 0, NULL),
+- INIT_CKEN("SSPCLK", SSP1, 13000000, 0, NULL),
+- INIT_CKEN("SSPCLK", SSP2, 13000000, 0, NULL),
+- INIT_CKEN("SSPCLK", SSP3, 13000000, 0, NULL),
+ INIT_CKEN("MSLCLK", MSL, 48000000, 0, NULL),
+ INIT_CKEN("USIMCLK", USIM, 48000000, 0, NULL),
+ INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0, NULL),
+@@ -304,6 +305,8 @@ static void __init pxa27x_init_pm(void)
+ {
+ pxa_cpu_pm_fns = &pxa27x_cpu_pm_fns;
+ }
++#else
++static inline void pxa27x_init_pm(void) {}
+ #endif
+
+ /* PXA27x: Various gpios can issue wakeup events. This logic only
+@@ -423,6 +426,11 @@ struct platform_device pxa27x_device_i2c_power = {
+ .num_resources = ARRAY_SIZE(i2c_power_resources),
+ };
+
++void __init pxa_set_i2c_power_info(struct i2c_pxa_platform_data *info)
++{
++ pxa27x_device_i2c_power.dev.platform_data = info;
++}
++
+ static struct platform_device *devices[] __initdata = {
+ &pxa_device_mci,
+ &pxa_device_udc,
+@@ -435,7 +443,9 @@ static struct platform_device *devices[] __initdata = {
+ &pxa_device_ficp,
+ &pxa_device_rtc,
+ &pxa27x_device_i2c_power,
+- &pxa27x_device_ohci,
++ &pxa27x_device_ssp1,
++ &pxa27x_device_ssp2,
++ &pxa27x_device_ssp3,
+ };
+
+ static int __init pxa27x_init(void)
+@@ -446,9 +456,9 @@ static int __init pxa27x_init(void)
+
+ if ((ret = pxa_init_dma(32)))
+ return ret;
+-#ifdef CONFIG_PM
++
+ pxa27x_init_pm();
+-#endif
++
+ ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+ }
+ return ret;
+diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c
+index 61d9c9d..ccab9da 100644
+--- a/arch/arm/mach-pxa/pxa3xx.c
++++ b/arch/arm/mach-pxa/pxa3xx.c
+@@ -189,8 +189,31 @@ static struct clk pxa3xx_clks[] = {
+
+ PXA3xx_CKEN("I2CCLK", I2C, 32842000, 0, &pxa_device_i2c.dev),
+ PXA3xx_CKEN("UDCCLK", UDC, 48000000, 5, &pxa_device_udc.dev),
++ PXA3xx_CKEN("USBCLK", USBH, 48000000, 0, &pxa27x_device_ohci.dev),
++
++ PXA3xx_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev),
++ PXA3xx_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev),
++ PXA3xx_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev),
++ PXA3xx_CKEN("SSPCLK", SSP4, 13000000, 0, &pxa3xx_device_ssp4.dev),
++
++ PXA3xx_CKEN("MMCCLK", MMC1, 19500000, 0, &pxa_device_mci.dev),
++ PXA3xx_CKEN("MMCCLK", MMC2, 19500000, 0, &pxa3xx_device_mci2.dev),
++ PXA3xx_CKEN("MMCCLK", MMC3, 19500000, 0, &pxa3xx_device_mci3.dev),
+ };
+
++#ifdef CONFIG_PM
++#define SLEEP_SAVE_SIZE 4
++
++#define ISRAM_START 0x5c000000
++#define ISRAM_SIZE SZ_256K
++
++static inline void pxa3xx_init_pm(void) {}
++static inline void pxa3xx_init_irq_pm(void) {}
++#else
++static inline void pxa3xx_init_pm(void) {}
++static inline void pxa3xx_init_irq_pm(void) {}
++#endif
++
+ void __init pxa3xx_init_irq(void)
+ {
+ /* enable CP6 access */
+@@ -202,6 +225,7 @@ void __init pxa3xx_init_irq(void)
+ pxa_init_irq_low();
+ pxa_init_irq_high();
+ pxa_init_irq_gpio(128);
++ pxa3xx_init_irq_pm();
+ }
+
+ /*
+@@ -219,6 +243,10 @@ static struct platform_device *devices[] __initdata = {
+ &pxa_device_i2s,
+ &pxa_device_ficp,
+ &pxa_device_rtc,
++ &pxa27x_device_ssp1,
++ &pxa27x_device_ssp2,
++ &pxa27x_device_ssp3,
++ &pxa3xx_device_ssp4,
+ };
+
+ static int __init pxa3xx_init(void)
+@@ -231,6 +259,8 @@ static int __init pxa3xx_init(void)
+ if ((ret = pxa_init_dma(32)))
+ return ret;
+
++ pxa3xx_init_pm();
++
+ return platform_add_devices(devices, ARRAY_SIZE(devices));
+ }
+ return 0;
+diff --git a/kernel/Makefile b/kernel/Makefile
+index dfa9695..6d9a87c 100644
+--- a/kernel/Makefile
++++ b/kernel/Makefile
+@@ -57,6 +57,7 @@ obj-$(CONFIG_SYSCTL) += utsname_sysctl.o
+ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
+ obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
+ obj-$(CONFIG_MARKERS) += marker.o
++obj-$(CONFIG_LATENCYTOP) += latencytop.o
+
+ ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
+ # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0021-Add-an-empty-drivers-gpio-directory-for-gpiolib-infr.patch b/recipes/linux/linux-rp-2.6.24/tosa/0021-Add-an-empty-drivers-gpio-directory-for-gpiolib-infr.patch
new file mode 100644
index 0000000000..3f8512128a
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0021-Add-an-empty-drivers-gpio-directory-for-gpiolib-infr.patch
@@ -0,0 +1,121 @@
+From b77665c545bc260d2b93add129413e4a724d7e6e Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell@users.sourceforge.net>
+Date: Fri, 18 Jan 2008 00:35:00 +0300
+Subject: [PATCH 21/64] Add an empty drivers/gpio directory for gpiolib infrastructure and GPIO
+ expanders. It will be populated by later patches.
+
+This won't be the only place to hold such gpio_chip code. Many external chips
+add a few GPIOs as secondary functionality (such as MFD drivers) and platform
+code frequently needs to closely integrate GPIO and IRQ support.
+
+This is placed *early* in the build/link sequence since it's common for other
+drivers to depend on GPIOs to do their work, so they must be initialized early
+in the device_initcall() sequence.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Acked-by: Jean Delvare <khali@linux-fr.org>
+Cc: Eric Miao <eric.miao@marvell.com>
+Cc: Sam Ravnborg <sam@ravnborg.org>
+Cc: Haavard Skinnemoen <hskinnemoen@atmel.com>
+Cc: Philipp Zabel <philipp.zabel@gmail.com>
+Cc: Russell King <rmk@arm.linux.org.uk>
+Cc: Ben Gardner <bgardner@wabtec.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ arch/arm/Kconfig | 2 ++
+ drivers/Kconfig | 2 ++
+ drivers/Makefile | 1 +
+ drivers/gpio/Kconfig | 32 ++++++++++++++++++++++++++++++++
+ drivers/gpio/Makefile | 3 +++
+ 5 files changed, 40 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/gpio/Kconfig
+ create mode 100644 drivers/gpio/Makefile
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 10faf9c..06ca241 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -1042,6 +1042,8 @@ source "drivers/i2c/Kconfig"
+
+ source "drivers/spi/Kconfig"
+
++source "drivers/gpio/Kconfig"
++
+ source "drivers/w1/Kconfig"
+
+ source "drivers/power/Kconfig"
+diff --git a/drivers/Kconfig b/drivers/Kconfig
+index f4076d9..90e295a 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig"
+
+ source "drivers/spi/Kconfig"
+
++source "drivers/gpio/Kconfig"
++
+ source "drivers/w1/Kconfig"
+
+ source "drivers/power/Kconfig"
+diff --git a/drivers/Makefile b/drivers/Makefile
+index 8cb37e3..8e5101f 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -5,6 +5,7 @@
+ # Rewritten to use lists instead of if-statements.
+ #
+
++obj-$(CONFIG_HAVE_GPIO_LIB) += gpio/
+ obj-$(CONFIG_PCI) += pci/
+ obj-$(CONFIG_PARISC) += parisc/
+ obj-$(CONFIG_RAPIDIO) += rapidio/
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
+new file mode 100644
+index 0000000..560687c
+--- /dev/null
++++ b/drivers/gpio/Kconfig
+@@ -0,0 +1,32 @@
++#
++# GPIO infrastructure and expanders
++#
++
++config HAVE_GPIO_LIB
++ bool
++ help
++ Platforms select gpiolib if they use this infrastructure
++ for all their GPIOs, usually starting with ones integrated
++ into SOC processors.
++
++menu "GPIO Support"
++ depends on HAVE_GPIO_LIB
++
++config DEBUG_GPIO
++ bool "Debug GPIO calls"
++ depends on DEBUG_KERNEL
++ help
++ Say Y here to add some extra checks and diagnostics to GPIO calls.
++ The checks help ensure that GPIOs have been properly initialized
++ before they are used and that sleeping calls aren not made from
++ nonsleeping contexts. They can make bitbanged serial protocols
++ slower. The diagnostics help catch the type of setup errors
++ that are most common when setting up new platforms or boards.
++
++# put expanders in the right section, in alphabetical order
++
++comment "I2C GPIO expanders:"
++
++comment "SPI GPIO expanders:"
++
++endmenu
+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
+new file mode 100644
+index 0000000..cdbba6b
+--- /dev/null
++++ b/drivers/gpio/Makefile
+@@ -0,0 +1,3 @@
++# gpio support: dedicated expander chips, etc
++
++ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0022-Provide-new-implementation-infrastructure-that-platf.patch b/recipes/linux/linux-rp-2.6.24/tosa/0022-Provide-new-implementation-infrastructure-that-platf.patch
new file mode 100644
index 0000000000..f39fedbbaa
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0022-Provide-new-implementation-infrastructure-that-platf.patch
@@ -0,0 +1,746 @@
+From 3a0251c01446f3a6763e4406ca5495102db63aa4 Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell@users.sourceforge.net>
+Date: Fri, 18 Jan 2008 00:35:20 +0300
+Subject: [PATCH 22/64] Provide new implementation infrastructure that platforms may choose to use
+ when implementing the GPIO programming interface. Platforms can update their
+ GPIO support to use this. In many cases the incremental cost to access a
+ non-inlined GPIO should be less than a dozen instructions, with the memory
+ cost being about a page (total) of extra data and code. The upside is:
+
+ * Providing two features which were "want to have (but OK to defer)" when
+ GPIO interfaces were first discussed in November 2006:
+
+ - A "struct gpio_chip" to plug in GPIOs that aren't directly supported
+ by SOC platforms, but come from FPGAs or other multifunction devices
+ using conventional device registers (like UCB-1x00 or SM501 GPIOs,
+ and southbridges in PCs with more open specs than usual).
+
+ - Full support for message-based GPIO expanders, where registers are
+ accessed through sleeping I/O calls. Previous support for these
+ "cansleep" calls was just stubs. (One example: the widely used
+ pcf8574 I2C chips, with 8 GPIOs each.)
+
+ * Including a non-stub implementation of the gpio_{request,free}() calls,
+ making those calls much more useful. The diagnostic labels are also
+ recorded given DEBUG_FS, so /sys/kernel/debug/gpio can show a snapshot
+ of all GPIOs known to this infrastructure.
+
+The driver programming interfaces introduced in 2.6.21 do not change at all;
+this infrastructure is entirely below those covers.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Cc: Sam Ravnborg <sam@ravnborg.org>
+Cc: Jean Delvare <khali@linux-fr.org>
+Cc: Eric Miao <eric.miao@marvell.com>
+Cc: Haavard Skinnemoen <hskinnemoen@atmel.com>
+Cc: Philipp Zabel <philipp.zabel@gmail.com>
+Cc: Russell King <rmk@arm.linux.org.uk>
+Cc: Ben Gardner <bgardner@wabtec.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ drivers/gpio/Makefile | 2 +
+ drivers/gpio/gpiolib.c | 567 ++++++++++++++++++++++++++++++++++++++++++++
+ include/asm-generic/gpio.h | 98 ++++++++
+ 3 files changed, 667 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/gpio/gpiolib.c
+
+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
+index cdbba6b..2db28ce 100644
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -1,3 +1,5 @@
+ # gpio support: dedicated expander chips, etc
+
+ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
++
++obj-$(CONFIG_HAVE_GPIO_LIB) += gpiolib.o
+diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
+new file mode 100644
+index 0000000..d8db2f8
+--- /dev/null
++++ b/drivers/gpio/gpiolib.c
+@@ -0,0 +1,567 @@
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/irq.h>
++#include <linux/spinlock.h>
++
++#include <asm/gpio.h>
++
++
++/* Optional implementation infrastructure for GPIO interfaces.
++ *
++ * Platforms may want to use this if they tend to use very many GPIOs
++ * that aren't part of a System-On-Chip core; or across I2C/SPI/etc.
++ *
++ * When kernel footprint or instruction count is an issue, simpler
++ * implementations may be preferred. The GPIO programming interface
++ * allows for inlining speed-critical get/set operations for common
++ * cases, so that access to SOC-integrated GPIOs can sometimes cost
++ * only an instruction or two per bit.
++ */
++
++
++/* When debugging, extend minimal trust to callers and platform code.
++ * Also emit diagnostic messages that may help initial bringup, when
++ * board setup or driver bugs are most common.
++ *
++ * Otherwise, minimize overhead in what may be bitbanging codepaths.
++ */
++#ifdef DEBUG
++#define extra_checks 1
++#else
++#define extra_checks 0
++#endif
++
++/* gpio_lock prevents conflicts during gpio_desc[] table updates.
++ * While any GPIO is requested, its gpio_chip is not removable;
++ * each GPIO's "requested" flag serves as a lock and refcount.
++ */
++static DEFINE_SPINLOCK(gpio_lock);
++
++struct gpio_desc {
++ struct gpio_chip *chip;
++ unsigned long flags;
++/* flag symbols are bit numbers */
++#define FLAG_REQUESTED 0
++#define FLAG_IS_OUT 1
++
++#ifdef CONFIG_DEBUG_FS
++ const char *label;
++#endif
++};
++static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
++
++static inline void desc_set_label(struct gpio_desc *d, const char *label)
++{
++#ifdef CONFIG_DEBUG_FS
++ d->label = label;
++#endif
++}
++
++/* Warn when drivers omit gpio_request() calls -- legal but ill-advised
++ * when setting direction, and otherwise illegal. Until board setup code
++ * and drivers use explicit requests everywhere (which won't happen when
++ * those calls have no teeth) we can't avoid autorequesting. This nag
++ * message should motivate switching to explicit requests...
++ */
++static void gpio_ensure_requested(struct gpio_desc *desc)
++{
++ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
++ pr_warning("GPIO-%d autorequested\n", (int)(desc - gpio_desc));
++ desc_set_label(desc, "[auto]");
++ }
++}
++
++/* caller holds gpio_lock *OR* gpio is marked as requested */
++static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
++{
++ return gpio_desc[gpio].chip;
++}
++
++/**
++ * gpiochip_add() - register a gpio_chip
++ * @chip: the chip to register, with chip->base initialized
++ * Context: potentially before irqs or kmalloc will work
++ *
++ * Returns a negative errno if the chip can't be registered, such as
++ * because the chip->base is invalid or already associated with a
++ * different chip. Otherwise it returns zero as a success code.
++ */
++int gpiochip_add(struct gpio_chip *chip)
++{
++ unsigned long flags;
++ int status = 0;
++ unsigned id;
++
++ /* NOTE chip->base negative is reserved to mean a request for
++ * dynamic allocation. We don't currently support that.
++ */
++
++ if (chip->base < 0 || (chip->base + chip->ngpio) >= ARCH_NR_GPIOS) {
++ status = -EINVAL;
++ goto fail;
++ }
++
++ spin_lock_irqsave(&gpio_lock, flags);
++
++ /* these GPIO numbers must not be managed by another gpio_chip */
++ for (id = chip->base; id < chip->base + chip->ngpio; id++) {
++ if (gpio_desc[id].chip != NULL) {
++ status = -EBUSY;
++ break;
++ }
++ }
++ if (status == 0) {
++ for (id = chip->base; id < chip->base + chip->ngpio; id++) {
++ gpio_desc[id].chip = chip;
++ gpio_desc[id].flags = 0;
++ }
++ }
++
++ spin_unlock_irqrestore(&gpio_lock, flags);
++fail:
++ /* failures here can mean systems won't boot... */
++ if (status)
++ pr_err("gpiochip_add: gpios %d..%d (%s) not registered\n",
++ chip->base, chip->base + chip->ngpio,
++ chip->label ? : "generic");
++ return status;
++}
++EXPORT_SYMBOL_GPL(gpiochip_add);
++
++/**
++ * gpiochip_remove() - unregister a gpio_chip
++ * @chip: the chip to unregister
++ *
++ * A gpio_chip with any GPIOs still requested may not be removed.
++ */
++int gpiochip_remove(struct gpio_chip *chip)
++{
++ unsigned long flags;
++ int status = 0;
++ unsigned id;
++
++ spin_lock_irqsave(&gpio_lock, flags);
++
++ for (id = chip->base; id < chip->base + chip->ngpio; id++) {
++ if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) {
++ status = -EBUSY;
++ break;
++ }
++ }
++ if (status == 0) {
++ for (id = chip->base; id < chip->base + chip->ngpio; id++)
++ gpio_desc[id].chip = NULL;
++ }
++
++ spin_unlock_irqrestore(&gpio_lock, flags);
++ return status;
++}
++EXPORT_SYMBOL_GPL(gpiochip_remove);
++
++
++/* These "optional" allocation calls help prevent drivers from stomping
++ * on each other, and help provide better diagnostics in debugfs.
++ * They're called even less than the "set direction" calls.
++ */
++int gpio_request(unsigned gpio, const char *label)
++{
++ struct gpio_desc *desc;
++ int status = -EINVAL;
++ unsigned long flags;
++
++ spin_lock_irqsave(&gpio_lock, flags);
++
++ if (gpio >= ARCH_NR_GPIOS)
++ goto done;
++ desc = &gpio_desc[gpio];
++ if (desc->chip == NULL)
++ goto done;
++
++ /* NOTE: gpio_request() can be called in early boot,
++ * before IRQs are enabled.
++ */
++
++ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
++ desc_set_label(desc, label ? : "?");
++ status = 0;
++ } else
++ status = -EBUSY;
++
++done:
++ if (status)
++ pr_debug("gpio_request: gpio-%d (%s) status %d\n",
++ gpio, label ? : "?", status);
++ spin_unlock_irqrestore(&gpio_lock, flags);
++ return status;
++}
++EXPORT_SYMBOL_GPL(gpio_request);
++
++void gpio_free(unsigned gpio)
++{
++ unsigned long flags;
++ struct gpio_desc *desc;
++
++ if (gpio >= ARCH_NR_GPIOS) {
++ WARN_ON(extra_checks);
++ return;
++ }
++
++ spin_lock_irqsave(&gpio_lock, flags);
++
++ desc = &gpio_desc[gpio];
++ if (desc->chip && test_and_clear_bit(FLAG_REQUESTED, &desc->flags))
++ desc_set_label(desc, NULL);
++ else
++ WARN_ON(extra_checks);
++
++ spin_unlock_irqrestore(&gpio_lock, flags);
++}
++EXPORT_SYMBOL_GPL(gpio_free);
++
++
++/**
++ * gpiochip_is_requested - return string iff signal was requested
++ * @chip: controller managing the signal
++ * @offset: of signal within controller's 0..(ngpio - 1) range
++ *
++ * Returns NULL if the GPIO is not currently requested, else a string.
++ * If debugfs support is enabled, the string returned is the label passed
++ * to gpio_request(); otherwise it is a meaningless constant.
++ *
++ * This function is for use by GPIO controller drivers. The label can
++ * help with diagnostics, and knowing that the signal is used as a GPIO
++ * can help avoid accidentally multiplexing it to another controller.
++ */
++const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
++{
++ unsigned gpio = chip->base + offset;
++
++ if (gpio >= ARCH_NR_GPIOS || gpio_desc[gpio].chip != chip)
++ return NULL;
++ if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0)
++ return NULL;
++#ifdef CONFIG_DEBUG_FS
++ return gpio_desc[gpio].label;
++#else
++ return "?";
++#endif
++}
++EXPORT_SYMBOL_GPL(gpiochip_is_requested);
++
++
++/* Drivers MUST set GPIO direction before making get/set calls. In
++ * some cases this is done in early boot, before IRQs are enabled.
++ *
++ * As a rule these aren't called more than once (except for drivers
++ * using the open-drain emulation idiom) so these are natural places
++ * to accumulate extra debugging checks. Note that we can't (yet)
++ * rely on gpio_request() having been called beforehand.
++ */
++
++int gpio_direction_input(unsigned gpio)
++{
++ unsigned long flags;
++ struct gpio_chip *chip;
++ struct gpio_desc *desc = &gpio_desc[gpio];
++ int status = -EINVAL;
++
++ spin_lock_irqsave(&gpio_lock, flags);
++
++ if (gpio >= ARCH_NR_GPIOS)
++ goto fail;
++ chip = desc->chip;
++ if (!chip || !chip->get || !chip->direction_input)
++ goto fail;
++ gpio -= chip->base;
++ if (gpio >= chip->ngpio)
++ goto fail;
++ gpio_ensure_requested(desc);
++
++ /* now we know the gpio is valid and chip won't vanish */
++
++ spin_unlock_irqrestore(&gpio_lock, flags);
++
++ might_sleep_if(extra_checks && chip->can_sleep);
++
++ status = chip->direction_input(chip, gpio);
++ if (status == 0)
++ clear_bit(FLAG_IS_OUT, &desc->flags);
++ return status;
++fail:
++ spin_unlock_irqrestore(&gpio_lock, flags);
++ if (status)
++ pr_debug("%s: gpio-%d status %d\n",
++ __FUNCTION__, gpio, status);
++ return status;
++}
++EXPORT_SYMBOL_GPL(gpio_direction_input);
++
++int gpio_direction_output(unsigned gpio, int value)
++{
++ unsigned long flags;
++ struct gpio_chip *chip;
++ struct gpio_desc *desc = &gpio_desc[gpio];
++ int status = -EINVAL;
++
++ spin_lock_irqsave(&gpio_lock, flags);
++
++ if (gpio >= ARCH_NR_GPIOS)
++ goto fail;
++ chip = desc->chip;
++ if (!chip || !chip->set || !chip->direction_output)
++ goto fail;
++ gpio -= chip->base;
++ if (gpio >= chip->ngpio)
++ goto fail;
++ gpio_ensure_requested(desc);
++
++ /* now we know the gpio is valid and chip won't vanish */
++
++ spin_unlock_irqrestore(&gpio_lock, flags);
++
++ might_sleep_if(extra_checks && chip->can_sleep);
++
++ status = chip->direction_output(chip, gpio, value);
++ if (status == 0)
++ set_bit(FLAG_IS_OUT, &desc->flags);
++ return status;
++fail:
++ spin_unlock_irqrestore(&gpio_lock, flags);
++ if (status)
++ pr_debug("%s: gpio-%d status %d\n",
++ __FUNCTION__, gpio, status);
++ return status;
++}
++EXPORT_SYMBOL_GPL(gpio_direction_output);
++
++
++/* I/O calls are only valid after configuration completed; the relevant
++ * "is this a valid GPIO" error checks should already have been done.
++ *
++ * "Get" operations are often inlinable as reading a pin value register,
++ * and masking the relevant bit in that register.
++ *
++ * When "set" operations are inlinable, they involve writing that mask to
++ * one register to set a low value, or a different register to set it high.
++ * Otherwise locking is needed, so there may be little value to inlining.
++ *
++ *------------------------------------------------------------------------
++ *
++ * IMPORTANT!!! The hot paths -- get/set value -- assume that callers
++ * have requested the GPIO. That can include implicit requesting by
++ * a direction setting call. Marking a gpio as requested locks its chip
++ * in memory, guaranteeing that these table lookups need no more locking
++ * and that gpiochip_remove() will fail.
++ *
++ * REVISIT when debugging, consider adding some instrumentation to ensure
++ * that the GPIO was actually requested.
++ */
++
++/**
++ * __gpio_get_value() - return a gpio's value
++ * @gpio: gpio whose value will be returned
++ * Context: any
++ *
++ * This is used directly or indirectly to implement gpio_get_value().
++ * It returns the zero or nonzero value provided by the associated
++ * gpio_chip.get() method; or zero if no such method is provided.
++ */
++int __gpio_get_value(unsigned gpio)
++{
++ struct gpio_chip *chip;
++
++ chip = gpio_to_chip(gpio);
++ WARN_ON(extra_checks && chip->can_sleep);
++ return chip->get ? chip->get(chip, gpio - chip->base) : 0;
++}
++EXPORT_SYMBOL_GPL(__gpio_get_value);
++
++/**
++ * __gpio_set_value() - assign a gpio's value
++ * @gpio: gpio whose value will be assigned
++ * @value: value to assign
++ * Context: any
++ *
++ * This is used directly or indirectly to implement gpio_set_value().
++ * It invokes the associated gpio_chip.set() method.
++ */
++void __gpio_set_value(unsigned gpio, int value)
++{
++ struct gpio_chip *chip;
++
++ chip = gpio_to_chip(gpio);
++ WARN_ON(extra_checks && chip->can_sleep);
++ chip->set(chip, gpio - chip->base, value);
++}
++EXPORT_SYMBOL_GPL(__gpio_set_value);
++
++/**
++ * __gpio_cansleep() - report whether gpio value access will sleep
++ * @gpio: gpio in question
++ * Context: any
++ *
++ * This is used directly or indirectly to implement gpio_cansleep(). It
++ * returns nonzero if access reading or writing the GPIO value can sleep.
++ */
++int __gpio_cansleep(unsigned gpio)
++{
++ struct gpio_chip *chip;
++
++ /* only call this on GPIOs that are valid! */
++ chip = gpio_to_chip(gpio);
++
++ return chip->can_sleep;
++}
++EXPORT_SYMBOL_GPL(__gpio_cansleep);
++
++
++
++/* There's no value in making it easy to inline GPIO calls that may sleep.
++ * Common examples include ones connected to I2C or SPI chips.
++ */
++
++int gpio_get_value_cansleep(unsigned gpio)
++{
++ struct gpio_chip *chip;
++
++ might_sleep_if(extra_checks);
++ chip = gpio_to_chip(gpio);
++ return chip->get(chip, gpio - chip->base);
++}
++EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);
++
++void gpio_set_value_cansleep(unsigned gpio, int value)
++{
++ struct gpio_chip *chip;
++
++ might_sleep_if(extra_checks);
++ chip = gpio_to_chip(gpio);
++ chip->set(chip, gpio - chip->base, value);
++}
++EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
++
++
++#ifdef CONFIG_DEBUG_FS
++
++#include <linux/debugfs.h>
++#include <linux/seq_file.h>
++
++
++static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
++{
++ unsigned i;
++ unsigned gpio = chip->base;
++ struct gpio_desc *gdesc = &gpio_desc[gpio];
++ int is_out;
++
++ for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
++ if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
++ continue;
++
++ is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
++ seq_printf(s, " gpio-%-3d (%-12s) %s %s",
++ gpio, gdesc->label,
++ is_out ? "out" : "in ",
++ chip->get
++ ? (chip->get(chip, i) ? "hi" : "lo")
++ : "? ");
++
++ if (!is_out) {
++ int irq = gpio_to_irq(gpio);
++ struct irq_desc *desc = irq_desc + irq;
++
++ /* This races with request_irq(), set_irq_type(),
++ * and set_irq_wake() ... but those are "rare".
++ *
++ * More significantly, trigger type flags aren't
++ * currently maintained by genirq.
++ */
++ if (irq >= 0 && desc->action) {
++ char *trigger;
++
++ switch (desc->status & IRQ_TYPE_SENSE_MASK) {
++ case IRQ_TYPE_NONE:
++ trigger = "(default)";
++ break;
++ case IRQ_TYPE_EDGE_FALLING:
++ trigger = "edge-falling";
++ break;
++ case IRQ_TYPE_EDGE_RISING:
++ trigger = "edge-rising";
++ break;
++ case IRQ_TYPE_EDGE_BOTH:
++ trigger = "edge-both";
++ break;
++ case IRQ_TYPE_LEVEL_HIGH:
++ trigger = "level-high";
++ break;
++ case IRQ_TYPE_LEVEL_LOW:
++ trigger = "level-low";
++ break;
++ default:
++ trigger = "?trigger?";
++ break;
++ }
++
++ seq_printf(s, " irq-%d %s%s",
++ irq, trigger,
++ (desc->status & IRQ_WAKEUP)
++ ? " wakeup" : "");
++ }
++ }
++
++ seq_printf(s, "\n");
++ }
++}
++
++static int gpiolib_show(struct seq_file *s, void *unused)
++{
++ struct gpio_chip *chip = NULL;
++ unsigned gpio;
++ int started = 0;
++
++ /* REVISIT this isn't locked against gpio_chip removal ... */
++
++ for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {
++ if (chip == gpio_desc[gpio].chip)
++ continue;
++ chip = gpio_desc[gpio].chip;
++ if (!chip)
++ continue;
++
++ seq_printf(s, "%sGPIOs %d-%d, %s%s:\n",
++ started ? "\n" : "",
++ chip->base, chip->base + chip->ngpio - 1,
++ chip->label ? : "generic",
++ chip->can_sleep ? ", can sleep" : "");
++ started = 1;
++ if (chip->dbg_show)
++ chip->dbg_show(s, chip);
++ else
++ gpiolib_dbg_show(s, chip);
++ }
++ return 0;
++}
++
++static int gpiolib_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, gpiolib_show, NULL);
++}
++
++static struct file_operations gpiolib_operations = {
++ .open = gpiolib_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static int __init gpiolib_debugfs_init(void)
++{
++ /* /sys/kernel/debug/gpio */
++ (void) debugfs_create_file("gpio", S_IFREG | S_IRUGO,
++ NULL, NULL, &gpiolib_operations);
++ return 0;
++}
++subsys_initcall(gpiolib_debugfs_init);
++
++#endif /* DEBUG_FS */
+diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
+index 2d0aab1..f29a502 100644
+--- a/include/asm-generic/gpio.h
++++ b/include/asm-generic/gpio.h
+@@ -1,6 +1,102 @@
+ #ifndef _ASM_GENERIC_GPIO_H
+ #define _ASM_GENERIC_GPIO_H
+
++#ifdef CONFIG_HAVE_GPIO_LIB
++
++/* Platforms may implement their GPIO interface with library code,
++ * at a small performance cost for non-inlined operations and some
++ * extra memory (for code and for per-GPIO table entries).
++ *
++ * While the GPIO programming interface defines valid GPIO numbers
++ * to be in the range 0..MAX_INT, this library restricts them to the
++ * smaller range 0..ARCH_NR_GPIOS.
++ */
++
++#ifndef ARCH_NR_GPIOS
++#define ARCH_NR_GPIOS 256
++#endif
++
++struct seq_file;
++
++/**
++ * struct gpio_chip - abstract a GPIO controller
++ * @label: for diagnostics
++ * @direction_input: configures signal "offset" as input, or returns error
++ * @get: returns value for signal "offset"; for output signals this
++ * returns either the value actually sensed, or zero
++ * @direction_output: configures signal "offset" as output, or returns error
++ * @set: assigns output value for signal "offset"
++ * @dbg_show: optional routine to show contents in debugfs; default code
++ * will be used when this is omitted, but custom code can show extra
++ * state (such as pullup/pulldown configuration).
++ * @base: identifies the first GPIO number handled by this chip; or, if
++ * negative during registration, requests dynamic ID allocation.
++ * @ngpio: the number of GPIOs handled by this controller; the last GPIO
++ * handled is (base + ngpio - 1).
++ * @can_sleep: flag must be set iff get()/set() methods sleep, as they
++ * must while accessing GPIO expander chips over I2C or SPI
++ *
++ * A gpio_chip can help platforms abstract various sources of GPIOs so
++ * they can all be accessed through a common programing interface.
++ * Example sources would be SOC controllers, FPGAs, multifunction
++ * chips, dedicated GPIO expanders, and so on.
++ *
++ * Each chip controls a number of signals, identified in method calls
++ * by "offset" values in the range 0..(@ngpio - 1). When those signals
++ * are referenced through calls like gpio_get_value(gpio), the offset
++ * is calculated by subtracting @base from the gpio number.
++ */
++struct gpio_chip {
++ char *label;
++
++ int (*direction_input)(struct gpio_chip *chip,
++ unsigned offset);
++ int (*get)(struct gpio_chip *chip,
++ unsigned offset);
++ int (*direction_output)(struct gpio_chip *chip,
++ unsigned offset, int value);
++ void (*set)(struct gpio_chip *chip,
++ unsigned offset, int value);
++ void (*dbg_show)(struct seq_file *s,
++ struct gpio_chip *chip);
++ int base;
++ u16 ngpio;
++ unsigned can_sleep:1;
++};
++
++extern const char *gpiochip_is_requested(struct gpio_chip *chip,
++ unsigned offset);
++
++/* add/remove chips */
++extern int gpiochip_add(struct gpio_chip *chip);
++extern int __must_check gpiochip_remove(struct gpio_chip *chip);
++
++
++/* Always use the library code for GPIO management calls,
++ * or when sleeping may be involved.
++ */
++extern int gpio_request(unsigned gpio, const char *label);
++extern void gpio_free(unsigned gpio);
++
++extern int gpio_direction_input(unsigned gpio);
++extern int gpio_direction_output(unsigned gpio, int value);
++
++extern int gpio_get_value_cansleep(unsigned gpio);
++extern void gpio_set_value_cansleep(unsigned gpio, int value);
++
++
++/* A platform's <asm/gpio.h> code may want to inline the I/O calls when
++ * the GPIO is constant and refers to some always-present controller,
++ * giving direct access to chip registers and tight bitbanging loops.
++ */
++extern int __gpio_get_value(unsigned gpio);
++extern void __gpio_set_value(unsigned gpio, int value);
++
++extern int __gpio_cansleep(unsigned gpio);
++
++
++#else
++
+ /* platforms that don't directly support access to GPIOs through I2C, SPI,
+ * or other blocking infrastructure can use these wrappers.
+ */
+@@ -22,4 +118,6 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value)
+ gpio_set_value(gpio, value);
+ }
+
++#endif
++
+ #endif /* _ASM_GENERIC_GPIO_H */
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0023-This-adds-gpiolib-support-for-the-PXA-architecture.patch b/recipes/linux/linux-rp-2.6.24/tosa/0023-This-adds-gpiolib-support-for-the-PXA-architecture.patch
new file mode 100644
index 0000000000..7a37be85cf
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0023-This-adds-gpiolib-support-for-the-PXA-architecture.patch
@@ -0,0 +1,498 @@
+From 49da9bd487e54164a75503e0037a054cce697ed5 Mon Sep 17 00:00:00 2001
+From: Philipp Zabel <philipp.zabel@gmail.com>
+Date: Tue, 12 Feb 2008 04:38:12 +0300
+Subject: [PATCH 23/64] This adds gpiolib support for the PXA architecture:
+ - move all GPIO API functions from generic.c into gpio.c
+ - convert the gpio_get/set_value macros into inline functions
+
+This makes it easier to hook up GPIOs provided by external chips like
+ASICs and CPLDs.
+
+Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com>
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>
+Cc: Jean Delvare <khali@linux-fr.org>
+Cc: Eric Miao <eric.miao@marvell.com>
+Cc: Sam Ravnborg <sam@ravnborg.org>
+Cc: Haavard Skinnemoen <hskinnemoen@atmel.com>
+Cc: Ben Gardner <bgardner@wabtec.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ arch/arm/Kconfig | 1 +
+ arch/arm/mach-pxa/Makefile | 3 +-
+ arch/arm/mach-pxa/generic.c | 93 ----------------
+ arch/arm/mach-pxa/generic.h | 1 +
+ arch/arm/mach-pxa/gpio.c | 197 +++++++++++++++++++++++++++++++++++
+ arch/arm/mach-pxa/irq.c | 2 +
+ include/asm-arm/arch-pxa/gpio.h | 48 ++++-----
+ include/asm-arm/arch-pxa/pxa-regs.h | 13 +++
+ 8 files changed, 236 insertions(+), 122 deletions(-)
+ create mode 100644 arch/arm/mach-pxa/gpio.c
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 06ca241..423e953 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -346,6 +346,7 @@ config ARCH_PXA
+ select GENERIC_TIME
+ select GENERIC_CLOCKEVENTS
+ select TICK_ONESHOT
++ select HAVE_GPIO_LIB
+ help
+ Support for Intel/Marvell's PXA2xx/PXA3xx processor line.
+
+diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
+index 4263527..5cb0216 100644
+--- a/arch/arm/mach-pxa/Makefile
++++ b/arch/arm/mach-pxa/Makefile
+@@ -3,7 +3,8 @@
+ #
+
+ # Common support (must be linked before board specific support)
+-obj-y += clock.o generic.o irq.o dma.o time.o
++obj-y += clock.o generic.o irq.o dma.o \
++ time.o gpio.o
+ obj-$(CONFIG_PXA25x) += pxa25x.o
+ obj-$(CONFIG_PXA27x) += pxa27x.o
+ obj-$(CONFIG_PXA3xx) += pxa3xx.o mfp.o
+diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c
+index 1c34946..6c07292 100644
+--- a/arch/arm/mach-pxa/generic.c
++++ b/arch/arm/mach-pxa/generic.c
+@@ -32,7 +32,6 @@
+ #include <asm/mach/map.h>
+
+ #include <asm/arch/pxa-regs.h>
+-#include <asm/arch/gpio.h>
+ #include <asm/arch/udc.h>
+ #include <asm/arch/pxafb.h>
+ #include <asm/arch/mmc.h>
+@@ -73,97 +72,6 @@ unsigned int get_memclk_frequency_10khz(void)
+ EXPORT_SYMBOL(get_memclk_frequency_10khz);
+
+ /*
+- * Handy function to set GPIO alternate functions
+- */
+-int pxa_last_gpio;
+-
+-int pxa_gpio_mode(int gpio_mode)
+-{
+- unsigned long flags;
+- int gpio = gpio_mode & GPIO_MD_MASK_NR;
+- int fn = (gpio_mode & GPIO_MD_MASK_FN) >> 8;
+- int gafr;
+-
+- if (gpio > pxa_last_gpio)
+- return -EINVAL;
+-
+- local_irq_save(flags);
+- if (gpio_mode & GPIO_DFLT_LOW)
+- GPCR(gpio) = GPIO_bit(gpio);
+- else if (gpio_mode & GPIO_DFLT_HIGH)
+- GPSR(gpio) = GPIO_bit(gpio);
+- if (gpio_mode & GPIO_MD_MASK_DIR)
+- GPDR(gpio) |= GPIO_bit(gpio);
+- else
+- GPDR(gpio) &= ~GPIO_bit(gpio);
+- gafr = GAFR(gpio) & ~(0x3 << (((gpio) & 0xf)*2));
+- GAFR(gpio) = gafr | (fn << (((gpio) & 0xf)*2));
+- local_irq_restore(flags);
+-
+- return 0;
+-}
+-
+-EXPORT_SYMBOL(pxa_gpio_mode);
+-
+-int gpio_direction_input(unsigned gpio)
+-{
+- unsigned long flags;
+- u32 mask;
+-
+- if (gpio > pxa_last_gpio)
+- return -EINVAL;
+-
+- mask = GPIO_bit(gpio);
+- local_irq_save(flags);
+- GPDR(gpio) &= ~mask;
+- local_irq_restore(flags);
+-
+- return 0;
+-}
+-EXPORT_SYMBOL(gpio_direction_input);
+-
+-int gpio_direction_output(unsigned gpio, int value)
+-{
+- unsigned long flags;
+- u32 mask;
+-
+- if (gpio > pxa_last_gpio)
+- return -EINVAL;
+-
+- mask = GPIO_bit(gpio);
+- local_irq_save(flags);
+- if (value)
+- GPSR(gpio) = mask;
+- else
+- GPCR(gpio) = mask;
+- GPDR(gpio) |= mask;
+- local_irq_restore(flags);
+-
+- return 0;
+-}
+-EXPORT_SYMBOL(gpio_direction_output);
+-
+-/*
+- * Return GPIO level
+- */
+-int pxa_gpio_get_value(unsigned gpio)
+-{
+- return __gpio_get_value(gpio);
+-}
+-
+-EXPORT_SYMBOL(pxa_gpio_get_value);
+-
+-/*
+- * Set output GPIO level
+- */
+-void pxa_gpio_set_value(unsigned gpio, int value)
+-{
+- __gpio_set_value(gpio, value);
+-}
+-
+-EXPORT_SYMBOL(pxa_gpio_set_value);
+-
+-/*
+ * Routine to safely enable or disable a clock in the CKEN
+ */
+ void __pxa_set_cken(int clock, int enable)
+@@ -178,7 +86,6 @@ void __pxa_set_cken(int clock, int enable)
+
+ local_irq_restore(flags);
+ }
+-
+ EXPORT_SYMBOL(__pxa_set_cken);
+
+ /*
+diff --git a/arch/arm/mach-pxa/generic.h b/arch/arm/mach-pxa/generic.h
+index b30f240..727a9f5 100644
+--- a/arch/arm/mach-pxa/generic.h
++++ b/arch/arm/mach-pxa/generic.h
+@@ -16,6 +16,7 @@ extern void __init pxa_init_irq_low(void);
+ extern void __init pxa_init_irq_high(void);
+ extern void __init pxa_init_irq_gpio(int gpio_nr);
+ extern void __init pxa_init_irq_set_wake(int (*set_wake)(unsigned int, unsigned int));
++extern void __init pxa_init_gpio(int gpio_nr);
+ extern void __init pxa25x_init_irq(void);
+ extern void __init pxa27x_init_irq(void);
+ extern void __init pxa3xx_init_irq(void);
+diff --git a/arch/arm/mach-pxa/gpio.c b/arch/arm/mach-pxa/gpio.c
+new file mode 100644
+index 0000000..8638dd7
+--- /dev/null
++++ b/arch/arm/mach-pxa/gpio.c
+@@ -0,0 +1,197 @@
++/*
++ * linux/arch/arm/mach-pxa/gpio.c
++ *
++ * Generic PXA GPIO handling
++ *
++ * Author: Nicolas Pitre
++ * Created: Jun 15, 2001
++ * Copyright: 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 version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++
++#include <asm/gpio.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/arch/pxa-regs.h>
++
++#include "generic.h"
++
++
++struct pxa_gpio_chip {
++ struct gpio_chip chip;
++ void __iomem *regbase;
++};
++
++int pxa_last_gpio;
++
++/*
++ * Configure pins for GPIO or other functions
++ */
++int pxa_gpio_mode(int gpio_mode)
++{
++ unsigned long flags;
++ int gpio = gpio_mode & GPIO_MD_MASK_NR;
++ int fn = (gpio_mode & GPIO_MD_MASK_FN) >> 8;
++ int gafr;
++
++ if (gpio > pxa_last_gpio)
++ return -EINVAL;
++
++ local_irq_save(flags);
++ if (gpio_mode & GPIO_DFLT_LOW)
++ GPCR(gpio) = GPIO_bit(gpio);
++ else if (gpio_mode & GPIO_DFLT_HIGH)
++ GPSR(gpio) = GPIO_bit(gpio);
++ if (gpio_mode & GPIO_MD_MASK_DIR)
++ GPDR(gpio) |= GPIO_bit(gpio);
++ else
++ GPDR(gpio) &= ~GPIO_bit(gpio);
++ gafr = GAFR(gpio) & ~(0x3 << (((gpio) & 0xf)*2));
++ GAFR(gpio) = gafr | (fn << (((gpio) & 0xf)*2));
++ local_irq_restore(flags);
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa_gpio_mode);
++
++static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
++{
++ unsigned long flags;
++ u32 mask = 1 << offset;
++ u32 value;
++ struct pxa_gpio_chip *pxa;
++ void __iomem *gpdr;
++
++ pxa = container_of(chip, struct pxa_gpio_chip, chip);
++ gpdr = pxa->regbase + GPDR_OFFSET;
++ local_irq_save(flags);
++ value = __raw_readl(gpdr);
++ value &= ~mask;
++ __raw_writel(value, gpdr);
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++static int pxa_gpio_direction_output(struct gpio_chip *chip,
++ unsigned offset, int value)
++{
++ unsigned long flags;
++ u32 mask = 1 << offset;
++ u32 tmp;
++ struct pxa_gpio_chip *pxa;
++ void __iomem *gpdr;
++
++ pxa = container_of(chip, struct pxa_gpio_chip, chip);
++ __raw_writel(mask,
++ pxa->regbase + (value ? GPSR_OFFSET : GPCR_OFFSET));
++ gpdr = pxa->regbase + GPDR_OFFSET;
++ local_irq_save(flags);
++ tmp = __raw_readl(gpdr);
++ tmp |= mask;
++ __raw_writel(tmp, gpdr);
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++/*
++ * Return GPIO level
++ */
++static int pxa_gpio_get(struct gpio_chip *chip, unsigned offset)
++{
++ u32 mask = 1 << offset;
++ struct pxa_gpio_chip *pxa;
++
++ pxa = container_of(chip, struct pxa_gpio_chip, chip);
++ return __raw_readl(pxa->regbase + GPLR_OFFSET) & mask;
++}
++
++/*
++ * Set output GPIO level
++ */
++static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
++{
++ u32 mask = 1 << offset;
++ struct pxa_gpio_chip *pxa;
++
++ pxa = container_of(chip, struct pxa_gpio_chip, chip);
++
++ if (value)
++ __raw_writel(mask, pxa->regbase + GPSR_OFFSET);
++ else
++ __raw_writel(mask, pxa->regbase + GPCR_OFFSET);
++}
++
++static struct pxa_gpio_chip pxa_gpio_chip[] = {
++ [0] = {
++ .regbase = GPIO0_BASE,
++ .chip = {
++ .label = "gpio-0",
++ .direction_input = pxa_gpio_direction_input,
++ .direction_output = pxa_gpio_direction_output,
++ .get = pxa_gpio_get,
++ .set = pxa_gpio_set,
++ .base = 0,
++ .ngpio = 32,
++ },
++ },
++ [1] = {
++ .regbase = GPIO1_BASE,
++ .chip = {
++ .label = "gpio-1",
++ .direction_input = pxa_gpio_direction_input,
++ .direction_output = pxa_gpio_direction_output,
++ .get = pxa_gpio_get,
++ .set = pxa_gpio_set,
++ .base = 32,
++ .ngpio = 32,
++ },
++ },
++ [2] = {
++ .regbase = GPIO2_BASE,
++ .chip = {
++ .label = "gpio-2",
++ .direction_input = pxa_gpio_direction_input,
++ .direction_output = pxa_gpio_direction_output,
++ .get = pxa_gpio_get,
++ .set = pxa_gpio_set,
++ .base = 64,
++ .ngpio = 32, /* 21 for PXA25x */
++ },
++ },
++#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
++ [3] = {
++ .regbase = GPIO3_BASE,
++ .chip = {
++ .label = "gpio-3",
++ .direction_input = pxa_gpio_direction_input,
++ .direction_output = pxa_gpio_direction_output,
++ .get = pxa_gpio_get,
++ .set = pxa_gpio_set,
++ .base = 96,
++ .ngpio = 32,
++ },
++ },
++#endif
++};
++
++void __init pxa_init_gpio(int gpio_nr)
++{
++ int i;
++
++ /* add a GPIO chip for each register bank.
++ * the last PXA25x register only contains 21 GPIOs
++ */
++ for (i = 0; i < gpio_nr; i += 32) {
++ if (i+32 > gpio_nr)
++ pxa_gpio_chip[i/32].chip.ngpio = gpio_nr - i;
++ gpiochip_add(&pxa_gpio_chip[i/32].chip);
++ }
++}
+diff --git a/arch/arm/mach-pxa/irq.c b/arch/arm/mach-pxa/irq.c
+index 07acb45..d0965ef 100644
+--- a/arch/arm/mach-pxa/irq.c
++++ b/arch/arm/mach-pxa/irq.c
+@@ -310,6 +310,8 @@ void __init pxa_init_irq_gpio(int gpio_nr)
+ /* Install handler for GPIO>=2 edge detect interrupts */
+ set_irq_chip(IRQ_GPIO_2_x, &pxa_internal_chip_low);
+ set_irq_chained_handler(IRQ_GPIO_2_x, pxa_gpio_demux_handler);
++
++ pxa_init_gpio(gpio_nr);
+ }
+
+ void __init pxa_init_irq_set_wake(int (*set_wake)(unsigned int, unsigned int))
+diff --git a/include/asm-arm/arch-pxa/gpio.h b/include/asm-arm/arch-pxa/gpio.h
+index 9dbc2dc..bdbf5f9 100644
+--- a/include/asm-arm/arch-pxa/gpio.h
++++ b/include/asm-arm/arch-pxa/gpio.h
+@@ -28,43 +28,35 @@
+ #include <asm/irq.h>
+ #include <asm/hardware.h>
+
+-static inline int gpio_request(unsigned gpio, const char *label)
+-{
+- return 0;
+-}
++#include <asm-generic/gpio.h>
+
+-static inline void gpio_free(unsigned gpio)
+-{
+- return;
+-}
+
+-extern int gpio_direction_input(unsigned gpio);
+-extern int gpio_direction_output(unsigned gpio, int value);
++/* NOTE: some PXAs have fewer on-chip GPIOs (like PXA255, with 85).
++ * Those cases currently cause holes in the GPIO number space.
++ */
++#define NR_BUILTIN_GPIO 128
+
+-static inline int __gpio_get_value(unsigned gpio)
++static inline int gpio_get_value(unsigned gpio)
+ {
+- return GPLR(gpio) & GPIO_bit(gpio);
++ if (__builtin_constant_p(gpio) && (gpio < NR_BUILTIN_GPIO))
++ return GPLR(gpio) & GPIO_bit(gpio);
++ else
++ return __gpio_get_value(gpio);
+ }
+
+-#define gpio_get_value(gpio) \
+- (__builtin_constant_p(gpio) ? \
+- __gpio_get_value(gpio) : \
+- pxa_gpio_get_value(gpio))
+-
+-static inline void __gpio_set_value(unsigned gpio, int value)
++static inline void gpio_set_value(unsigned gpio, int value)
+ {
+- if (value)
+- GPSR(gpio) = GPIO_bit(gpio);
+- else
+- GPCR(gpio) = GPIO_bit(gpio);
++ if (__builtin_constant_p(gpio) && (gpio < NR_BUILTIN_GPIO)) {
++ if (value)
++ GPSR(gpio) = GPIO_bit(gpio);
++ else
++ GPCR(gpio) = GPIO_bit(gpio);
++ } else {
++ __gpio_set_value(gpio, value);
++ }
+ }
+
+-#define gpio_set_value(gpio,value) \
+- (__builtin_constant_p(gpio) ? \
+- __gpio_set_value(gpio, value) : \
+- pxa_gpio_set_value(gpio, value))
+-
+-#include <asm-generic/gpio.h> /* cansleep wrappers */
++#define gpio_cansleep __gpio_cansleep
+
+ #define gpio_to_irq(gpio) IRQ_GPIO(gpio)
+ #define irq_to_gpio(irq) IRQ_TO_GPIO(irq)
+diff --git a/include/asm-arm/arch-pxa/pxa-regs.h b/include/asm-arm/arch-pxa/pxa-regs.h
+index 1bd398d..bd57417 100644
+--- a/include/asm-arm/arch-pxa/pxa-regs.h
++++ b/include/asm-arm/arch-pxa/pxa-regs.h
+@@ -1131,6 +1131,19 @@
+ * General Purpose I/O
+ */
+
++#define GPIO0_BASE ((void __iomem *)io_p2v(0x40E00000))
++#define GPIO1_BASE ((void __iomem *)io_p2v(0x40E00004))
++#define GPIO2_BASE ((void __iomem *)io_p2v(0x40E00008))
++#define GPIO3_BASE ((void __iomem *)io_p2v(0x40E00100))
++
++#define GPLR_OFFSET 0x00
++#define GPDR_OFFSET 0x0C
++#define GPSR_OFFSET 0x18
++#define GPCR_OFFSET 0x24
++#define GRER_OFFSET 0x30
++#define GFER_OFFSET 0x3C
++#define GEDR_OFFSET 0x48
++
+ #define GPLR0 __REG(0x40E00000) /* GPIO Pin-Level Register GPIO<31:0> */
+ #define GPLR1 __REG(0x40E00004) /* GPIO Pin-Level Register GPIO<63:32> */
+ #define GPLR2 __REG(0x40E00008) /* GPIO Pin-Level Register GPIO<80:64> */
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0024-Update-Documentation-gpio.txt-primarily-to-include.patch b/recipes/linux/linux-rp-2.6.24/tosa/0024-Update-Documentation-gpio.txt-primarily-to-include.patch
new file mode 100644
index 0000000000..e460379de6
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0024-Update-Documentation-gpio.txt-primarily-to-include.patch
@@ -0,0 +1,238 @@
+From 7ba82399f2d2df6114ad552999f2e1b9a19cb47a Mon Sep 17 00:00:00 2001
+From: David Brownell <dbrownell@users.sourceforge.net>
+Date: Sat, 19 Jan 2008 19:41:18 +0300
+Subject: [PATCH 24/64] Update Documentation/gpio.txt, primarily to include the new "gpiolib"
+ infrastructure.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Cc: Jean Delvare <khali@linux-fr.org>
+Cc: Eric Miao <eric.miao@marvell.com>
+Cc: Sam Ravnborg <sam@ravnborg.org>
+Cc: Haavard Skinnemoen <hskinnemoen@atmel.com>
+Cc: Philipp Zabel <philipp.zabel@gmail.com>
+Cc: Russell King <rmk@arm.linux.org.uk>
+Cc: Ben Gardner <bgardner@wabtec.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ Documentation/gpio.txt | 133 +++++++++++++++++++++++++++++++++++++++++++----
+ 1 files changed, 121 insertions(+), 12 deletions(-)
+
+diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt
+index 6bc2ba2..8da724e 100644
+--- a/Documentation/gpio.txt
++++ b/Documentation/gpio.txt
+@@ -32,7 +32,7 @@ The exact capabilities of GPIOs vary between systems. Common options:
+ - Input values are likewise readable (1, 0). Some chips support readback
+ of pins configured as "output", which is very useful in such "wire-OR"
+ cases (to support bidirectional signaling). GPIO controllers may have
+- input de-glitch logic, sometimes with software controls.
++ input de-glitch/debounce logic, sometimes with software controls.
+
+ - Inputs can often be used as IRQ signals, often edge triggered but
+ sometimes level triggered. Such IRQs may be configurable as system
+@@ -60,10 +60,13 @@ used on a board that's wired differently. Only least-common-denominator
+ functionality can be very portable. Other features are platform-specific,
+ and that can be critical for glue logic.
+
+-Plus, this doesn't define an implementation framework, just an interface.
++Plus, this doesn't require any implementation framework, just an interface.
+ One platform might implement it as simple inline functions accessing chip
+ registers; another might implement it by delegating through abstractions
+-used for several very different kinds of GPIO controller.
++used for several very different kinds of GPIO controller. (There is some
++optional code supporting such an implementation strategy, described later
++in this document, but drivers acting as clients to the GPIO interface must
++not care how it's implemented.)
+
+ That said, if the convention is supported on their platform, drivers should
+ use it when possible. Platforms should declare GENERIC_GPIO support in
+@@ -121,6 +124,11 @@ before tasking is enabled, as part of early board setup.
+ For output GPIOs, the value provided becomes the initial output value.
+ This helps avoid signal glitching during system startup.
+
++For compatibility with legacy interfaces to GPIOs, setting the direction
++of a GPIO implicitly requests that GPIO (see below) if it has not been
++requested already. That compatibility may be removed in the future;
++explicitly requesting GPIOs is strongly preferred.
++
+ Setting the direction can fail if the GPIO number is invalid, or when
+ that particular GPIO can't be used in that mode. It's generally a bad
+ idea to rely on boot firmware to have set the direction correctly, since
+@@ -133,6 +141,7 @@ Spinlock-Safe GPIO access
+ -------------------------
+ Most GPIO controllers can be accessed with memory read/write instructions.
+ That doesn't need to sleep, and can safely be done from inside IRQ handlers.
++(That includes hardirq contexts on RT kernels.)
+
+ Use these calls to access such GPIOs:
+
+@@ -145,7 +154,7 @@ Use these calls to access such GPIOs:
+ The values are boolean, zero for low, nonzero for high. When reading the
+ value of an output pin, the value returned should be what's seen on the
+ pin ... that won't always match the specified output value, because of
+-issues including wire-OR and output latencies.
++issues including open-drain signaling and output latencies.
+
+ The get/set calls have no error returns because "invalid GPIO" should have
+ been reported earlier from gpio_direction_*(). However, note that not all
+@@ -170,7 +179,8 @@ get to the head of a queue to transmit a command and get its response.
+ This requires sleeping, which can't be done from inside IRQ handlers.
+
+ Platforms that support this type of GPIO distinguish them from other GPIOs
+-by returning nonzero from this call:
++by returning nonzero from this call (which requires a valid GPIO number,
++either explicitly or implicitly requested):
+
+ int gpio_cansleep(unsigned gpio);
+
+@@ -209,8 +219,11 @@ before tasking is enabled, as part of early board setup.
+ These calls serve two basic purposes. One is marking the signals which
+ are actually in use as GPIOs, for better diagnostics; systems may have
+ several hundred potential GPIOs, but often only a dozen are used on any
+-given board. Another is to catch conflicts between drivers, reporting
+-errors when drivers wrongly think they have exclusive use of that signal.
++given board. Another is to catch conflicts, identifying errors when
++(a) two or more drivers wrongly think they have exclusive use of that
++signal, or (b) something wrongly believes it's safe to remove drivers
++needed to manage a signal that's in active use. That is, requesting a
++GPIO can serve as a kind of lock.
+
+ These two calls are optional because not not all current Linux platforms
+ offer such functionality in their GPIO support; a valid implementation
+@@ -223,6 +236,9 @@ Note that requesting a GPIO does NOT cause it to be configured in any
+ way; it just marks that GPIO as in use. Separate code must handle any
+ pin setup (e.g. controlling which pin the GPIO uses, pullup/pulldown).
+
++Also note that it's your responsibility to have stopped using a GPIO
++before you free it.
++
+
+ GPIOs mapped to IRQs
+ --------------------
+@@ -238,7 +254,7 @@ map between them using calls like:
+
+ Those return either the corresponding number in the other namespace, or
+ else a negative errno code if the mapping can't be done. (For example,
+-some GPIOs can't used as IRQs.) It is an unchecked error to use a GPIO
++some GPIOs can't be used as IRQs.) It is an unchecked error to use a GPIO
+ number that wasn't set up as an input using gpio_direction_input(), or
+ to use an IRQ number that didn't originally come from gpio_to_irq().
+
+@@ -299,17 +315,110 @@ Related to multiplexing is configuration and enabling of the pullups or
+ pulldowns integrated on some platforms. Not all platforms support them,
+ or support them in the same way; and any given board might use external
+ pullups (or pulldowns) so that the on-chip ones should not be used.
++(When a circuit needs 5 kOhm, on-chip 100 kOhm resistors won't do.)
+
+ There are other system-specific mechanisms that are not specified here,
+ like the aforementioned options for input de-glitching and wire-OR output.
+ Hardware may support reading or writing GPIOs in gangs, but that's usually
+ configuration dependent: for GPIOs sharing the same bank. (GPIOs are
+ commonly grouped in banks of 16 or 32, with a given SOC having several such
+-banks.) Some systems can trigger IRQs from output GPIOs. Code relying on
+-such mechanisms will necessarily be nonportable.
++banks.) Some systems can trigger IRQs from output GPIOs, or read values
++from pins not managed as GPIOs. Code relying on such mechanisms will
++necessarily be nonportable.
+
+-Dynamic definition of GPIOs is not currently supported; for example, as
++Dynamic definition of GPIOs is not currently standard; for example, as
+ a side effect of configuring an add-on board with some GPIO expanders.
+
+ These calls are purely for kernel space, but a userspace API could be built
+-on top of it.
++on top of them.
++
++
++GPIO implementor's framework (OPTIONAL)
++=======================================
++As noted earlier, there is an optional implementation framework making it
++easier for platforms to support different kinds of GPIO controller using
++the same programming interface.
++
++As a debugging aid, if debugfs is available a /sys/kernel/debug/gpio file
++will be found there. That will list all the controllers registered through
++this framework, and the state of the GPIOs currently in use.
++
++
++Controller Drivers: gpio_chip
++-----------------------------
++In this framework each GPIO controller is packaged as a "struct gpio_chip"
++with information common to each controller of that type:
++
++ - methods to establish GPIO direction
++ - methods used to access GPIO values
++ - flag saying whether calls to its methods may sleep
++ - optional debugfs dump method (showing extra state like pullup config)
++ - label for diagnostics
++
++There is also per-instance data, which may come from device.platform_data:
++the number of its first GPIO, and how many GPIOs it exposes.
++
++The code implementing a gpio_chip should support multiple instances of the
++controller, possibly using the driver model. That code will configure each
++gpio_chip and issue gpiochip_add(). Removing a GPIO controller should be
++rare; use gpiochip_remove() when it is unavoidable.
++
++Most often a gpio_chip is part of an instance-specific structure with state
++not exposed by the GPIO interfaces, such as addressing, power management,
++and more. Chips such as codecs will have complex non-GPIO state,
++
++Any debugfs dump method should normally ignore signals which haven't been
++requested as GPIOs. They can use gpiochip_is_requested(), which returns
++either NULL or the label associated with that GPIO when it was requested.
++
++
++Platform Support
++----------------
++To support this framework, a platform's Kconfig will "select HAVE_GPIO_LIB"
++and arrange that its <asm/gpio.h> includes <asm-generic/gpio.h> and defines
++three functions: gpio_get_value(), gpio_set_value(), and gpio_cansleep().
++They may also want to provide a custom value for ARCH_NR_GPIOS.
++
++Trivial implementations of those functions can directly use framework
++code, which always dispatches through the gpio_chip:
++
++ #define gpio_get_value __gpio_get_value
++ #define gpio_set_value __gpio_set_value
++ #define gpio_cansleep __gpio_cansleep
++
++Fancier implementations could instead define those as inline functions with
++logic optimizing access to specific SOC-based GPIOs. For example, if the
++referenced GPIO is the constant "12", getting or setting its value could
++cost as little as two or three instructions, never sleeping. When such an
++optimization is not possible those calls must delegate to the framework
++code, costing at least a few dozen instructions. For bitbanged I/O, such
++instruction savings can be significant.
++
++For SOCs, platform-specific code defines and registers gpio_chip instances
++for each bank of on-chip GPIOs. Those GPIOs should be numbered/labeled to
++match chip vendor documentation, and directly match board schematics. They
++may well start at zero and go up to a platform-specific limit. Such GPIOs
++are normally integrated into platform initialization to make them always be
++available, from arch_initcall() or earlier; they can often serve as IRQs.
++
++
++Board Support
++-------------
++For external GPIO controllers -- such as I2C or SPI expanders, ASICs, multi
++function devices, FPGAs or CPLDs -- most often board-specific code handles
++registering controller devices and ensures that their drivers know what GPIO
++numbers to use with gpiochip_add(). Their numbers often start right after
++platform-specific GPIOs.
++
++For example, board setup code could create structures identifying the range
++of GPIOs that chip will expose, and passes them to each GPIO expander chip
++using platform_data. Then the chip driver's probe() routine could pass that
++data to gpiochip_add().
++
++Initialization order can be important. For example, when a device relies on
++an I2C-based GPIO, its probe() routine should only be called after that GPIO
++becomes available. That may mean the device should not be registered until
++calls for that GPIO can work. One way to address such dependencies is for
++such gpio_chip controllers to provide setup() and teardown() callbacks to
++board specific code; those board specific callbacks would register devices
++once all the necessary resources are available.
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0025-Signed-off-by-Dmitry-Baryshkov-dbaryshkov-gmail.co.patch b/recipes/linux/linux-rp-2.6.24/tosa/0025-Signed-off-by-Dmitry-Baryshkov-dbaryshkov-gmail.co.patch
new file mode 100644
index 0000000000..84d0fd3e19
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0025-Signed-off-by-Dmitry-Baryshkov-dbaryshkov-gmail.co.patch
@@ -0,0 +1,434 @@
+From 39717c1328f6aa13330eded0e0e268993cfd1eea Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Tue, 12 Feb 2008 10:39:53 +0300
+Subject: [PATCH 25/64] Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+
+---
+ arch/arm/mach-pxa/Makefile | 2 +-
+ arch/arm/mach-pxa/devices.c | 401 +++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 402 insertions(+), 1 deletions(-)
+ create mode 100644 arch/arm/mach-pxa/devices.c
+
+diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
+index 5cb0216..f276d24 100644
+--- a/arch/arm/mach-pxa/Makefile
++++ b/arch/arm/mach-pxa/Makefile
+@@ -4,7 +4,7 @@
+
+ # Common support (must be linked before board specific support)
+ obj-y += clock.o generic.o irq.o dma.o \
+- time.o gpio.o
++ time.o gpio.o devices.o
+ obj-$(CONFIG_PXA25x) += pxa25x.o
+ obj-$(CONFIG_PXA27x) += pxa27x.o
+ obj-$(CONFIG_PXA3xx) += pxa3xx.o mfp.o
+diff --git a/arch/arm/mach-pxa/devices.c b/arch/arm/mach-pxa/devices.c
+new file mode 100644
+index 0000000..928131a
+--- /dev/null
++++ b/arch/arm/mach-pxa/devices.c
+@@ -0,0 +1,401 @@
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++
++#include <asm/arch/gpio.h>
++#include <asm/arch/udc.h>
++#include <asm/arch/pxafb.h>
++#include <asm/arch/mmc.h>
++#include <asm/arch/irda.h>
++#include <asm/arch/i2c.h>
++#include <asm/arch/ohci.h>
++
++#include "devices.h"
++
++#ifdef CONFIG_PXA25x
++
++static u64 pxa25x_ssp_dma_mask = DMA_BIT_MASK(32);
++
++static struct resource pxa25x_resource_ssp[] = {
++ [0] = {
++ .start = 0x41000000,
++ .end = 0x4100001f,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_SSP,
++ .end = IRQ_SSP,
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ /* DRCMR for RX */
++ .start = 13,
++ .end = 13,
++ .flags = IORESOURCE_DMA,
++ },
++ [3] = {
++ /* DRCMR for TX */
++ .start = 14,
++ .end = 14,
++ .flags = IORESOURCE_DMA,
++ },
++};
++
++struct platform_device pxa25x_device_ssp = {
++ .name = "pxa25x-ssp",
++ .id = 0,
++ .dev = {
++ .dma_mask = &pxa25x_ssp_dma_mask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .resource = pxa25x_resource_ssp,
++ .num_resources = ARRAY_SIZE(pxa25x_resource_ssp),
++};
++
++static u64 pxa25x_nssp_dma_mask = DMA_BIT_MASK(32);
++
++static struct resource pxa25x_resource_nssp[] = {
++ [0] = {
++ .start = 0x41400000,
++ .end = 0x4140002f,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_NSSP,
++ .end = IRQ_NSSP,
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ /* DRCMR for RX */
++ .start = 15,
++ .end = 15,
++ .flags = IORESOURCE_DMA,
++ },
++ [3] = {
++ /* DRCMR for TX */
++ .start = 16,
++ .end = 16,
++ .flags = IORESOURCE_DMA,
++ },
++};
++
++struct platform_device pxa25x_device_nssp = {
++ .name = "pxa25x-nssp",
++ .id = 1,
++ .dev = {
++ .dma_mask = &pxa25x_nssp_dma_mask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .resource = pxa25x_resource_nssp,
++ .num_resources = ARRAY_SIZE(pxa25x_resource_nssp),
++};
++
++static u64 pxa25x_assp_dma_mask = DMA_BIT_MASK(32);
++
++static struct resource pxa25x_resource_assp[] = {
++ [0] = {
++ .start = 0x41500000,
++ .end = 0x4150002f,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_ASSP,
++ .end = IRQ_ASSP,
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ /* DRCMR for RX */
++ .start = 23,
++ .end = 23,
++ .flags = IORESOURCE_DMA,
++ },
++ [3] = {
++ /* DRCMR for TX */
++ .start = 24,
++ .end = 24,
++ .flags = IORESOURCE_DMA,
++ },
++};
++
++struct platform_device pxa25x_device_assp = {
++ /* ASSP is basically equivalent to NSSP */
++ .name = "pxa25x-nssp",
++ .id = 2,
++ .dev = {
++ .dma_mask = &pxa25x_assp_dma_mask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .resource = pxa25x_resource_assp,
++ .num_resources = ARRAY_SIZE(pxa25x_resource_assp),
++};
++#endif /* CONFIG_PXA25x */
++
++#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
++
++static u64 pxa27x_ohci_dma_mask = DMA_BIT_MASK(32);
++
++static struct resource pxa27x_resource_ohci[] = {
++ [0] = {
++ .start = 0x4C000000,
++ .end = 0x4C00ff6f,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_USBH1,
++ .end = IRQ_USBH1,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++struct platform_device pxa27x_device_ohci = {
++ .name = "pxa27x-ohci",
++ .id = -1,
++ .dev = {
++ .dma_mask = &pxa27x_ohci_dma_mask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .num_resources = ARRAY_SIZE(pxa27x_resource_ohci),
++ .resource = pxa27x_resource_ohci,
++};
++
++void __init pxa_set_ohci_info(struct pxaohci_platform_data *info)
++{
++ pxa_register_device(&pxa27x_device_ohci, info);
++}
++
++static u64 pxa27x_ssp1_dma_mask = DMA_BIT_MASK(32);
++
++static struct resource pxa27x_resource_ssp1[] = {
++ [0] = {
++ .start = 0x41000000,
++ .end = 0x4100003f,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_SSP,
++ .end = IRQ_SSP,
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ /* DRCMR for RX */
++ .start = 13,
++ .end = 13,
++ .flags = IORESOURCE_DMA,
++ },
++ [3] = {
++ /* DRCMR for TX */
++ .start = 14,
++ .end = 14,
++ .flags = IORESOURCE_DMA,
++ },
++};
++
++struct platform_device pxa27x_device_ssp1 = {
++ .name = "pxa27x-ssp",
++ .id = 0,
++ .dev = {
++ .dma_mask = &pxa27x_ssp1_dma_mask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .resource = pxa27x_resource_ssp1,
++ .num_resources = ARRAY_SIZE(pxa27x_resource_ssp1),
++};
++
++static u64 pxa27x_ssp2_dma_mask = DMA_BIT_MASK(32);
++
++static struct resource pxa27x_resource_ssp2[] = {
++ [0] = {
++ .start = 0x41700000,
++ .end = 0x4170003f,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_SSP2,
++ .end = IRQ_SSP2,
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ /* DRCMR for RX */
++ .start = 15,
++ .end = 15,
++ .flags = IORESOURCE_DMA,
++ },
++ [3] = {
++ /* DRCMR for TX */
++ .start = 16,
++ .end = 16,
++ .flags = IORESOURCE_DMA,
++ },
++};
++
++struct platform_device pxa27x_device_ssp2 = {
++ .name = "pxa27x-ssp",
++ .id = 1,
++ .dev = {
++ .dma_mask = &pxa27x_ssp2_dma_mask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .resource = pxa27x_resource_ssp2,
++ .num_resources = ARRAY_SIZE(pxa27x_resource_ssp2),
++};
++
++static u64 pxa27x_ssp3_dma_mask = DMA_BIT_MASK(32);
++
++static struct resource pxa27x_resource_ssp3[] = {
++ [0] = {
++ .start = 0x41900000,
++ .end = 0x4190003f,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_SSP3,
++ .end = IRQ_SSP3,
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ /* DRCMR for RX */
++ .start = 66,
++ .end = 66,
++ .flags = IORESOURCE_DMA,
++ },
++ [3] = {
++ /* DRCMR for TX */
++ .start = 67,
++ .end = 67,
++ .flags = IORESOURCE_DMA,
++ },
++};
++
++struct platform_device pxa27x_device_ssp3 = {
++ .name = "pxa27x-ssp",
++ .id = 2,
++ .dev = {
++ .dma_mask = &pxa27x_ssp3_dma_mask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .resource = pxa27x_resource_ssp3,
++ .num_resources = ARRAY_SIZE(pxa27x_resource_ssp3),
++};
++#endif /* CONFIG_PXA27x || CONFIG_PXA3xx */
++
++#ifdef CONFIG_PXA3xx
++static u64 pxa3xx_ssp4_dma_mask = DMA_BIT_MASK(32);
++
++static struct resource pxa3xx_resource_ssp4[] = {
++ [0] = {
++ .start = 0x41a00000,
++ .end = 0x41a0003f,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_SSP4,
++ .end = IRQ_SSP4,
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ /* DRCMR for RX */
++ .start = 2,
++ .end = 2,
++ .flags = IORESOURCE_DMA,
++ },
++ [3] = {
++ /* DRCMR for TX */
++ .start = 3,
++ .end = 3,
++ .flags = IORESOURCE_DMA,
++ },
++};
++
++struct platform_device pxa3xx_device_ssp4 = {
++ /* PXA3xx SSP is basically equivalent to PXA27x */
++ .name = "pxa27x-ssp",
++ .id = 3,
++ .dev = {
++ .dma_mask = &pxa3xx_ssp4_dma_mask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .resource = pxa3xx_resource_ssp4,
++ .num_resources = ARRAY_SIZE(pxa3xx_resource_ssp4),
++};
++
++static struct resource pxa3xx_resources_mci2[] = {
++ [0] = {
++ .start = 0x42000000,
++ .end = 0x42000fff,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_MMC2,
++ .end = IRQ_MMC2,
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ .start = 93,
++ .end = 93,
++ .flags = IORESOURCE_DMA,
++ },
++ [3] = {
++ .start = 94,
++ .end = 94,
++ .flags = IORESOURCE_DMA,
++ },
++};
++
++struct platform_device pxa3xx_device_mci2 = {
++ .name = "pxa2xx-mci",
++ .id = 1,
++ .dev = {
++ .dma_mask = &pxamci_dmamask,
++ .coherent_dma_mask = 0xffffffff,
++ },
++ .num_resources = ARRAY_SIZE(pxa3xx_resources_mci2),
++ .resource = pxa3xx_resources_mci2,
++};
++
++void __init pxa3xx_set_mci2_info(struct pxamci_platform_data *info)
++{
++ pxa_register_device(&pxa3xx_device_mci2, info);
++}
++
++static struct resource pxa3xx_resources_mci3[] = {
++ [0] = {
++ .start = 0x42500000,
++ .end = 0x42500fff,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_MMC3,
++ .end = IRQ_MMC3,
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ .start = 100,
++ .end = 100,
++ .flags = IORESOURCE_DMA,
++ },
++ [3] = {
++ .start = 101,
++ .end = 101,
++ .flags = IORESOURCE_DMA,
++ },
++};
++
++struct platform_device pxa3xx_device_mci3 = {
++ .name = "pxa2xx-mci",
++ .id = 2,
++ .dev = {
++ .dma_mask = &pxamci_dmamask,
++ .coherent_dma_mask = 0xffffffff,
++ },
++ .num_resources = ARRAY_SIZE(pxa3xx_resources_mci3),
++ .resource = pxa3xx_resources_mci3,
++};
++
++void __init pxa3xx_set_mci3_info(struct pxamci_platform_data *info)
++{
++ pxa_register_device(&pxa3xx_device_mci3, info);
++}
++
++#endif /* CONFIG_PXA3xx */
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0026-I-don-t-think-we-should-check-for-IRQs-when-determin.patch b/recipes/linux/linux-rp-2.6.24/tosa/0026-I-don-t-think-we-should-check-for-IRQs-when-determin.patch
new file mode 100644
index 0000000000..e1323e4edc
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0026-I-don-t-think-we-should-check-for-IRQs-when-determin.patch
@@ -0,0 +1,134 @@
+From cbe46408b666983284e8be290950d526dbc0f0a4 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 02:08:16 +0300
+Subject: [PATCH 26/64] I don't think we should check for IRQs when determining which one
+ of power supplies to register. Better use is_{ac,usb}_online
+ callbacks, this will not produce an obstacle to implement polling --
+ when irqs aren't mandatory. I'll send my two pending patches to show
+ the idea.
+
+For this particular issue, I think something like that should work.
+If it works for you, I'll commit that version, preserving your
+authorship, of course.
+---
+ drivers/power/pda_power.c | 80 ++++++++++++++++++++++++--------------------
+ 1 files changed, 44 insertions(+), 36 deletions(-)
+
+diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c
+index c058f28..d98622f 100644
+--- a/drivers/power/pda_power.c
++++ b/drivers/power/pda_power.c
+@@ -168,66 +168,74 @@ static int pda_power_probe(struct platform_device *pdev)
+ pda_power_supplies[1].num_supplicants = pdata->num_supplicants;
+ }
+
+- ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]);
+- if (ret) {
+- dev_err(dev, "failed to register %s power supply\n",
+- pda_power_supplies[0].name);
+- goto supply0_failed;
+- }
++ if (pdata->is_ac_online) {
++ ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]);
++ if (ret) {
++ dev_err(dev, "failed to register %s power supply\n",
++ pda_power_supplies[0].name);
++ goto ac_supply_failed;
++ }
+
+- ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]);
+- if (ret) {
+- dev_err(dev, "failed to register %s power supply\n",
+- pda_power_supplies[1].name);
+- goto supply1_failed;
++ if (ac_irq) {
++ ret = request_irq(ac_irq->start, power_changed_isr,
++ get_irq_flags(ac_irq), ac_irq->name,
++ &pda_power_supplies[0]);
++ if (ret) {
++ dev_err(dev, "request ac irq failed\n");
++ goto ac_irq_failed;
++ }
++ }
+ }
+
+- if (ac_irq) {
+- ret = request_irq(ac_irq->start, power_changed_isr,
+- get_irq_flags(ac_irq), ac_irq->name,
+- &pda_power_supplies[0]);
++ if (pdata->is_usb_online) {
++ ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]);
+ if (ret) {
+- dev_err(dev, "request ac irq failed\n");
+- goto ac_irq_failed;
++ dev_err(dev, "failed to register %s power supply\n",
++ pda_power_supplies[1].name);
++ goto usb_supply_failed;
+ }
+- }
+
+- if (usb_irq) {
+- ret = request_irq(usb_irq->start, power_changed_isr,
+- get_irq_flags(usb_irq), usb_irq->name,
+- &pda_power_supplies[1]);
+- if (ret) {
+- dev_err(dev, "request usb irq failed\n");
+- goto usb_irq_failed;
++ if (usb_irq) {
++ ret = request_irq(usb_irq->start, power_changed_isr,
++ get_irq_flags(usb_irq),
++ usb_irq->name,
++ &pda_power_supplies[1]);
++ if (ret) {
++ dev_err(dev, "request usb irq failed\n");
++ goto usb_irq_failed;
++ }
+ }
+ }
+
+- goto success;
++ return 0;
+
+ usb_irq_failed:
+- if (ac_irq)
++ if (pdata->is_usb_online)
++ power_supply_unregister(&pda_power_supplies[1]);
++usb_supply_failed:
++ if (pdata->is_ac_online && ac_irq)
+ free_irq(ac_irq->start, &pda_power_supplies[0]);
+ ac_irq_failed:
+- power_supply_unregister(&pda_power_supplies[1]);
+-supply1_failed:
+- power_supply_unregister(&pda_power_supplies[0]);
+-supply0_failed:
++ if (pdata->is_ac_online)
++ power_supply_unregister(&pda_power_supplies[0]);
++ac_supply_failed:
+ noirqs:
+ wrongid:
+-success:
+ return ret;
+ }
+
+ static int pda_power_remove(struct platform_device *pdev)
+ {
+- if (usb_irq)
++ if (pdata->is_usb_online && usb_irq)
+ free_irq(usb_irq->start, &pda_power_supplies[1]);
+- if (ac_irq)
++ if (pdata->is_ac_online && ac_irq)
+ free_irq(ac_irq->start, &pda_power_supplies[0]);
+ del_timer_sync(&charger_timer);
+ del_timer_sync(&supply_timer);
+- power_supply_unregister(&pda_power_supplies[1]);
+- power_supply_unregister(&pda_power_supplies[0]);
++ if (pdata->is_usb_online)
++ power_supply_unregister(&pda_power_supplies[1]);
++ if (pdata->is_ac_online)
++ power_supply_unregister(&pda_power_supplies[0]);
+ return 0;
+ }
+
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0027-Add-LiMn-one-of-the-most-common-for-small-non-recha.patch b/recipes/linux/linux-rp-2.6.24/tosa/0027-Add-LiMn-one-of-the-most-common-for-small-non-recha.patch
new file mode 100644
index 0000000000..240d2d0bd9
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0027-Add-LiMn-one-of-the-most-common-for-small-non-recha.patch
@@ -0,0 +1,59 @@
+From e5e9808fd5ed9cb54dd9da9fb91b32c4f7e9da52 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 02:08:17 +0300
+Subject: [PATCH 27/64] Add LiMn (one of the most common for small non-rechargable batteries)i
+ battery technology and voltage_min/_max properties support.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/power/power_supply_sysfs.c | 5 ++++-
+ include/linux/power_supply.h | 3 +++
+ 2 files changed, 7 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
+index 249f61b..45d2f95 100644
+--- a/drivers/power/power_supply_sysfs.c
++++ b/drivers/power/power_supply_sysfs.c
+@@ -46,7 +46,8 @@ static ssize_t power_supply_show_property(struct device *dev,
+ "Unspecified failure"
+ };
+ static char *technology_text[] = {
+- "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd"
++ "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
++ "LiMn"
+ };
+ static char *capacity_level_text[] = {
+ "Unknown", "Critical", "Low", "Normal", "High", "Full"
+@@ -88,6 +89,8 @@ static struct device_attribute power_supply_attrs[] = {
+ POWER_SUPPLY_ATTR(present),
+ POWER_SUPPLY_ATTR(online),
+ POWER_SUPPLY_ATTR(technology),
++ POWER_SUPPLY_ATTR(voltage_max),
++ POWER_SUPPLY_ATTR(voltage_min),
+ POWER_SUPPLY_ATTR(voltage_max_design),
+ POWER_SUPPLY_ATTR(voltage_min_design),
+ POWER_SUPPLY_ATTR(voltage_now),
+diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
+index 606c095..cdbc5b8 100644
+--- a/include/linux/power_supply.h
++++ b/include/linux/power_supply.h
+@@ -54,6 +54,7 @@ enum {
+ POWER_SUPPLY_TECHNOLOGY_LIPO,
+ POWER_SUPPLY_TECHNOLOGY_LiFe,
+ POWER_SUPPLY_TECHNOLOGY_NiCd,
++ POWER_SUPPLY_TECHNOLOGY_LiMn,
+ };
+
+ enum {
+@@ -72,6 +73,8 @@ enum power_supply_property {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
++ POWER_SUPPLY_PROP_VOLTAGE_MAX,
++ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0028-Add-suspend-resume-wakeup-support-for-pda_power.patch b/recipes/linux/linux-rp-2.6.24/tosa/0028-Add-suspend-resume-wakeup-support-for-pda_power.patch
new file mode 100644
index 0000000000..ac5df97dff
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0028-Add-suspend-resume-wakeup-support-for-pda_power.patch
@@ -0,0 +1,72 @@
+From df0801d2cd6a7081700c79f437d1185cbe1960a7 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 02:08:18 +0300
+Subject: [PATCH 28/64] Add suspend/resume/wakeup support for pda_power.
+ Now with device_init_wakeup.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/power/pda_power.c | 34 ++++++++++++++++++++++++++++++++++
+ 1 files changed, 34 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c
+index d98622f..28360e8 100644
+--- a/drivers/power/pda_power.c
++++ b/drivers/power/pda_power.c
+@@ -207,6 +207,8 @@ static int pda_power_probe(struct platform_device *pdev)
+ }
+ }
+
++ device_init_wakeup(&pdev->dev, 1);
++
+ return 0;
+
+ usb_irq_failed:
+@@ -239,12 +241,44 @@ static int pda_power_remove(struct platform_device *pdev)
+ return 0;
+ }
+
++#ifdef CONFIG_PM
++static int pda_power_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ if (device_may_wakeup(&pdev->dev)) {
++ if (ac_irq)
++ enable_irq_wake(ac_irq->start);
++ if (usb_irq)
++ enable_irq_wake(usb_irq->start);
++ }
++
++ return 0;
++}
++
++static int pda_power_resume(struct platform_device *pdev)
++{
++ if (device_may_wakeup(&pdev->dev)) {
++ if (usb_irq)
++ disable_irq_wake(usb_irq->start);
++ if (ac_irq)
++ disable_irq_wake(ac_irq->start);
++ }
++
++ return 0;
++}
++#else
++#define pda_power_suspend NULL
++#define pda_power_resume NULL
++#endif
++
++
+ static struct platform_driver pda_power_pdrv = {
+ .driver = {
+ .name = "pda-power",
+ },
+ .probe = pda_power_probe,
+ .remove = pda_power_remove,
++ .suspend = pda_power_suspend,
++ .resume = pda_power_resume,
+ };
+
+ static int __init pda_power_init(void)
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0029-Support-using-VOLTAGE_-properties-for-apm-calculati.patch b/recipes/linux/linux-rp-2.6.24/tosa/0029-Support-using-VOLTAGE_-properties-for-apm-calculati.patch
new file mode 100644
index 0000000000..7347fd5a00
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0029-Support-using-VOLTAGE_-properties-for-apm-calculati.patch
@@ -0,0 +1,163 @@
+From 57d1450b4e5f27fa78c75895dc30213bde7191bc Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 02:08:18 +0300
+Subject: [PATCH 29/64] Support using VOLTAGE_* properties for apm calculations. It's pretty
+ dummy, but useful for batteries for which we can only get voltages.
+
+---
+ drivers/power/apm_power.c | 63 ++++++++++++++++++++++++++++++++++++--------
+ 1 files changed, 51 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c
+index bbf3ee1..526c96e 100644
+--- a/drivers/power/apm_power.c
++++ b/drivers/power/apm_power.c
+@@ -13,6 +13,12 @@
+ #include <linux/power_supply.h>
+ #include <linux/apm-emulation.h>
+
++typedef enum {
++ SOURCE_ENERGY,
++ SOURCE_CHARGE,
++ SOURCE_VOLTAGE,
++} apm_source;
++
+ #define PSY_PROP(psy, prop, val) psy->get_property(psy, \
+ POWER_SUPPLY_PROP_##prop, val)
+
+@@ -87,7 +93,7 @@ static void find_main_battery(void)
+ }
+ }
+
+-static int calculate_time(int status, int using_charge)
++static int calculate_time(int status, apm_source source)
+ {
+ union power_supply_propval full;
+ union power_supply_propval empty;
+@@ -106,20 +112,34 @@ static int calculate_time(int status, int using_charge)
+ return -1;
+ }
+
+- if (using_charge) {
++ switch (source) {
++ case SOURCE_CHARGE:
+ full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
+ full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
+ empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
+ empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
+ cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
+ cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
+- } else {
++ break;
++ case SOURCE_ENERGY:
+ full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
+ full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
+ empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
+ empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
+ cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
+ cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
++ break;
++ case SOURCE_VOLTAGE:
++ full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
++ full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
++ empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
++ empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
++ cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
++ cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
++ break;
++ default:
++ printk(KERN_ERR "Unsupported source: %d\n", source);
++ return -1;
+ }
+
+ if (_MPSY_PROP(full_prop, &full)) {
+@@ -146,7 +166,7 @@ static int calculate_time(int status, int using_charge)
+ return -((cur.intval - empty.intval) * 60L) / I.intval;
+ }
+
+-static int calculate_capacity(int using_charge)
++static int calculate_capacity(apm_source source)
+ {
+ enum power_supply_property full_prop, empty_prop;
+ enum power_supply_property full_design_prop, empty_design_prop;
+@@ -154,20 +174,33 @@ static int calculate_capacity(int using_charge)
+ union power_supply_propval empty, full, cur;
+ int ret;
+
+- if (using_charge) {
++ switch (source) {
++ case SOURCE_CHARGE:
+ full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
+ empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
+ full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
+ empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
+ now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
+ avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
+- } else {
++ break;
++ case SOURCE_ENERGY:
+ full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
+ empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
+ full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
+ empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
+ now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
+ avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
++ case SOURCE_VOLTAGE:
++ full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
++ empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
++ full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
++ empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
++ now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
++ avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
++ break;
++ default:
++ printk(KERN_ERR "Unsupported source: %d\n", source);
++ return -1;
+ }
+
+ if (_MPSY_PROP(full_prop, &full)) {
+@@ -234,10 +267,12 @@ static void apm_battery_apm_get_power_status(struct apm_power_info *info)
+ info->battery_life = capacity.intval;
+ } else {
+ /* try calculate using energy */
+- info->battery_life = calculate_capacity(0);
++ info->battery_life = calculate_capacity(SOURCE_ENERGY);
+ /* if failed try calculate using charge instead */
+ if (info->battery_life == -1)
+- info->battery_life = calculate_capacity(1);
++ info->battery_life = calculate_capacity(SOURCE_CHARGE);
++ if (info->battery_life == -1)
++ info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
+ }
+
+ /* charging status */
+@@ -263,18 +298,22 @@ static void apm_battery_apm_get_power_status(struct apm_power_info *info)
+ !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) {
+ info->time = time_to_full.intval / 60;
+ } else {
+- info->time = calculate_time(status.intval, 0);
++ info->time = calculate_time(status.intval, SOURCE_ENERGY);
+ if (info->time == -1)
+- info->time = calculate_time(status.intval, 1);
++ info->time = calculate_time(status.intval, SOURCE_CHARGE);
++ if (info->time == -1)
++ info->time = calculate_time(status.intval, SOURCE_VOLTAGE);
+ }
+ } else {
+ if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
+ !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) {
+ info->time = time_to_empty.intval / 60;
+ } else {
+- info->time = calculate_time(status.intval, 0);
++ info->time = calculate_time(status.intval, SOURCE_ENERGY);
++ if (info->time == -1)
++ info->time = calculate_time(status.intval, SOURCE_CHARGE);
+ if (info->time == -1)
+- info->time = calculate_time(status.intval, 1);
++ info->time = calculate_time(status.intval, SOURCE_VOLTAGE);
+ }
+ }
+
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0030-Core-driver-for-WM97xx-touchscreens.patch b/recipes/linux/linux-rp-2.6.24/tosa/0030-Core-driver-for-WM97xx-touchscreens.patch
new file mode 100644
index 0000000000..1c86a39c74
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0030-Core-driver-for-WM97xx-touchscreens.patch
@@ -0,0 +1,1083 @@
+From d3e044e0e10e6c6b75716cb927e92b4ec284132f Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Date: Sat, 26 Jan 2008 21:14:20 +0300
+Subject: [PATCH 30/64] Core driver for WM97xx touchscreens
+
+This patch series adds support for the touchscreen controllers provided
+by Wolfson Microelectronics WM97xx series chips in both polled and
+streaming modes.
+
+These drivers have been maintained out of tree since 2003. During that
+time the driver the primary maintainer was Liam Girdwood and a number of
+people have made contributions including Stanley Cai, Rodolfo Giometti,
+Russell King, Marc Kleine-Budde, Ian Molton, Vincent Sanders, Andrew
+Zabolotny, Graeme Gregory, Mike Arthur and myself. Apologies to anyone
+I have omitted.
+
+Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com>
+Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com>
+Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Cc: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Cc: Stanley Cai <stanley.cai@intel.com>
+Cc: Rodolfo Giometti <giometti@enneenne.com>
+Cc: Russell King <rmk@arm.linux.org.uk>
+Cc: Marc Kleine-Budde <mkl@pengutronix.de>
+Cc: Ian Molton <spyro@f2s.com>
+Cc: Vincent Sanders <vince@kyllikki.org>
+Cc: Andrew Zabolotny <zap@homelink.ru>
+---
+ drivers/input/touchscreen/wm97xx-core.c | 724 +++++++++++++++++++++++++++++++
+ include/linux/wm97xx.h | 309 +++++++++++++
+ 2 files changed, 1033 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/input/touchscreen/wm97xx-core.c
+ create mode 100644 include/linux/wm97xx.h
+
+diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
+new file mode 100644
+index 0000000..27a0a99
+--- /dev/null
++++ b/drivers/input/touchscreen/wm97xx-core.c
+@@ -0,0 +1,724 @@
++/*
++ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712
++ * and WM9713 AC97 Codecs.
++ *
++ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ * Notes:
++ *
++ * Features:
++ * - supports WM9705, WM9712, WM9713
++ * - polling mode
++ * - continuous mode (arch-dependent)
++ * - adjustable rpu/dpp settings
++ * - adjustable pressure current
++ * - adjustable sample settle delay
++ * - 4 and 5 wire touchscreens (5 wire is WM9712 only)
++ * - pen down detection
++ * - battery monitor
++ * - sample AUX adcs
++ * - power management
++ * - codec GPIO
++ * - codec event notification
++ * Todo
++ * - Support for async sampling control for noisy LCDs.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/string.h>
++#include <linux/proc_fs.h>
++#include <linux/pm.h>
++#include <linux/interrupt.h>
++#include <linux/bitops.h>
++#include <linux/workqueue.h>
++#include <linux/wm97xx.h>
++#include <linux/uaccess.h>
++#include <linux/io.h>
++
++#define TS_NAME "wm97xx"
++#define WM_CORE_VERSION "0.65"
++#define DEFAULT_PRESSURE 0xb0c0
++
++
++/*
++ * Touchscreen absolute values
++ *
++ * These parameters are used to help the input layer discard out of
++ * range readings and reduce jitter etc.
++ *
++ * o min, max:- indicate the min and max values your touch screen returns
++ * o fuzz:- use a higher number to reduce jitter
++ *
++ * The default values correspond to Mainstone II in QVGA mode
++ *
++ * Please read
++ * Documentation/input/input-programming.txt for more details.
++ */
++
++static int abs_x[3] = {350, 3900, 5};
++module_param_array(abs_x, int, NULL, 0);
++MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
++
++static int abs_y[3] = {320, 3750, 40};
++module_param_array(abs_y, int, NULL, 0);
++MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
++
++static int abs_p[3] = {0, 150, 4};
++module_param_array(abs_p, int, NULL, 0);
++MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz");
++
++/*
++ * wm97xx IO access, all IO locking done by AC97 layer
++ */
++int wm97xx_reg_read(struct wm97xx *wm, u16 reg)
++{
++ if (wm->ac97)
++ return wm->ac97->bus->ops->read(wm->ac97, reg);
++ else
++ return -1;
++}
++EXPORT_SYMBOL_GPL(wm97xx_reg_read);
++
++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val)
++{
++ /* cache digitiser registers */
++ if (reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3)
++ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val;
++
++ /* cache gpio regs */
++ if (reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE)
++ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val;
++
++ /* wm9713 irq reg */
++ if (reg == 0x5a)
++ wm->misc = val;
++
++ if (wm->ac97)
++ wm->ac97->bus->ops->write(wm->ac97, reg, val);
++}
++EXPORT_SYMBOL_GPL(wm97xx_reg_write);
++
++/**
++ * wm97xx_read_aux_adc - Read the aux adc.
++ * @wm: wm97xx device.
++ * @adcsel: codec ADC to be read
++ *
++ * Reads the selected AUX ADC.
++ */
++
++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
++{
++ int power_adc = 0, auxval;
++ u16 power = 0;
++
++ /* get codec */
++ mutex_lock(&wm->codec_mutex);
++
++ /* When the touchscreen is not in use, we may have to power up
++ * the AUX ADC before we can use sample the AUX inputs->
++ */
++ if (wm->id == WM9713_ID2 &&
++ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) {
++ power_adc = 1;
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff);
++ }
++
++ /* Prepare the codec for AUX reading */
++ wm->codec->aux_prepare(wm);
++
++ /* Turn polling mode on to read AUX ADC */
++ wm->pen_probably_down = 1;
++ wm->codec->poll_sample(wm, adcsel, &auxval);
++
++ if (power_adc)
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
++
++ wm->codec->dig_restore(wm);
++
++ wm->pen_probably_down = 0;
++
++ mutex_unlock(&wm->codec_mutex);
++ return auxval & 0xfff;
++}
++EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
++
++/**
++ * wm97xx_get_gpio - Get the status of a codec GPIO.
++ * @wm: wm97xx device.
++ * @gpio: gpio
++ *
++ * Get the status of a codec GPIO pin
++ */
++
++enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio)
++{
++ u16 status;
++ enum wm97xx_gpio_status ret;
++
++ mutex_lock(&wm->codec_mutex);
++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++
++ if (status & gpio)
++ ret = WM97XX_GPIO_HIGH;
++ else
++ ret = WM97XX_GPIO_LOW;
++
++ mutex_unlock(&wm->codec_mutex);
++ return ret;
++}
++EXPORT_SYMBOL_GPL(wm97xx_get_gpio);
++
++/**
++ * wm97xx_set_gpio - Set the status of a codec GPIO.
++ * @wm: wm97xx device.
++ * @gpio: gpio
++ *
++ *
++ * Set the status of a codec GPIO pin
++ */
++
++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
++ enum wm97xx_gpio_status status)
++{
++ u16 reg;
++
++ mutex_lock(&wm->codec_mutex);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++
++ if (status & WM97XX_GPIO_HIGH)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ if (wm->id == WM9712_ID2)
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1);
++ else
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg);
++ mutex_unlock(&wm->codec_mutex);
++}
++EXPORT_SYMBOL_GPL(wm97xx_set_gpio);
++
++/*
++ * Codec GPIO pin configuration, this sets pin direction, polarity,
++ * stickyness and wake up.
++ */
++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir,
++ enum wm97xx_gpio_pol pol, enum wm97xx_gpio_sticky sticky,
++ enum wm97xx_gpio_wake wake)
++{
++ u16 reg;
++
++ mutex_lock(&wm->codec_mutex);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++
++ if (pol == WM97XX_GPIO_POL_HIGH)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
++
++ if (sticky == WM97XX_GPIO_STICKY)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
++
++ if (wake == WM97XX_GPIO_WAKE)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++
++ if (dir == WM97XX_GPIO_IN)
++ reg |= gpio;
++ else
++ reg &= ~gpio;
++
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg);
++ mutex_unlock(&wm->codec_mutex);
++}
++EXPORT_SYMBOL_GPL(wm97xx_config_gpio);
++
++/*
++ * Handle a pen down interrupt.
++ */
++static void wm97xx_pen_irq_worker(struct work_struct *work)
++{
++ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work);
++
++ /* do we need to enable the touch panel reader */
++ if (wm->id == WM9705_ID2) {
++ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) &
++ WM97XX_PEN_DOWN)
++ wm->pen_is_down = 1;
++ else
++ wm->pen_is_down = 0;
++ } else {
++ u16 status, pol;
++ mutex_lock(&wm->codec_mutex);
++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++
++ if (WM97XX_GPIO_13 & pol & status) {
++ wm->pen_is_down = 1;
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol &
++ ~WM97XX_GPIO_13);
++ } else {
++ wm->pen_is_down = 0;
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol |
++ WM97XX_GPIO_13);
++ }
++
++ if (wm->id == WM9712_ID2)
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status &
++ ~WM97XX_GPIO_13) << 1);
++ else
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status &
++ ~WM97XX_GPIO_13);
++ mutex_unlock(&wm->codec_mutex);
++ }
++
++ queue_delayed_work(wm->ts_workq, &wm->ts_reader, 0);
++
++ if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->mach_ops->acc_pen_up(wm);
++ wm->mach_ops->irq_enable(wm, 1);
++}
++
++/*
++ * Codec PENDOWN irq handler
++ *
++ * We have to disable the codec interrupt in the handler because it can
++ * take upto 1ms to clear the interrupt source. The interrupt is then enabled
++ * again in the slow handler when the source has been cleared.
++ */
++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id)
++{
++ struct wm97xx *wm = dev_id;
++ wm->mach_ops->irq_enable(wm, 0);
++ queue_work(wm->ts_workq, &wm->pen_event_work);
++ return IRQ_HANDLED;
++}
++
++/*
++ * initialise pen IRQ handler and workqueue
++ */
++static int wm97xx_init_pen_irq(struct wm97xx *wm)
++{
++ u16 reg;
++
++ /* If an interrupt is supplied an IRQ enable operation must also be
++ * provided. */
++ BUG_ON(!wm->mach_ops->irq_enable);
++
++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker);
++
++ if (request_irq(wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED,
++ "wm97xx-pen", wm)) {
++ dev_err(wm->dev,
++ "Failed to register pen down interrupt, polling");
++ wm->pen_irq = 0;
++ return -EINVAL;
++ }
++
++ /* enable PEN down on wm9712/13 */
++ if (wm->id != WM9705_ID2) {
++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb);
++ reg = wm97xx_reg_read(wm, 0x5a);
++ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001);
++ }
++
++ return 0;
++}
++
++static int wm97xx_read_samples(struct wm97xx *wm)
++{
++ struct wm97xx_data data;
++ int rc;
++
++ mutex_lock(&wm->codec_mutex);
++
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ rc = wm->mach_ops->acc_pen_down(wm);
++ else
++ rc = wm->codec->poll_touch(wm, &data);
++
++ if (rc & RC_PENUP) {
++ if (wm->pen_is_down) {
++ wm->pen_is_down = 0;
++ dev_dbg(wm->dev, "pen up\n");
++ input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
++ input_sync(wm->input_dev);
++ } else if (!(rc & RC_AGAIN)) {
++ /* We need high frequency updates only while
++ * pen is down, the user never will be able to
++ * touch screen faster than a few times per
++ * second... On the other hand, when the user
++ * is actively working with the touchscreen we
++ * don't want to lose the quick response. So we
++ * will slowly increase sleep time after the
++ * pen is up and quicky restore it to ~one task
++ * switch when pen is down again.
++ */
++ if (wm->ts_reader_interval < HZ / 10)
++ wm->ts_reader_interval++;
++ }
++
++ } else if (rc & RC_VALID) {
++ dev_dbg(wm->dev,
++ "pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
++ data.x >> 12, data.x & 0xfff, data.y >> 12,
++ data.y & 0xfff, data.p >> 12, data.p & 0xfff);
++ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
++ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
++ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
++ input_sync(wm->input_dev);
++ wm->pen_is_down = 1;
++ wm->ts_reader_interval = wm->ts_reader_min_interval;
++ } else if (rc & RC_PENDOWN) {
++ dev_dbg(wm->dev, "pen down");
++ wm->pen_is_down = 1;
++ wm->ts_reader_interval = wm->ts_reader_min_interval;
++ }
++
++ mutex_unlock(&wm->codec_mutex);
++ return rc;
++}
++
++/*
++* The touchscreen sample reader.
++*/
++static void wm97xx_ts_reader(struct work_struct *work)
++{
++ int rc;
++ struct wm97xx *wm = container_of(work, struct wm97xx, ts_reader.work);
++
++ BUG_ON(!wm->codec);
++
++ do {
++ rc = wm97xx_read_samples(wm);
++ } while (rc & RC_AGAIN);
++
++ if (wm->pen_is_down || !wm->pen_irq)
++ queue_delayed_work(wm->ts_workq, &wm->ts_reader,
++ wm->ts_reader_interval);
++}
++
++/**
++ * wm97xx_ts_input_open - Open the touch screen input device.
++ * @idev: Input device to be opened.
++ *
++ * Called by the input sub system to open a wm97xx touchscreen device.
++ * Starts the touchscreen thread and touch digitiser.
++ */
++static int wm97xx_ts_input_open(struct input_dev *idev)
++{
++ struct wm97xx *wm = input_get_drvdata(idev);
++
++ wm->ts_workq = create_singlethread_workqueue("kwm97xx");
++ if (wm->ts_workq == NULL) {
++ dev_err(wm->dev,
++ "Failed to create workqueue\n");
++ return -EINVAL;
++ }
++
++ /* start digitiser */
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->codec->acc_enable(wm, 1);
++ wm->codec->dig_enable(wm, 1);
++
++ INIT_DELAYED_WORK(&wm->ts_reader, wm97xx_ts_reader);
++
++ wm->ts_reader_min_interval = HZ >= 100 ? HZ / 100 : 1;
++ if (wm->ts_reader_min_interval < 1)
++ wm->ts_reader_min_interval = 1;
++ wm->ts_reader_interval = wm->ts_reader_min_interval;
++
++ wm->pen_is_down = 0;
++ if (wm->pen_irq)
++ wm97xx_init_pen_irq(wm);
++ else
++ dev_err(wm->dev, "No IRQ specified\n");
++
++ /* If we either don't have an interrupt for pen down events or
++ * failed to acquire it then we need to poll.
++ */
++ if (wm->pen_irq == 0)
++ queue_delayed_work(wm->ts_workq, &wm->ts_reader,
++ wm->ts_reader_interval);
++
++ return 0;
++}
++
++/**
++ * wm97xx_ts_input_close - Close the touch screen input device.
++ * @idev: Input device to be closed.
++ *
++ * Called by the input sub system to close a wm97xx touchscreen device.
++ * Kills the touchscreen thread and stops the touch digitiser.
++ */
++
++static void wm97xx_ts_input_close(struct input_dev *idev)
++{
++ struct wm97xx *wm = input_get_drvdata(idev);
++
++ if (wm->pen_irq)
++ free_irq(wm->pen_irq, wm);
++
++ wm->pen_is_down = 0;
++
++ /* ts_reader rearms itself so we need to explicitly stop it
++ * before we destroy the workqueue.
++ */
++ cancel_delayed_work_sync(&wm->ts_reader);
++ destroy_workqueue(wm->ts_workq);
++
++ /* stop digitiser */
++ wm->codec->dig_enable(wm, 0);
++ if (wm->mach_ops && wm->mach_ops->acc_enabled)
++ wm->codec->acc_enable(wm, 0);
++}
++
++static int wm97xx_probe(struct device *dev)
++{
++ struct wm97xx *wm;
++ int ret = 0, id = 0;
++
++ wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL);
++ if (!wm)
++ return -ENOMEM;
++ mutex_init(&wm->codec_mutex);
++
++ wm->dev = dev;
++ dev->driver_data = wm;
++ wm->ac97 = to_ac97_t(dev);
++
++ /* check that we have a supported codec */
++ id = wm97xx_reg_read(wm, AC97_VENDOR_ID1);
++ if (id != WM97XX_ID1) {
++ dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id);
++ kfree(wm);
++ return -ENODEV;
++ }
++
++ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
++
++ dev_info(wm->dev, "detected a wm97%02x codec", wm->id & 0xff);
++
++ switch (wm->id & 0xff) {
++#ifdef CONFIG_TOUCHSCREEN_WM9705
++ case 0x05:
++ wm->codec = &wm9705_codec;
++ break;
++#endif
++#ifdef CONFIG_TOUCHSCREEN_WM9712
++ case 0x12:
++ wm->codec = &wm9712_codec;
++ break;
++#endif
++#ifdef CONFIG_TOUCHSCREEN_WM9713
++ case 0x13:
++ wm->codec = &wm9713_codec;
++ break;
++#endif
++ default:
++ dev_err(wm->dev, "Support for wm97%02x not compiled in.\n",
++ wm->id & 0xff);
++ kfree(wm);
++ return -ENODEV;
++ }
++
++ wm->input_dev = input_allocate_device();
++ if (wm->input_dev == NULL) {
++ kfree(wm);
++ return -ENOMEM;
++ }
++
++ /* set up touch configuration */
++ wm->input_dev->name = "wm97xx touchscreen";
++ wm->input_dev->open = wm97xx_ts_input_open;
++ wm->input_dev->close = wm97xx_ts_input_close;
++ set_bit(EV_ABS, wm->input_dev->evbit);
++ set_bit(ABS_X, wm->input_dev->absbit);
++ set_bit(ABS_Y, wm->input_dev->absbit);
++ set_bit(ABS_PRESSURE, wm->input_dev->absbit);
++ input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1],
++ abs_x[2], 0);
++ input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1],
++ abs_y[2], 0);
++ input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1],
++ abs_p[2], 0);
++ input_set_drvdata(wm->input_dev, wm);
++ wm->input_dev->dev.parent = dev;
++ ret = input_register_device(wm->input_dev);
++ if (ret < 0) {
++ input_free_device(wm->input_dev);
++ kfree(wm);
++ return -ENOMEM;
++ }
++
++ /* set up physical characteristics */
++ wm->codec->phy_init(wm);
++
++ /* load gpio cache */
++ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
++ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
++ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
++ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
++ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
++
++ /* register our battery device */
++ wm->battery_dev = platform_device_alloc("wm97xx-battery", 0);
++ if (!wm->battery_dev)
++ goto batt_err;
++ platform_set_drvdata(wm->battery_dev, wm);
++ wm->battery_dev->dev.parent = dev;
++ ret = platform_device_register(wm->battery_dev);
++ if (ret < 0)
++ goto batt_reg_err;
++
++ /* register our extended touch device (for machine specific
++ * extensions) */
++ wm->touch_dev = platform_device_alloc("wm97xx-touch", 0);
++ if (!wm->touch_dev)
++ goto touch_err;
++ platform_set_drvdata(wm->touch_dev, wm);
++ wm->touch_dev->dev.parent = dev;
++ ret = platform_device_register(wm->touch_dev);
++ if (ret < 0)
++ goto touch_reg_err;
++
++ return ret;
++
++ touch_reg_err:
++ platform_device_put(wm->touch_dev);
++ touch_err:
++ platform_device_unregister(wm->battery_dev);
++ batt_reg_err:
++ platform_device_put(wm->battery_dev);
++ batt_err:
++ input_unregister_device(wm->input_dev);
++ kfree(wm);
++ return ret;
++}
++
++static int wm97xx_remove(struct device *dev)
++{
++ struct wm97xx *wm = dev_get_drvdata(dev);
++
++ platform_device_unregister(wm->battery_dev);
++ platform_device_unregister(wm->touch_dev);
++ input_unregister_device(wm->input_dev);
++
++ kfree(wm);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int wm97xx_resume(struct device *dev)
++{
++ struct wm97xx *wm = dev_get_drvdata(dev);
++
++ /* restore digitiser and gpios */
++ if (wm->id == WM9713_ID2) {
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]);
++ wm97xx_reg_write(wm, 0x5a, wm->misc);
++ if (wm->input_dev->users) {
++ u16 reg;
++ reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff;
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
++ }
++ }
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]);
++
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]);
++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]);
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]);
++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]);
++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]);
++
++ return 0;
++}
++
++#else
++#define wm97xx_resume NULL
++#endif
++
++/*
++ * Machine specific operations
++ */
++int wm97xx_register_mach_ops(struct wm97xx *wm,
++ struct wm97xx_mach_ops *mach_ops)
++{
++ mutex_lock(&wm->codec_mutex);
++ if (wm->mach_ops) {
++ mutex_unlock(&wm->codec_mutex);
++ return -EINVAL;
++ }
++ wm->mach_ops = mach_ops;
++ mutex_unlock(&wm->codec_mutex);
++ return 0;
++}
++EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops);
++
++void wm97xx_unregister_mach_ops(struct wm97xx *wm)
++{
++ mutex_lock(&wm->codec_mutex);
++ wm->mach_ops = NULL;
++ mutex_unlock(&wm->codec_mutex);
++}
++EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
++
++static struct device_driver wm97xx_driver = {
++ .name = "ac97",
++ .bus = &ac97_bus_type,
++ .owner = THIS_MODULE,
++ .probe = wm97xx_probe,
++ .remove = wm97xx_remove,
++ .resume = wm97xx_resume,
++};
++
++static int __init wm97xx_init(void)
++{
++ return driver_register(&wm97xx_driver);
++}
++
++static void __exit wm97xx_exit(void)
++{
++ driver_unregister(&wm97xx_driver);
++}
++
++module_init(wm97xx_init);
++module_exit(wm97xx_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
++MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver");
++MODULE_LICENSE("GPL");
+diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h
+new file mode 100644
+index 0000000..fc6e0b3
+--- /dev/null
++++ b/include/linux/wm97xx.h
+@@ -0,0 +1,309 @@
++
++/*
++ * Register bits and API for Wolfson WM97xx series of codecs
++ */
++
++#ifndef _LINUX_WM97XX_H
++#define _LINUX_WM97XX_H
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/ac97_codec.h>
++#include <sound/initval.h>
++#include <linux/types.h>
++#include <linux/list.h>
++#include <linux/input.h> /* Input device layer */
++#include <linux/platform_device.h>
++
++/*
++ * WM97xx AC97 Touchscreen registers
++ */
++#define AC97_WM97XX_DIGITISER1 0x76
++#define AC97_WM97XX_DIGITISER2 0x78
++#define AC97_WM97XX_DIGITISER_RD 0x7a
++#define AC97_WM9713_DIG1 0x74
++#define AC97_WM9713_DIG2 AC97_WM97XX_DIGITISER1
++#define AC97_WM9713_DIG3 AC97_WM97XX_DIGITISER2
++
++/*
++ * WM97xx register bits
++ */
++#define WM97XX_POLL 0x8000 /* initiate a polling measurement */
++#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */
++#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */
++#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */
++#define WM97XX_ADCSEL_MASK 0x7000
++#define WM97XX_COO 0x0800 /* enable coordinate mode */
++#define WM97XX_CTC 0x0400 /* enable continuous mode */
++#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */
++#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */
++#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */
++#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */
++#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */
++#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */
++#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */
++#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */
++#define WM97XX_CM_RATE_MASK 0x03f0
++#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0))
++#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */
++#define WM97XX_DELAY_MASK 0x00f0
++#define WM97XX_SLEN 0x0008 /* slot read back enable */
++#define WM97XX_SLT(i) ((i - 5) & 0x7) /* panel slot (5-11) */
++#define WM97XX_SLT_MASK 0x0007
++#define WM97XX_PRP_DETW 0x4000 /* detect on, digitise off, wake */
++#define WM97XX_PRP_DET 0x8000 /* detect on, digitise off, no wake */
++#define WM97XX_PRP_DET_DIG 0xc000 /* setect on, digitise on */
++#define WM97XX_RPR 0x2000 /* wake up on pen down */
++#define WM97XX_PEN_DOWN 0x8000 /* pen is down */
++#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */
++
++#define WM97XX_AUX_ID1 0x8001
++#define WM97XX_AUX_ID2 0x8002
++#define WM97XX_AUX_ID3 0x8003
++#define WM97XX_AUX_ID4 0x8004
++
++
++/* WM9712 Bits */
++#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */
++#define WM9712_PDEN 0x0800 /* measure only when pen down */
++#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */
++#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */
++#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */
++#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */
++#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */
++#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */
++#define WM9712_PD(i) (0x1 << i) /* power management */
++
++/* WM9712 Registers */
++#define AC97_WM9712_POWER 0x24
++#define AC97_WM9712_REV 0x58
++
++/* WM9705 Bits */
++#define WM9705_PDEN 0x1000 /* measure only when pen is down */
++#define WM9705_PINV 0x0800 /* inverts sense of pen down output */
++#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */
++#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */
++#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */
++#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */
++#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */
++#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */
++#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */
++#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */
++#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */
++
++
++/* WM9713 Bits */
++#define WM9713_PDPOL 0x0400 /* Pen down polarity */
++#define WM9713_POLL 0x0200 /* initiate a polling measurement */
++#define WM9713_CTC 0x0100 /* enable continuous mode */
++#define WM9713_ADCSEL_X 0x0002 /* X measurement */
++#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */
++#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */
++#define WM9713_COO 0x0001 /* enable coordinate mode */
++#define WM9713_PDEN 0x0800 /* measure only when pen down */
++#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */
++#define WM9713_WAIT 0x0200 /* coordinate wait */
++
++/* AUX ADC ID's */
++#define TS_COMP1 0x0
++#define TS_COMP2 0x1
++#define TS_BMON 0x2
++#define TS_WIPER 0x3
++
++/* ID numbers */
++#define WM97XX_ID1 0x574d
++#define WM9712_ID2 0x4c12
++#define WM9705_ID2 0x4c05
++#define WM9713_ID2 0x4c13
++
++/* Codec GPIO's */
++#define WM97XX_MAX_GPIO 16
++#define WM97XX_GPIO_1 (1 << 1)
++#define WM97XX_GPIO_2 (1 << 2)
++#define WM97XX_GPIO_3 (1 << 3)
++#define WM97XX_GPIO_4 (1 << 4)
++#define WM97XX_GPIO_5 (1 << 5)
++#define WM97XX_GPIO_6 (1 << 6)
++#define WM97XX_GPIO_7 (1 << 7)
++#define WM97XX_GPIO_8 (1 << 8)
++#define WM97XX_GPIO_9 (1 << 9)
++#define WM97XX_GPIO_10 (1 << 10)
++#define WM97XX_GPIO_11 (1 << 11)
++#define WM97XX_GPIO_12 (1 << 12)
++#define WM97XX_GPIO_13 (1 << 13)
++#define WM97XX_GPIO_14 (1 << 14)
++#define WM97XX_GPIO_15 (1 << 15)
++
++
++#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */
++
++
++/*---------------- Return codes from sample reading functions ---------------*/
++
++/* More data is available; call the sample gathering function again */
++#define RC_AGAIN 0x00000001
++/* The returned sample is valid */
++#define RC_VALID 0x00000002
++/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */
++#define RC_PENUP 0x00000004
++/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful
++ to tell the handler that the pen is down but we don't know yet his coords,
++ so the handler should not sleep or wait for pendown irq) */
++#define RC_PENDOWN 0x00000008
++
++/*
++ * The wm97xx driver provides a private API for writing platform-specific
++ * drivers.
++ */
++
++/* The structure used to return arch specific sampled data into */
++struct wm97xx_data {
++ int x;
++ int y;
++ int p;
++};
++
++/*
++ * Codec GPIO status
++ */
++enum wm97xx_gpio_status {
++ WM97XX_GPIO_HIGH,
++ WM97XX_GPIO_LOW
++};
++
++/*
++ * Codec GPIO direction
++ */
++enum wm97xx_gpio_dir {
++ WM97XX_GPIO_IN,
++ WM97XX_GPIO_OUT
++};
++
++/*
++ * Codec GPIO polarity
++ */
++enum wm97xx_gpio_pol {
++ WM97XX_GPIO_POL_HIGH,
++ WM97XX_GPIO_POL_LOW
++};
++
++/*
++ * Codec GPIO sticky
++ */
++enum wm97xx_gpio_sticky {
++ WM97XX_GPIO_STICKY,
++ WM97XX_GPIO_NOTSTICKY
++};
++
++/*
++ * Codec GPIO wake
++ */
++enum wm97xx_gpio_wake {
++ WM97XX_GPIO_WAKE,
++ WM97XX_GPIO_NOWAKE
++};
++
++/*
++ * Digitiser ioctl commands
++ */
++#define WM97XX_DIG_START 0x1
++#define WM97XX_DIG_STOP 0x2
++#define WM97XX_PHY_INIT 0x3
++#define WM97XX_AUX_PREPARE 0x4
++#define WM97XX_DIG_RESTORE 0x5
++
++struct wm97xx;
++
++extern struct wm97xx_codec_drv wm9705_codec;
++extern struct wm97xx_codec_drv wm9712_codec;
++extern struct wm97xx_codec_drv wm9713_codec;
++
++/*
++ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs
++ */
++struct wm97xx_codec_drv {
++ u16 id;
++ char *name;
++
++ /* read 1 sample */
++ int (*poll_sample) (struct wm97xx *, int adcsel, int *sample);
++
++ /* read X,Y,[P] in poll */
++ int (*poll_touch) (struct wm97xx *, struct wm97xx_data *);
++
++ int (*acc_enable) (struct wm97xx *, int enable);
++ void (*phy_init) (struct wm97xx *);
++ void (*dig_enable) (struct wm97xx *, int enable);
++ void (*dig_restore) (struct wm97xx *);
++ void (*aux_prepare) (struct wm97xx *);
++};
++
++
++/* Machine specific and accelerated touch operations */
++struct wm97xx_mach_ops {
++
++ /* accelerated touch readback - coords are transmited on AC97 link */
++ int acc_enabled;
++ void (*acc_pen_up) (struct wm97xx *);
++ int (*acc_pen_down) (struct wm97xx *);
++ int (*acc_startup) (struct wm97xx *);
++ void (*acc_shutdown) (struct wm97xx *);
++
++ /* interrupt mask control - required for accelerated operation */
++ void (*irq_enable) (struct wm97xx *, int enable);
++
++ /* pre and post sample - can be used to minimise any analog noise */
++ void (*pre_sample) (int); /* function to run before sampling */
++ void (*post_sample) (int); /* function to run after sampling */
++};
++
++struct wm97xx {
++ u16 dig[3], id, gpio[6], misc; /* Cached codec registers */
++ u16 dig_save[3]; /* saved during aux reading */
++ struct wm97xx_codec_drv *codec; /* attached codec driver*/
++ struct input_dev *input_dev; /* touchscreen input device */
++ struct snd_ac97 *ac97; /* ALSA codec access */
++ struct device *dev; /* ALSA device */
++ struct platform_device *battery_dev;
++ struct platform_device *touch_dev;
++ struct wm97xx_mach_ops *mach_ops;
++ struct mutex codec_mutex;
++ struct delayed_work ts_reader; /* Used to poll touchscreen */
++ unsigned long ts_reader_interval; /* Current interval for timer */
++ unsigned long ts_reader_min_interval; /* Minimum interval */
++ unsigned int pen_irq; /* Pen IRQ number in use */
++ struct workqueue_struct *ts_workq;
++ struct work_struct pen_event_work;
++ u16 acc_slot; /* AC97 slot used for acc touch data */
++ u16 acc_rate; /* acc touch data rate */
++ unsigned pen_is_down:1; /* Pen is down */
++ unsigned aux_waiting:1; /* aux measurement waiting */
++ unsigned pen_probably_down:1; /* used in polling mode */
++};
++
++/*
++ * Codec GPIO access (not supported on WM9705)
++ * This can be used to set/get codec GPIO and Virtual GPIO status.
++ */
++enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio);
++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
++ enum wm97xx_gpio_status status);
++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio,
++ enum wm97xx_gpio_dir dir,
++ enum wm97xx_gpio_pol pol,
++ enum wm97xx_gpio_sticky sticky,
++ enum wm97xx_gpio_wake wake);
++
++/* codec AC97 IO access */
++int wm97xx_reg_read(struct wm97xx *wm, u16 reg);
++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val);
++
++/* aux adc readback */
++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel);
++
++/* machine ops */
++int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *);
++void wm97xx_unregister_mach_ops(struct wm97xx *);
++
++#endif
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0031-Add-chip-driver-for-WM9705-touchscreen.patch b/recipes/linux/linux-rp-2.6.24/tosa/0031-Add-chip-driver-for-WM9705-touchscreen.patch
new file mode 100644
index 0000000000..3890795f61
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0031-Add-chip-driver-for-WM9705-touchscreen.patch
@@ -0,0 +1,383 @@
+From 7b366ca784d0540613a43908de803e4dedc100d3 Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Date: Sat, 26 Jan 2008 21:14:20 +0300
+Subject: [PATCH 31/64] Add chip driver for WM9705 touchscreen
+
+Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com>
+Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com>
+Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Cc: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Cc: Stanley Cai <stanley.cai@intel.com>
+Cc: Rodolfo Giometti <giometti@enneenne.com>
+Cc: Russell King <rmk@arm.linux.org.uk>
+Cc: Marc Kleine-Budde <mkl@pengutronix.de>
+Cc: Ian Molton <spyro@f2s.com>
+Cc: Vince Sanders <vince@kyllikki.org>
+Cc: Andrew Zabolotny <zap@homelink.ru>
+---
+ drivers/input/touchscreen/wm9705.c | 352 ++++++++++++++++++++++++++++++++++++
+ 1 files changed, 352 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/input/touchscreen/wm9705.c
+
+diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c
+new file mode 100644
+index 0000000..f185104
+--- /dev/null
++++ b/drivers/input/touchscreen/wm9705.c
+@@ -0,0 +1,352 @@
++/*
++ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec.
++ *
++ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9705_VERSION "0.62"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Module parameters
++ */
++
++/*
++ * Set current used for pressure measurement.
++ *
++ * Set pil = 2 to use 400uA
++ * pil = 1 to use 200uA and
++ * pil = 0 to disable pressure measurement.
++ *
++ * This is used to increase the range of values returned by the adc
++ * when measureing touchpanel pressure.
++ */
++static int pil;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * Set adc sample delay.
++ *
++ * For accurate touchpanel measurements, some settling time may be
++ * required between the switch matrix applying a voltage across the
++ * touchpanel plate and the ADC sampling the signal.
++ *
++ * This delay can be set by setting delay = n, where n is the array
++ * position of the delay in the array delay_table below.
++ * Long delays > 1ms are supported for completeness, but are not
++ * recommended.
++ */
++static int delay = 4;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * Pen detect comparator threshold.
++ *
++ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold
++ * i.e. 1 = Vmid/15 threshold
++ * 15 = Vmid/1 threshold
++ *
++ * Adjust this value if you are having problems with pen detect not
++ * detecting any down events.
++ */
++static int pdd = 8;
++module_param(pdd, int, 0);
++MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 21, /* 1 AC97 Link frames */
++ 42, /* 2 */
++ 84, /* 4 */
++ 167, /* 8 */
++ 333, /* 16 */
++ 667, /* 32 */
++ 1000, /* 48 */
++ 1333, /* 64 */
++ 2000, /* 96 */
++ 2667, /* 128 */
++ 3333, /* 160 */
++ 4000, /* 192 */
++ 4667, /* 224 */
++ 5333, /* 256 */
++ 6000, /* 288 */
++ 0 /* No delay, switch matrix always on */
++};
++
++/*
++ * Delay after issuing a POLL command.
++ *
++ * The delay is 3 AC97 link frames + the touchpanel settling delay
++ */
++static inline void poll_delay(int d)
++{
++ udelay(3 * AC97_LINK_FRAME + delay_table[d]);
++}
++
++/*
++ * set up the physical settings of the WM9705
++ */
++static void wm9705_phy_init(struct wm97xx *wm)
++{
++ u16 dig1 = 0, dig2 = WM97XX_RPR;
++
++ /*
++ * mute VIDEO and AUX as they share X and Y touchscreen
++ * inputs on the WM9705
++ */
++ wm97xx_reg_write(wm, AC97_AUX, 0x8000);
++ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
++
++ /* touchpanel pressure current*/
++ if (pil == 2) {
++ dig2 |= WM9705_PIL;
++ dev_dbg(wm->dev,
++ "setting pressure measurement current to 400uA.");
++ } else if (pil)
++ dev_dbg(wm->dev,
++ "setting pressure measurement current to 200uA.");
++ if (!pil)
++ pressure = 0;
++
++ /* polling mode sample settling delay */
++ if (delay != 4) {
++ if (delay < 0 || delay > 15) {
++ dev_dbg(wm->dev, "supplied delay out of range.");
++ delay = 4;
++ }
++ }
++ dig1 &= 0xff0f;
++ dig1 |= WM97XX_DELAY(delay);
++ dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
++ delay_table[delay]);
++
++ /* WM9705 pdd */
++ dig2 |= (pdd & 0x000f);
++ dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
++
++ /* mask */
++ dig2 |= ((mask & 0x3) << 4);
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++}
++
++static void wm9705_dig_enable(struct wm97xx *wm, int enable)
++{
++ if (enable) {
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
++ wm->dig[2] | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ } else
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
++ wm->dig[2] & ~WM97XX_PRP_DET_DIG);
++}
++
++static void wm9705_aux_prepare(struct wm97xx *wm)
++{
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
++}
++
++static void wm9705_dig_restore(struct wm97xx *wm)
++{
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
++}
++
++static inline int is_pden(struct wm97xx *wm)
++{
++ return wm->dig[2] & WM9705_PDEN;
++}
++
++/*
++ * Read a sample from the WM9705 adc in polling mode.
++ */
++static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = ((adcsel & 0x7fff) + 3) << 12;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
++ adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay(delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
++ && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dev_dbg(wm->dev, "adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
++ dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSEL_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Sample the WM9705 touchscreen in polling mode
++ */
++static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x);
++ if (rc != RC_VALID)
++ return rc;
++ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y);
++ if (rc != RC_VALID)
++ return rc;
++ if (pil) {
++ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p);
++ if (rc != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9705 continuous mode, i.e. touch data is streamed across
++ * an AC97 slot
++ */
++static int wm9705_acc_enable(struct wm97xx *wm, int enable)
++{
++ u16 dig1, dig2;
++ int ret = 0;
++
++ dig1 = wm->dig[1];
++ dig2 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup &&
++ (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
++ WM97XX_DELAY(delay) |
++ WM97XX_SLT(wm->acc_slot) |
++ WM97XX_RATE(wm->acc_rate);
++ if (pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++ dig2 |= WM9705_PDEN;
++ } else {
++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
++ dig2 &= ~WM9705_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++ return ret;
++}
++
++struct wm97xx_codec_drv wm9705_codec = {
++ .id = WM9705_ID2,
++ .name = "wm9705",
++ .poll_sample = wm9705_poll_sample,
++ .poll_touch = wm9705_poll_touch,
++ .acc_enable = wm9705_acc_enable,
++ .phy_init = wm9705_phy_init,
++ .dig_enable = wm9705_dig_enable,
++ .dig_restore = wm9705_dig_restore,
++ .aux_prepare = wm9705_aux_prepare,
++};
++EXPORT_SYMBOL_GPL(wm9705_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
++MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0032-Add-chip-driver-for-WM9712-touchscreen.patch b/recipes/linux/linux-rp-2.6.24/tosa/0032-Add-chip-driver-for-WM9712-touchscreen.patch
new file mode 100644
index 0000000000..6265910a1e
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0032-Add-chip-driver-for-WM9712-touchscreen.patch
@@ -0,0 +1,492 @@
+From b2640063b8321bdfb324c00d5f0c3366ac31696b Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Date: Sat, 26 Jan 2008 21:14:19 +0300
+Subject: [PATCH 32/64] Add chip driver for WM9712 touchscreen
+
+Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com>
+Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com>
+Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Cc: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Cc: Stanley Cai <stanley.cai@intel.com>
+Cc: Rodolfo Giometti <giometti@enneenne.com>
+Cc: Russell King <rmk@arm.linux.org.uk>
+Cc: Marc Kleine-Budde <mkl@pengutronix.de>
+Cc: Ian Molton <spyro@f2s.com>
+Cc: Vince Sanders <vince@kyllikki.org>
+Cc: Andrew Zabolotny <zap@homelink.ru>
+---
+ drivers/input/touchscreen/wm9712.c | 461 ++++++++++++++++++++++++++++++++++++
+ 1 files changed, 461 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/input/touchscreen/wm9712.c
+
+diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c
+new file mode 100644
+index 0000000..eaab326
+--- /dev/null
++++ b/drivers/input/touchscreen/wm9712.c
+@@ -0,0 +1,461 @@
++/*
++ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs.
++ *
++ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9712_VERSION "0.61"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Module parameters
++ */
++
++/*
++ * Set internal pull up for pen detect.
++ *
++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
++ * i.e. pull up resistance = 64k Ohms / rpu.
++ *
++ * Adjust this value if you are having problems with pen detect not
++ * detecting any down event.
++ */
++static int rpu = 8;
++module_param(rpu, int, 0);
++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
++
++/*
++ * Set current used for pressure measurement.
++ *
++ * Set pil = 2 to use 400uA
++ * pil = 1 to use 200uA and
++ * pil = 0 to disable pressure measurement.
++ *
++ * This is used to increase the range of values returned by the adc
++ * when measureing touchpanel pressure.
++ */
++static int pil;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * Set adc sample delay.
++ *
++ * For accurate touchpanel measurements, some settling time may be
++ * required between the switch matrix applying a voltage across the
++ * touchpanel plate and the ADC sampling the signal.
++ *
++ * This delay can be set by setting delay = n, where n is the array
++ * position of the delay in the array delay_table below.
++ * Long delays > 1ms are supported for completeness, but are not
++ * recommended.
++ */
++static int delay = 3;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * Set five_wire = 1 to use a 5 wire touchscreen.
++ *
++ * NOTE: Five wire mode does not allow for readback of pressure.
++ */
++static int five_wire;
++module_param(five_wire, int, 0);
++MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * Coordinate Polling Enable.
++ *
++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
++ * for every poll.
++ */
++static int coord;
++module_param(coord, int, 0);
++MODULE_PARM_DESC(coord, "Polling coordinate mode");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 21, /* 1 AC97 Link frames */
++ 42, /* 2 */
++ 84, /* 4 */
++ 167, /* 8 */
++ 333, /* 16 */
++ 667, /* 32 */
++ 1000, /* 48 */
++ 1333, /* 64 */
++ 2000, /* 96 */
++ 2667, /* 128 */
++ 3333, /* 160 */
++ 4000, /* 192 */
++ 4667, /* 224 */
++ 5333, /* 256 */
++ 6000, /* 288 */
++ 0 /* No delay, switch matrix always on */
++};
++
++/*
++ * Delay after issuing a POLL command.
++ *
++ * The delay is 3 AC97 link frames + the touchpanel settling delay
++ */
++static inline void poll_delay(int d)
++{
++ udelay(3 * AC97_LINK_FRAME + delay_table[d]);
++}
++
++/*
++ * set up the physical settings of the WM9712
++ */
++static void wm9712_phy_init(struct wm97xx *wm)
++{
++ u16 dig1 = 0;
++ u16 dig2 = WM97XX_RPR | WM9712_RPU(1);
++
++ /* WM9712 rpu */
++ if (rpu) {
++ dig2 &= 0xffc0;
++ dig2 |= WM9712_RPU(rpu);
++ dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms",
++ 64000 / rpu);
++ }
++
++ /* touchpanel pressure current*/
++ if (pil == 2) {
++ dig2 |= WM9712_PIL;
++ dev_dbg(wm->dev,
++ "setting pressure measurement current to 400uA.");
++ } else if (pil)
++ dev_dbg(wm->dev,
++ "setting pressure measurement current to 200uA.");
++ if (!pil)
++ pressure = 0;
++
++ /* WM9712 five wire */
++ if (five_wire) {
++ dig2 |= WM9712_45W;
++ dev_dbg(wm->dev, "setting 5-wire touchscreen mode.");
++ }
++
++ /* polling mode sample settling delay */
++ if (delay < 0 || delay > 15) {
++ dev_dbg(wm->dev, "supplied delay out of range.");
++ delay = 4;
++ }
++ dig1 &= 0xff0f;
++ dig1 |= WM97XX_DELAY(delay);
++ dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
++ delay_table[delay]);
++
++ /* mask */
++ dig2 |= ((mask & 0x3) << 6);
++ if (mask) {
++ u16 reg;
++ /* Set GPIO4 as Mask Pin*/
++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4);
++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4);
++ }
++
++ /* wait - coord mode */
++ if (coord)
++ dig2 |= WM9712_WAIT;
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++}
++
++static void wm9712_dig_enable(struct wm97xx *wm, int enable)
++{
++ u16 dig2 = wm->dig[2];
++
++ if (enable) {
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
++ dig2 | WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ } else
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
++ dig2 & ~WM97XX_PRP_DET_DIG);
++}
++
++static void wm9712_aux_prepare(struct wm97xx *wm)
++{
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
++}
++
++static void wm9712_dig_restore(struct wm97xx *wm)
++{
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
++}
++
++static inline int is_pden(struct wm97xx *wm)
++{
++ return wm->dig[2] & WM9712_PDEN;
++}
++
++/*
++ * Read a sample from the WM9712 adc in polling mode.
++ */
++static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = ((adcsel & 0x7fff) + 3) << 12;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
++ adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay(delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
++ && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dev_dbg(wm->dev, "adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
++ dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSEL_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Read a coord from the WM9712 adc in polling mode.
++ */
++static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
++{
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data_rd = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data_rd & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
++ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay));
++
++ /* wait 3 AC97 time slots + delay for conversion and read x */
++ poll_delay(delay);
++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
++ && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dev_dbg(wm->dev, "adc sample timeout");
++ return RC_PENUP;
++ }
++
++ /* read back y data */
++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (pil)
++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ else
++ data->p = DEFAULT_PRESSURE;
++
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ /* check we have correct sample */
++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
++ goto err;
++ if (pil && !(data->p & WM97XX_ADCSEL_PRES))
++ goto err;
++
++ if (!(data->x & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++ return RC_VALID;
++err:
++ return RC_PENUP;
++}
++
++/*
++ * Sample the WM9712 touchscreen in polling mode
++ */
++static int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if (coord) {
++ rc = wm9712_poll_coord(wm, data);
++ if (rc != RC_VALID)
++ return rc;
++ } else {
++ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x);
++ if (rc != RC_VALID)
++ return rc;
++
++ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y);
++ if (rc != RC_VALID)
++ return rc;
++
++ if (pil && !five_wire) {
++ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES,
++ &data->p);
++ if (rc != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++ }
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9712 continuous mode, i.e. touch data is streamed across
++ * an AC97 slot
++ */
++static int wm9712_acc_enable(struct wm97xx *wm, int enable)
++{
++ u16 dig1, dig2;
++ int ret = 0;
++
++ dig1 = wm->dig[1];
++ dig2 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup) {
++ ret = wm->mach_ops->acc_startup(wm);
++ if (ret < 0)
++ return ret;
++ }
++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
++ WM97XX_DELAY(delay) |
++ WM97XX_SLT(wm->acc_slot) |
++ WM97XX_RATE(wm->acc_rate);
++ if (pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++ dig2 |= WM9712_PDEN;
++ } else {
++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
++ dig2 &= ~WM9712_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
++ return 0;
++}
++
++struct wm97xx_codec_drv wm9712_codec = {
++ .id = WM9712_ID2,
++ .name = "wm9712",
++ .poll_sample = wm9712_poll_sample,
++ .poll_touch = wm9712_poll_touch,
++ .acc_enable = wm9712_acc_enable,
++ .phy_init = wm9712_phy_init,
++ .dig_enable = wm9712_dig_enable,
++ .dig_restore = wm9712_dig_restore,
++ .aux_prepare = wm9712_aux_prepare,
++};
++EXPORT_SYMBOL_GPL(wm9712_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
++MODULE_DESCRIPTION("WM9712 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0033-Add-chip-driver-for-WM9713-touchscreen.patch b/recipes/linux/linux-rp-2.6.24/tosa/0033-Add-chip-driver-for-WM9713-touchscreen.patch
new file mode 100644
index 0000000000..a9dfa18557
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0033-Add-chip-driver-for-WM9713-touchscreen.patch
@@ -0,0 +1,490 @@
+From 05b2a361eedb5461e902c73ebc6e30f9916b3a8a Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Date: Sat, 26 Jan 2008 21:14:19 +0300
+Subject: [PATCH 33/64] Add chip driver for WM9713 touchscreen
+
+Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com>
+Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com>
+Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Cc: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Cc: Stanley Cai <stanley.cai@intel.com>
+Cc: Rodolfo Giometti <giometti@enneenne.com>
+Cc: Russell King <rmk@arm.linux.org.uk>
+Cc: Marc Kleine-Budde <mkl@pengutronix.de>
+Cc: Ian Molton <spyro@f2s.com>
+Cc: Vince Sanders <vince@kyllikki.org>
+Cc: Andrew Zabolotny <zap@homelink.ru>
+---
+ drivers/input/touchscreen/wm9713.c | 459 ++++++++++++++++++++++++++++++++++++
+ 1 files changed, 459 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/input/touchscreen/wm9713.c
+
+diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c
+new file mode 100644
+index 0000000..5067e59
+--- /dev/null
++++ b/drivers/input/touchscreen/wm9713.c
+@@ -0,0 +1,459 @@
++/*
++ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec.
++ *
++ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ * Russell King <rmk@arm.linux.org.uk>
++ *
++ * 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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/wm97xx.h>
++
++#define TS_NAME "wm97xx"
++#define WM9713_VERSION "0.53"
++#define DEFAULT_PRESSURE 0xb0c0
++
++/*
++ * Module parameters
++ */
++
++/*
++ * Set internal pull up for pen detect.
++ *
++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
++ * i.e. pull up resistance = 64k Ohms / rpu.
++ *
++ * Adjust this value if you are having problems with pen detect not
++ * detecting any down event.
++ */
++static int rpu = 8;
++module_param(rpu, int, 0);
++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
++
++/*
++ * Set current used for pressure measurement.
++ *
++ * Set pil = 2 to use 400uA
++ * pil = 1 to use 200uA and
++ * pil = 0 to disable pressure measurement.
++ *
++ * This is used to increase the range of values returned by the adc
++ * when measureing touchpanel pressure.
++ */
++static int pil;
++module_param(pil, int, 0);
++MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
++
++/*
++ * Set threshold for pressure measurement.
++ *
++ * Pen down pressure below threshold is ignored.
++ */
++static int pressure = DEFAULT_PRESSURE & 0xfff;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
++
++/*
++ * Set adc sample delay.
++ *
++ * For accurate touchpanel measurements, some settling time may be
++ * required between the switch matrix applying a voltage across the
++ * touchpanel plate and the ADC sampling the signal.
++ *
++ * This delay can be set by setting delay = n, where n is the array
++ * position of the delay in the array delay_table below.
++ * Long delays > 1ms are supported for completeness, but are not
++ * recommended.
++ */
++static int delay = 4;
++module_param(delay, int, 0);
++MODULE_PARM_DESC(delay, "Set adc sample delay.");
++
++/*
++ * Set adc mask function.
++ *
++ * Sources of glitch noise, such as signals driving an LCD display, may feed
++ * through to the touch screen plates and affect measurement accuracy. In
++ * order to minimise this, a signal may be applied to the MASK pin to delay or
++ * synchronise the sampling.
++ *
++ * 0 = No delay or sync
++ * 1 = High on pin stops conversions
++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
++ * 3 = Edge triggered, edge on pin starts conversion after delay param
++ */
++static int mask;
++module_param(mask, int, 0);
++MODULE_PARM_DESC(mask, "Set adc mask function.");
++
++/*
++ * Coordinate Polling Enable.
++ *
++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
++ * for every poll.
++ */
++static int coord;
++module_param(coord, int, 0);
++MODULE_PARM_DESC(coord, "Polling coordinate mode");
++
++/*
++ * ADC sample delay times in uS
++ */
++static const int delay_table[] = {
++ 21, /* 1 AC97 Link frames */
++ 42, /* 2 */
++ 84, /* 4 */
++ 167, /* 8 */
++ 333, /* 16 */
++ 667, /* 32 */
++ 1000, /* 48 */
++ 1333, /* 64 */
++ 2000, /* 96 */
++ 2667, /* 128 */
++ 3333, /* 160 */
++ 4000, /* 192 */
++ 4667, /* 224 */
++ 5333, /* 256 */
++ 6000, /* 288 */
++ 0 /* No delay, switch matrix always on */
++};
++
++/*
++ * Delay after issuing a POLL command.
++ *
++ * The delay is 3 AC97 link frames + the touchpanel settling delay
++ */
++static inline void poll_delay(int d)
++{
++ udelay(3 * AC97_LINK_FRAME + delay_table[d]);
++}
++
++/*
++ * set up the physical settings of the WM9713
++ */
++static void wm9713_phy_init(struct wm97xx *wm)
++{
++ u16 dig1 = 0, dig2, dig3;
++
++ /* default values */
++ dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5);
++ dig3 = WM9712_RPU(1);
++
++ /* rpu */
++ if (rpu) {
++ dig3 &= 0xffc0;
++ dig3 |= WM9712_RPU(rpu);
++ dev_info(wm->dev, "setting pen detect pull-up to %d Ohms\n",
++ 64000 / rpu);
++ }
++
++ /* touchpanel pressure */
++ if (pil == 2) {
++ dig3 |= WM9712_PIL;
++ dev_info(wm->dev,
++ "setting pressure measurement current to 400uA.");
++ } else if (pil)
++ dev_info(wm->dev,
++ "setting pressure measurement current to 200uA.");
++ if (!pil)
++ pressure = 0;
++
++ /* sample settling delay */
++ if (delay < 0 || delay > 15) {
++ dev_info(wm->dev, "supplied delay out of range.");
++ delay = 4;
++ dev_info(wm->dev, "setting adc sample delay to %d u Secs.",
++ delay_table[delay]);
++ }
++ dig2 &= 0xff0f;
++ dig2 |= WM97XX_DELAY(delay);
++
++ /* mask */
++ dig3 |= ((mask & 0x3) << 4);
++ if (coord)
++ dig3 |= WM9713_WAIT;
++
++ wm->misc = wm97xx_reg_read(wm, 0x5a);
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0);
++}
++
++static void wm9713_dig_enable(struct wm97xx *wm, int enable)
++{
++ u16 val;
++
++ if (enable) {
++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] |
++ WM97XX_PRP_DET_DIG);
++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
++ } else {
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] &
++ ~WM97XX_PRP_DET_DIG);
++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000);
++ }
++}
++
++static void wm9713_dig_restore(struct wm97xx *wm)
++{
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]);
++}
++
++static void wm9713_aux_prepare(struct wm97xx *wm)
++{
++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG);
++}
++
++static inline int is_pden(struct wm97xx *wm)
++{
++ return wm->dig[2] & WM9713_PDEN;
++}
++
++/*
++ * Read a sample from the WM9713 adc in polling mode.
++ */
++static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
++{
++ u16 dig1;
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ if (adcsel & 0x8000)
++ adcsel = 1 << ((adcsel & 0x7fff) + 3);
++
++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
++ dig1 &= ~WM9713_ADCSEL_MASK;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(adcsel);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel | WM9713_POLL);
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay(delay);
++
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) &&
++ timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dev_dbg(wm->dev, "adc sample timeout");
++ return RC_PENUP;
++ }
++
++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(adcsel);
++
++ /* check we have correct sample */
++ if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) {
++ dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
++ *sample & WM97XX_ADCSRC_MASK);
++ return RC_PENUP;
++ }
++
++ if (!(*sample & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++
++ return RC_VALID;
++}
++
++/*
++ * Read a coordinate from the WM9713 adc in polling mode.
++ */
++static int wm9713_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
++{
++ u16 dig1;
++ int timeout = 5 * delay;
++
++ if (!wm->pen_probably_down) {
++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (!(data & WM97XX_PEN_DOWN))
++ return RC_PENUP;
++ wm->pen_probably_down = 1;
++ }
++
++ /* set up digitiser */
++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
++ dig1 &= ~WM9713_ADCSEL_MASK;
++ if (pil)
++ dig1 |= WM97XX_ADCSEL_PRES;
++
++ if (wm->mach_ops && wm->mach_ops->pre_sample)
++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1,
++ dig1 | WM9713_POLL | WM9713_COO);
++
++ /* wait 3 AC97 time slots + delay for conversion */
++ poll_delay(delay);
++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ /* wait for POLL to go low */
++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL)
++ && timeout) {
++ udelay(AC97_LINK_FRAME);
++ timeout--;
++ }
++
++ if (timeout <= 0) {
++ /* If PDEN is set, we can get a timeout when pen goes up */
++ if (is_pden(wm))
++ wm->pen_probably_down = 0;
++ else
++ dev_dbg(wm->dev, "adc sample timeout");
++ return RC_PENUP;
++ }
++
++ /* read back data */
++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ if (pil)
++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
++ else
++ data->p = DEFAULT_PRESSURE;
++
++ if (wm->mach_ops && wm->mach_ops->post_sample)
++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
++
++ /* check we have correct sample */
++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
++ goto err;
++ if (pil && !(data->p & WM97XX_ADCSEL_PRES))
++ goto err;
++
++ if (!(data->x & WM97XX_PEN_DOWN)) {
++ wm->pen_probably_down = 0;
++ return RC_PENUP;
++ }
++ return RC_VALID;
++err:
++ return RC_PENUP;
++}
++
++/*
++ * Sample the WM9713 touchscreen in polling mode
++ */
++static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
++{
++ int rc;
++
++ if (coord) {
++ rc = wm9713_poll_coord(wm, data);
++ if (rc != RC_VALID)
++ return rc;
++ } else {
++ rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x);
++ if (rc != RC_VALID)
++ return rc;
++ rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y);
++ if (rc != RC_VALID)
++ return rc;
++ if (pil) {
++ rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES,
++ &data->p);
++ if (rc != RC_VALID)
++ return rc;
++ } else
++ data->p = DEFAULT_PRESSURE;
++ }
++ return RC_VALID;
++}
++
++/*
++ * Enable WM9713 continuous mode, i.e. touch data is streamed across
++ * an AC97 slot
++ */
++static int wm9713_acc_enable(struct wm97xx *wm, int enable)
++{
++ u16 dig1, dig2, dig3;
++ int ret = 0;
++
++ dig1 = wm->dig[0];
++ dig2 = wm->dig[1];
++ dig3 = wm->dig[2];
++
++ if (enable) {
++ /* continous mode */
++ if (wm->mach_ops->acc_startup &&
++ (ret = wm->mach_ops->acc_startup(wm)) < 0)
++ return ret;
++
++ dig1 &= ~WM9713_ADCSEL_MASK;
++ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X |
++ WM9713_ADCSEL_Y;
++ if (pil)
++ dig1 |= WM9713_ADCSEL_PRES;
++ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK |
++ WM97XX_CM_RATE_MASK);
++ dig2 |= WM97XX_SLEN | WM97XX_DELAY(delay) |
++ WM97XX_SLT(wm->acc_slot) | WM97XX_RATE(wm->acc_rate);
++ dig3 |= WM9713_PDEN;
++ } else {
++ dig1 &= ~(WM9713_CTC | WM9713_COO);
++ dig2 &= ~WM97XX_SLEN;
++ dig3 &= ~WM9713_PDEN;
++ if (wm->mach_ops->acc_shutdown)
++ wm->mach_ops->acc_shutdown(wm);
++ }
++
++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
++ return ret;
++}
++
++struct wm97xx_codec_drv wm9713_codec = {
++ .id = WM9713_ID2,
++ .name = "wm9713",
++ .poll_sample = wm9713_poll_sample,
++ .poll_touch = wm9713_poll_touch,
++ .acc_enable = wm9713_acc_enable,
++ .phy_init = wm9713_phy_init,
++ .dig_enable = wm9713_dig_enable,
++ .dig_restore = wm9713_dig_restore,
++ .aux_prepare = wm9713_aux_prepare,
++};
++EXPORT_SYMBOL_GPL(wm9713_codec);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
++MODULE_DESCRIPTION("WM9713 Touch Screen Driver");
++MODULE_LICENSE("GPL");
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0034-Driver-for-WM97xx-touchscreens-in-streaming-mode-on.patch b/recipes/linux/linux-rp-2.6.24/tosa/0034-Driver-for-WM97xx-touchscreens-in-streaming-mode-on.patch
new file mode 100644
index 0000000000..0391cfcd83
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0034-Driver-for-WM97xx-touchscreens-in-streaming-mode-on.patch
@@ -0,0 +1,329 @@
+From 821604bad5ce1ef942eeb420afd9ea2c5c92875e Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Date: Sat, 26 Jan 2008 21:14:19 +0300
+Subject: [PATCH 34/64] Driver for WM97xx touchscreens in streaming mode on Mainstone
+
+Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com>
+Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com>
+Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Cc: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Cc: Stanley Cai <stanley.cai@intel.com>
+Cc: Rodolfo Giometti <giometti@enneenne.com>
+Cc: Russell King <rmk@arm.linux.org.uk>
+Cc: Marc Kleine-Budde <mkl@pengutronix.de>
+Cc: Ian Molton <spyro@f2s.com>
+Cc: Vince Sanders <vince@kyllikki.org>
+Cc: Andrew Zabolotny <zap@homelink.ru>
+---
+ drivers/input/touchscreen/mainstone-wm97xx.c | 298 ++++++++++++++++++++++++++
+ 1 files changed, 298 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/input/touchscreen/mainstone-wm97xx.c
+
+diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
+new file mode 100644
+index 0000000..8e1c35d
+--- /dev/null
++++ b/drivers/input/touchscreen/mainstone-wm97xx.c
+@@ -0,0 +1,298 @@
++/*
++ * mainstone-wm97xx.c -- Mainstone Continuous Touch screen driver for
++ * Wolfson WM97xx AC97 Codecs.
++ *
++ * Copyright 2004, 2007 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
++ * Parts Copyright : Ian Molton <spyro@f2s.com>
++ * Andrew Zabolotny <zap@homelink.ru>
++ *
++ * 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.
++ *
++ * Notes:
++ * This is a wm97xx extended touch driver to capture touch
++ * data in a continuous manner on the Intel XScale archictecture
++ *
++ * Features:
++ * - codecs supported:- WM9705, WM9712, WM9713
++ * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++#include <linux/wm97xx.h>
++#include <linux/io.h>
++#include <asm/arch/pxa-regs.h>
++
++#define VERSION "0.13"
++
++struct continuous {
++ u16 id; /* codec id */
++ u8 code; /* continuous code */
++ u8 reads; /* number of coord reads per read cycle */
++ u32 speed; /* number of coords per second */
++};
++
++#define WM_READS(sp) ((sp / HZ) + 1)
++
++static const struct continuous cinfo[] = {
++ {WM9705_ID2, 0, WM_READS(94), 94},
++ {WM9705_ID2, 1, WM_READS(188), 188},
++ {WM9705_ID2, 2, WM_READS(375), 375},
++ {WM9705_ID2, 3, WM_READS(750), 750},
++ {WM9712_ID2, 0, WM_READS(94), 94},
++ {WM9712_ID2, 1, WM_READS(188), 188},
++ {WM9712_ID2, 2, WM_READS(375), 375},
++ {WM9712_ID2, 3, WM_READS(750), 750},
++ {WM9713_ID2, 0, WM_READS(94), 94},
++ {WM9713_ID2, 1, WM_READS(120), 120},
++ {WM9713_ID2, 2, WM_READS(154), 154},
++ {WM9713_ID2, 3, WM_READS(188), 188},
++};
++
++/* continuous speed index */
++static int sp_idx;
++static u16 last, tries;
++
++/*
++ * Pen sampling frequency (Hz) in continuous mode.
++ */
++static int cont_rate = 200;
++module_param(cont_rate, int, 0);
++MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
++
++/*
++ * Pen down detection.
++ *
++ * This driver can either poll or use an interrupt to indicate a pen down
++ * event. If the irq request fails then it will fall back to polling mode.
++ */
++static int pen_int;
++module_param(pen_int, int, 0);
++MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
++
++/*
++ * Pressure readback.
++ *
++ * Set to 1 to read back pen down pressure
++ */
++static int pressure;
++module_param(pressure, int, 0);
++MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
++
++/*
++ * AC97 touch data slot.
++ *
++ * Touch screen readback data ac97 slot
++ */
++static int ac97_touch_slot = 5;
++module_param(ac97_touch_slot, int, 0);
++MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
++
++
++/* flush AC97 slot 5 FIFO on pxa machines */
++#ifdef CONFIG_PXA27x
++static void wm97xx_acc_pen_up(struct wm97xx *wm)
++{
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ while (MISR & (1 << 2))
++ MODR;
++}
++#else
++static void wm97xx_acc_pen_up(struct wm97xx *wm)
++{
++ int count = 16;
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ while (count < 16) {
++ MODR;
++ count--;
++ }
++}
++#endif
++
++static int wm97xx_acc_pen_down(struct wm97xx *wm)
++{
++ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
++ int reads = 0;
++
++ /* data is never immediately available after pen down irq */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++
++ if (tries > 5) {
++ tries = 0;
++ return RC_PENUP;
++ }
++
++ x = MODR;
++ if (x == last) {
++ tries++;
++ return RC_AGAIN;
++ }
++ last = x;
++ do {
++ if (reads)
++ x = MODR;
++ y = MODR;
++ if (pressure)
++ p = MODR;
++
++ /* are samples valid */
++ if ((x & 0x7000) != WM97XX_ADCSEL_X ||
++ (y & 0x7000) != WM97XX_ADCSEL_Y ||
++ (p & 0x7000) != WM97XX_ADCSEL_PRES)
++ goto up;
++
++ /* coordinate is good */
++ tries = 0;
++ input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
++ input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
++ input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
++ input_sync(wm->input_dev);
++ reads++;
++ } while (reads < cinfo[sp_idx].reads);
++up:
++ return RC_PENDOWN | RC_AGAIN;
++}
++
++static int wm97xx_acc_startup(struct wm97xx *wm)
++{
++ int idx = 0;
++
++ /* check we have a codec */
++ if (wm->ac97 == NULL)
++ return -ENODEV;
++
++ /* Go you big red fire engine */
++ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
++ if (wm->id != cinfo[idx].id)
++ continue;
++ sp_idx = idx;
++ if (cont_rate <= cinfo[idx].speed)
++ break;
++ }
++ wm->acc_rate = cinfo[sp_idx].code;
++ wm->acc_slot = ac97_touch_slot;
++ dev_info(wm->dev,
++ "mainstone accelerated touchscreen driver, %d samples/sec\n",
++ cinfo[sp_idx].speed);
++
++ /* codec specific irq config */
++ if (pen_int) {
++ switch (wm->id) {
++ case WM9705_ID2:
++ wm->pen_irq = IRQ_GPIO(4);
++ set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE);
++ break;
++ case WM9712_ID2:
++ case WM9713_ID2:
++ /* enable pen down interrupt */
++ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */
++ wm->pen_irq = MAINSTONE_AC97_IRQ;
++ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
++ WM97XX_GPIO_POL_HIGH,
++ WM97XX_GPIO_STICKY,
++ WM97XX_GPIO_WAKE);
++ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
++ WM97XX_GPIO_POL_HIGH,
++ WM97XX_GPIO_NOTSTICKY,
++ WM97XX_GPIO_NOWAKE);
++ break;
++ default:
++ dev_err(wm->dev,
++ "pen down irq not supported on this device\n");
++ pen_int = 0;
++ break;
++ }
++ }
++
++ return 0;
++}
++
++static void wm97xx_acc_shutdown(struct wm97xx *wm)
++{
++ /* codec specific deconfig */
++ if (pen_int) {
++ switch (wm->id & 0xffff) {
++ case WM9705_ID2:
++ wm->pen_irq = 0;
++ break;
++ case WM9712_ID2:
++ case WM9713_ID2:
++ /* disable interrupt */
++ wm->pen_irq = 0;
++ break;
++ }
++ }
++}
++
++static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
++{
++ if (enable)
++ enable_irq(wm->pen_irq);
++ else
++ disable_irq(wm->pen_irq);
++}
++
++static struct wm97xx_mach_ops mainstone_mach_ops = {
++ .acc_enabled = 1,
++ .acc_pen_up = wm97xx_acc_pen_up,
++ .acc_pen_down = wm97xx_acc_pen_down,
++ .acc_startup = wm97xx_acc_startup,
++ .acc_shutdown = wm97xx_acc_shutdown,
++ .irq_enable = wm97xx_irq_enable,
++};
++
++static int mainstone_wm97xx_probe(struct platform_device *pdev)
++{
++ struct wm97xx *wm = platform_get_drvdata(pdev);
++ return wm97xx_register_mach_ops(wm, &mainstone_mach_ops);
++}
++
++static int mainstone_wm97xx_remove(struct platform_device *pdev)
++{
++ struct wm97xx *wm = platform_get_drvdata(pdev);
++ wm97xx_unregister_mach_ops(wm);
++ return 0;
++}
++
++static struct platform_driver mainstone_wm97xx_driver = {
++ .probe = mainstone_wm97xx_probe,
++ .remove = mainstone_wm97xx_remove,
++ .driver = {
++ .name = "wm97xx-touch",
++ },
++};
++
++static int __init mainstone_wm97xx_init(void)
++{
++ return platform_driver_register(&mainstone_wm97xx_driver);
++}
++
++static void __exit mainstone_wm97xx_exit(void)
++{
++ platform_driver_unregister(&mainstone_wm97xx_driver);
++}
++
++module_init(mainstone_wm97xx_init);
++module_exit(mainstone_wm97xx_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
++MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone");
++MODULE_LICENSE("GPL");
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0035-Build-system-and-MAINTAINERS-entry-for-WM97xx-touchs.patch b/recipes/linux/linux-rp-2.6.24/tosa/0035-Build-system-and-MAINTAINERS-entry-for-WM97xx-touchs.patch
new file mode 100644
index 0000000000..aa0918f43e
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0035-Build-system-and-MAINTAINERS-entry-for-WM97xx-touchs.patch
@@ -0,0 +1,122 @@
+From eba6a504393932764a33aae64021827dd2c5c70c Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Date: Sat, 26 Jan 2008 21:14:18 +0300
+Subject: [PATCH 35/64] Build system and MAINTAINERS entry for WM97xx touchscreen drivers
+
+Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+---
+ MAINTAINERS | 10 +++++++
+ drivers/input/touchscreen/Kconfig | 52 ++++++++++++++++++++++++++++++++++++
+ drivers/input/touchscreen/Makefile | 7 +++++
+ 3 files changed, 69 insertions(+), 0 deletions(-)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 2340cfb..f02851c 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -4204,6 +4204,16 @@ L: linux-wireless@vger.kernel.org
+ W: http://oops.ghostprotocols.net:81/blog
+ S: Maintained
+
++WM97XX TOUCHSCREEN DRIVERS
++P: Mark Brown
++M: broonie@opensource.wolfsonmicro.com
++P: Liam Girdwood
++M: liam.girdwood@wolfsonmicro.com
++L: linux-input@vger.kernel.org
++T: git git://opensource.wolfsonmicro.com/linux-2.6-touch
++W: http://opensource.wolfsonmicro.com/node/7
++S: Supported
++
+ X.25 NETWORK LAYER
+ P: Henner Eisen
+ M: eis@baty.hanse.de
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index 90e8e92..0be05a2 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -158,6 +158,58 @@ config TOUCHSCREEN_TOUCHRIGHT
+ To compile this driver as a module, choose M here: the
+ module will be called touchright.
+
++config TOUCHSCREEN_WM97XX
++ tristate "Support for WM97xx AC97 touchscreen controllers"
++ depends on AC97_BUS
++
++config TOUCHSCREEN_WM9705
++ bool "WM9705 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have a Wolfson Microelectronics WM9705 touchscreen
++ controller connected to your system.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9705.
++
++config TOUCHSCREEN_WM9712
++ bool "WM9712 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have a Wolfson Microelectronics WM9712 touchscreen
++ controller connected to your system.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9712.
++
++config TOUCHSCREEN_WM9713
++ bool "WM9713 Touchscreen interface support"
++ depends on TOUCHSCREEN_WM97XX
++ help
++ Say Y here if you have a Wolfson Microelectronics WM9713 touchscreen
++ controller connected to your system.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called wm9713.
++
++config TOUCHSCREEN_WM97XX_MAINSTONE
++ tristate "WM97xx Mainstone accelerated touch"
++ depends on TOUCHSCREEN_WM97XX && ARCH_PXA
++ help
++ Say Y here for support for streaming mode with WM97xx touchscreens
++ on Mainstone systems.
++
++ If unsure, say N
++
++ To compile this driver as a module, choose M here: the
++ module will be called mainstone-wm97xx
++
+ config TOUCHSCREEN_TOUCHWIN
+ tristate "Touchwin serial touchscreen"
+ select SERIO
+diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
+index 35d4097..d38156e 100644
+--- a/drivers/input/touchscreen/Makefile
++++ b/drivers/input/touchscreen/Makefile
+@@ -4,6 +4,8 @@
+
+ # Each configuration option enables a list of files.
+
++wm97xx-ts-y := wm97xx-core.o
++
+ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
+ obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
+ obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
+@@ -19,3 +21,8 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
+ obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o
++obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
++obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
++wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
++wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
++wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0036-Set-id-to-1-for-wm97xx-subdevices.patch b/recipes/linux/linux-rp-2.6.24/tosa/0036-Set-id-to-1-for-wm97xx-subdevices.patch
new file mode 100644
index 0000000000..dd10b34586
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0036-Set-id-to-1-for-wm97xx-subdevices.patch
@@ -0,0 +1,35 @@
+From 9ea478cbd5473f52ca036cccc00dddad717d7861 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 30 Jan 2008 19:27:13 +0300
+Subject: [PATCH 36/64] Set id to -1 for wm97xx subdevices
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/input/touchscreen/wm97xx-core.c | 4 ++--
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
+index 27a0a99..e066acc 100644
+--- a/drivers/input/touchscreen/wm97xx-core.c
++++ b/drivers/input/touchscreen/wm97xx-core.c
+@@ -592,7 +592,7 @@ static int wm97xx_probe(struct device *dev)
+ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
+
+ /* register our battery device */
+- wm->battery_dev = platform_device_alloc("wm97xx-battery", 0);
++ wm->battery_dev = platform_device_alloc("wm97xx-battery", -1);
+ if (!wm->battery_dev)
+ goto batt_err;
+ platform_set_drvdata(wm->battery_dev, wm);
+@@ -603,7 +603,7 @@ static int wm97xx_probe(struct device *dev)
+
+ /* register our extended touch device (for machine specific
+ * extensions) */
+- wm->touch_dev = platform_device_alloc("wm97xx-touch", 0);
++ wm->touch_dev = platform_device_alloc("wm97xx-touch", -1);
+ if (!wm->touch_dev)
+ goto touch_err;
+ platform_set_drvdata(wm->touch_dev, wm);
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0037-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch b/recipes/linux/linux-rp-2.6.24/tosa/0037-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch
new file mode 100644
index 0000000000..010194dd96
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0037-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch
@@ -0,0 +1,41 @@
+From d2888c7643b07687b14a839239cbe7fc5bf565e6 Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Date: Mon, 14 Jan 2008 23:24:26 +0300
+Subject: [PATCH 37/64] Don't lock the codec list in snd_soc_dapm_new_widgets()
+
+snd_soc_dapm_new_widgets() takes the codec lock when adding new widgets,
+causing lockdep warnings when applications later call down through ALSA
+to adjust controls. Since widgets are only added during probe this lock
+should be unneeded so don't take it.
+
+Thanks to Dmitry Baryshkov <dbaryshkov@gmail.com> for reporting this issue.
+
+Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Cc: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ sound/soc/soc-dapm.c | 2 --
+ 1 files changed, 0 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
+index 29a546f..e46cdc5 100644
+--- a/sound/soc/soc-dapm.c
++++ b/sound/soc/soc-dapm.c
+@@ -963,7 +963,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
+ {
+ struct snd_soc_dapm_widget *w;
+
+- mutex_lock(&codec->mutex);
+ list_for_each_entry(w, &codec->dapm_widgets, list)
+ {
+ if (w->new)
+@@ -998,7 +997,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
+ }
+
+ dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+- mutex_unlock(&codec->mutex);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0038-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch b/recipes/linux/linux-rp-2.6.24/tosa/0038-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch
new file mode 100644
index 0000000000..7a3eb61a27
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0038-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch
@@ -0,0 +1,57 @@
+From 5bae1fab16c7b14a458aa90e5654fe3a1d8d960f Mon Sep 17 00:00:00 2001
+From: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Date: Sun, 20 Jan 2008 00:06:06 +0300
+Subject: [PATCH 38/64] Don't lock the codec list in snd_soc_dapm_new_widgets()
+
+On Wed, Jan 16, 2008 at 02:40:55AM +0300, Dmitry wrote:
+
+> I'm sorry, but I tested this patch only now. And I just got another
+> message from lockdep:
+
+Could you give this patch a try, please?
+---
+ sound/soc/soc-core.c | 7 +++++--
+ 1 files changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
+index e6a67b5..7f3ed9f 100644
+--- a/sound/soc/soc-core.c
++++ b/sound/soc/soc-core.c
+@@ -1090,7 +1090,6 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
+ struct snd_soc_machine *machine = socdev->machine;
+ int ret = 0, i, ac97 = 0, err = 0;
+
+- mutex_lock(&codec->mutex);
+ for(i = 0; i < machine->num_links; i++) {
+ if (socdev->machine->dai_link[i].init) {
+ err = socdev->machine->dai_link[i].init(codec);
+@@ -1116,12 +1115,14 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
+ goto out;
+ }
+
++ mutex_lock(&codec->mutex);
+ #ifdef CONFIG_SND_SOC_AC97_BUS
+ if (ac97) {
+ ret = soc_ac97_dev_register(codec);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: AC97 device register failed\n");
+ snd_card_free(codec->card);
++ mutex_unlock(&codec->mutex);
+ goto out;
+ }
+ }
+@@ -1134,8 +1135,10 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
+ err = device_create_file(socdev->dev, &dev_attr_codec_reg);
+ if (err < 0)
+ printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n");
+-out:
++
+ mutex_unlock(&codec->mutex);
++
++out:
+ return ret;
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_register_card);
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch b/recipes/linux/linux-rp-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch
new file mode 100644
index 0000000000..c09c208c6a
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch
@@ -0,0 +1,446 @@
+From 62c9a23cfa7181369637d0b61a8e90c83c562f03 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Mon, 4 Feb 2008 03:01:06 +0300
+Subject: [PATCH 39/64] Add generic framework for managing clocks.
+
+Provide a generic framework that platform may choose
+to support clocks api. In particular this provides
+platform-independant struct clk definition, a full
+implementation of clocks api and a set of functions
+for registering and unregistering clocks in a safe way.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ include/linux/clklib.h | 85 ++++++++++++++
+ init/Kconfig | 7 +
+ kernel/Makefile | 1 +
+ kernel/clklib.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 388 insertions(+), 0 deletions(-)
+ create mode 100644 include/linux/clklib.h
+ create mode 100644 kernel/clklib.c
+
+diff --git a/include/linux/clklib.h b/include/linux/clklib.h
+new file mode 100644
+index 0000000..4bd9b4a
+--- /dev/null
++++ b/include/linux/clklib.h
+@@ -0,0 +1,85 @@
++/*
++ * Copyright (C) 2008 Dmitry Baryshkov
++ *
++ * This file is released under the GPL v2.
++ */
++
++#ifndef CLKLIB_H
++#define CLKLIB_H
++
++#include <linux/list.h>
++
++struct clk {
++ struct list_head node;
++ struct clk *parent;
++
++ const char *name;
++ struct module *owner;
++
++ int users;
++ unsigned long rate;
++ int delay;
++
++ int (*can_get) (struct clk *, struct device *);
++ int (*set_parent) (struct clk *, struct clk *);
++ int (*enable) (struct clk *);
++ void (*disable) (struct clk *);
++ unsigned long (*getrate) (struct clk*);
++ int (*setrate) (struct clk *, unsigned long);
++ long (*roundrate) (struct clk *, unsigned long);
++
++ void *priv;
++};
++
++int clk_register(struct clk *clk);
++void clk_unregister(struct clk *clk);
++static void __maybe_unused clks_register(struct clk *clks, size_t num)
++{
++ int i;
++ for (i = 0; i < num; i++) {
++ clk_register(&clks[i]);
++ }
++}
++
++
++int clk_alloc_function(const char *parent, struct clk *clk);
++
++struct clk_function {
++ const char *parent;
++ struct clk *clk;
++};
++
++#define CLK_FUNC(_clock, _function, _can_get, _data, _format) \
++ { \
++ .parent = _clock, \
++ .clk = &(struct clk) { \
++ .name= _function, \
++ .owner = THIS_MODULE, \
++ .can_get = _can_get, \
++ .priv = _data, \
++ .format = _format, \
++ }, \
++ }
++
++static int __maybe_unused clk_alloc_functions(
++ struct clk_function *funcs,
++ int num)
++{
++ int i;
++ int rc;
++
++ for (i = 0; i < num; i++) {
++ rc = clk_alloc_function(funcs[i].parent, funcs[i].clk);
++
++ if (rc) {
++ printk(KERN_ERR "Error allocating %s.%s function.\n",
++ funcs[i].parent,
++ funcs[i].clk->name);
++ return rc;
++ }
++ }
++
++ return 0;
++}
++
++#endif
+diff --git a/init/Kconfig b/init/Kconfig
+index b9d11a8..05b62ba 100644
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -435,6 +435,13 @@ config CC_OPTIMIZE_FOR_SIZE
+ config SYSCTL
+ bool
+
++config HAVE_CLOCK_LIB
++ bool
++ help
++ Platforms select clocklib if they use this infrastructure
++ for managing their clocks both built into SoC and provided
++ by external devices.
++
+ menuconfig EMBEDDED
+ bool "Configure standard kernel features (for small systems)"
+ help
+diff --git a/kernel/Makefile b/kernel/Makefile
+index 6d9a87c..0b2ade7 100644
+--- a/kernel/Makefile
++++ b/kernel/Makefile
+@@ -58,6 +58,7 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
+ obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
+ obj-$(CONFIG_MARKERS) += marker.o
+ obj-$(CONFIG_LATENCYTOP) += latencytop.o
++obj-$(CONFIG_HAVE_CLOCK_LIB) += clklib.o
+
+ ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
+ # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
+diff --git a/kernel/clklib.c b/kernel/clklib.c
+new file mode 100644
+index 0000000..203af3d
+--- /dev/null
++++ b/kernel/clklib.c
+@@ -0,0 +1,295 @@
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/clk.h>
++#include <linux/clklib.h>
++#include <linux/spinlock.h>
++#include <linux/err.h>
++#include <linux/delay.h>
++
++static LIST_HEAD(clocks);
++static DEFINE_SPINLOCK(clocks_lock);
++
++static int __clk_register(struct clk *clk)
++{
++ if (clk->parent &&
++ !try_module_get(clk->parent->owner))
++ return -EINVAL;
++
++ list_add_tail(&clk->node, &clocks);
++
++ return 0;
++}
++
++int clk_register(struct clk *clk)
++{
++ unsigned long flags;
++ int rc;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ rc = __clk_register(clk);
++
++ spin_unlock_irqrestore(&clocks_lock, flags);
++
++ return rc;
++}
++EXPORT_SYMBOL(clk_register);
++
++void clk_unregister(struct clk *clk)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++ list_del(&clk->node);
++ if (clk->parent)
++ module_put(clk->parent->owner);
++ spin_unlock_irqrestore(&clocks_lock, flags);
++}
++EXPORT_SYMBOL(clk_unregister);
++
++struct clk *clk_get(struct device *dev, const char *id)
++{
++ struct clk *p, *clk = ERR_PTR(-ENOENT);
++ unsigned long flags;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ list_for_each_entry(p, &clocks, node) {
++ if (strcmp(id, p->name) == 0 &&
++ (!p->can_get || p->can_get(p, dev)) &&
++ try_module_get(p->owner)) {
++ clk = p;
++ break;
++ }
++ }
++
++ spin_unlock_irqrestore(&clocks_lock, flags);
++
++ return clk;
++}
++EXPORT_SYMBOL(clk_get);
++
++void clk_put(struct clk *clk)
++{
++ unsigned long flags;
++
++ if (!clk || IS_ERR(clk))
++ return;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ module_put(clk->owner);
++
++ spin_unlock_irqrestore(&clocks_lock, flags);
++}
++EXPORT_SYMBOL(clk_put);
++
++int clk_set_parent(struct clk *clk, struct clk *parent)
++{
++ int rc;
++ unsigned long flags;
++
++ if (!clk || IS_ERR(clk))
++ return -EINVAL;
++
++ if (!clk->set_parent)
++ return -EINVAL;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ rc = clk->set_parent(clk, parent);
++ if (!rc)
++ clk->parent = parent;
++
++ spin_unlock_irqrestore(&clocks_lock, flags);
++
++ return rc;
++}
++EXPORT_SYMBOL(clk_set_parent);
++
++static int __clk_enable(struct clk *clk)
++{
++ int rc = 0;
++
++ if (clk->parent) {
++ rc = __clk_enable(clk->parent);
++
++ if (rc)
++ return rc;
++ }
++
++ if (clk->users++ == 0)
++ if (clk->enable)
++ rc = clk->enable(clk);
++
++ if (clk->delay)
++ udelay(clk->delay);
++
++ return rc;
++}
++
++int clk_enable(struct clk *clk)
++{
++ unsigned long flags;
++ int rc;
++
++ if (!clk || IS_ERR(clk))
++ return -EINVAL;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ rc = __clk_enable(clk);
++
++ spin_unlock_irqrestore(&clocks_lock, flags);
++
++ return rc;
++}
++EXPORT_SYMBOL(clk_enable);
++
++static void __clk_disable(struct clk *clk)
++{
++ if (clk->users <= 0) {
++ WARN_ON(1);
++ return;
++ }
++
++ if (--clk->users == 0)
++ if (clk->disable)
++ clk->disable(clk);
++
++ if (clk->parent)
++ __clk_disable(clk->parent);
++}
++
++void clk_disable(struct clk *clk)
++{
++ unsigned long flags;
++
++ if (!clk || IS_ERR(clk))
++ return;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ __clk_disable(clk);
++
++ spin_unlock_irqrestore(&clocks_lock, flags);
++}
++EXPORT_SYMBOL(clk_disable);
++
++static unsigned long __clk_get_rate(struct clk *clk)
++{
++ unsigned long rate = 0;
++
++ for (;;) {
++ if (rate || !clk)
++ return rate;
++
++ if (clk->getrate)
++ rate = clk->getrate(clk);
++ else if (clk->rate)
++ rate = clk->rate;
++ else
++ clk = clk->parent;
++ }
++}
++
++unsigned long clk_get_rate(struct clk *clk)
++{
++ unsigned long rate = 0;
++ unsigned long flags;
++
++ if (!clk || IS_ERR(clk))
++ return -EINVAL;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ rate = __clk_get_rate(clk);
++
++ spin_unlock_irqrestore(&clocks_lock, flags);
++
++ return rate;
++}
++EXPORT_SYMBOL(clk_get_rate);
++
++long clk_round_rate(struct clk *clk, unsigned long rate)
++{
++ long res;
++ unsigned long flags;
++
++ if (!clk || IS_ERR(clk))
++ return -EINVAL;
++
++ if (!clk->roundrate)
++ return -EINVAL;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ res = clk->roundrate(clk, rate);
++
++ spin_unlock_irqrestore(&clocks_lock, flags);
++
++ return res;
++}
++EXPORT_SYMBOL(clk_round_rate);
++
++int clk_set_rate(struct clk *clk, unsigned long rate)
++{
++ int rc;
++ unsigned long flags;
++
++ if (!clk || IS_ERR(clk))
++ return -EINVAL;
++
++ if (!clk->setrate)
++ return -EINVAL;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ rc = clk->setrate(clk, rate);
++
++ spin_unlock_irqrestore(&clocks_lock, flags);
++
++ return rc;
++}
++EXPORT_SYMBOL(clk_set_rate);
++
++int clk_alloc_function(const char *parent, struct clk *clk)
++{
++ int rc = 0;
++ unsigned long flags;
++ struct clk *pclk;
++ bool found = false;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ list_for_each_entry(pclk, &clocks, node) {
++ if (strcmp(parent, pclk->name) == 0 &&
++ try_module_get(pclk->owner)) {
++ found = true;
++ break;
++ }
++ }
++
++ if (!found) {
++ rc = -ENODEV;
++ goto out;
++ }
++
++ clk->parent = pclk;
++
++ __clk_register(clk);
++ /*
++ * We locked parent owner during search
++ * and also in __clk_register. Free one reference
++ */
++ module_put(pclk->owner);
++
++out:
++ if (rc) {
++ kfree(clk);
++ }
++ spin_unlock_irqrestore(&clocks_lock, flags);
++
++ return rc;
++}
++EXPORT_SYMBOL(clk_alloc_function);
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0040-Clocklib-debugfs-support.patch b/recipes/linux/linux-rp-2.6.24/tosa/0040-Clocklib-debugfs-support.patch
new file mode 100644
index 0000000000..160b274f4f
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0040-Clocklib-debugfs-support.patch
@@ -0,0 +1,108 @@
+From cae12d96586dac77d223559d686487ea2d457a41 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Mon, 4 Feb 2008 03:01:05 +0300
+Subject: [PATCH 40/64] Clocklib debugfs support
+
+Provide /sys/kernel/debug/clock to ease debugging.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ include/linux/clklib.h | 5 +++
+ kernel/clklib.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 73 insertions(+), 0 deletions(-)
+
+diff --git a/include/linux/clklib.h b/include/linux/clklib.h
+index 4bd9b4a..f916693 100644
+--- a/include/linux/clklib.h
++++ b/include/linux/clklib.h
+@@ -28,6 +28,11 @@ struct clk {
+ int (*setrate) (struct clk *, unsigned long);
+ long (*roundrate) (struct clk *, unsigned long);
+
++ /*
++ * format any additional info
++ */
++ int (*format) (struct clk *, struct seq_file *);
++
+ void *priv;
+ };
+
+diff --git a/kernel/clklib.c b/kernel/clklib.c
+index 203af3d..b782220 100644
+--- a/kernel/clklib.c
++++ b/kernel/clklib.c
+@@ -293,3 +293,71 @@ out:
+ return rc;
+ }
+ EXPORT_SYMBOL(clk_alloc_function);
++
++#ifdef CONFIG_DEBUG_FS
++
++#include <linux/debugfs.h>
++#include <linux/seq_file.h>
++static void dump_clocks(struct seq_file *s, struct clk *parent, int nest)
++{
++ struct clk *clk;
++ int i;
++
++ list_for_each_entry(clk, &clocks, node) {
++ if (clk->parent == parent) {
++ for (i = 0; i < nest; i++)
++ seq_putc(s, ' ');
++ seq_puts(s, clk->name);
++
++ i = nest + strlen(clk->name);
++ if (i >= 16)
++ i = 15;
++ for (; i < 16; i++)
++ seq_putc(s, ' ');
++ seq_printf(s, "%c use=%d rate=%lu KHz",
++ clk->set_parent ? '*' : ' ',
++ clk->users,
++ __clk_get_rate(clk));
++ if (clk->format)
++ clk->format(clk, s);
++ seq_putc(s, '\n');
++
++ dump_clocks(s, clk, nest + 1);
++ }
++ }
++}
++
++static int clocklib_show(struct seq_file *s, void *unused)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&clocks_lock, flags);
++
++ dump_clocks(s, NULL, 0);
++
++ spin_unlock_irqrestore(&clocks_lock, flags);
++
++ return 0;
++}
++
++static int clocklib_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, clocklib_show, NULL);
++}
++
++static struct file_operations clocklib_operations = {
++ .open = clocklib_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static int __init clocklib_debugfs_init(void)
++{
++ debugfs_create_file("clock", S_IFREG | S_IRUGO,
++ NULL, NULL, &clocklib_operations);
++ return 0;
++}
++subsys_initcall(clocklib_debugfs_init);
++
++#endif /* DEBUG_FS */
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0041-From-80a359e60c2aec59ccf4fca0a7fd20495f82b1d2-Mon-Se.patch b/recipes/linux/linux-rp-2.6.24/tosa/0041-From-80a359e60c2aec59ccf4fca0a7fd20495f82b1d2-Mon-Se.patch
new file mode 100644
index 0000000000..9c95c67e78
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0041-From-80a359e60c2aec59ccf4fca0a7fd20495f82b1d2-Mon-Se.patch
@@ -0,0 +1,593 @@
+From 2a143b9546b01fd6c58ebaac7eb46568a17d6a41 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Tue, 12 Feb 2008 04:58:59 +0300
+Subject: [PATCH 41/64] From 80a359e60c2aec59ccf4fca0a7fd20495f82b1d2 Mon Sep 17 00:00:00 2001
+ In-Reply-To: <20080207005839.GA28509@doriath.ww600.siemens.net>
+ References: <20080207005839.GA28509@doriath.ww600.siemens.net>
+ Date: Thu, 7 Feb 2008 03:35:08 +0300
+ Subject: [PATCH 3/5] Use clocklib for ARM pxa sub-arch.
+ Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+
+---
+ arch/arm/Kconfig | 1 +
+ arch/arm/mach-pxa/clock.c | 108 ++++++--------------------------------------
+ arch/arm/mach-pxa/clock.h | 58 +++++++++++++-----------
+ arch/arm/mach-pxa/pxa25x.c | 64 +++++++++++++++-----------
+ arch/arm/mach-pxa/pxa27x.c | 61 +++++++++++++-----------
+ arch/arm/mach-pxa/pxa3xx.c | 91 +++++++++++++++++++++----------------
+ 6 files changed, 169 insertions(+), 214 deletions(-)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 423e953..47f3c73 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -347,6 +347,7 @@ config ARCH_PXA
+ select GENERIC_CLOCKEVENTS
+ select TICK_ONESHOT
+ select HAVE_GPIO_LIB
++ select HAVE_CLOCK_LIB
+ help
+ Support for Intel/Marvell's PXA2xx/PXA3xx processor line.
+
+diff --git a/arch/arm/mach-pxa/clock.c b/arch/arm/mach-pxa/clock.c
+index 83ef5ec..3296b02 100644
+--- a/arch/arm/mach-pxa/clock.c
++++ b/arch/arm/mach-pxa/clock.c
+@@ -8,6 +8,7 @@
+ #include <linux/err.h>
+ #include <linux/string.h>
+ #include <linux/clk.h>
++#include <linux/clklib.h>
+ #include <linux/spinlock.h>
+ #include <linux/platform_device.h>
+ #include <linux/delay.h>
+@@ -19,123 +20,42 @@
+ #include "generic.h"
+ #include "clock.h"
+
+-static LIST_HEAD(clocks);
+-static DEFINE_MUTEX(clocks_mutex);
+-static DEFINE_SPINLOCK(clocks_lock);
+-
+-struct clk *clk_get(struct device *dev, const char *id)
+-{
+- struct clk *p, *clk = ERR_PTR(-ENOENT);
+-
+- mutex_lock(&clocks_mutex);
+- list_for_each_entry(p, &clocks, node) {
+- if (strcmp(id, p->name) == 0 &&
+- (p->dev == NULL || p->dev == dev)) {
+- clk = p;
+- break;
+- }
+- }
+- mutex_unlock(&clocks_mutex);
+-
+- return clk;
+-}
+-EXPORT_SYMBOL(clk_get);
+-
+-void clk_put(struct clk *clk)
++static int clk_gpio27_enable(struct clk *clk)
+ {
+-}
+-EXPORT_SYMBOL(clk_put);
+-
+-int clk_enable(struct clk *clk)
+-{
+- unsigned long flags;
+-
+- spin_lock_irqsave(&clocks_lock, flags);
+- if (clk->enabled++ == 0)
+- clk->ops->enable(clk);
+- spin_unlock_irqrestore(&clocks_lock, flags);
+-
+- if (clk->delay)
+- udelay(clk->delay);
++ pxa_gpio_mode(GPIO11_3_6MHz_MD);
+
+ return 0;
+ }
+-EXPORT_SYMBOL(clk_enable);
+-
+-void clk_disable(struct clk *clk)
+-{
+- unsigned long flags;
+-
+- WARN_ON(clk->enabled == 0);
+-
+- spin_lock_irqsave(&clocks_lock, flags);
+- if (--clk->enabled == 0)
+- clk->ops->disable(clk);
+- spin_unlock_irqrestore(&clocks_lock, flags);
+-}
+-EXPORT_SYMBOL(clk_disable);
+-
+-unsigned long clk_get_rate(struct clk *clk)
+-{
+- unsigned long rate;
+-
+- rate = clk->rate;
+- if (clk->ops->getrate)
+- rate = clk->ops->getrate(clk);
+-
+- return rate;
+-}
+-EXPORT_SYMBOL(clk_get_rate);
+-
+-
+-static void clk_gpio27_enable(struct clk *clk)
+-{
+- pxa_gpio_mode(GPIO11_3_6MHz_MD);
+-}
+
+ static void clk_gpio27_disable(struct clk *clk)
+ {
++ /* FIXME: disable clock */
+ }
+
+-static const struct clkops clk_gpio27_ops = {
+- .enable = clk_gpio27_enable,
+- .disable = clk_gpio27_disable,
+-};
+-
+-
+-void clk_cken_enable(struct clk *clk)
++int clk_cken_enable(struct clk *clk)
+ {
+- CKEN |= 1 << clk->cken;
++ int cken = ((struct clk_cken_priv *)clk->priv)->cken;
++ CKEN |= 1 << cken;
++
++ return 0;
+ }
+
+ void clk_cken_disable(struct clk *clk)
+ {
+- CKEN &= ~(1 << clk->cken);
++ int cken = ((struct clk_cken_priv *)clk->priv)->cken;
++ CKEN &= ~(1 << cken);
+ }
+
+-const struct clkops clk_cken_ops = {
+- .enable = clk_cken_enable,
+- .disable = clk_cken_disable,
+-};
+-
+ static struct clk common_clks[] = {
+ {
+ .name = "GPIO27_CLK",
+- .ops = &clk_gpio27_ops,
+ .rate = 3686400,
++ .owner = THIS_MODULE,
++ .enable = clk_gpio27_enable,
++ .disable = clk_gpio27_disable,
+ },
+ };
+
+-void clks_register(struct clk *clks, size_t num)
+-{
+- int i;
+-
+- mutex_lock(&clocks_mutex);
+- for (i = 0; i < num; i++)
+- list_add(&clks[i].node, &clocks);
+- mutex_unlock(&clocks_mutex);
+-}
+-
+ static int __init clk_init(void)
+ {
+ clks_register(common_clks, ARRAY_SIZE(common_clks));
+diff --git a/arch/arm/mach-pxa/clock.h b/arch/arm/mach-pxa/clock.h
+index bc6b77e..5d0d067 100644
+--- a/arch/arm/mach-pxa/clock.h
++++ b/arch/arm/mach-pxa/clock.h
+@@ -1,43 +1,47 @@
+-struct clk;
++#include <linux/clklib.h>
++#include <linux/seq_file.h>
+
+-struct clkops {
+- void (*enable)(struct clk *);
+- void (*disable)(struct clk *);
+- unsigned long (*getrate)(struct clk *);
++struct clk_cken_priv {
++ unsigned int cken;
+ };
+
+-struct clk {
+- struct list_head node;
+- const char *name;
+- struct device *dev;
+- const struct clkops *ops;
+- unsigned long rate;
+- unsigned int cken;
+- unsigned int delay;
+- unsigned int enabled;
+-};
+-
+-#define INIT_CKEN(_name, _cken, _rate, _delay, _dev) \
++#define INIT_CKEN(_name, _cken, _rate, _delay) \
+ { \
+ .name = _name, \
+- .dev = _dev, \
+- .ops = &clk_cken_ops, \
++ .enable = clk_cken_enable, \
++ .disable = clk_cken_disable, \
+ .rate = _rate, \
+- .cken = CKEN_##_cken, \
+ .delay = _delay, \
++ .priv = &(struct clk_cken_priv) { \
++ .cken = CKEN_##_cken, \
++ }, \
+ }
+
+-#define INIT_CK(_name, _cken, _ops, _dev) \
++#define INIT_CK(_name, _cken, _getrate) \
+ { \
+ .name = _name, \
+- .dev = _dev, \
+- .ops = _ops, \
+- .cken = CKEN_##_cken, \
++ .enable = clk_cken_enable, \
++ .disable = clk_cken_disable, \
++ .getrate = _getrate, \
++ .priv = &(struct clk_cken_priv) { \
++ .cken = CKEN_##_cken, \
++ }, \
+ }
+
+-extern const struct clkops clk_cken_ops;
+-
+-void clk_cken_enable(struct clk *clk);
++int clk_cken_enable(struct clk *clk);
+ void clk_cken_disable(struct clk *clk);
+
+ void clks_register(struct clk *clks, size_t num);
++
++static int __maybe_unused clk_dev_can_get(struct clk *clk, struct device *dev)
++{
++ return (dev == clk->priv);
++}
++
++static int __maybe_unused clk_dev_format(struct clk *clk, struct seq_file *s)
++{
++ BUG_ON(!clk->priv);
++ seq_puts(s, "for device ");
++ seq_puts(s, ((struct device *)clk->priv)->bus_id);
++ return 0;
++}
+diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c
+index 5988d99..ed3719b 100644
+--- a/arch/arm/mach-pxa/pxa25x.c
++++ b/arch/arm/mach-pxa/pxa25x.c
+@@ -100,40 +100,50 @@ static unsigned long clk_pxa25x_lcd_getrate(struct clk *clk)
+ return pxa25x_get_memclk_frequency_10khz() * 10000;
+ }
+
+-static const struct clkops clk_pxa25x_lcd_ops = {
+- .enable = clk_cken_enable,
+- .disable = clk_cken_disable,
+- .getrate = clk_pxa25x_lcd_getrate,
+-};
+-
+ /*
+ * 3.6864MHz -> OST, GPIO, SSP, PWM, PLLs (95.842MHz, 147.456MHz)
+ * 95.842MHz -> MMC 19.169MHz, I2C 31.949MHz, FICP 47.923MHz, USB 47.923MHz
+ * 147.456MHz -> UART 14.7456MHz, AC97 12.288MHz, I2S 5.672MHz (allegedly)
+ */
+-static struct clk pxa25x_hwuart_clk =
+- INIT_CKEN("UARTCLK", HWUART, 14745600, 1, &pxa_device_hwuart.dev)
+-;
++static struct clk pxa25x_hwuart_clk[] = {
++ INIT_CKEN("HWUARTCLK", HWUART, 14745600, 1),
++ {
++ .parent = &pxa25x_hwuart_clk[0],
++ .name = "UARTCLK",
++ .can_get = clk_dev_can_get,
++ .priv = &pxa_device_hwuart.dev,
++ },
++};
+
+ static struct clk pxa25x_clks[] = {
+- INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_ops, &pxa_device_fb.dev),
+- INIT_CKEN("UARTCLK", FFUART, 14745600, 1, &pxa_device_ffuart.dev),
+- INIT_CKEN("UARTCLK", BTUART, 14745600, 1, &pxa_device_btuart.dev),
+- INIT_CKEN("UARTCLK", STUART, 14745600, 1, NULL),
+- INIT_CKEN("UDCCLK", USB, 47923000, 5, &pxa_device_udc.dev),
+- INIT_CKEN("MMCCLK", MMC, 19169000, 0, &pxa_device_mci.dev),
+- INIT_CKEN("I2CCLK", I2C, 31949000, 0, &pxa_device_i2c.dev),
+-
+- INIT_CKEN("SSPCLK", SSP, 3686400, 0, &pxa25x_device_ssp.dev),
+- INIT_CKEN("SSPCLK", NSSP, 3686400, 0, &pxa25x_device_nssp.dev),
+- INIT_CKEN("SSPCLK", ASSP, 3686400, 0, &pxa25x_device_assp.dev),
++ INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_getrate),
++ INIT_CKEN("FFUARTCLK", FFUART, 14745600, 1),
++ INIT_CKEN("BTUARTCLK", BTUART, 14745600, 1),
++ INIT_CKEN("STUARTCLK", STUART, 14745600, 1),
++ INIT_CKEN("UDCCLK", USB, 47923000, 5),
++ INIT_CKEN("MMCCLK", MMC, 19169000, 0),
++ INIT_CKEN("I2CCLK", I2C, 31949000, 0),
++
++ INIT_CKEN("SSP_CLK", SSP, 3686400, 0),
++ INIT_CKEN("NSSPCLK", NSSP, 3686400, 0),
++ INIT_CKEN("ASSPCLK", ASSP, 3686400, 0),
+
+ /*
+- INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL),
+- INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL),
+- INIT_CKEN("I2SCLK", I2S, 14745600, 0, NULL),
++ INIT_CKEN("PWMCLK", PWM0, 3686400, 0),
++ INIT_CKEN("PWMCLK", PWM0, 3686400, 0),
++ INIT_CKEN("I2SCLK", I2S, 14745600, 0),
+ */
+- INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL),
++ INIT_CKEN("FICPCLK", FICP, 47923000, 0),
++};
++
++static struct clk_function __initdata pxa25x_clk_funcs[] = {
++ CLK_FUNC("FFUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_ffuart.dev, clk_dev_format),
++ CLK_FUNC("BTUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_btuart.dev, clk_dev_format),
++ CLK_FUNC("STUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_stuart.dev, clk_dev_format),
++ CLK_FUNC("STUARTCLK", "SIRCLK", NULL, NULL, NULL),
++ CLK_FUNC("SSP_CLK", "SSPCLK", clk_dev_can_get, &pxa25x_device_ssp.dev, clk_dev_format),
++ CLK_FUNC("NSSPCLK", "SSPCLK", clk_dev_can_get, &pxa25x_device_nssp.dev, clk_dev_format),
++ CLK_FUNC("ASSPCLK", "SSPCLK", clk_dev_can_get, &pxa25x_device_assp.dev, clk_dev_format),
+ };
+
+ #ifdef CONFIG_PM
+@@ -313,11 +323,13 @@ static int __init pxa25x_init(void)
+ int ret = 0;
+
+ /* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */
+- if (cpu_is_pxa25x())
+- clks_register(&pxa25x_hwuart_clk, 1);
++ if (cpu_is_pxa25x()) {
++ clks_register(pxa25x_hwuart_clk, ARRAY_SIZE(pxa25x_hwuart_clk));
++ }
+
+ if (cpu_is_pxa21x() || cpu_is_pxa25x()) {
+ clks_register(pxa25x_clks, ARRAY_SIZE(pxa25x_clks));
++ clk_alloc_functions(pxa25x_clk_funcs, ARRAY_SIZE(pxa25x_clk_funcs));
+
+ if ((ret = pxa_init_dma(16)))
+ return ret;
+diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c
+index 30ca4fd..c51e7b2 100644
+--- a/arch/arm/mach-pxa/pxa27x.c
++++ b/arch/arm/mach-pxa/pxa27x.c
+@@ -126,44 +126,48 @@ static unsigned long clk_pxa27x_lcd_getrate(struct clk *clk)
+ return pxa27x_get_lcdclk_frequency_10khz() * 10000;
+ }
+
+-static const struct clkops clk_pxa27x_lcd_ops = {
+- .enable = clk_cken_enable,
+- .disable = clk_cken_disable,
+- .getrate = clk_pxa27x_lcd_getrate,
+-};
+-
+ static struct clk pxa27x_clks[] = {
+- INIT_CK("LCDCLK", LCD, &clk_pxa27x_lcd_ops, &pxa_device_fb.dev),
+- INIT_CK("CAMCLK", CAMERA, &clk_pxa27x_lcd_ops, NULL),
++ INIT_CK("LCDCLK", LCD, &clk_pxa27x_lcd_getrate),
++ INIT_CK("CAMCLK", CAMERA, &clk_pxa27x_lcd_getrate),
+
+- INIT_CKEN("UARTCLK", FFUART, 14857000, 1, &pxa_device_ffuart.dev),
+- INIT_CKEN("UARTCLK", BTUART, 14857000, 1, &pxa_device_btuart.dev),
+- INIT_CKEN("UARTCLK", STUART, 14857000, 1, NULL),
++ INIT_CKEN("FFUARTCLK", FFUART, 14857000, 1),
++ INIT_CKEN("BTUARTCLK", BTUART, 14857000, 1),
++ INIT_CKEN("STUARTCLK", STUART, 14857000, 1),
+
+- INIT_CKEN("I2SCLK", I2S, 14682000, 0, &pxa_device_i2s.dev),
+- INIT_CKEN("I2CCLK", I2C, 32842000, 0, &pxa_device_i2c.dev),
+- INIT_CKEN("UDCCLK", USB, 48000000, 5, &pxa_device_udc.dev),
+- INIT_CKEN("MMCCLK", MMC, 19500000, 0, &pxa_device_mci.dev),
+- INIT_CKEN("FICPCLK", FICP, 48000000, 0, &pxa_device_ficp.dev),
++ INIT_CKEN("I2SCLK", I2S, 14682000, 0),
++ INIT_CKEN("I2CCLK", I2C, 32842000, 0),
++ INIT_CKEN("UDCCLK", USB, 48000000, 5),
++ INIT_CKEN("MMCCLK", MMC, 19500000, 0),
++ INIT_CKEN("FICPCLK", FICP, 48000000, 0),
+
+- INIT_CKEN("USBCLK", USBHOST, 48000000, 0, &pxa27x_device_ohci.dev),
+- INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0, &pxa27x_device_i2c_power.dev),
+- INIT_CKEN("KBDCLK", KEYPAD, 32768, 0, NULL),
++ INIT_CKEN("USBCLK", USBHOST, 48000000, 0),
++ INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0),
++ INIT_CKEN("KBDCLK", KEYPAD, 32768, 0),
+
+- INIT_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev),
+- INIT_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev),
+- INIT_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev),
++ INIT_CKEN("SSP1CLK", SSP1, 13000000, 0),
++ INIT_CKEN("SSP2CLK", SSP2, 13000000, 0),
++ INIT_CKEN("SSP3CLK", SSP3, 13000000, 0),
+
+ /*
+- INIT_CKEN("PWMCLK", PWM0, 13000000, 0, NULL),
+- INIT_CKEN("MSLCLK", MSL, 48000000, 0, NULL),
+- INIT_CKEN("USIMCLK", USIM, 48000000, 0, NULL),
+- INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0, NULL),
+- INIT_CKEN("IMCLK", IM, 0, 0, NULL),
+- INIT_CKEN("MEMCLK", MEMC, 0, 0, NULL),
++ INIT_CKEN("PWMCLK", PWM0, 13000000, 0),
++ INIT_CKEN("MSLCLK", MSL, 48000000, 0),
++ INIT_CKEN("USIMCLK", USIM, 48000000, 0),
++ INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0),
++ INIT_CKEN("IMCLK", IM, 0, 0),
++ INIT_CKEN("MEMCLK", MEMC, 0, 0),
+ */
+ };
+
++static struct clk_function __initdata pxa27x_clk_funcs[] = {
++ CLK_FUNC("FFUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_ffuart.dev, clk_dev_format),
++ CLK_FUNC("BTUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_btuart.dev, clk_dev_format),
++ CLK_FUNC("STUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_stuart.dev, clk_dev_format),
++ CLK_FUNC("STUARTCLK", "SIRCLK", NULL, NULL, NULL),
++ CLK_FUNC("SSP1CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp1.dev, clk_dev_format),
++ CLK_FUNC("SSP2CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp2.dev, clk_dev_format),
++ CLK_FUNC("SSP3CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp3.dev, clk_dev_format),
++};
++
+ #ifdef CONFIG_PM
+
+ #define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
+@@ -453,6 +457,7 @@ static int __init pxa27x_init(void)
+ int ret = 0;
+ if (cpu_is_pxa27x()) {
+ clks_register(pxa27x_clks, ARRAY_SIZE(pxa27x_clks));
++ clk_alloc_functions(pxa27x_clk_funcs, ARRAY_SIZE(pxa27x_clk_funcs));
+
+ if ((ret = pxa_init_dma(32)))
+ return ret;
+diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c
+index ccab9da..0f8bbf3 100644
+--- a/arch/arm/mach-pxa/pxa3xx.c
++++ b/arch/arm/mach-pxa/pxa3xx.c
+@@ -122,27 +122,31 @@ static unsigned long clk_pxa3xx_hsio_getrate(struct clk *clk)
+ return hsio_clk;
+ }
+
+-static void clk_pxa3xx_cken_enable(struct clk *clk)
++static int clk_pxa3xx_cken_enable(struct clk *clk)
+ {
+- unsigned long mask = 1ul << (clk->cken & 0x1f);
++ int cken = ((struct clk_cken_priv *)clk->priv)->cken;
++ unsigned long mask = 1ul << (cken & 0x1f);
+
+ local_irq_disable();
+
+- if (clk->cken < 32)
++ if (cken < 32)
+ CKENA |= mask;
+ else
+ CKENB |= mask;
+
+ local_irq_enable();
++
++ return 0;
+ }
+
+ static void clk_pxa3xx_cken_disable(struct clk *clk)
+ {
+- unsigned long mask = 1ul << (clk->cken & 0x1f);
++ int cken = ((struct clk_cken_priv *)clk->priv)->cken;
++ unsigned long mask = 1ul << (cken & 0x1f);
+
+ local_irq_disable();
+
+- if (clk->cken < 32)
++ if (cken < 32)
+ CKENA &= ~mask;
+ else
+ CKENB &= ~mask;
+@@ -150,55 +154,63 @@ static void clk_pxa3xx_cken_disable(struct clk *clk)
+ local_irq_enable();
+ }
+
+-static const struct clkops clk_pxa3xx_cken_ops = {
+- .enable = clk_pxa3xx_cken_enable,
+- .disable = clk_pxa3xx_cken_disable,
+-};
+-
+-static const struct clkops clk_pxa3xx_hsio_ops = {
+- .enable = clk_pxa3xx_cken_enable,
+- .disable = clk_pxa3xx_cken_disable,
+- .getrate = clk_pxa3xx_hsio_getrate,
+-};
+-
+-#define PXA3xx_CKEN(_name, _cken, _rate, _delay, _dev) \
++#define PXA3xx_CKEN(_name, _cken, _rate, _delay) \
+ { \
+ .name = _name, \
+- .dev = _dev, \
+- .ops = &clk_pxa3xx_cken_ops, \
++ .enable = clk_pxa3xx_cken_enable, \
++ .disable = clk_pxa3xx_cken_disable, \
+ .rate = _rate, \
+- .cken = CKEN_##_cken, \
+ .delay = _delay, \
++ .priv = &(struct clk_cken_priv) { \
++ .cken = CKEN_##_cken, \
++ }, \
+ }
+
+-#define PXA3xx_CK(_name, _cken, _ops, _dev) \
++#define PXA3xx_CK(_name, _cken, _getrate) \
+ { \
+ .name = _name, \
+- .dev = _dev, \
+- .ops = _ops, \
+- .cken = CKEN_##_cken, \
++ .enable = clk_pxa3xx_cken_enable, \
++ .disable = clk_pxa3xx_cken_disable, \
++ .getrate = _getrate, \
++ .priv = &(struct clk_cken_priv) { \
++ .cken = CKEN_##_cken, \
++ }, \
+ }
+
+ static struct clk pxa3xx_clks[] = {
+- PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_ops, &pxa_device_fb.dev),
+- PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_ops, NULL),
++ PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_getrate),
++ PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_getrate),
+
+- PXA3xx_CKEN("UARTCLK", FFUART, 14857000, 1, &pxa_device_ffuart.dev),
+- PXA3xx_CKEN("UARTCLK", BTUART, 14857000, 1, &pxa_device_btuart.dev),
+- PXA3xx_CKEN("UARTCLK", STUART, 14857000, 1, NULL),
++ PXA3xx_CKEN("FFUARTCLK", FFUART, 14857000, 1),
++ PXA3xx_CKEN("BTUARTCLK", BTUART, 14857000, 1),
++ PXA3xx_CKEN("STUARTCLK", STUART, 14857000, 1),
+
+- PXA3xx_CKEN("I2CCLK", I2C, 32842000, 0, &pxa_device_i2c.dev),
+- PXA3xx_CKEN("UDCCLK", UDC, 48000000, 5, &pxa_device_udc.dev),
+- PXA3xx_CKEN("USBCLK", USBH, 48000000, 0, &pxa27x_device_ohci.dev),
++ PXA3xx_CKEN("I2CCLK", I2C, 32842000, 0),
++ PXA3xx_CKEN("UDCCLK", UDC, 48000000, 5),
++ PXA3xx_CKEN("USBCLK", USBH, 48000000, 0),
+
+- PXA3xx_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev),
+- PXA3xx_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev),
+- PXA3xx_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev),
+- PXA3xx_CKEN("SSPCLK", SSP4, 13000000, 0, &pxa3xx_device_ssp4.dev),
++ PXA3xx_CKEN("SSP1CLK", SSP1, 13000000, 0),
++ PXA3xx_CKEN("SSP2CLK", SSP2, 13000000, 0),
++ PXA3xx_CKEN("SSP3CLK", SSP3, 13000000, 0),
++ PXA3xx_CKEN("SSP4CLK", SSP4, 13000000, 0),
++
++ PXA3xx_CKEN("MMC1CLK", MMC1, 19500000, 0),
++ PXA3xx_CKEN("MMC2CLK", MMC2, 19500000, 0),
++ PXA3xx_CKEN("MMC3CLK", MMC3, 19500000, 0),
++};
+
+- PXA3xx_CKEN("MMCCLK", MMC1, 19500000, 0, &pxa_device_mci.dev),
+- PXA3xx_CKEN("MMCCLK", MMC2, 19500000, 0, &pxa3xx_device_mci2.dev),
+- PXA3xx_CKEN("MMCCLK", MMC3, 19500000, 0, &pxa3xx_device_mci3.dev),
++static struct clk_function __initdata pxa3xx_clk_funcs[] = {
++ CLK_FUNC("FFUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_ffuart.dev, clk_dev_format),
++ CLK_FUNC("BTUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_btuart.dev, clk_dev_format),
++ CLK_FUNC("STUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_stuart.dev, clk_dev_format),
++ CLK_FUNC("STUARTCLK", "SIRCLK", NULL, NULL, NULL),
++ CLK_FUNC("SSP1CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp1.dev, clk_dev_format),
++ CLK_FUNC("SSP2CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp2.dev, clk_dev_format),
++ CLK_FUNC("SSP3CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp3.dev, clk_dev_format),
++ CLK_FUNC("SSP4CLK", "SSPCLK", clk_dev_can_get, &pxa3xx_device_ssp4.dev, clk_dev_format),
++ CLK_FUNC("MMC1CLK", "MMCCLK", clk_dev_can_get, &pxa_device_mci.dev, clk_dev_format),
++ CLK_FUNC("MMC2CLK", "MMCCLK", clk_dev_can_get, &pxa3xx_device_mci2.dev, clk_dev_format),
++ CLK_FUNC("MMC3CLK", "MMCCLK", clk_dev_can_get, &pxa3xx_device_mci3.dev, clk_dev_format),
+ };
+
+ #ifdef CONFIG_PM
+@@ -255,6 +267,7 @@ static int __init pxa3xx_init(void)
+
+ if (cpu_is_pxa3xx()) {
+ clks_register(pxa3xx_clks, ARRAY_SIZE(pxa3xx_clks));
++ clk_alloc_functions(pxa3xx_clk_funcs, ARRAY_SIZE(pxa3xx_clk_funcs));
+
+ if ((ret = pxa_init_dma(32)))
+ return ret;
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0042-Use-correct-clock-for-IrDA-on-pxa.patch b/recipes/linux/linux-rp-2.6.24/tosa/0042-Use-correct-clock-for-IrDA-on-pxa.patch
new file mode 100644
index 0000000000..a605735df0
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0042-Use-correct-clock-for-IrDA-on-pxa.patch
@@ -0,0 +1,26 @@
+From 70dfe7e736467af6242c61092cb64f44d2fd50e3 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Mon, 4 Feb 2008 03:01:05 +0300
+Subject: [PATCH 42/64] Use correct clock for IrDA on pxa
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/net/irda/pxaficp_ir.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c
+index 8c09344..36d2ec0 100644
+--- a/drivers/net/irda/pxaficp_ir.c
++++ b/drivers/net/irda/pxaficp_ir.c
+@@ -814,7 +814,7 @@ static int pxa_irda_probe(struct platform_device *pdev)
+ si->dev = &pdev->dev;
+ si->pdata = pdev->dev.platform_data;
+
+- si->sir_clk = clk_get(&pdev->dev, "UARTCLK");
++ si->sir_clk = clk_get(&pdev->dev, "SIRCLK");
+ si->fir_clk = clk_get(&pdev->dev, "FICPCLK");
+ if (IS_ERR(si->sir_clk) || IS_ERR(si->fir_clk)) {
+ err = PTR_ERR(IS_ERR(si->sir_clk) ? si->sir_clk : si->fir_clk);
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0043-Use-clocklib-for-sa1100-sub-arch.patch b/recipes/linux/linux-rp-2.6.24/tosa/0043-Use-clocklib-for-sa1100-sub-arch.patch
new file mode 100644
index 0000000000..22b8414b2d
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0043-Use-clocklib-for-sa1100-sub-arch.patch
@@ -0,0 +1,153 @@
+From 3932e0f5c4c05200c030b60606ed2eb83550f4bb Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Mon, 4 Feb 2008 03:01:04 +0300
+Subject: [PATCH 43/64] Use clocklib for sa1100 sub-arch.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/Kconfig | 1 +
+ arch/arm/mach-sa1100/clock.c | 95 ++---------------------------------------
+ 2 files changed, 6 insertions(+), 90 deletions(-)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 47f3c73..fa47201 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -370,6 +370,7 @@ config ARCH_SA1100
+ select ARCH_MTD_XIP
+ select GENERIC_GPIO
+ select GENERIC_TIME
++ select HAVE_CLOCK_LIB
+ help
+ Support for StrongARM 11x0 based boards.
+
+diff --git a/arch/arm/mach-sa1100/clock.c b/arch/arm/mach-sa1100/clock.c
+index fc97fe5..6b3cc51 100644
+--- a/arch/arm/mach-sa1100/clock.c
++++ b/arch/arm/mach-sa1100/clock.c
+@@ -8,83 +8,13 @@
+ #include <linux/err.h>
+ #include <linux/string.h>
+ #include <linux/clk.h>
++#include <linux/clklib.h>
+ #include <linux/spinlock.h>
+ #include <linux/mutex.h>
+
+ #include <asm/hardware.h>
+
+-/*
+- * Very simple clock implementation - we only have one clock to
+- * deal with at the moment, so we only match using the "name".
+- */
+-struct clk {
+- struct list_head node;
+- unsigned long rate;
+- const char *name;
+- unsigned int enabled;
+- void (*enable)(void);
+- void (*disable)(void);
+-};
+-
+-static LIST_HEAD(clocks);
+-static DEFINE_MUTEX(clocks_mutex);
+-static DEFINE_SPINLOCK(clocks_lock);
+-
+-struct clk *clk_get(struct device *dev, const char *id)
+-{
+- struct clk *p, *clk = ERR_PTR(-ENOENT);
+-
+- mutex_lock(&clocks_mutex);
+- list_for_each_entry(p, &clocks, node) {
+- if (strcmp(id, p->name) == 0) {
+- clk = p;
+- break;
+- }
+- }
+- mutex_unlock(&clocks_mutex);
+-
+- return clk;
+-}
+-EXPORT_SYMBOL(clk_get);
+-
+-void clk_put(struct clk *clk)
+-{
+-}
+-EXPORT_SYMBOL(clk_put);
+-
+-int clk_enable(struct clk *clk)
+-{
+- unsigned long flags;
+-
+- spin_lock_irqsave(&clocks_lock, flags);
+- if (clk->enabled++ == 0)
+- clk->enable();
+- spin_unlock_irqrestore(&clocks_lock, flags);
+- return 0;
+-}
+-EXPORT_SYMBOL(clk_enable);
+-
+-void clk_disable(struct clk *clk)
+-{
+- unsigned long flags;
+-
+- WARN_ON(clk->enabled == 0);
+-
+- spin_lock_irqsave(&clocks_lock, flags);
+- if (--clk->enabled == 0)
+- clk->disable();
+- spin_unlock_irqrestore(&clocks_lock, flags);
+-}
+-EXPORT_SYMBOL(clk_disable);
+-
+-unsigned long clk_get_rate(struct clk *clk)
+-{
+- return clk->rate;
+-}
+-EXPORT_SYMBOL(clk_get_rate);
+-
+-
+-static void clk_gpio27_enable(void)
++static int clk_gpio27_enable(struct clk *clk)
+ {
+ /*
+ * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111:
+@@ -93,9 +23,11 @@ static void clk_gpio27_enable(void)
+ GAFR |= GPIO_32_768kHz;
+ GPDR |= GPIO_32_768kHz;
+ TUCR = TUCR_3_6864MHz;
++
++ return 0;
+ }
+
+-static void clk_gpio27_disable(void)
++static void clk_gpio27_disable(struct clk *clk)
+ {
+ TUCR = 0;
+ GPDR &= ~GPIO_32_768kHz;
+@@ -109,23 +41,6 @@ static struct clk clk_gpio27 = {
+ .disable = clk_gpio27_disable,
+ };
+
+-int clk_register(struct clk *clk)
+-{
+- mutex_lock(&clocks_mutex);
+- list_add(&clk->node, &clocks);
+- mutex_unlock(&clocks_mutex);
+- return 0;
+-}
+-EXPORT_SYMBOL(clk_register);
+-
+-void clk_unregister(struct clk *clk)
+-{
+- mutex_lock(&clocks_mutex);
+- list_del(&clk->node);
+- mutex_unlock(&clocks_mutex);
+-}
+-EXPORT_SYMBOL(clk_unregister);
+-
+ static int __init clk_init(void)
+ {
+ clk_register(&clk_gpio27);
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0044-fix-tmio_mmc-debug-compilation.patch b/recipes/linux/linux-rp-2.6.24/tosa/0044-fix-tmio_mmc-debug-compilation.patch
new file mode 100644
index 0000000000..5ca8228604
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0044-fix-tmio_mmc-debug-compilation.patch
@@ -0,0 +1,26 @@
+From 03fdebde257197c13c0d10882e16a2a888ab4e0a Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Sat, 2 Feb 2008 20:23:01 +0300
+Subject: [PATCH 44/64] fix tmio_mmc debug compilation
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/mmc/host/tmio_mmc.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
+index 735c386..b0d38e2 100644
+--- a/drivers/mmc/host/tmio_mmc.c
++++ b/drivers/mmc/host/tmio_mmc.c
+@@ -329,7 +329,7 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid)
+ if (!ireg) {
+ disable_mmc_irqs(ctl, status & ~irq_mask);
+ #ifdef CONFIG_MMC_DEBUG
+- WARN("tmio_mmc: Spurious MMC irq, disabling! 0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg);
++ printk(KERN_WARNING "tmio_mmc: Spurious MMC irq, disabling! 0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg);
+ debug_status(status);
+ #endif
+ goto out;
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0045-Update-tmio_ohci.patch b/recipes/linux/linux-rp-2.6.24/tosa/0045-Update-tmio_ohci.patch
new file mode 100644
index 0000000000..10f483b89d
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0045-Update-tmio_ohci.patch
@@ -0,0 +1,416 @@
+From fe3c05491370965eb821aedc95f771b86ebab3ab Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 02:01:44 +0300
+Subject: [PATCH 45/64] Update tmio_ohci:
+ Ports management.
+ Basic support for ohci suspend/resume.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/mfd/tc6393xb.c | 40 ++++++++
+ drivers/usb/host/ohci-tmio.c | 206 +++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 235 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
+index 9439f39..5d17687 100644
+--- a/drivers/mfd/tc6393xb.c
++++ b/drivers/mfd/tc6393xb.c
+@@ -224,6 +224,44 @@ static int tc6393xb_ohci_enable(struct platform_device *ohci)
+ return 0;
+ }
+
++static int tc6393xb_ohci_suspend(struct platform_device *ohci)
++{
++ struct platform_device *dev = to_platform_device(ohci->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_ccr ccr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ ccr.raw = ioread16(&scr->ccr);
++ ccr.bits.usbcken = 0;
++ iowrite16(ccr.raw, &scr->ccr);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++static int tc6393xb_ohci_resume(struct platform_device *ohci)
++{
++ struct platform_device *dev = to_platform_device(ohci->dev.parent);
++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ union tc6393xb_scr_ccr ccr;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ ccr.raw = ioread16(&scr->ccr);
++ ccr.bits.usbcken = 1;
++ iowrite16(ccr.raw, &scr->ccr);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
+ static int tc6393xb_fb_disable(struct platform_device *fb)
+ {
+ struct platform_device *dev = to_platform_device(fb->dev.parent);
+@@ -423,6 +461,8 @@ static struct mfd_cell tc6393xb_cells[] = {
+ .name = "tmio-ohci",
+ .enable = tc6393xb_ohci_enable,
+ .disable = tc6393xb_ohci_disable,
++ .suspend = tc6393xb_ohci_suspend,
++ .resume = tc6393xb_ohci_resume,
+ .num_resources = ARRAY_SIZE(tc6393xb_ohci_resources),
+ .resources = tc6393xb_ohci_resources,
+ },
+diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c
+index be609f3..65e3cd3 100644
+--- a/drivers/usb/host/ohci-tmio.c
++++ b/drivers/usb/host/ohci-tmio.c
+@@ -75,10 +75,13 @@ struct tmio_uhccr {
+ u8 x07[3];
+ } __attribute__((packed));
+
++#define MAX_TMIO_OHCI_PORTS 3
++
+ #define UHCCR_PM_GKEN 0x0001
+ #define UHCCR_PM_CKRNEN 0x0002
+ #define UHCCR_PM_USBPW1 0x0004
+ #define UHCCR_PM_USBPW2 0x0008
++#define UHCCR_PM_USBPW3 0x0008
+ #define UHCCR_PM_PMEE 0x0100
+ #define UHCCR_PM_PMES 0x8000
+
+@@ -86,44 +89,96 @@ struct tmio_uhccr {
+
+ struct tmio_hcd {
+ struct tmio_uhccr __iomem *ccr;
++ spinlock_t lock; /* protects RMW cycles and disabled_ports data */
++ bool disabled_ports[MAX_TMIO_OHCI_PORTS];
+ };
+
+ #define hcd_to_tmio(hcd) ((struct tmio_hcd *)(hcd_to_ohci(hcd) + 1))
+ #define ohci_to_tmio(ohci) ((struct tmio_hcd *)(ohci + 1))
+
++struct indexed_device_attribute{
++ struct device_attribute dev_attr;
++ int index;
++};
++#define to_indexed_dev_attr(_dev_attr) \
++ container_of(_dev_attr, struct indexed_device_attribute, dev_attr)
++
++#define INDEXED_ATTR(_name, _mode, _show, _store, _index) \
++ { .dev_attr = __ATTR(_name ## _index, _mode, _show, _store), \
++ .index = _index }
++
++#define INDEXED_DEVICE_ATTR(_name, _mode, _show, _store, _index) \
++struct indexed_device_attribute dev_attr_##_name ## _index \
++ = INDEXED_ATTR(_name, _mode, _show, _store, _index)
++
++static bool disabled_tmio_ports[MAX_TMIO_OHCI_PORTS];
++module_param_array(disabled_tmio_ports, bool, NULL, 0644);
++MODULE_PARM_DESC(disabled_tmio_ports,
++ "disable specified TC6393 usb ports (default: all enabled)");
++
+ /*-------------------------------------------------------------------------*/
+
++static void tmio_write_pm(struct platform_device *dev)
++{
++ struct usb_hcd *hcd = platform_get_drvdata(dev);
++ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
++ struct tmio_uhccr __iomem *ccr = tmio->ccr;
++ u16 pm;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tmio->lock, flags);
++
++ pm = UHCCR_PM_GKEN | UHCCR_PM_CKRNEN |
++ UHCCR_PM_PMEE | UHCCR_PM_PMES;
++
++ if (tmio->disabled_ports[0])
++ pm |= UHCCR_PM_USBPW1;
++ if (tmio->disabled_ports[1])
++ pm |= UHCCR_PM_USBPW2;
++ if (tmio->disabled_ports[2])
++ pm |= UHCCR_PM_USBPW3;
++
++ iowrite16(pm, &ccr->pm);
++ spin_unlock_irqrestore(&tmio->lock, flags);
++}
++
+ static void tmio_stop_hc(struct platform_device *dev)
+ {
+ struct mfd_cell *cell = mfd_get_cell(dev);
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
++ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
+ struct tmio_uhccr __iomem *ccr = tmio->ccr;
+ u16 pm;
+
+- pm = UHCCR_PM_GKEN | UHCCR_PM_CKRNEN | UHCCR_PM_USBPW1 | UHCCR_PM_USBPW2;
++ pm = UHCCR_PM_GKEN | UHCCR_PM_CKRNEN;
++ switch (ohci->num_ports) {
++ default:
++ dev_err(&dev->dev, "Unsupported amount of ports: %d\n", ohci->num_ports);
++ case 3:
++ pm |= UHCCR_PM_USBPW3;
++ case 2:
++ pm |= UHCCR_PM_USBPW2;
++ case 1:
++ pm |= UHCCR_PM_USBPW1;
++ }
+ iowrite8(0, &ccr->intc);
+ iowrite8(0, &ccr->ilme);
+ iowrite16(0, &ccr->basel);
+ iowrite16(0, &ccr->baseh);
+- iowrite16(pm, &ccr->pm);
++ iowrite16(pm, &ccr->pm);
+
+ cell->disable(dev);
+ }
+
+ static void tmio_start_hc(struct platform_device *dev)
+ {
+- struct mfd_cell *cell = mfd_get_cell(dev);
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
+ struct tmio_uhccr __iomem *ccr = tmio->ccr;
+- u16 pm;
+ unsigned long base = hcd->rsrc_start;
+
+- pm = UHCCR_PM_CKRNEN | UHCCR_PM_GKEN | UHCCR_PM_PMEE | UHCCR_PM_PMES;
+- cell->enable(dev);
+-
+- iowrite16(pm, &ccr->pm);
++ tmio_write_pm(dev);
+ iowrite16(base, &ccr->basel);
+ iowrite16(base >> 16, &ccr->baseh);
+ iowrite8(1, &ccr->ilme);
+@@ -133,9 +188,56 @@ static void tmio_start_hc(struct platform_device *dev)
+ ioread8(&ccr->revid), hcd->rsrc_start, hcd->irq);
+ }
+
++static ssize_t tmio_disabled_port_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct usb_hcd *hcd = dev_get_drvdata(dev);
++ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
++ int index = to_indexed_dev_attr(attr)->index;
++ return snprintf(buf, PAGE_SIZE, "%c",
++ tmio->disabled_ports[index-1]? 'Y': 'N');
++}
++
++static ssize_t tmio_disabled_port_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct usb_hcd *hcd = dev_get_drvdata(dev);
++ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
++ int index = to_indexed_dev_attr(attr)->index;
++
++ if (!count)
++ return -EINVAL;
++
++ switch (buf[0]) {
++ case 'y': case 'Y': case '1':
++ tmio->disabled_ports[index-1] = true;
++ break;
++ case 'n': case 'N': case '0':
++ tmio->disabled_ports[index-1] = false;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ tmio_write_pm(to_platform_device(dev));
++
++ return 1;
++}
++
++
++static INDEXED_DEVICE_ATTR(disabled_usb_port, S_IRUGO | S_IWUSR,
++ tmio_disabled_port_show, tmio_disabled_port_store, 1);
++static INDEXED_DEVICE_ATTR(disabled_usb_port, S_IRUGO | S_IWUSR,
++ tmio_disabled_port_show, tmio_disabled_port_store, 2);
++static INDEXED_DEVICE_ATTR(disabled_usb_port, S_IRUGO | S_IWUSR,
++ tmio_disabled_port_show, tmio_disabled_port_store, 3);
++
+ static int usb_hcd_tmio_probe(const struct hc_driver *driver,
+ struct platform_device *dev)
+ {
++ struct mfd_cell *cell = mfd_get_cell(dev);
+ struct resource *config = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_CONFIG);
+ struct resource *regs = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_CONTROL);
+ struct resource *sram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_SRAM);
+@@ -159,6 +261,12 @@ static int usb_hcd_tmio_probe(const struct hc_driver *driver,
+
+ tmio = hcd_to_tmio(hcd);
+
++ spin_lock_init(&tmio->lock);
++
++ memcpy(tmio->disabled_ports,
++ disabled_tmio_ports,
++ sizeof(disabled_tmio_ports));
++
+ tmio->ccr = ioremap(config->start, config->end - config->start + 1);
+ if (!tmio->ccr) {
+ retval = -ENOMEM;
+@@ -183,17 +291,46 @@ static int usb_hcd_tmio_probe(const struct hc_driver *driver,
+ if (retval)
+ goto err_dmabounce_register_dev;
+
++ retval = cell->enable(dev);
++ if (retval)
++ goto err_enable;
++
+ tmio_start_hc(dev);
+ ohci = hcd_to_ohci(hcd);
+ ohci_hcd_init(ohci);
+
+ retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
++ if (retval)
++ goto err_add_hcd;
++
++ switch (ohci->num_ports) {
++ default:
++ dev_err(&dev->dev, "Unsupported amount of ports: %d\n",
++ ohci->num_ports);
++ case 3:
++ retval |= device_create_file(&dev->dev,
++ &dev_attr_disabled_usb_port3.dev_attr);
++ case 2:
++ retval |= device_create_file(&dev->dev,
++ &dev_attr_disabled_usb_port2.dev_attr);
++ case 1:
++ retval |= device_create_file(&dev->dev,
++ &dev_attr_disabled_usb_port1.dev_attr);
++ }
+
+ if (retval == 0)
+ return retval;
+
+- tmio_stop_hc(dev);
++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port3.dev_attr);
++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port2.dev_attr);
++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port1.dev_attr);
++
++ usb_remove_hcd(hcd);
+
++err_add_hcd:
++ tmio_stop_hc(dev);
++ cell->disable(dev);
++err_enable:
+ dmabounce_unregister_dev(&dev->dev);
+ err_dmabounce_register_dev:
+ dma_release_declared_memory(&dev->dev);
+@@ -212,6 +349,9 @@ static void usb_hcd_tmio_remove(struct usb_hcd *hcd, struct platform_device *dev
+ {
+ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
+
++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port3.dev_attr);
++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port2.dev_attr);
++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port1.dev_attr);
+ usb_remove_hcd(hcd);
+ tmio_stop_hc(dev);
+ dmabounce_unregister_dev(&dev->dev);
+@@ -297,13 +437,22 @@ static u64 dma_mask = DMA_32BIT_MASK;
+ static int ohci_hcd_tmio_drv_probe(struct platform_device *dev)
+ {
+ struct resource *sram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_SRAM);
++ int retval;
+
+ dev->dev.dma_mask = &dma_mask;
+ dev->dev.coherent_dma_mask = DMA_32BIT_MASK;
+
++ /* FIXME: move dmabounce checkers to tc6393xb core? */
+ dmabounce_register_checker(tmio_dmabounce_check, sram);
+
+- return usb_hcd_tmio_probe(&ohci_tmio_hc_driver, dev);
++ retval = usb_hcd_tmio_probe(&ohci_tmio_hc_driver, dev);
++
++ if (retval == 0)
++ return retval;
++
++ dmabounce_remove_checker(tmio_dmabounce_check, sram);
++
++ return retval;
+ }
+
+ static int ohci_hcd_tmio_drv_remove(struct platform_device *dev)
+@@ -323,14 +472,31 @@ static int ohci_hcd_tmio_drv_remove(struct platform_device *dev)
+ #ifdef CONFIG_PM
+ static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t state)
+ {
++ struct mfd_cell *cell = mfd_get_cell(dev);
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
++ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
++ struct tmio_uhccr __iomem *ccr = tmio->ccr;
++ unsigned long flags;
++ u8 misc;
++ int ret;
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+- tmio_stop_hc(dev);
++ spin_lock_irqsave(&tmio->lock, flags);
++
++ misc = ioread8(&ccr->misc);
++ misc |= 1 << 3; /* USSUSP */
++ iowrite8(misc, &ccr->misc);
++
++ spin_unlock_irqrestore(&tmio->lock, flags);
++
++ ret = cell->suspend(dev);
++ if (ret)
++ return ret;
++
+ hcd->state = HC_STATE_SUSPENDED;
+ dev->dev.power.power_state = PMSG_SUSPEND;
+
+@@ -339,15 +505,33 @@ static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t s
+
+ static int ohci_hcd_tmio_drv_resume(struct platform_device *dev)
+ {
++ struct mfd_cell *cell = mfd_get_cell(dev);
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
++ struct tmio_hcd *tmio = hcd_to_tmio(hcd);
++ struct tmio_uhccr __iomem *ccr = tmio->ccr;
++ unsigned long flags;
++ u8 misc;
++ int ret;
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
++ ret = cell->resume(dev);
++ if (ret)
++ return ret;
++
+ tmio_start_hc(dev);
+
++ spin_lock_irqsave(&tmio->lock, flags);
++
++ misc = ioread8(&ccr->misc);
++ misc &= ~(1 << 3); /* USSUSP */
++ iowrite8(misc, &ccr->misc);
++
++ spin_unlock_irqrestore(&tmio->lock, flags);
++
+ dev->dev.power.power_state = PMSG_ON;
+ usb_hcd_resume_root_hub(hcd);
+
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0046-patch-tc6393xb-cleanup.patch b/recipes/linux/linux-rp-2.6.24/tosa/0046-patch-tc6393xb-cleanup.patch
new file mode 100644
index 0000000000..c4b57cb2d1
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0046-patch-tc6393xb-cleanup.patch
@@ -0,0 +1,66 @@
+From edaab7ec86235871d8ad219a1d225ce12f67f8af Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 02:13:29 +0300
+Subject: [PATCH 46/64] patch tc6393xb-cleanup
+
+---
+ drivers/mfd/tc6393xb.c | 20 ++++++++++++--------
+ 1 files changed, 12 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
+index 5d17687..dfae61d 100644
+--- a/drivers/mfd/tc6393xb.c
++++ b/drivers/mfd/tc6393xb.c
+@@ -590,16 +590,8 @@ static int tc6393xb_hw_init(struct platform_device *dev, int resume)
+ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- int ret;
+ int i;
+
+- if (resume)
+- ret = tcpd->resume(dev);
+- else
+- ret = tcpd->enable(dev);
+- if (ret)
+- return ret;
+-
+ iowrite8(resume ?
+ tc6393xb->suspend_state.fer.raw :
+ 0, &scr->fer);
+@@ -664,6 +656,10 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
+ goto err_ioremap;
+ }
+
++ retval = tcpd->enable(dev);
++ if (retval)
++ goto err_enable;
++
+ retval = tc6393xb_hw_init(dev, 0);
+ if (retval)
+ goto err_hw_init;
+@@ -690,6 +686,8 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
+ tc6393xb_detach_irq(dev);
+
+ err_hw_init:
++ tcpd->disable(dev);
++err_enable:
+ iounmap(tc6393xb->scr);
+ err_ioremap:
+ release_resource(rscr);
+@@ -743,6 +741,12 @@ static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state)
+
+ static int tc6393xb_resume(struct platform_device *dev)
+ {
++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
++ int ret = tcpd->resume(dev);
++
++ if (ret)
++ return ret;
++
+ return tc6393xb_hw_init(dev, 1);
+ }
+ #else
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0047-tc6393xb-use-bitmasks-instead-of-bit-field-structs.patch b/recipes/linux/linux-rp-2.6.24/tosa/0047-tc6393xb-use-bitmasks-instead-of-bit-field-structs.patch
new file mode 100644
index 0000000000..54e88253d1
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0047-tc6393xb-use-bitmasks-instead-of-bit-field-structs.patch
@@ -0,0 +1,412 @@
+From c18b8e34c39ec0d395988318e6651076a748d6bd Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Tue, 12 Feb 2008 04:40:54 +0300
+Subject: [PATCH 47/64] tc6393xb: use bitmasks instead of bit-field structs
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/mfd/tc6393xb.c | 162 ++++++++++++++++++++++++-----------------
+ include/linux/mfd/tc6393xb.h | 63 +++-------------
+ 2 files changed, 107 insertions(+), 118 deletions(-)
+
+diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
+index dfae61d..1a394e4 100644
+--- a/drivers/mfd/tc6393xb.c
++++ b/drivers/mfd/tc6393xb.c
+@@ -24,6 +24,31 @@
+ #include <linux/mfd/tmio.h>
+ #include <linux/mfd/tc6393xb.h>
+
++#define TC6393XB_FER_USBEN BIT(0) /* USB host enable */
++#define TC6393XB_FER_LCDCVEN BIT(1) /* polysilicon TFT enable */
++#define TC6393XB_FER_SLCDEN BIT(2) /* SLCD enable */
++
++enum pincontrol {
++ opendrain = 0,
++ tristate = 1,
++ pushpull = 2,
++ /* reserved = 3, */
++};
++
++#define TC6393XB_MCR_RDY_MASK (3 << 0)
++#define TC6393XB_MCR_RDY_OPENDRAIN (0 << 0)
++#define TC6393XB_MCR_RDY_TRISTATE (1 << 0)
++#define TC6393XB_MCR_RDY_PUSHPULL (2 << 0)
++#define TC6393XB_MCR_RDY_UNK BIT(2)
++#define TC6393XB_MCR_RDY_EN BIT(3)
++#define TC6393XB_MCR_INT_MASK (3 << 4)
++#define TC6393XB_MCR_INT_OPENDRAIN (0 << 4)
++#define TC6393XB_MCR_INT_TRISTATE (1 << 4)
++#define TC6393XB_MCR_INT_PUSHPULL (2 << 4)
++#define TC6393XB_MCR_INT_UNK BIT(6)
++#define TC6393XB_MCR_INT_EN BIT(7)
++/* bits 8 - 16 are unknown */
++
+ struct tc6393xb_scr {
+ u8 x00[8];
+ u8 revid; /* 0x08 Revision ID */
+@@ -74,8 +99,8 @@ struct tc6393xb {
+ spinlock_t lock; /* protects RMW cycles */
+
+ struct {
+- union tc6393xb_scr_fer fer;
+- union tc6393xb_scr_ccr ccr;
++ u8 fer;
++ u16 ccr;
+ u8 gpi_bcr[4];
+ } suspend_state;
+
+@@ -90,13 +115,13 @@ static int tc6393xb_mmc_enable(struct platform_device *mmc) {
+ struct platform_device *dev = to_platform_device(mmc->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_ccr ccr;
++ u16 ccr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+- ccr.raw = ioread16(&scr->ccr);
+- ccr.bits.ck32ken = 1;
+- iowrite16(ccr.raw, &scr->ccr);
++ ccr = ioread16(&scr->ccr);
++ ccr |= TC6393XB_CCR_CK32K;
++ iowrite16(ccr, &scr->ccr);
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+ return 0;
+@@ -106,13 +131,13 @@ static int tc6393xb_mmc_disable(struct platform_device *mmc) {
+ struct platform_device *dev = to_platform_device(mmc->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_ccr ccr;
++ u16 ccr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+- ccr.raw = ioread16(&scr->ccr);
+- ccr.bits.ck32ken = 0;
+- iowrite16(ccr.raw, &scr->ccr);
++ ccr = ioread16(&scr->ccr);
++ ccr &= ~TC6393XB_CCR_CK32K;
++ iowrite16(ccr, &scr->ccr);
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+ return 0;
+@@ -148,14 +173,17 @@ int tc6393xb_lcd_set_power(struct platform_device *fb, bool on)
+ struct platform_device *dev = to_platform_device(fb->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_fer fer;
++ u8 fer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+
+- fer.raw = ioread8(&scr->fer);
+- fer.bits.slcden = on ? 1 : 0;
+- iowrite8(fer.raw, &scr->fer);
++ fer = ioread8(&scr->fer);
++ if (on)
++ fer |= TC6393XB_FER_SLCDEN;
++ else
++ fer &= ~TC6393XB_FER_SLCDEN;
++ iowrite8(fer, &scr->fer);
+
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+@@ -181,19 +209,19 @@ static int tc6393xb_ohci_disable(struct platform_device *ohci)
+ struct platform_device *dev = to_platform_device(ohci->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_ccr ccr;
+- union tc6393xb_scr_fer fer;
++ u16 ccr;
++ u8 fer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+
+- fer.raw = ioread8(&scr->fer);
+- fer.bits.usben = 0;
+- iowrite8(fer.raw, &scr->fer);
++ fer = ioread8(&scr->fer);
++ fer &= ~TC6393XB_FER_USBEN;
++ iowrite8(fer, &scr->fer);
+
+- ccr.raw = ioread16(&scr->ccr);
+- ccr.bits.usbcken = 0;
+- iowrite16(ccr.raw, &scr->ccr);
++ ccr = ioread16(&scr->ccr);
++ ccr &= ~TC6393XB_CCR_USBCK;
++ iowrite16(ccr, &scr->ccr);
+
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+@@ -205,19 +233,19 @@ static int tc6393xb_ohci_enable(struct platform_device *ohci)
+ struct platform_device *dev = to_platform_device(ohci->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_ccr ccr;
+- union tc6393xb_scr_fer fer;
++ u16 ccr;
++ u8 fer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+
+- ccr.raw = ioread16(&scr->ccr);
+- ccr.bits.usbcken = 1;
+- iowrite16(ccr.raw, &scr->ccr);
++ ccr = ioread16(&scr->ccr);
++ ccr |= TC6393XB_CCR_USBCK;
++ iowrite16(ccr, &scr->ccr);
+
+- fer.raw = ioread8(&scr->fer);
+- fer.bits.usben = 1;
+- iowrite8(fer.raw, &scr->fer);
++ fer = ioread8(&scr->fer);
++ fer |= TC6393XB_FER_USBEN;
++ iowrite8(fer, &scr->fer);
+
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+@@ -229,14 +257,14 @@ static int tc6393xb_ohci_suspend(struct platform_device *ohci)
+ struct platform_device *dev = to_platform_device(ohci->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_ccr ccr;
++ u16 ccr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+
+- ccr.raw = ioread16(&scr->ccr);
+- ccr.bits.usbcken = 0;
+- iowrite16(ccr.raw, &scr->ccr);
++ ccr = ioread16(&scr->ccr);
++ ccr &= ~TC6393XB_CCR_USBCK;
++ iowrite16(ccr, &scr->ccr);
+
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+@@ -248,14 +276,14 @@ static int tc6393xb_ohci_resume(struct platform_device *ohci)
+ struct platform_device *dev = to_platform_device(ohci->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_ccr ccr;
++ u16 ccr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+
+- ccr.raw = ioread16(&scr->ccr);
+- ccr.bits.usbcken = 1;
+- iowrite16(ccr.raw, &scr->ccr);
++ ccr = ioread16(&scr->ccr);
++ ccr |= TC6393XB_CCR_USBCK;
++ iowrite16(ccr, &scr->ccr);
+
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+@@ -267,8 +295,8 @@ static int tc6393xb_fb_disable(struct platform_device *fb)
+ struct platform_device *dev = to_platform_device(fb->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_ccr ccr;
+- union tc6393xb_scr_fer fer;
++ u16 ccr;
++ u8 fer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+@@ -276,14 +304,13 @@ static int tc6393xb_fb_disable(struct platform_device *fb)
+ /*
+ * FIXME: is this correct or it should be moved to other _disable?
+ */
+- fer.raw = ioread8(&scr->fer);
+- fer.bits.slcden = 0;
+-/* fer.bits.lcdcven = 0; */
+- iowrite8(fer.raw, &scr->fer);
++ fer = ioread8(&scr->fer);
++ fer &= ~TC6393XB_FER_SLCDEN;
++ iowrite8(fer, &scr->fer);
+
+- ccr.raw = ioread16(&scr->ccr);
+- ccr.bits.mclksel = disable;
+- iowrite16(ccr.raw, &scr->ccr);
++ ccr = ioread16(&scr->ccr);
++ ccr = (ccr & ~TC6393XB_CCR_MCLK_MASK) | TC6393XB_CCR_MCLK_OFF;
++ iowrite16(ccr, &scr->ccr);
+
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+@@ -295,14 +322,14 @@ static int tc6393xb_fb_enable(struct platform_device *fb)
+ struct platform_device *dev = to_platform_device(fb->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_ccr ccr;
++ u16 ccr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+
+- ccr.raw = ioread16(&scr->ccr);
+- ccr.bits.mclksel = m48MHz;
+- iowrite16(ccr.raw, &scr->ccr);
++ ccr = ioread16(&scr->ccr);
++ ccr = (ccr & ~TC6393XB_CCR_MCLK_MASK) | TC6393XB_CCR_MCLK_48;
++ iowrite16(ccr, &scr->ccr);
+
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+@@ -314,14 +341,14 @@ static int tc6393xb_fb_suspend(struct platform_device *fb)
+ struct platform_device *dev = to_platform_device(fb->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_ccr ccr;
++ u16 ccr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+
+- ccr.raw = ioread16(&scr->ccr);
+- ccr.bits.mclksel = disable;
+- iowrite16(ccr.raw, &scr->ccr);
++ ccr = ioread16(&scr->ccr);
++ ccr = (ccr & ~TC6393XB_CCR_MCLK_MASK) | TC6393XB_CCR_MCLK_OFF;
++ iowrite16(ccr, &scr->ccr);
+
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+@@ -333,14 +360,14 @@ static int tc6393xb_fb_resume(struct platform_device *fb)
+ struct platform_device *dev = to_platform_device(fb->dev.parent);
+ struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+- union tc6393xb_scr_ccr ccr;
++ u16 ccr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tc6393xb->lock, flags);
+
+- ccr.raw = ioread16(&scr->ccr);
+- ccr.bits.mclksel = m48MHz;
+- iowrite16(ccr.raw, &scr->ccr);
++ ccr = ioread16(&scr->ccr);
++ ccr = (ccr & ~TC6393XB_CCR_MCLK_MASK) | TC6393XB_CCR_MCLK_48;
++ iowrite16(ccr, &scr->ccr);
+
+ spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+@@ -592,14 +619,15 @@ static int tc6393xb_hw_init(struct platform_device *dev, int resume)
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+ int i;
+
+- iowrite8(resume ?
+- tc6393xb->suspend_state.fer.raw :
+- 0, &scr->fer);
++ iowrite8(resume ? tc6393xb->suspend_state.fer : 0,
++ &scr->fer);
+ iowrite16(tcpd->scr_pll2cr, &scr->pll2cr);
+ iowrite16(resume?
+- tc6393xb->suspend_state.ccr.raw :
+- tcpd->scr_ccr.raw, &scr->ccr);
+- iowrite16(tcpd->scr_mcr.raw, &scr->mcr);
++ tc6393xb->suspend_state.ccr :
++ tcpd->scr_ccr, &scr->ccr);
++ iowrite16(TC6393XB_MCR_RDY_OPENDRAIN | TC6393XB_MCR_RDY_UNK | TC6393XB_MCR_RDY_EN |
++ TC6393XB_MCR_INT_OPENDRAIN | TC6393XB_MCR_INT_UNK | TC6393XB_MCR_INT_EN |
++ BIT(15), &scr->mcr);
+ iowrite16(tcpd->scr_gper, &scr->gper);
+ iowrite8(0, &scr->irr);
+ iowrite8(0xbf, &scr->imr);
+@@ -730,8 +758,8 @@ static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state)
+ int i;
+
+
+- tc6393xb->suspend_state.ccr.raw = ioread16(&scr->ccr);
+- tc6393xb->suspend_state.fer.raw = ioread8(&scr->fer);
++ tc6393xb->suspend_state.ccr = ioread16(&scr->ccr);
++ tc6393xb->suspend_state.fer = ioread8(&scr->fer);
+ for (i = 0; i < 4; i++)
+ tc6393xb->suspend_state.gpi_bcr[i] =
+ ioread8(scr->gpi_bcr + i);
+diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h
+index e699294..2c69f63 100644
+--- a/include/linux/mfd/tc6393xb.h
++++ b/include/linux/mfd/tc6393xb.h
+@@ -20,60 +20,21 @@
+ #include <linux/mfd-core.h>
+ #include <linux/mfd/tmio.h>
+
+-union tc6393xb_scr_fer {
+- u8 raw;
+-struct {
+- unsigned usben:1; /* D0 USB enable */
+- unsigned lcdcven:1; /* D1 polysylicon TFT enable */
+- unsigned slcden:1; /* D2 SLCD enable */
+-} __attribute__ ((packed)) bits;
+-} __attribute__ ((packed));
+-
+-union tc6393xb_scr_ccr {
+- u16 raw;
+-struct {
+- unsigned ck32ken:1; /* D0 SD host clock enable */
+- unsigned usbcken:1; /* D1 USB host clock enable */
+- unsigned x00:2;
+- unsigned sharp:1; /* D4 ??? set in Sharp's code */
+- unsigned x01:3;
+- enum { disable = 0,
+- m12MHz = 1,
+- m24MHz = 2,
+- m48MHz = 3,
+- } mclksel:3; /* D10-D8 LCD controller clock */
+- unsigned x02:1;
+- enum { h24MHz = 0,
+- h48MHz = 1,
+- } hclksel:2; /* D13-D12 host bus clock */
+- unsigned x03:2;
+-} __attribute__ ((packed)) bits;
+-} __attribute__ ((packed));
+-
+-enum pincontrol {
+- opendrain = 0,
+- tristate = 1,
+- pushpull = 2,
+- /* reserved = 3, */
+-};
+-
+-union tc6393xb_scr_mcr {
+- u16 raw;
+-struct {
+- enum pincontrol rdyst:2; /* D1-D0 HRDY control */
+- unsigned x00:1;
+- unsigned aren:1; /* D3 HRDY pull up resistance cut off */
+- enum pincontrol intst:2; /* D5-D4 #HINT control */
+- unsigned x01:1;
+- unsigned aien:1; /* D7 #HINT pull up resitance cut off */
+- unsigned x02:8;
+-} __attribute__ ((packed)) bits;
+-} __attribute__ ((packed));
++#define TC6393XB_CCR_CK32K BIT(0)
++#define TC6393XB_CCR_USBCK BIT(1)
++#define TC6393XB_CCR_UNK1 BIT(4)
++#define TC6393XB_CCR_MCLK_MASK (7 << 8)
++#define TC6393XB_CCR_MCLK_OFF (0 << 8)
++#define TC6393XB_CCR_MCLK_12 (1 << 8)
++#define TC6393XB_CCR_MCLK_24 (2 << 8)
++#define TC6393XB_CCR_MCLK_48 (3 << 8)
++#define TC6393XB_CCR_HCLK_MASK (3 << 12)
++#define TC6393XB_CCR_HCLK_24 (0 << 12)
++#define TC6393XB_CCR_HCLK_48 (1 << 12)
+
+ struct tc6393xb_platform_data {
+ u16 scr_pll2cr; /* PLL2 Control */
+- union tc6393xb_scr_ccr scr_ccr; /* Clock Control */
+- union tc6393xb_scr_mcr scr_mcr; /* Mode Control */
++ u16 scr_ccr; /* Clock Control */
+ u16 scr_gper; /* GP Enable */
+ u32 scr_gpo_doecr; /* GPO Data OE Control */
+ u32 scr_gpo_dsr; /* GPO Data Set */
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0048-tc6393xb-GPIO-support.patch b/recipes/linux/linux-rp-2.6.24/tosa/0048-tc6393xb-GPIO-support.patch
new file mode 100644
index 0000000000..ef47d6cc21
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0048-tc6393xb-GPIO-support.patch
@@ -0,0 +1,225 @@
+From 4fb4d83c7090ea21619bb652f2ea9b5c8c0c453e Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 01:42:58 +0300
+Subject: [PATCH 48/64] tc6393xb GPIO support
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/mfd/tc6393xb.c | 124 ++++++++++++++++++++++++++++++++++++++++--
+ include/linux/mfd/tc6393xb.h | 2 +-
+ 2 files changed, 119 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
+index 1a394e4..9001687 100644
+--- a/drivers/mfd/tc6393xb.c
++++ b/drivers/mfd/tc6393xb.c
+@@ -49,6 +49,8 @@ enum pincontrol {
+ #define TC6393XB_MCR_INT_EN BIT(7)
+ /* bits 8 - 16 are unknown */
+
++#include <asm/gpio.h>
++
+ struct tc6393xb_scr {
+ u8 x00[8];
+ u8 revid; /* 0x08 Revision ID */
+@@ -96,6 +98,8 @@ struct tc6393xb_scr {
+ struct tc6393xb {
+ struct tc6393xb_scr __iomem *scr;
+
++ struct gpio_chip gpio;
++
+ spinlock_t lock; /* protects RMW cycles */
+
+ struct {
+@@ -513,6 +517,96 @@ static struct mfd_cell tc6393xb_cells[] = {
+
+ /*--------------------------------------------------------------------------*/
+
++static int tc6393xb_gpio_get(struct gpio_chip *chip,
++ unsigned offset)
++{
++ struct tc6393xb *tc6393xb = container_of(chip,
++ struct tc6393xb, gpio);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ u32 mask = 1 << offset;
++
++ return tmio_ioread32(scr->gpo_dsr) & mask;
++}
++
++static void __tc6393xb_gpio_set(struct gpio_chip *chip,
++ unsigned offset, int value)
++{
++ struct tc6393xb *tc6393xb = container_of(chip,
++ struct tc6393xb, gpio);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ u32 dsr;
++
++ dsr = tmio_ioread32(scr->gpo_dsr);
++ if (value)
++ dsr |= (1L << offset);
++ else
++ dsr &= ~(1L << offset);
++
++ tmio_iowrite32(dsr, scr->gpo_dsr);
++}
++
++static void tc6393xb_gpio_set(struct gpio_chip *chip,
++ unsigned offset, int value)
++{
++ struct tc6393xb *tc6393xb = container_of(chip,
++ struct tc6393xb, gpio);
++ unsigned long flags;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ __tc6393xb_gpio_set(chip, offset, value);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++}
++
++static int tc6393xb_gpio_direction_input(struct gpio_chip *chip,
++ unsigned offset)
++{
++ struct tc6393xb *tc6393xb = container_of(chip,
++ struct tc6393xb, gpio);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ unsigned long flags;
++ u32 doecr;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ doecr = tmio_ioread32(scr->gpo_doecr);
++
++ doecr &= ~(1 << offset);
++
++ tmio_iowrite32(doecr, scr->gpo_doecr);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++static int tc6393xb_gpio_direction_output(struct gpio_chip *chip,
++ unsigned offset, int value)
++{
++ struct tc6393xb *tc6393xb = container_of(chip,
++ struct tc6393xb, gpio);
++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
++ unsigned long flags;
++ u32 doecr;
++
++ spin_lock_irqsave(&tc6393xb->lock, flags);
++
++ doecr = tmio_ioread32(scr->gpo_doecr);
++
++ doecr |= (1 << offset);
++
++ tmio_iowrite32(doecr, scr->gpo_doecr);
++
++ __tc6393xb_gpio_set(chip, offset, value);
++
++ spin_unlock_irqrestore(&tc6393xb->lock, flags);
++
++ return 0;
++}
++
++/*--------------------------------------------------------------------------*/
++
+ static void
+ tc6393xb_irq(unsigned int irq, struct irq_desc *desc)
+ {
+@@ -631,10 +725,8 @@ static int tc6393xb_hw_init(struct platform_device *dev, int resume)
+ iowrite16(tcpd->scr_gper, &scr->gper);
+ iowrite8(0, &scr->irr);
+ iowrite8(0xbf, &scr->imr);
+- iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr + 0);
+- iowrite16(tcpd->scr_gpo_dsr >> 16, scr->gpo_dsr + 1);
+- iowrite16(tcpd->scr_gpo_doecr, scr->gpo_doecr + 0);
+- iowrite16(tcpd->scr_gpo_doecr >> 16, scr->gpo_doecr + 1);
++ tmio_iowrite32(tcpd->scr_gpo_dsr, &scr->gpo_dsr);
++ tmio_iowrite32(tcpd->scr_gpo_doecr, &scr->gpo_doecr);
+
+ if (resume)
+ for (i = 0; i < 4; i++)
+@@ -650,7 +742,7 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
+ struct tc6393xb *tc6393xb;
+ struct resource *iomem;
+ struct resource *rscr;
+- int retval;
++ int retval, temp;
+
+ iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!iomem)
+@@ -696,6 +788,18 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
+ ioread8(&tc6393xb->scr->revid),
+ (unsigned long) iomem->start, tc6393xb->irq);
+
++ tc6393xb->gpio.label = "tc6393xb";
++ tc6393xb->gpio.base = tcpd->gpio_base;
++ tc6393xb->gpio.ngpio = 16; /* FIXME: actually 32, but I'm not sure */
++ tc6393xb->gpio.set = tc6393xb_gpio_set;
++ tc6393xb->gpio.get = tc6393xb_gpio_get;
++ tc6393xb->gpio.direction_input = tc6393xb_gpio_direction_input;
++ tc6393xb->gpio.direction_output = tc6393xb_gpio_direction_output;
++
++ retval = gpiochip_add(&tc6393xb->gpio);
++ if (retval)
++ goto err_gpio_add;
++
+ if (tc6393xb->irq)
+ tc6393xb_attach_irq(dev);
+
+@@ -713,6 +817,8 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
+ if (tc6393xb->irq)
+ tc6393xb_detach_irq(dev);
+
++err_gpio_add:
++ temp = gpiochip_remove(&tc6393xb->gpio);
+ err_hw_init:
+ tcpd->disable(dev);
+ err_enable:
+@@ -734,6 +840,12 @@ static int __devexit tc6393xb_remove(struct platform_device *dev) {
+ if (tc6393xb->irq)
+ tc6393xb_detach_irq(dev);
+
++ ret = gpiochip_remove(&tc6393xb->gpio);
++ if (ret) {
++ dev_err(&dev->dev, "Can't remove gpio chip: %d\n", ret);
++ return ret;
++ }
++
+ ret = tcpd->disable(dev);
+
+ iounmap(tc6393xb->scr);
+@@ -804,7 +916,7 @@ static void __exit tc6393xb_exit(void)
+ platform_driver_unregister(&tc6393xb_driver);
+ }
+
+-module_init(tc6393xb_init);
++subsys_initcall(tc6393xb_init);
+ module_exit(tc6393xb_exit);
+
+ MODULE_LICENSE("GPL");
+diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h
+index 2c69f63..97c4c7c 100644
+--- a/include/linux/mfd/tc6393xb.h
++++ b/include/linux/mfd/tc6393xb.h
+@@ -45,6 +45,7 @@ struct tc6393xb_platform_data {
+ int (*resume)(struct platform_device *dev);
+
+ int irq_base; /* a base for cascaded irq */
++ int gpio_base;
+
+ struct tmio_nand_data *nand_data;
+ struct tmio_fb_data *fb_data;
+@@ -54,7 +55,6 @@ extern int tc6393xb_lcd_set_power(struct platform_device *fb_dev, bool on);
+ extern int tc6393xb_lcd_mode(struct platform_device *fb_dev,
+ struct fb_videomode *mode);
+
+-
+ /*
+ * Relative to irq_base
+ */
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0049-platform-support-for-TMIO-on-tosa.patch b/recipes/linux/linux-rp-2.6.24/tosa/0049-platform-support-for-TMIO-on-tosa.patch
new file mode 100644
index 0000000000..ff1186cb71
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0049-platform-support-for-TMIO-on-tosa.patch
@@ -0,0 +1,373 @@
+From 30588bdd5c5cdd9fbe269643f582862a76f09efb Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Tue, 12 Feb 2008 04:52:48 +0300
+Subject: [PATCH 49/64] platform support for TMIO on tosa
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/tosa.c | 179 ++++++++++++++++++++++++++++++++++++++-
+ include/asm-arm/arch-pxa/irqs.h | 1 +
+ include/asm-arm/arch-pxa/tosa.h | 45 ++++++++--
+ sound/soc/pxa/tosa.c | 3 +-
+ 4 files changed, 216 insertions(+), 12 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index 5268e94..e2eec0f 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -18,7 +18,13 @@
+ #include <linux/major.h>
+ #include <linux/fs.h>
+ #include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/fb.h>
+ #include <linux/mmc/host.h>
++#include <linux/mfd/tc6393xb.h>
++#include <linux/mfd/tmio.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
+ #include <linux/pm.h>
+ #include <linux/delay.h>
+ #include <linux/gpio_keys.h>
+@@ -298,12 +304,183 @@ static struct platform_device tosaled_device = {
+ .id = -1,
+ };
+
++/*
++ * Toshiba Mobile IO Controller
++ */
++static struct resource tc6393xb_resources[] = {
++ [0] = {
++ .start = TOSA_LCDC_PHYS,
++ .end = TOSA_LCDC_PHYS + 0x3ffffff,
++ .flags = IORESOURCE_MEM,
++ },
++
++ [1] = {
++ .start = TOSA_IRQ_GPIO_TC6393XB_INT,
++ .end = TOSA_IRQ_GPIO_TC6393XB_INT,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++
++static int tosa_tc6393xb_enable(struct platform_device *dev)
++{
++
++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_L3V_ON);
++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_SUSPEND);
++ reset_scoop_gpio(&tosascoop_device.dev, TOSA_SCOOP_TC6393XB_REST_IN); /* #PCLR */
++ pxa_gpio_mode(GPIO11_3_6MHz_MD);
++ pxa_gpio_mode(GPIO18_RDY_MD);
++ mdelay(1);
++ set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_SUSPEND);
++ mdelay(10);
++ set_scoop_gpio(&tosascoop_device.dev, TOSA_SCOOP_TC6393XB_REST_IN); /* #PCLR */
++ set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_L3V_ON);
++
++ return 0;
++}
++
++static int tosa_tc6393xb_disable(struct platform_device *dev)
++{
++
++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_L3V_ON);
++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_SUSPEND);
++ reset_scoop_gpio(&tosascoop_device.dev, TOSA_SCOOP_TC6393XB_REST_IN); /* #PCLR */
++ pxa_gpio_mode(GPIO11_3_6MHz_MD|GPIO_OUT);
++ GPSR0 = GPIO_bit(GPIO11_3_6MHz);
++
++ return 0;
++}
++
++static int tosa_tc6393xb_resume(struct platform_device *dev)
++{
++
++ pxa_gpio_mode(GPIO11_3_6MHz_MD);
++ pxa_gpio_mode(GPIO18_RDY_MD);
++ mdelay(1);
++ set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_SUSPEND);
++ mdelay(10);
++ set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_L3V_ON);
++ mdelay(10);
++
++ return 0;
++}
++
++static int tosa_tc6393xb_suspend(struct platform_device *dev)
++{
++
++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_L3V_ON);
++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_SUSPEND);
++ pxa_gpio_mode(GPIO11_3_6MHz_MD|GPIO_OUT);
++ GPSR0 = GPIO_bit(GPIO11_3_6MHz);
++
++ return 0;
++}
++
++static struct mtd_partition tosa_nand_partition[] = {
++ {
++ .name = "smf",
++ .offset = 0,
++ .size = 7 * 1024 * 1024,
++ },
++ {
++ .name = "root",
++ .offset = MTDPART_OFS_APPEND,
++ .size = 28 * 1024 * 1024,
++ },
++ {
++ .name = "home",
++ .offset = MTDPART_OFS_APPEND,
++ .size = MTDPART_SIZ_FULL,
++ },
++};
++
++static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
++
++static struct nand_bbt_descr tosa_tc6393xb_nand_bbt = {
++ .options = 0,
++ .offs = 4,
++ .len = 2,
++ .pattern = scan_ff_pattern
++};
++
++static struct tmio_nand_data tosa_tc6393xb_nand_config = {
++ .num_partitions = ARRAY_SIZE(tosa_nand_partition),
++ .partition = tosa_nand_partition,
++ .badblock_pattern = &tosa_tc6393xb_nand_bbt,
++};
++
++static struct fb_videomode tosa_tc6393xb_lcd_mode[] = {
++ {
++ .xres = 480,
++ .yres = 640,
++ .pixclock = 0x002cdf00,/* PLL divisor */
++ .left_margin = 0x004c,
++ .right_margin = 0x005b,
++ .upper_margin = 0x0001,
++ .lower_margin = 0x000d,
++ .hsync_len = 0x0002,
++ .vsync_len = 0x0001,
++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++ .vmode = FB_VMODE_NONINTERLACED,
++ },{
++ .xres = 240,
++ .yres = 320,
++ .pixclock = 0x00e7f203,/* PLL divisor */
++ .left_margin = 0x0024,
++ .right_margin = 0x002f,
++ .upper_margin = 0x0001,
++ .lower_margin = 0x000d,
++ .hsync_len = 0x0002,
++ .vsync_len = 0x0001,
++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++ .vmode = FB_VMODE_NONINTERLACED,
++ }
++};
++
++static struct tmio_fb_data tosa_tc6393xb_fb_config = {
++ .lcd_set_power = tc6393xb_lcd_set_power,
++ .lcd_mode = tc6393xb_lcd_mode,
++ .num_modes = ARRAY_SIZE(tosa_tc6393xb_lcd_mode),
++ .modes = &tosa_tc6393xb_lcd_mode[0],
++};
++
++static struct tc6393xb_platform_data tosa_tc6393xb_setup = {
++ .scr_pll2cr = 0x0cc1,
++ .scr_ccr = TC6393XB_CCR_UNK1 | TC6393XB_CCR_HCLK_48,
++ .scr_gper = 0x3300,
++ .scr_gpo_dsr = TOSA_TC6393XB_CARD_VCC_ON | TOSA_TC6393XB_CHARGE_OFF_JC,
++ .scr_gpo_doecr = TOSA_TC6393XB_GPO_OE,
++
++ .irq_base = IRQ_BOARD_START,
++
++ .enable = tosa_tc6393xb_enable,
++ .disable = tosa_tc6393xb_disable,
++ .suspend = tosa_tc6393xb_suspend,
++ .resume = tosa_tc6393xb_resume,
++
++ .nand_data = &tosa_tc6393xb_nand_config,
++ .fb_data = &tosa_tc6393xb_fb_config,
++};
++
++
++struct platform_device tc6393xb_device = {
++ .name = "tc6393xb",
++ .id = -1,
++ .dev = {
++ .platform_data = &tosa_tc6393xb_setup,
++ },
++ .num_resources = ARRAY_SIZE(tc6393xb_resources),
++ .resource = tc6393xb_resources,
++};
++EXPORT_SYMBOL(tc6393xb_device);
++
+ static struct platform_device *devices[] __initdata = {
+ &tosascoop_device,
+ &tosascoop_jc_device,
+ &tosakbd_device,
+ &tosa_gpio_keys_device,
+ &tosaled_device,
++ &tc6393xb_device,
+ };
+
+ static void tosa_poweroff(void)
+@@ -332,7 +509,7 @@ static void __init tosa_init(void)
+ arm_pm_restart = tosa_restart;
+
+ pxa_gpio_mode(TOSA_GPIO_ON_RESET | GPIO_IN);
+- pxa_gpio_mode(TOSA_GPIO_TC6393_INT | GPIO_IN);
++ pxa_gpio_mode(TOSA_GPIO_TC6393XB_INT | GPIO_IN);
+ pxa_gpio_mode(TOSA_GPIO_USB_IN | GPIO_IN);
+
+ /* setup sleep mode values */
+diff --git a/include/asm-arm/arch-pxa/irqs.h b/include/asm-arm/arch-pxa/irqs.h
+index b76ee6d..bf622d8 100644
+--- a/include/asm-arm/arch-pxa/irqs.h
++++ b/include/asm-arm/arch-pxa/irqs.h
+@@ -180,6 +180,7 @@
+ #define NR_IRQS (IRQ_LOCOMO_SPI_TEND + 1)
+ #elif defined(CONFIG_ARCH_LUBBOCK) || \
+ defined(CONFIG_MACH_LOGICPD_PXA270) || \
++ defined(CONFIG_MACH_TOSA) || \
+ defined(CONFIG_MACH_MAINSTONE)
+ #define NR_IRQS (IRQ_BOARD_END)
+ #else
+diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h
+index c05e4fa..1b202b2 100644
+--- a/include/asm-arm/arch-pxa/tosa.h
++++ b/include/asm-arm/arch-pxa/tosa.h
+@@ -20,11 +20,35 @@
+ /* Jacket Scoop */
+ #define TOSA_SCOOP_PHYS (PXA_CS5_PHYS + 0x00800000)
+
++#define TC6393XB_GPIO(i) (1 << (i))
++/*
++ * TC6393 GPIOs
++ */
++#define TOSA_TC6393XB_TG_ON TC6393XB_GPIO(0)
++#define TOSA_TC6393XB_L_MUTE TC6393XB_GPIO(1)
++#define TOSA_TC6393XB_BL_C20MA TC6393XB_GPIO(3)
++#define TOSA_TC6393XB_CARD_VCC_ON TC6393XB_GPIO(4)
++#define TOSA_TC6393XB_CHARGE_OFF TC6393XB_GPIO(6)
++#define TOSA_TC6393XB_CHARGE_OFF_JC TC6393XB_GPIO(7)
++#define TOSA_TC6393XB_BAT0_V_ON TC6393XB_GPIO(9)
++#define TOSA_TC6393XB_BAT1_V_ON TC6393XB_GPIO(10)
++#define TOSA_TC6393XB_BU_CHRG_ON TC6393XB_GPIO(11)
++#define TOSA_TC6393XB_BAT_SW_ON TC6393XB_GPIO(12)
++#define TOSA_TC6393XB_BAT0_TH_ON TC6393XB_GPIO(14)
++#define TOSA_TC6393XB_BAT1_TH_ON TC6393XB_GPIO(15)
++
++#define TOSA_TC6393XB_GPO_OE (TOSA_TC6393XB_TG_ON | TOSA_TC6393XB_L_MUTE | TOSA_TC6393XB_BL_C20MA | \
++ TOSA_TC6393XB_CARD_VCC_ON | TOSA_TC6393XB_CHARGE_OFF | \
++ TOSA_TC6393XB_CHARGE_OFF_JC | TOSA_TC6393XB_BAT0_V_ON | \
++ TOSA_TC6393XB_BAT1_V_ON | TOSA_TC6393XB_BU_CHRG_ON | \
++ TOSA_TC6393XB_BAT_SW_ON | TOSA_TC6393XB_BAT0_TH_ON | \
++ TOSA_TC6393XB_BAT1_TH_ON)
++
+ /*
+ * SCOOP2 internal GPIOs
+ */
+ #define TOSA_SCOOP_PXA_VCORE1 SCOOP_GPCR_PA11
+-#define TOSA_SCOOP_TC6393_REST_IN SCOOP_GPCR_PA12
++#define TOSA_SCOOP_TC6393XB_REST_IN SCOOP_GPCR_PA12
+ #define TOSA_SCOOP_IR_POWERDWN SCOOP_GPCR_PA13
+ #define TOSA_SCOOP_SD_WP SCOOP_GPCR_PA14
+ #define TOSA_SCOOP_PWR_ON SCOOP_GPCR_PA15
+@@ -34,11 +58,11 @@
+ #define TOSA_SCOOP_AC_IN_OL SCOOP_GPCR_PA19
+
+ /* GPIO Direction 1 : output mode / 0:input mode */
+-#define TOSA_SCOOP_IO_DIR ( TOSA_SCOOP_PXA_VCORE1 | TOSA_SCOOP_TC6393_REST_IN | \
++#define TOSA_SCOOP_IO_DIR ( TOSA_SCOOP_PXA_VCORE1 | TOSA_SCOOP_TC6393XB_REST_IN | \
+ TOSA_SCOOP_IR_POWERDWN | TOSA_SCOOP_PWR_ON | TOSA_SCOOP_AUD_PWR_ON |\
+ TOSA_SCOOP_BT_RESET | TOSA_SCOOP_BT_PWR_EN )
+ /* GPIO out put level when init 1: Hi */
+-#define TOSA_SCOOP_IO_OUT ( TOSA_SCOOP_TC6393_REST_IN )
++#define TOSA_SCOOP_IO_OUT ( TOSA_SCOOP_TC6393XB_REST_IN )
+
+ /*
+ * SCOOP2 jacket GPIOs
+@@ -47,8 +71,8 @@
+ #define TOSA_SCOOP_JC_NOTE_LED SCOOP_GPCR_PA12
+ #define TOSA_SCOOP_JC_CHRG_ERR_LED SCOOP_GPCR_PA13
+ #define TOSA_SCOOP_JC_USB_PULLUP SCOOP_GPCR_PA14
+-#define TOSA_SCOOP_JC_TC6393_SUSPEND SCOOP_GPCR_PA15
+-#define TOSA_SCOOP_JC_TC3693_L3V_ON SCOOP_GPCR_PA16
++#define TOSA_SCOOP_JC_TC6393XB_SUSPEND SCOOP_GPCR_PA15
++#define TOSA_SCOOP_JC_TC6393XB_L3V_ON SCOOP_GPCR_PA16
+ #define TOSA_SCOOP_JC_WLAN_DETECT SCOOP_GPCR_PA17
+ #define TOSA_SCOOP_JC_WLAN_LED SCOOP_GPCR_PA18
+ #define TOSA_SCOOP_JC_CARD_LIMIT_SEL SCOOP_GPCR_PA19
+@@ -56,7 +80,7 @@
+ /* GPIO Direction 1 : output mode / 0:input mode */
+ #define TOSA_SCOOP_JC_IO_DIR ( TOSA_SCOOP_JC_BT_LED | TOSA_SCOOP_JC_NOTE_LED | \
+ TOSA_SCOOP_JC_CHRG_ERR_LED | TOSA_SCOOP_JC_USB_PULLUP | \
+- TOSA_SCOOP_JC_TC6393_SUSPEND | TOSA_SCOOP_JC_TC3693_L3V_ON | \
++ TOSA_SCOOP_JC_TC6393XB_SUSPEND | TOSA_SCOOP_JC_TC6393XB_L3V_ON | \
+ TOSA_SCOOP_JC_WLAN_LED | TOSA_SCOOP_JC_CARD_LIMIT_SEL )
+ /* GPIO out put level when init 1: Hi */
+ #define TOSA_SCOOP_JC_IO_OUT ( 0 )
+@@ -94,13 +118,13 @@
+ #define TOSA_GPIO_JACKET_DETECT (7)
+ #define TOSA_GPIO_nSD_DETECT (9)
+ #define TOSA_GPIO_nSD_INT (10)
+-#define TOSA_GPIO_TC6393_CLK (11)
++#define TOSA_GPIO_TC6393XB_CLK (11)
+ #define TOSA_GPIO_BAT1_CRG (12)
+ #define TOSA_GPIO_CF_CD (13)
+ #define TOSA_GPIO_BAT0_CRG (14)
+-#define TOSA_GPIO_TC6393_INT (15)
++#define TOSA_GPIO_TC6393XB_INT (15)
+ #define TOSA_GPIO_BAT0_LOW (17)
+-#define TOSA_GPIO_TC6393_RDY (18)
++#define TOSA_GPIO_TC6393XB_RDY (18)
+ #define TOSA_GPIO_ON_RESET (19)
+ #define TOSA_GPIO_EAR_IN (20)
+ #define TOSA_GPIO_CF_IRQ (21) /* CF slot0 Ready */
+@@ -147,7 +171,7 @@
+ #define TOSA_IRQ_GPIO_BAT1_CRG IRQ_GPIO(TOSA_GPIO_BAT1_CRG)
+ #define TOSA_IRQ_GPIO_CF_CD IRQ_GPIO(TOSA_GPIO_CF_CD)
+ #define TOSA_IRQ_GPIO_BAT0_CRG IRQ_GPIO(TOSA_GPIO_BAT0_CRG)
+-#define TOSA_IRQ_GPIO_TC6393_INT IRQ_GPIO(TOSA_GPIO_TC6393_INT)
++#define TOSA_IRQ_GPIO_TC6393XB_INT IRQ_GPIO(TOSA_GPIO_TC6393XB_INT)
+ #define TOSA_IRQ_GPIO_BAT0_LOW IRQ_GPIO(TOSA_GPIO_BAT0_LOW)
+ #define TOSA_IRQ_GPIO_EAR_IN IRQ_GPIO(TOSA_GPIO_EAR_IN)
+ #define TOSA_IRQ_GPIO_CF_IRQ IRQ_GPIO(TOSA_GPIO_CF_IRQ)
+@@ -161,6 +185,7 @@
+
+ #define TOSA_IRQ_GPIO_MAIN_BAT_LOW IRQ_GPIO(TOSA_GPIO_MAIN_BAT_LOW)
+
++extern struct platform_device tc6393xb_device;
+ extern struct platform_device tosascoop_jc_device;
+ extern struct platform_device tosascoop_device;
+
+diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
+index 5504e30..21c51b5 100644
+--- a/sound/soc/pxa/tosa.c
++++ b/sound/soc/pxa/tosa.c
+@@ -32,7 +32,6 @@
+ #include <sound/soc-dapm.h>
+
+ #include <asm/mach-types.h>
+-#include <asm/hardware/tmio.h>
+ #include <asm/arch/pxa-regs.h>
+ #include <asm/arch/hardware.h>
+ #include <asm/arch/audio.h>
+@@ -138,10 +137,12 @@ static int tosa_set_spk(struct snd_kcontrol *kcontrol,
+ /* tosa dapm event handlers */
+ static int tosa_hp_event(struct snd_soc_dapm_widget *w, int event)
+ {
++#if 0
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
+ else
+ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
++#endif
+ return 0;
+ }
+
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0050-tosa-update-for-tc6393xb-gpio.patch b/recipes/linux/linux-rp-2.6.24/tosa/0050-tosa-update-for-tc6393xb-gpio.patch
new file mode 100644
index 0000000000..c9b5ac29d4
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0050-tosa-update-for-tc6393xb-gpio.patch
@@ -0,0 +1,99 @@
+From f24c23ba56cdd072b332e8de3e0cff8a31e7e36a Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 02:03:19 +0300
+Subject: [PATCH 50/64] tosa update for tc6393xb gpio
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/tosa.c | 6 +++++-
+ include/asm-arm/arch-pxa/tosa.h | 36 ++++++++++++++++++++++++------------
+ 2 files changed, 29 insertions(+), 13 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index e2eec0f..3e832dc 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -35,6 +35,7 @@
+ #include <asm/mach-types.h>
+ #include <asm/hardware.h>
+ #include <asm/irq.h>
++#include <asm/gpio.h>
+ #include <asm/system.h>
+ #include <asm/arch/pxa-regs.h>
+ #include <asm/arch/irda.h>
+@@ -448,10 +449,13 @@ static struct tc6393xb_platform_data tosa_tc6393xb_setup = {
+ .scr_pll2cr = 0x0cc1,
+ .scr_ccr = TC6393XB_CCR_UNK1 | TC6393XB_CCR_HCLK_48,
+ .scr_gper = 0x3300,
+- .scr_gpo_dsr = TOSA_TC6393XB_CARD_VCC_ON | TOSA_TC6393XB_CHARGE_OFF_JC,
++ .scr_gpo_dsr =
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_CARD_VCC_ON) |
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_CHARGE_OFF_JC),
+ .scr_gpo_doecr = TOSA_TC6393XB_GPO_OE,
+
+ .irq_base = IRQ_BOARD_START,
++ .gpio_base = TOSA_TC6393XB_GPIO_BASE,
+
+ .enable = tosa_tc6393xb_enable,
+ .disable = tosa_tc6393xb_disable,
+diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h
+index 1b202b2..410fa9a 100644
+--- a/include/asm-arm/arch-pxa/tosa.h
++++ b/include/asm-arm/arch-pxa/tosa.h
+@@ -20,16 +20,21 @@
+ /* Jacket Scoop */
+ #define TOSA_SCOOP_PHYS (PXA_CS5_PHYS + 0x00800000)
+
+-#define TC6393XB_GPIO(i) (1 << (i))
+ /*
+ * TC6393 GPIOs
+ */
+-#define TOSA_TC6393XB_TG_ON TC6393XB_GPIO(0)
+-#define TOSA_TC6393XB_L_MUTE TC6393XB_GPIO(1)
+-#define TOSA_TC6393XB_BL_C20MA TC6393XB_GPIO(3)
+-#define TOSA_TC6393XB_CARD_VCC_ON TC6393XB_GPIO(4)
++
++#define TOSA_TC6393XB_GPIO_BASE NR_BUILTIN_GPIO
++
++#define TC6393XB_GPIO(i) (TOSA_TC6393XB_GPIO_BASE + (i))
++#define TC6393XB_GPIO_BIT(gpio) (1 << (gpio - TOSA_TC6393XB_GPIO_BASE))
++
++#define TOSA_TC6393XB_TG_ON TC6393XB_GPIO(0)
++#define TOSA_TC6393XB_L_MUTE TC6393XB_GPIO(1)
++#define TOSA_TC6393XB_BL_C20MA TC6393XB_GPIO(3)
++#define TOSA_TC6393XB_CARD_VCC_ON TC6393XB_GPIO(4)
+ #define TOSA_TC6393XB_CHARGE_OFF TC6393XB_GPIO(6)
+-#define TOSA_TC6393XB_CHARGE_OFF_JC TC6393XB_GPIO(7)
++#define TOSA_TC6393XB_CHARGE_OFF_JC TC6393XB_GPIO(7)
+ #define TOSA_TC6393XB_BAT0_V_ON TC6393XB_GPIO(9)
+ #define TOSA_TC6393XB_BAT1_V_ON TC6393XB_GPIO(10)
+ #define TOSA_TC6393XB_BU_CHRG_ON TC6393XB_GPIO(11)
+@@ -37,12 +42,19 @@
+ #define TOSA_TC6393XB_BAT0_TH_ON TC6393XB_GPIO(14)
+ #define TOSA_TC6393XB_BAT1_TH_ON TC6393XB_GPIO(15)
+
+-#define TOSA_TC6393XB_GPO_OE (TOSA_TC6393XB_TG_ON | TOSA_TC6393XB_L_MUTE | TOSA_TC6393XB_BL_C20MA | \
+- TOSA_TC6393XB_CARD_VCC_ON | TOSA_TC6393XB_CHARGE_OFF | \
+- TOSA_TC6393XB_CHARGE_OFF_JC | TOSA_TC6393XB_BAT0_V_ON | \
+- TOSA_TC6393XB_BAT1_V_ON | TOSA_TC6393XB_BU_CHRG_ON | \
+- TOSA_TC6393XB_BAT_SW_ON | TOSA_TC6393XB_BAT0_TH_ON | \
+- TOSA_TC6393XB_BAT1_TH_ON)
++#define TOSA_TC6393XB_GPO_OE ( \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_TG_ON) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_L_MUTE) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BL_C20MA) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_CARD_VCC_ON) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_CHARGE_OFF) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_CHARGE_OFF_JC) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BAT0_V_ON) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BAT1_V_ON) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BU_CHRG_ON) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BAT_SW_ON) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BAT0_TH_ON) | \
++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BAT1_TH_ON))
+
+ /*
+ * SCOOP2 internal GPIOs
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0051-fix-sound-soc-pxa-tosa.c-to-new-gpio-api.patch b/recipes/linux/linux-rp-2.6.24/tosa/0051-fix-sound-soc-pxa-tosa.c-to-new-gpio-api.patch
new file mode 100644
index 0000000000..585f1af288
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0051-fix-sound-soc-pxa-tosa.c-to-new-gpio-api.patch
@@ -0,0 +1,86 @@
+From 38ef1b452cc3138157b92d02b31cad439d12d0ca Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 02:03:34 +0300
+Subject: [PATCH 51/64] fix sound/soc/pxa/tosa.c to new gpio api
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ sound/soc/pxa/tosa.c | 33 ++++++++++++++++++++++++++-------
+ 1 files changed, 26 insertions(+), 7 deletions(-)
+
+diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
+index 21c51b5..b758de8 100644
+--- a/sound/soc/pxa/tosa.c
++++ b/sound/soc/pxa/tosa.c
+@@ -36,6 +36,7 @@
+ #include <asm/arch/hardware.h>
+ #include <asm/arch/audio.h>
+ #include <asm/arch/tosa.h>
++#include <asm/gpio.h>
+
+ #include "../codecs/wm9712.h"
+ #include "pxa2xx-pcm.h"
+@@ -137,11 +138,11 @@ static int tosa_set_spk(struct snd_kcontrol *kcontrol,
+ /* tosa dapm event handlers */
+ static int tosa_hp_event(struct snd_soc_dapm_widget *w, int event)
+ {
+-#if 0
++#ifdef CONFIG_MFD_TC6393XB
+ if (SND_SOC_DAPM_EVENT_ON(event))
+- set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
++ gpio_set_value(TOSA_TC6393XB_L_MUTE, 1);
+ else
+- reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
++ gpio_set_value(TOSA_TC6393XB_L_MUTE, 0);
+ #endif
+ return 0;
+ }
+@@ -262,16 +263,31 @@ static int __init tosa_init(void)
+ if (!machine_is_tosa())
+ return -ENODEV;
+
++#ifdef CONFIG_MFD_TC6393XB
++ ret = gpio_request(TOSA_TC6393XB_L_MUTE, "Headphone Jack");
++ if (ret)
++ return ret;
++ gpio_direction_output(TOSA_TC6393XB_L_MUTE, 0);
++#endif
+ tosa_snd_device = platform_device_alloc("soc-audio", -1);
+- if (!tosa_snd_device)
+- return -ENOMEM;
++ if (!tosa_snd_device) {
++ ret = -ENOMEM;
++ goto err_alloc;
++ }
+
+ platform_set_drvdata(tosa_snd_device, &tosa_snd_devdata);
+ tosa_snd_devdata.dev = &tosa_snd_device->dev;
+ ret = platform_device_add(tosa_snd_device);
+
+- if (ret)
+- platform_device_put(tosa_snd_device);
++ if (!ret)
++ return 0;
++
++ platform_device_put(tosa_snd_device);
++
++err_alloc:
++#ifdef CONFIG_MFD_TC6393XB
++ gpio_free(TOSA_TC6393XB_L_MUTE);
++#endif
+
+ return ret;
+ }
+@@ -279,6 +295,9 @@ static int __init tosa_init(void)
+ static void __exit tosa_exit(void)
+ {
+ platform_device_unregister(tosa_snd_device);
++#ifdef CONFIG_MFD_TC6393XB
++ gpio_free(TOSA_TC6393XB_L_MUTE);
++#endif
+ }
+
+ module_init(tosa_init);
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0052-tosa-platform-backlight-support.patch b/recipes/linux/linux-rp-2.6.24/tosa/0052-tosa-platform-backlight-support.patch
new file mode 100644
index 0000000000..ef5263c18e
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0052-tosa-platform-backlight-support.patch
@@ -0,0 +1,400 @@
+From c7537657bc33d4ee1616accd0259e160d57c5c1b Mon Sep 17 00:00:00 2001
+From: Ian Molton <spyro@f2s.com>
+Date: Wed, 9 Jan 2008 02:05:40 +0300
+Subject: [PATCH 52/64] tosa platform backlight support
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/video/backlight/Kconfig | 10 +
+ drivers/video/backlight/Makefile | 1 +
+ drivers/video/backlight/tosa_bl.c | 345 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 356 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/video/backlight/tosa_bl.c
+
+diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
+index 9609a6c..f47a601 100644
+--- a/drivers/video/backlight/Kconfig
++++ b/drivers/video/backlight/Kconfig
+@@ -59,6 +59,16 @@ config BACKLIGHT_CORGI
+ known as the Corgi backlight driver. If you have a Sharp Zaurus
+ SL-C7xx, SL-Cxx00 or SL-6000x say y. Most users can say n.
+
++config BACKLIGHT_TOSA
++ tristate "Sharp Tosa LCD/Backlight Driver (SL-6000)"
++ depends on BACKLIGHT_CLASS_DEVICE && MACH_TOSA
++ default y
++ select I2C
++ select I2C_PXA
++ select PXA_SSP
++ help
++ If you have a Sharp Zaurus SL-6000y enable this driver.
++
+ config BACKLIGHT_LOCOMO
+ tristate "Sharp LOCOMO LCD/Backlight Driver"
+ depends on BACKLIGHT_CLASS_DEVICE && SHARP_LOCOMO
+diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
+index 965a78b..e8a6a7c 100644
+--- a/drivers/video/backlight/Makefile
++++ b/drivers/video/backlight/Makefile
+@@ -5,6 +5,7 @@ obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o
+
+ obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
+ obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o
++obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o
+ obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
+ obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
+ obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
+diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c
+new file mode 100644
+index 0000000..11a89c6
+--- /dev/null
++++ b/drivers/video/backlight/tosa_bl.c
+@@ -0,0 +1,345 @@
++/*
++ * LCD / Backlight control code for Sharp SL-6000x (tosa)
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ * Copyright (c) 2007 Dmitry Baryshkov
++ *
++ * 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/kernel.h>
++#include <linux/module.h>
++#include <linux/i2c.h>
++#include <linux/backlight.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/fb.h>
++#include <linux/mfd/tc6393xb.h>
++
++#include <asm/hardware/scoop.h>
++#include <asm/mach/sharpsl_param.h>
++#include <asm/arch/ssp.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/tosa.h>
++#include <asm/gpio.h>
++
++#define DAC_BASE 0x4e
++#define DAC_CH1 0
++#define DAC_CH2 1
++
++#define TG_REG0_VQV 0x0001
++#define TG_REG0_COLOR 0x0002
++#define TG_REG0_UD 0x0004
++#define TG_REG0_LR 0x0008
++#define COMADJ_DEFAULT 97
++
++static unsigned short normal_i2c[] = { DAC_BASE, I2C_CLIENT_END };
++I2C_CLIENT_INSMOD;
++
++struct tosa_bl_data {
++ struct i2c_client client;
++
++ int comadj;
++ spinlock_t nssp_lock;
++ struct ssp_dev nssp_dev;
++ struct ssp_state nssp_state;
++
++ struct backlight_device *bl_dev;
++};
++
++static struct i2c_driver tosa_bl_driver;
++
++static void pxa_nssp_output(struct tosa_bl_data *data, unsigned char reg, unsigned char value)
++{
++ unsigned long flag;
++ u32 dummy;
++ u32 dat = ( ((reg << 5) & 0xe0) | (value & 0x1f) );
++ spin_lock_irqsave(&data->nssp_lock, flag);
++
++ ssp_config(&data->nssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(128));
++ ssp_enable(&data->nssp_dev);
++
++ ssp_write_word(&data->nssp_dev,dat);
++
++ /* Read null data back from device to prevent SSP overflow */
++ ssp_read_word(&data->nssp_dev, &dummy);
++ ssp_disable(&data->nssp_dev);
++ spin_unlock_irqrestore(&data->nssp_lock, flag);
++
++}
++
++static void tosa_set_backlight(struct tosa_bl_data *data, int brightness)
++{
++ /* SetBacklightDuty */
++ i2c_smbus_write_byte_data(&data->client, DAC_CH2, (unsigned char)brightness);
++
++ /* SetBacklightVR */
++ if (brightness)
++ gpio_set_value(TOSA_TC6393XB_BL_C20MA, 1);
++ else
++ gpio_set_value(TOSA_TC6393XB_BL_C20MA, 0);
++
++ /* bl_enable GP04=1 otherwise GP04=0*/
++ pxa_nssp_output(data, TG_GPODR2, brightness ? 0x01 : 0x00);
++}
++
++static void tosa_lcd_tg_init(struct tosa_bl_data *data)
++{
++ dev_dbg(&data->bl_dev->dev, "tosa_lcd_init\n");
++
++ /* L3V On */
++ set_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393XB_L3V_ON);
++ mdelay(60);
++
++ /* TG On */
++ gpio_set_value(TOSA_TC6393XB_TG_ON, 0);
++ mdelay(60);
++
++ pxa_nssp_output(data, TG_TPOSCTL,0x00); /* delayed 0clk TCTL signal for VGA */
++ pxa_nssp_output(data, TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
++}
++
++static void tosa_lcd_tg_on(struct tosa_bl_data *data/*, const struct fb_videomode *mode*/)
++{
++ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
++
++ tosa_lcd_tg_init(data);
++
++ dev_dbg(&data->bl_dev->dev, "tosa_lcd_on\n");
++ pxa_nssp_output(data, TG_PNLCTL, value | (/*mode->yres == 320 ? 0 : */ TG_REG0_VQV));
++
++ /* TG LCD pannel power up */
++ pxa_nssp_output(data, TG_PINICTL,0x4);
++ mdelay(50);
++
++ /* TG LCD GVSS */
++ pxa_nssp_output(data, TG_PINICTL,0x0);
++ mdelay(50);
++
++ /* set common voltage */
++ i2c_smbus_write_byte_data(&data->client, DAC_CH1, data->comadj);
++}
++
++static void tosa_lcd_tg_off(struct tosa_bl_data *data)
++{
++ tosa_set_backlight(data, 0);
++ dev_dbg(&data->bl_dev->dev, "tosa_lcd_off\n");
++ /* TG LCD VHSA off */
++ pxa_nssp_output(data, TG_PINICTL,0x4);
++ mdelay(50);
++
++ /* TG LCD signal off */
++ pxa_nssp_output(data, TG_PINICTL,0x6);
++ mdelay(50);
++
++ /* TG Off */
++ gpio_set_value(TOSA_TC6393XB_TG_ON, 1);
++ mdelay(100);
++
++ /* L3V Off */
++ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393XB_L3V_ON);
++}
++
++
++static int tosa_bl_update_status(struct backlight_device *dev)
++{
++ struct backlight_properties *props = &dev->props;
++ struct tosa_bl_data *data = dev_get_drvdata(&dev->dev);
++ int new_power = max(props->power, props->fb_blank);
++
++ tosa_set_backlight(data, props->brightness);
++
++ if (new_power)
++ tosa_lcd_tg_off(data);
++ else
++ tosa_lcd_tg_on(data);
++
++ return 0;
++}
++
++static int tosa_bl_get_brightness(struct backlight_device *dev)
++{
++ struct backlight_properties *props = &dev->props;
++
++ return props->brightness;
++}
++
++static struct backlight_ops tosa_bl_ops = {
++ .get_brightness = tosa_bl_get_brightness,
++ .update_status = tosa_bl_update_status,
++};
++
++static int tosa_bl_detect_client(struct i2c_adapter *adapter, int address,
++ int kind)
++{
++ int err = 0;
++ struct i2c_client *client;
++ struct tosa_bl_data *data;
++
++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA ))
++ goto out;
++
++ if (!(data = kzalloc(sizeof(struct tosa_bl_data), GFP_KERNEL))) {
++ err = -ENOMEM;
++ goto out;
++ }
++
++ client = &data->client;
++ i2c_set_clientdata(client, data);
++
++ client->addr = address;
++ client->adapter = adapter;
++ client->driver = &tosa_bl_driver;
++
++ strlcpy(client->name, "tosa_bl", I2C_NAME_SIZE);
++
++ spin_lock_init(&data->nssp_lock);
++ data->comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj;
++
++ err = gpio_request(TOSA_TC6393XB_BL_C20MA, "backlight");
++ if (err) {
++ dev_dbg(&data->bl_dev->dev, "Unable to request gpio!\n");
++ goto err_gpio_bl;
++ }
++
++ err = gpio_request(TOSA_TC6393XB_TG_ON, "tg");
++ if (err) {
++ dev_dbg(&data->bl_dev->dev, "Unable to request gpio!\n");
++ goto err_gpio_tg;
++ }
++
++ err = ssp_init(&data->nssp_dev,2,0);
++ if (err) {
++ dev_err(&data->bl_dev->dev, "Unable to register NSSP handler!\n");
++ goto err_ssp_init;
++ }
++
++ /* Tell the i2c layer a new client has arrived */
++ err = i2c_attach_client(client);
++ if (err)
++ goto err_i2c_attach;
++
++ gpio_direction_output(TOSA_TC6393XB_BL_C20MA, 0);
++ gpio_direction_output(TOSA_TC6393XB_TG_ON, 1);
++
++ tosa_lcd_tg_init(data);
++
++ data->bl_dev = backlight_device_register("tosa_bl",
++ &client->dev, data, &tosa_bl_ops);
++ if (err)
++ goto err_bl_register;
++
++ data->bl_dev->props.brightness = 69;
++ data->bl_dev->props.max_brightness = 255;
++ data->bl_dev->props.power = FB_BLANK_UNBLANK;
++ backlight_update_status(data->bl_dev);
++
++
++ return 0;
++
++err_bl_register:
++ tosa_set_backlight(data, 0);
++ tosa_lcd_tg_off(data);
++
++ err = i2c_detach_client(client);
++ if (err)
++ return err;
++err_i2c_attach:
++ ssp_exit(&data->nssp_dev);
++err_ssp_init:
++ gpio_free(TOSA_TC6393XB_TG_ON);
++err_gpio_tg:
++ gpio_free(TOSA_TC6393XB_BL_C20MA);
++err_gpio_bl:
++ kfree(data);
++out:
++ return err;
++}
++
++static int tosa_bl_detach_client(struct i2c_client *client)
++{
++ int err = 0;
++ struct tosa_bl_data *data = i2c_get_clientdata(client);
++
++ backlight_device_unregister(data->bl_dev);
++
++ tosa_set_backlight(data, 0);
++ tosa_lcd_tg_off(data);
++
++ /* Try to detach the client from i2c space */
++ if ((err = i2c_detach_client(client)))
++ return err;
++
++ ssp_exit(&data->nssp_dev);
++
++ gpio_free(TOSA_TC6393XB_TG_ON);
++ gpio_free(TOSA_TC6393XB_BL_C20MA);
++
++ kfree(data);
++
++ return err;
++}
++
++#ifdef CONFIG_PM
++static int tosa_bl_suspend(struct i2c_client *client, pm_message_t mesg)
++{
++ struct tosa_bl_data *data = i2c_get_clientdata(client);
++
++ tosa_lcd_tg_off(data);
++ ssp_flush(&data->nssp_dev);
++ ssp_save_state(&data->nssp_dev,&data->nssp_state);
++
++ return 0;
++}
++
++static int tosa_bl_resume(struct i2c_client *client)
++{
++ struct tosa_bl_data *data = i2c_get_clientdata(client);
++
++ ssp_restore_state(&data->nssp_dev,&data->nssp_state);
++ ssp_enable(&data->nssp_dev);
++ tosa_bl_update_status(data->bl_dev);
++
++ return 0;
++}
++#else
++#define tosa_bl_suspend NULL
++#define tosa_bl_resume NULL
++#endif
++
++static int tosa_bl_attach_adapter(struct i2c_adapter *adapter)
++{
++ return i2c_probe(adapter, &addr_data, &tosa_bl_detect_client);
++}
++
++static struct i2c_driver tosa_bl_driver = {
++ .driver = {
++ .name = "tosa_bl",
++ },
++
++ .attach_adapter = tosa_bl_attach_adapter,
++ .detach_client = tosa_bl_detach_client,
++
++ .suspend = tosa_bl_suspend,
++ .resume = tosa_bl_resume,
++};
++
++static int __init tosa_bl_init(void)
++{
++ return i2c_add_driver(&tosa_bl_driver);
++}
++
++static void __exit tosa_bl_cleanup (void)
++{
++ i2c_del_driver(&tosa_bl_driver);
++}
++
++module_init(tosa_bl_init);
++module_exit(tosa_bl_cleanup);
++
++MODULE_DESCRIPTION("Tosa LCD device");
++MODULE_AUTHOR("Dirk Opfer, Dmitry Baryshkov");
++MODULE_LICENSE("GPL v2");
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0053-sound-soc-codecs-wm9712.c-28.patch b/recipes/linux/linux-rp-2.6.24/tosa/0053-sound-soc-codecs-wm9712.c-28.patch
new file mode 100644
index 0000000000..0675342508
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0053-sound-soc-codecs-wm9712.c-28.patch
@@ -0,0 +1,56 @@
+From 47616d22f8f303dfd66cf3b9125af212194a0f3c Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 9 Jan 2008 02:08:17 +0300
+Subject: [PATCH 53/64] sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++----------
+ 1 file changed, 18 insertions(+), 10 deletions(-)
+
+Index: git/sound/soc/codecs/wm9712.c
+===================================================================
+---
+ sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++----------
+ 1 files changed, 18 insertions(+), 10 deletions(-)
+
+diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
+index 986b5d5..dfb31e1 100644
+--- a/sound/soc/codecs/wm9712.c
++++ b/sound/soc/codecs/wm9712.c
+@@ -606,18 +606,26 @@ static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
+
+ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
+ {
+- if (try_warm && soc_ac97_ops.warm_reset) {
+- soc_ac97_ops.warm_reset(codec->ac97);
+- if (!(ac97_read(codec, 0) & 0x8000))
+- return 1;
+- }
++ int retry = 3;
+
+- soc_ac97_ops.reset(codec->ac97);
+- if (ac97_read(codec, 0) & 0x8000)
+- goto err;
+- return 0;
++ while (retry--)
++ {
++ if(try_warm && soc_ac97_ops.warm_reset) {
++ soc_ac97_ops.warm_reset(codec->ac97);
++ if(ac97_read(codec, 0) & 0x8000)
++ continue;
++ else
++ return 1;
++ }
++
++ soc_ac97_ops.reset(codec->ac97);
++ if(ac97_read(codec, 0) & 0x8000)
++ continue;
++ else
++ return 0;
++
++ }
+
+-err:
+ printk(KERN_ERR "WM9712 AC97 reset failed\n");
+ return -EIO;
+ }
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0054-sound-soc-codecs-wm9712.c-2.patch b/recipes/linux/linux-rp-2.6.24/tosa/0054-sound-soc-codecs-wm9712.c-2.patch
new file mode 100644
index 0000000000..be7300ab24
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0054-sound-soc-codecs-wm9712.c-2.patch
@@ -0,0 +1,28 @@
+From 08fbae2307163b3f0c3b704c4b00a9447752a45e Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Thu, 10 Jan 2008 17:56:58 +0300
+Subject: [PATCH 54/64] sound/soc/codecs/wm9712.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: git/sound/soc/codecs/wm9712.c
+===================================================================
+---
+ sound/soc/codecs/wm9712.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
+index dfb31e1..a3d9f96 100644
+--- a/sound/soc/codecs/wm9712.c
++++ b/sound/soc/codecs/wm9712.c
+@@ -647,7 +647,7 @@ static int wm9712_soc_resume(struct platform_device *pdev)
+ int i, ret;
+ u16 *cache = codec->reg_cache;
+
+- ret = wm9712_reset(codec, 1);
++ ret = wm9712_reset(codec, 0);
+ if (ret < 0){
+ printk(KERN_ERR "could not reset AC97 codec\n");
+ return ret;
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0055-Add-GPIO_POWERON-to-the-list-of-devices-that-we-supp.patch b/recipes/linux/linux-rp-2.6.24/tosa/0055-Add-GPIO_POWERON-to-the-list-of-devices-that-we-supp.patch
new file mode 100644
index 0000000000..5bf691cbda
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0055-Add-GPIO_POWERON-to-the-list-of-devices-that-we-supp.patch
@@ -0,0 +1,30 @@
+From bee8b808445a53a7dbb6c15a27064f14dec410c5 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Sun, 20 Jan 2008 03:01:41 +0300
+Subject: [PATCH 55/64] Add GPIO_POWERON to the list of devices that we support resume on.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/tosa.c | 6 +++---
+ 1 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index 3e832dc..d1cf3dc 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -517,9 +517,9 @@ static void __init tosa_init(void)
+ pxa_gpio_mode(TOSA_GPIO_USB_IN | GPIO_IN);
+
+ /* setup sleep mode values */
+- PWER = 0x00000002;
+- PFER = 0x00000000;
+- PRER = 0x00000002;
++ PWER = BIT(TOSA_GPIO_POWERON) | BIT(TOSA_GPIO_RESET);
++ PFER = 0;
++ PRER = BIT(TOSA_GPIO_POWERON) | BIT(TOSA_GPIO_RESET);
+ PGSR0 = 0x00000000;
+ PGSR1 = 0x00FF0002;
+ PGSR2 = 0x00014000;
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0056-Support-resetting-by-asserting-GPIO-pin.patch b/recipes/linux/linux-rp-2.6.24/tosa/0056-Support-resetting-by-asserting-GPIO-pin.patch
new file mode 100644
index 0000000000..99220f9200
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0056-Support-resetting-by-asserting-GPIO-pin.patch
@@ -0,0 +1,126 @@
+From e039614a0ce6df645f8fa4cbe32e4b21fe46a288 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Sun, 20 Jan 2008 02:44:03 +0300
+Subject: [PATCH 56/64] Support resetting by asserting GPIO pin
+
+This adds support for resetting via assertion of GPIO pin.
+This e.g. is used on Sharp Zaurus SL-6000.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/gpio.c | 43 +++++++++++++++++++++++++++++++++++++
+ arch/arm/mach-pxa/pm.c | 4 +-
+ include/asm-arm/arch-pxa/system.h | 10 ++++++++
+ 3 files changed, 55 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/gpio.c b/arch/arm/mach-pxa/gpio.c
+index 8638dd7..589da3b 100644
+--- a/arch/arm/mach-pxa/gpio.c
++++ b/arch/arm/mach-pxa/gpio.c
+@@ -19,6 +19,7 @@
+ #include <asm/hardware.h>
+ #include <asm/io.h>
+ #include <asm/arch/pxa-regs.h>
++#include <asm/arch/system.h>
+
+ #include "generic.h"
+
+@@ -194,4 +195,46 @@ void __init pxa_init_gpio(int gpio_nr)
+ pxa_gpio_chip[i/32].chip.ngpio = gpio_nr - i;
+ gpiochip_add(&pxa_gpio_chip[i/32].chip);
+ }
++
++ if (reset_gpio < gpio_nr)
++ init_reset_gpio();
++}
++
++int reset_gpio = -1;
++static int __init reset_gpio_setup(char *str)
++{
++ if (get_option(&str, &reset_gpio) != 1) {
++ printk(KERN_ERR "reset_gpio: bad value secified");
++ return 0;
++ }
++
++ return 1;
++}
++
++__setup("reset_gpio=", reset_gpio_setup);
++
++int init_reset_gpio(void)
++{
++ int rc = 0;
++ if (reset_gpio == -1)
++ goto out;
++
++ rc = gpio_request(reset_gpio, "reset generator");
++ if (rc) {
++ printk(KERN_ERR "Can't request reset_gpio\n");
++ goto out;
++ }
++
++ rc = gpio_direction_input(reset_gpio);
++ if (rc) {
++ printk(KERN_ERR "Can't configure reset_gpio for input\n");
++ gpio_free(reset_gpio);
++ goto out;
++ }
++
++out:
++ if (rc)
++ reset_gpio = -1;
++
++ return rc;
+ }
+diff --git a/arch/arm/mach-pxa/pm.c b/arch/arm/mach-pxa/pm.c
+index a941c71..64f37e5 100644
+--- a/arch/arm/mach-pxa/pm.c
++++ b/arch/arm/mach-pxa/pm.c
+@@ -40,8 +40,8 @@ int pxa_pm_enter(suspend_state_t state)
+
+ pxa_cpu_pm_fns->save(sleep_save);
+
+- /* Clear sleep reset status */
+- RCSR = RCSR_SMR;
++ /* Clear reset status */
++ RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR;
+
+ /* before sleeping, calculate and save a checksum */
+ for (i = 0; i < pxa_cpu_pm_fns->save_size - 1; i++)
+diff --git a/include/asm-arm/arch-pxa/system.h b/include/asm-arm/arch-pxa/system.h
+index 1d56a3e..c075018 100644
+--- a/include/asm-arm/arch-pxa/system.h
++++ b/include/asm-arm/arch-pxa/system.h
+@@ -11,6 +11,7 @@
+ */
+
+ #include <asm/proc-fns.h>
++#include <asm/gpio.h>
+ #include "hardware.h"
+ #include "pxa-regs.h"
+
+@@ -19,12 +20,21 @@ static inline void arch_idle(void)
+ cpu_do_idle();
+ }
+
++extern int reset_gpio;
++
++int init_reset_gpio(void);
+
+ static inline void arch_reset(char mode)
+ {
++ RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR;
++
+ if (mode == 's') {
+ /* Jump into ROM at address 0 */
+ cpu_reset(0);
++ } else if (mode == 'g' && reset_gpio != -1) {
++ /* Use GPIO reset */
++ gpio_direction_output(reset_gpio, 0);
++ gpio_set_value(reset_gpio, 1);
+ } else {
+ /* Initialize the watchdog and let it fire */
+ OWER = OWER_WME;
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0057-Clean-up-tosa-resetting.patch b/recipes/linux/linux-rp-2.6.24/tosa/0057-Clean-up-tosa-resetting.patch
new file mode 100644
index 0000000000..441e1bba75
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0057-Clean-up-tosa-resetting.patch
@@ -0,0 +1,70 @@
+From a6f03929fa4d20cef339dbed7ef5cd1e040d0548 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Sun, 20 Jan 2008 02:48:07 +0300
+Subject: [PATCH 57/64] Clean up tosa resetting
+
+Use new gpio-assertion reset.
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/tosa.c | 16 +++++++---------
+ 1 files changed, 7 insertions(+), 9 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index d1cf3dc..2b4aef7 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -41,6 +41,8 @@
+ #include <asm/arch/irda.h>
+ #include <asm/arch/mmc.h>
+ #include <asm/arch/udc.h>
++#include <asm/arch/pm.h>
++#include <asm/arch/system.h>
+
+ #include <asm/mach/arch.h>
+ #include <asm/mach/map.h>
+@@ -489,13 +491,7 @@ static struct platform_device *devices[] __initdata = {
+
+ static void tosa_poweroff(void)
+ {
+- RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR;
+-
+- pxa_gpio_mode(TOSA_GPIO_ON_RESET | GPIO_OUT);
+- GPSR(TOSA_GPIO_ON_RESET) = GPIO_bit(TOSA_GPIO_ON_RESET);
+-
+- mdelay(1000);
+- arm_machine_restart('h');
++ arm_machine_restart('g');
+ }
+
+ static void tosa_restart(char mode)
+@@ -504,7 +500,7 @@ static void tosa_restart(char mode)
+ if((MSC0 & 0xffff0000) == 0x7ff00000)
+ MSC0 = (MSC0 & 0xffff) | 0x7ee00000;
+
+- tosa_poweroff();
++ arm_machine_restart('g');
+ }
+
+ static void __init tosa_init(void)
+@@ -512,7 +508,6 @@ static void __init tosa_init(void)
+ pm_power_off = tosa_poweroff;
+ arm_pm_restart = tosa_restart;
+
+- pxa_gpio_mode(TOSA_GPIO_ON_RESET | GPIO_IN);
+ pxa_gpio_mode(TOSA_GPIO_TC6393XB_INT | GPIO_IN);
+ pxa_gpio_mode(TOSA_GPIO_USB_IN | GPIO_IN);
+
+@@ -544,6 +539,9 @@ static void __init fixup_tosa(struct machine_desc *desc,
+ mi->bank[0].start = 0xa0000000;
+ mi->bank[0].node = 0;
+ mi->bank[0].size = (64*1024*1024);
++
++ if (reset_gpio == -1)
++ reset_gpio = TOSA_GPIO_ON_RESET;
+ }
+
+ MACHINE_START(TOSA, "SHARP Tosa")
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0058-Fix-tosakbd-suspend.patch b/recipes/linux/linux-rp-2.6.24/tosa/0058-Fix-tosakbd-suspend.patch
new file mode 100644
index 0000000000..e965857dff
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0058-Fix-tosakbd-suspend.patch
@@ -0,0 +1,27 @@
+From 8b57c409802e5feef64c4bb7659570e06558c0f2 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Sun, 20 Jan 2008 02:24:43 +0300
+Subject: [PATCH 58/64] Fix tosakbd suspend
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/input/keyboard/tosakbd.c | 3 +++
+ 1 files changed, 3 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c
+index 3884d1e..306cbe8 100644
+--- a/drivers/input/keyboard/tosakbd.c
++++ b/drivers/input/keyboard/tosakbd.c
+@@ -210,6 +210,9 @@ static int tosakbd_suspend(struct platform_device *dev, pm_message_t state)
+
+ del_timer_sync(&tosakbd->timer);
+
++ PGSR1 = (PGSR1 & ~TOSA_GPIO_LOW_STROBE_BIT);
++ PGSR2 = (PGSR2 & ~TOSA_GPIO_HIGH_STROBE_BIT);
++
+ return 0;
+ }
+
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0059-patch-tosa-wakeup-test.patch b/recipes/linux/linux-rp-2.6.24/tosa/0059-patch-tosa-wakeup-test.patch
new file mode 100644
index 0000000000..812b5bad41
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0059-patch-tosa-wakeup-test.patch
@@ -0,0 +1,46 @@
+From 00f6e9b946d1f653fc776d71c86a1f6a7534cd1d Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 25 Jan 2008 19:16:20 +0300
+Subject: [PATCH 59/64] patch tosa-wakeup-test
+
+---
+ arch/arm/mach-pxa/tosa.c | 18 +++++++++++++++++-
+ 1 files changed, 17 insertions(+), 1 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index 2b4aef7..7008919 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -260,12 +260,28 @@ static struct platform_device tosakbd_device = {
+ };
+
+ static struct gpio_keys_button tosa_gpio_keys[] = {
++ /*
++ * Two following keys are directly tied to "ON" button of tosa. Why?
++ * The first one can be used as a wakeup source, the second can't:
++ * it's outside of permitted area.
++ */
++ {
++ .type = EV_PWR,
++ .code = KEY_RESERVED,
++ .gpio = TOSA_GPIO_POWERON,
++ .desc = "Poweron",
++ .wakeup = 1,
++ .active_low = 1,
++ },
+ {
+ .type = EV_PWR,
+ .code = KEY_SUSPEND,
+ .gpio = TOSA_GPIO_ON_KEY,
+ .desc = "On key",
+- .wakeup = 1,
++ /*
++ * can't be used as wakeup
++ * .wakeup = 1,
++ */
+ .active_low = 1,
+ },
+ {
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0060-Add-support-for-power_supply-on-tosa.patch b/recipes/linux/linux-rp-2.6.24/tosa/0060-Add-support-for-power_supply-on-tosa.patch
new file mode 100644
index 0000000000..f7420de040
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0060-Add-support-for-power_supply-on-tosa.patch
@@ -0,0 +1,623 @@
+From f6ec15733eb55e851c8ad19c2143d425558f6044 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Mon, 4 Feb 2008 20:11:58 +0300
+Subject: [PATCH 60/64] Add support for power_supply on tosa
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/Makefile | 2 +-
+ arch/arm/mach-pxa/tosa_power.c | 82 +++++++
+ drivers/leds/leds-tosa.c | 2 +-
+ drivers/power/Kconfig | 7 +
+ drivers/power/Makefile | 1 +
+ drivers/power/tosa_battery.c | 458 ++++++++++++++++++++++++++++++++++++++++
+ 6 files changed, 550 insertions(+), 2 deletions(-)
+ create mode 100644 arch/arm/mach-pxa/tosa_power.c
+ create mode 100644 drivers/power/tosa_battery.c
+
+diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
+index f276d24..2b68254 100644
+--- a/arch/arm/mach-pxa/Makefile
++++ b/arch/arm/mach-pxa/Makefile
+@@ -21,7 +21,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o cor
+ obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o
+ obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o
+ obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o
+-obj-$(CONFIG_MACH_TOSA) += tosa.o
++obj-$(CONFIG_MACH_TOSA) += tosa.o tosa_power.o
+ obj-$(CONFIG_MACH_EM_X270) += em-x270.o
+
+ ifeq ($(CONFIG_MACH_ZYLONITE),y)
+diff --git a/arch/arm/mach-pxa/tosa_power.c b/arch/arm/mach-pxa/tosa_power.c
+new file mode 100644
+index 0000000..61ca7dc
+--- /dev/null
++++ b/arch/arm/mach-pxa/tosa_power.c
+@@ -0,0 +1,82 @@
++/*
++ * Battery and Power Management code for the Sharp SL-6000x
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ * Copyright (c) 2008 Dmitry Baryshkov
++ *
++ * 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/kernel.h>
++#include <linux/module.h>
++#include <linux/power_supply.h>
++#include <linux/pda_power.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++
++#include <asm/arch/tosa.h>
++#include <asm/gpio.h>
++
++static int tosa_power_ac_online(void)
++{
++ return gpio_get_value(TOSA_GPIO_AC_IN) == 0;
++}
++
++static char *tosa_ac_supplied_to[] = {
++ "main-battery",
++ "backup-battery",
++ "jacket-battery",
++};
++
++static struct pda_power_pdata tosa_power_data = {
++ .is_ac_online = tosa_power_ac_online,
++ .supplied_to = tosa_ac_supplied_to,
++ .num_supplicants = ARRAY_SIZE(tosa_ac_supplied_to),
++};
++
++static struct resource tosa_power_resource[] = {
++ {
++ .name = "ac",
++ .start = gpio_to_irq(TOSA_GPIO_AC_IN),
++ .end = gpio_to_irq(TOSA_GPIO_AC_IN),
++ .flags = IORESOURCE_IRQ |
++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ },
++};
++
++static struct platform_device tosa_power_device = {
++ .name = "pda-power",
++ .id = -1,
++ .dev.platform_data = &tosa_power_data,
++ .resource = tosa_power_resource,
++ .num_resources = ARRAY_SIZE(tosa_power_resource),
++};
++
++static int __init tosa_power_init(void)
++{
++ int ret = gpio_request(TOSA_GPIO_AC_IN, "ac");
++ if (ret)
++ goto err_gpio_req;
++
++ ret = gpio_direction_input(TOSA_GPIO_AC_IN);
++ if (ret)
++ goto err_gpio_in;
++
++ return platform_device_register(&tosa_power_device);
++
++err_gpio_in:
++ gpio_free(TOSA_GPIO_AC_IN);
++err_gpio_req:
++ return ret;
++}
++
++static void __exit tosa_power_exit(void)
++{
++ platform_device_unregister(&tosa_power_device);
++ gpio_free(TOSA_GPIO_AC_IN);
++}
++
++module_init(tosa_power_init);
++module_exit(tosa_power_exit);
+diff --git a/drivers/leds/leds-tosa.c b/drivers/leds/leds-tosa.c
+index fb2416a..b4498b5 100644
+--- a/drivers/leds/leds-tosa.c
++++ b/drivers/leds/leds-tosa.c
+@@ -46,7 +46,7 @@ static void tosaled_green_set(struct led_classdev *led_cdev,
+
+ static struct led_classdev tosa_amber_led = {
+ .name = "tosa:amber",
+- .default_trigger = "sharpsl-charge",
++ .default_trigger = "main-battery-charging",
+ .brightness_set = tosaled_amber_set,
+ };
+
+diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
+index 58c806e..e3a9c37 100644
+--- a/drivers/power/Kconfig
++++ b/drivers/power/Kconfig
+@@ -49,4 +49,11 @@ config BATTERY_OLPC
+ help
+ Say Y to enable support for the battery on the OLPC laptop.
+
++config BATTERY_TOSA
++ tristate "Sharp SL-6000 (tosa) battery"
++ depends on MACH_TOSA && MFD_TC6393XB
++ help
++ Say Y to enable support for the battery on the Sharp Zaurus
++ SL-6000 (tosa) models.
++
+ endif # POWER_SUPPLY
+diff --git a/drivers/power/Makefile b/drivers/power/Makefile
+index 6413ded..1e408fa 100644
+--- a/drivers/power/Makefile
++++ b/drivers/power/Makefile
+@@ -20,3 +20,4 @@ obj-$(CONFIG_APM_POWER) += apm_power.o
+ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
+ obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
+ obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
++obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
+diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c
+new file mode 100644
+index 0000000..b0fd2f2
+--- /dev/null
++++ b/drivers/power/tosa_battery.c
+@@ -0,0 +1,458 @@
++/*
++ * Battery and Power Management code for the Sharp SL-6000x
++ *
++ * Copyright (c) 2005 Dirk Opfer
++ * Copyright (c) 2008 Dmitry Baryshkov
++ *
++ * 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/kernel.h>
++#include <linux/module.h>
++#include <linux/power_supply.h>
++#include <linux/wm97xx.h>
++#include <linux/delay.h>
++#include <linux/spinlock.h>
++#include <linux/interrupt.h>
++
++#include <asm/mach-types.h>
++#include <asm/gpio.h>
++#include <asm/arch/tosa.h>
++
++#define BAT_TO_VOLTS(v) ((v) * 1000000 / 414)
++#define BU_TO_VOLTS(v) ((v) * 1000000 / 1266)
++/*
++ * It's pretty strange value, but that's roughly what I get from
++ * zaurus maintainer menu
++ */
++//#define BAT_TO_TEMP(t) ((t) * 10000/2000)
++#define BAT_TO_TEMP(t) (t)
++
++static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
++static struct work_struct bat_work;
++
++struct tosa_bat {
++ int status;
++ struct power_supply psy;
++ int full_chrg;
++
++ struct mutex work_lock; /* protects data */
++ bool (*is_present)(struct tosa_bat *bat);
++ int gpio_full;
++ int gpio_charge_off;
++ int gpio_bat;
++ int adc_bat;
++ int gpio_temp;
++ int adc_temp;
++};
++
++static struct tosa_bat tosa_bat_main;
++static struct tosa_bat tosa_bat_jacket;
++
++static enum power_supply_property tosa_bat_main_props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_TECHNOLOGY,
++ POWER_SUPPLY_PROP_VOLTAGE_NOW,
++ POWER_SUPPLY_PROP_VOLTAGE_MAX,
++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
++ POWER_SUPPLY_PROP_TEMP,
++ POWER_SUPPLY_PROP_PRESENT,
++};
++
++static enum power_supply_property tosa_bat_bu_props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_TECHNOLOGY,
++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
++ POWER_SUPPLY_PROP_VOLTAGE_NOW,
++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
++ POWER_SUPPLY_PROP_PRESENT,
++};
++
++static unsigned long tosa_read_bat(struct tosa_bat *bat)
++{
++ unsigned long value = 0;
++
++ if (bat->gpio_bat < 0 || bat->adc_bat < 0)
++ return 0;
++
++ mutex_lock(&bat_lock);
++ gpio_set_value(bat->gpio_bat, 1);
++ mdelay(5);
++ value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data, bat->adc_bat);
++ gpio_set_value(bat->gpio_bat, 0);
++ mutex_unlock(&bat_lock);
++ return value;
++}
++
++static unsigned long tosa_read_temp(struct tosa_bat *bat)
++{
++ unsigned long value = 0;
++
++ if (bat->gpio_temp < 0 || bat->adc_temp < 0)
++ return 0;
++
++ mutex_lock(&bat_lock);
++ gpio_set_value(bat->gpio_temp, 1);
++ mdelay(5);
++ value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data, bat->adc_temp);
++ gpio_set_value(bat->gpio_temp, 0);
++ mutex_unlock(&bat_lock);
++ return value;
++}
++
++static int tosa_bat_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ int ret = 0;
++ struct tosa_bat *bat = container_of(psy, struct tosa_bat, psy);
++
++ if (bat->is_present && !bat->is_present(bat)
++ && psp != POWER_SUPPLY_PROP_PRESENT) {
++ return -ENODEV;
++ }
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_STATUS:
++ val->intval = bat->status;
++ break;
++ case POWER_SUPPLY_PROP_TECHNOLOGY:
++ val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
++ val->intval = BAT_TO_VOLTS(tosa_read_bat(bat));
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
++ if (bat->full_chrg == -1)
++ val->intval = -1;
++ else
++ val->intval = BAT_TO_VOLTS(bat->full_chrg);
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
++ val->intval = BAT_TO_VOLTS(1551);
++ break;
++ case POWER_SUPPLY_PROP_TEMP:
++ val->intval = BAT_TO_TEMP(tosa_read_temp(bat));
++ break;
++ case POWER_SUPPLY_PROP_PRESENT:
++ val->intval = bat->is_present ? bat->is_present(bat) : 1;
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++ return ret;
++}
++
++static int tosa_bu_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ int ret = 0;
++ struct tosa_bat *bat = container_of(psy, struct tosa_bat, psy);
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_STATUS:
++ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
++ break;
++ case POWER_SUPPLY_PROP_TECHNOLOGY:
++ val->intval = POWER_SUPPLY_TECHNOLOGY_LiMn;
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
++ val->intval = 0;
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
++ val->intval = 3 * 1000000; /* 3 V */
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
++ /* I think so */
++ val->intval = BU_TO_VOLTS(tosa_read_bat(bat));
++ break;
++ case POWER_SUPPLY_PROP_PRESENT:
++ val->intval = 1;
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++ return ret;
++}
++
++static bool tosa_jacket_bat_is_present(struct tosa_bat *bat) {
++ // FIXME
++ return 1;
++}
++
++static void tosa_bat_external_power_changed(struct power_supply *psy)
++{
++ schedule_work(&bat_work);
++}
++
++static irqreturn_t tosa_bat_full_isr(int irq, void *data)
++{
++ printk(KERN_ERR "bat_full irq: %d\n", gpio_get_value(irq_to_gpio(irq)));
++ schedule_work(&bat_work);
++ return IRQ_HANDLED;
++}
++
++static void tosa_bat_update(struct tosa_bat *bat)
++{
++ int old = bat->status;
++ struct power_supply *psy = &bat->psy;
++
++ mutex_lock(&bat->work_lock);
++
++ if (bat->is_present && !bat->is_present(bat)) {
++ printk(KERN_DEBUG "%s not present\n", psy->name);
++ bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
++ bat->full_chrg = -1;
++ } else if (power_supply_am_i_supplied(psy)) {
++ if (gpio_get_value(bat->gpio_full)) {
++ printk(KERN_DEBUG "%s full\n", psy->name);
++
++ if (old == POWER_SUPPLY_STATUS_CHARGING || bat->full_chrg == -1)
++ bat->full_chrg = tosa_read_bat(bat);
++
++ gpio_set_value(bat->gpio_charge_off, 1);
++ bat->status = POWER_SUPPLY_STATUS_FULL;
++ } else {
++ printk(KERN_ERR "%s charge\n", psy->name);
++ gpio_set_value(bat->gpio_charge_off, 0);
++ bat->status = POWER_SUPPLY_STATUS_CHARGING;
++ }
++ } else {
++ printk(KERN_ERR "%s discharge\n", psy->name);
++ gpio_set_value(bat->gpio_charge_off, 1);
++ bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
++ }
++
++ if (old != bat->status)
++ power_supply_changed(psy);
++
++ mutex_unlock(&bat->work_lock);
++}
++
++static void tosa_bat_work(struct work_struct *work)
++{
++ tosa_bat_update(&tosa_bat_main);
++ tosa_bat_update(&tosa_bat_jacket);
++}
++
++
++static struct tosa_bat tosa_bat_main = {
++ .status = POWER_SUPPLY_STATUS_UNKNOWN,
++ .full_chrg = -1,
++ .psy = {
++ .name = "main-battery",
++ .type = POWER_SUPPLY_TYPE_BATTERY,
++ .properties = tosa_bat_main_props,
++ .num_properties = ARRAY_SIZE(tosa_bat_main_props),
++ .get_property = tosa_bat_get_property,
++ .external_power_changed = tosa_bat_external_power_changed,
++ .use_for_apm = 1,
++ },
++
++ .gpio_full = TOSA_GPIO_BAT0_CRG,
++ .gpio_charge_off = TOSA_TC6393XB_CHARGE_OFF,
++ .gpio_bat = TOSA_TC6393XB_BAT0_V_ON,
++ .adc_bat = WM97XX_AUX_ID3,
++ .gpio_temp = TOSA_TC6393XB_BAT1_TH_ON,
++ .adc_temp = WM97XX_AUX_ID2,
++};
++
++static struct tosa_bat tosa_bat_jacket = {
++ .status = POWER_SUPPLY_STATUS_UNKNOWN,
++ .full_chrg = -1,
++ .psy = {
++ .name = "jacket-battery",
++ .type = POWER_SUPPLY_TYPE_BATTERY,
++ .properties = tosa_bat_main_props,
++ .num_properties = ARRAY_SIZE(tosa_bat_main_props),
++ .get_property = tosa_bat_get_property,
++ .external_power_changed = tosa_bat_external_power_changed,
++// .use_for_apm = 1,
++ },
++
++ .is_present = tosa_jacket_bat_is_present,
++ .gpio_full = TOSA_GPIO_BAT1_CRG,
++ .gpio_charge_off = TOSA_TC6393XB_CHARGE_OFF_JC,
++ .gpio_bat = TOSA_TC6393XB_BAT1_V_ON,
++ .adc_bat = WM97XX_AUX_ID3,
++ .gpio_temp = TOSA_TC6393XB_BAT0_TH_ON,
++ .adc_temp = WM97XX_AUX_ID2,
++};
++
++static struct tosa_bat tosa_bat_bu = {
++ .status = POWER_SUPPLY_STATUS_UNKNOWN,
++ .full_chrg = -1,
++
++ .psy = {
++ .name = "backup-battery",
++ .type = POWER_SUPPLY_TYPE_BATTERY,
++ .properties = tosa_bat_bu_props,
++ .num_properties = ARRAY_SIZE(tosa_bat_bu_props),
++ .get_property = tosa_bu_get_property,
++ .external_power_changed = tosa_bat_external_power_changed,
++ },
++
++ .gpio_full = -1,
++ .gpio_charge_off = -1,
++ .gpio_bat = TOSA_TC6393XB_BU_CHRG_ON,
++ .adc_bat = WM97XX_AUX_ID4,
++ .gpio_temp = -1,
++ .adc_temp = -1,
++};
++
++static struct {
++ int gpio;
++ char *name;
++ bool output;
++ int value;
++} gpios[] = {
++ { TOSA_TC6393XB_CHARGE_OFF, "main charge off", 1, 1 },
++ { TOSA_TC6393XB_CHARGE_OFF_JC, "jacket charge off", 1, 1 },
++ { TOSA_TC6393XB_BAT_SW_ON, "battery switch", 1, 0 },
++ { TOSA_TC6393XB_BAT0_V_ON, "main battery", 1, 0 },
++ { TOSA_TC6393XB_BAT1_V_ON, "jacket battery", 1, 0 },
++ { TOSA_TC6393XB_BAT1_TH_ON, "main battery temp", 1, 0 },
++ { TOSA_TC6393XB_BAT0_TH_ON, "jacket battery temp", 1, 0 },
++ { TOSA_TC6393XB_BU_CHRG_ON, "backup battery", 1, 0 },
++ { TOSA_GPIO_BAT0_CRG, "main battery full", 0, 0 },
++ { TOSA_GPIO_BAT1_CRG, "jacket battery full", 0, 0 },
++ { TOSA_GPIO_BAT0_LOW, "main battery low", 0, 0 },
++ { TOSA_GPIO_BAT1_LOW, "jacket battery low", 0, 0 },
++};
++
++#ifdef CONFIG_PM
++static int tosa_bat_suspend(struct device *dev, pm_message_t state)
++{
++ /* do nothing */
++ return 0;
++}
++
++static int tosa_bat_resume(struct device *dev)
++{
++ schedule_work(&bat_work);
++ return 0;
++}
++#else
++#define tosa_bat_suspend NULL
++#define tosa_bat_resume NULL
++#endif
++
++static int __devinit tosa_bat_probe(struct device *dev)
++{
++ int ret;
++ int i;
++
++ if (!machine_is_tosa())
++ return -ENODEV;
++
++ for (i = 0; i < ARRAY_SIZE(gpios); i++) {
++ ret = gpio_request(gpios[i].gpio, gpios[i].name);
++ if (ret) {
++ i --;
++ goto err_gpio;
++ }
++
++ if (gpios[i].output)
++ ret = gpio_direction_output(gpios[i].gpio,
++ gpios[i].value);
++ else
++ ret = gpio_direction_input(gpios[i].gpio);
++
++ if (ret)
++ goto err_gpio;
++ }
++
++ mutex_init(&tosa_bat_main.work_lock);
++ mutex_init(&tosa_bat_jacket.work_lock);
++
++ INIT_WORK(&bat_work, tosa_bat_work);
++
++ ret = power_supply_register(dev, &tosa_bat_main.psy);
++ if (ret)
++ goto err_psy_reg_main;
++ ret = power_supply_register(dev, &tosa_bat_jacket.psy);
++ if (ret)
++ goto err_psy_reg_jacket;
++ ret = power_supply_register(dev, &tosa_bat_bu.psy);
++ if (ret)
++ goto err_psy_reg_bu;
++
++ ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG),
++ tosa_bat_full_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ "main full", &tosa_bat_main);
++ if (ret)
++ goto err_req_main;
++
++ ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG),
++ tosa_bat_full_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ "jacket full", &tosa_bat_jacket);
++ if (!ret) {
++ schedule_work(&bat_work);
++ return 0;
++ }
++
++ free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
++err_req_main:
++ power_supply_unregister(&tosa_bat_bu.psy);
++err_psy_reg_bu:
++ power_supply_unregister(&tosa_bat_jacket.psy);
++err_psy_reg_jacket:
++ power_supply_unregister(&tosa_bat_main.psy);
++err_psy_reg_main:
++
++ i --;
++err_gpio:
++ for (; i >= 0; i --)
++ gpio_free(gpios[i].gpio);
++
++ return ret;
++}
++
++static int __devexit tosa_bat_remove(struct device *dev)
++{
++ int i;
++
++ free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket);
++ free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
++
++ power_supply_unregister(&tosa_bat_bu.psy);
++ power_supply_unregister(&tosa_bat_jacket.psy);
++ power_supply_unregister(&tosa_bat_main.psy);
++
++ for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i --)
++ gpio_free(gpios[i].gpio);
++
++ return 0;
++}
++
++static struct device_driver tosa_bat_driver = {
++ .name = "wm97xx-battery",
++ .bus = &wm97xx_bus_type,
++ .owner = THIS_MODULE,
++ .probe = tosa_bat_probe,
++ .remove = __devexit_p(tosa_bat_remove),
++ .suspend = tosa_bat_suspend,
++ .resume = tosa_bat_resume,
++};
++
++static int __init tosa_bat_init(void)
++{
++ return driver_register(&tosa_bat_driver);
++}
++
++static void __exit tosa_bat_exit(void)
++{
++ driver_unregister(&tosa_bat_driver);
++}
++
++module_init(tosa_bat_init);
++module_exit(tosa_bat_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Dmitry Baryshkov");
++MODULE_DESCRIPTION("Tosa battery driver");
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0061-tosa-bat-unify.patch b/recipes/linux/linux-rp-2.6.24/tosa/0061-tosa-bat-unify.patch
new file mode 100644
index 0000000000..2bcede36a9
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0061-tosa-bat-unify.patch
@@ -0,0 +1,342 @@
+From f05aa38af5bd5962ae04c4b128644e7f55451527 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 8 Feb 2008 01:14:48 +0300
+Subject: [PATCH 61/64] tosa-bat-unify
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/power/tosa_battery.c | 161 ++++++++++++++++++++---------------------
+ 1 files changed, 79 insertions(+), 82 deletions(-)
+
+diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c
+index b0fd2f2..008e791 100644
+--- a/drivers/power/tosa_battery.c
++++ b/drivers/power/tosa_battery.c
+@@ -21,15 +21,6 @@
+ #include <asm/gpio.h>
+ #include <asm/arch/tosa.h>
+
+-#define BAT_TO_VOLTS(v) ((v) * 1000000 / 414)
+-#define BU_TO_VOLTS(v) ((v) * 1000000 / 1266)
+-/*
+- * It's pretty strange value, but that's roughly what I get from
+- * zaurus maintainer menu
+- */
+-//#define BAT_TO_TEMP(t) ((t) * 10000/2000)
+-#define BAT_TO_TEMP(t) (t)
+-
+ static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
+ static struct work_struct bat_work;
+
+@@ -39,37 +30,27 @@ struct tosa_bat {
+ int full_chrg;
+
+ struct mutex work_lock; /* protects data */
++
+ bool (*is_present)(struct tosa_bat *bat);
+ int gpio_full;
+ int gpio_charge_off;
++
++ int technology;
++
+ int gpio_bat;
+ int adc_bat;
++ int adc_bat_divider;
++ int bat_max;
++ int bat_min;
++
+ int gpio_temp;
+ int adc_temp;
++ int adc_temp_divider;
+ };
+
+ static struct tosa_bat tosa_bat_main;
+ static struct tosa_bat tosa_bat_jacket;
+
+-static enum power_supply_property tosa_bat_main_props[] = {
+- POWER_SUPPLY_PROP_STATUS,
+- POWER_SUPPLY_PROP_TECHNOLOGY,
+- POWER_SUPPLY_PROP_VOLTAGE_NOW,
+- POWER_SUPPLY_PROP_VOLTAGE_MAX,
+- POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+- POWER_SUPPLY_PROP_TEMP,
+- POWER_SUPPLY_PROP_PRESENT,
+-};
+-
+-static enum power_supply_property tosa_bat_bu_props[] = {
+- POWER_SUPPLY_PROP_STATUS,
+- POWER_SUPPLY_PROP_TECHNOLOGY,
+- POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+- POWER_SUPPLY_PROP_VOLTAGE_NOW,
+- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+- POWER_SUPPLY_PROP_PRESENT,
+-};
+-
+ static unsigned long tosa_read_bat(struct tosa_bat *bat)
+ {
+ unsigned long value = 0;
+@@ -83,6 +64,9 @@ static unsigned long tosa_read_bat(struct tosa_bat *bat)
+ value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data, bat->adc_bat);
+ gpio_set_value(bat->gpio_bat, 0);
+ mutex_unlock(&bat_lock);
++
++ value = value * 1000000 / bat->adc_bat_divider;
++
+ return value;
+ }
+
+@@ -99,6 +83,9 @@ static unsigned long tosa_read_temp(struct tosa_bat *bat)
+ value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data, bat->adc_temp);
+ gpio_set_value(bat->gpio_temp, 0);
+ mutex_unlock(&bat_lock);
++
++ value = value * 10000 / bat->adc_temp_divider;
++
+ return value;
+ }
+
+@@ -119,22 +106,25 @@ static int tosa_bat_get_property(struct power_supply *psy,
+ val->intval = bat->status;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+- val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
++ val->intval = bat->technology;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+- val->intval = BAT_TO_VOLTS(tosa_read_bat(bat));
++ val->intval = tosa_read_bat(bat);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ if (bat->full_chrg == -1)
+- val->intval = -1;
++ val->intval = bat->bat_max;
+ else
+- val->intval = BAT_TO_VOLTS(bat->full_chrg);
++ val->intval = bat->full_chrg;
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
++ val->intval = bat->bat_max;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+- val->intval = BAT_TO_VOLTS(1551);
++ val->intval = bat->bat_min;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+- val->intval = BAT_TO_TEMP(tosa_read_temp(bat));
++ val->intval = tosa_read_temp(bat);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = bat->is_present ? bat->is_present(bat) : 1;
+@@ -146,40 +136,6 @@ static int tosa_bat_get_property(struct power_supply *psy,
+ return ret;
+ }
+
+-static int tosa_bu_get_property(struct power_supply *psy,
+- enum power_supply_property psp,
+- union power_supply_propval *val)
+-{
+- int ret = 0;
+- struct tosa_bat *bat = container_of(psy, struct tosa_bat, psy);
+-
+- switch (psp) {
+- case POWER_SUPPLY_PROP_STATUS:
+- val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+- break;
+- case POWER_SUPPLY_PROP_TECHNOLOGY:
+- val->intval = POWER_SUPPLY_TECHNOLOGY_LiMn;
+- break;
+- case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+- val->intval = 0;
+- break;
+- case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+- val->intval = 3 * 1000000; /* 3 V */
+- break;
+- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+- /* I think so */
+- val->intval = BU_TO_VOLTS(tosa_read_bat(bat));
+- break;
+- case POWER_SUPPLY_PROP_PRESENT:
+- val->intval = 1;
+- break;
+- default:
+- ret = -EINVAL;
+- break;
+- }
+- return ret;
+-}
+-
+ static bool tosa_jacket_bat_is_present(struct tosa_bat *bat) {
+ // FIXME
+ return 1;
+@@ -241,6 +197,25 @@ static void tosa_bat_work(struct work_struct *work)
+ }
+
+
++static enum power_supply_property tosa_bat_main_props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_TECHNOLOGY,
++ POWER_SUPPLY_PROP_VOLTAGE_NOW,
++ POWER_SUPPLY_PROP_VOLTAGE_MAX,
++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
++ POWER_SUPPLY_PROP_TEMP,
++ POWER_SUPPLY_PROP_PRESENT,
++};
++
++static enum power_supply_property tosa_bat_bu_props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_TECHNOLOGY,
++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
++ POWER_SUPPLY_PROP_VOLTAGE_NOW,
++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
++ POWER_SUPPLY_PROP_PRESENT,
++};
++
+ static struct tosa_bat tosa_bat_main = {
+ .status = POWER_SUPPLY_STATUS_UNKNOWN,
+ .full_chrg = -1,
+@@ -256,10 +231,18 @@ static struct tosa_bat tosa_bat_main = {
+
+ .gpio_full = TOSA_GPIO_BAT0_CRG,
+ .gpio_charge_off = TOSA_TC6393XB_CHARGE_OFF,
++
++ .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
++
+ .gpio_bat = TOSA_TC6393XB_BAT0_V_ON,
+ .adc_bat = WM97XX_AUX_ID3,
++ .adc_bat_divider = 414,
++ .bat_max = 4310000,
++ .bat_min = 1551 * 100000 / 414,
++
+ .gpio_temp = TOSA_TC6393XB_BAT1_TH_ON,
+ .adc_temp = WM97XX_AUX_ID2,
++ .adc_temp_divider = 10000,
+ };
+
+ static struct tosa_bat tosa_bat_jacket = {
+@@ -278,10 +261,18 @@ static struct tosa_bat tosa_bat_jacket = {
+ .is_present = tosa_jacket_bat_is_present,
+ .gpio_full = TOSA_GPIO_BAT1_CRG,
+ .gpio_charge_off = TOSA_TC6393XB_CHARGE_OFF_JC,
++
++ .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
++
+ .gpio_bat = TOSA_TC6393XB_BAT1_V_ON,
+ .adc_bat = WM97XX_AUX_ID3,
++ .adc_bat_divider = 414,
++ .bat_max = 4310000,
++ .bat_min = 1551 * 100000 / 414,
++
+ .gpio_temp = TOSA_TC6393XB_BAT0_TH_ON,
+ .adc_temp = WM97XX_AUX_ID2,
++ .adc_temp_divider = 10000,
+ };
+
+ static struct tosa_bat tosa_bat_bu = {
+@@ -293,16 +284,22 @@ static struct tosa_bat tosa_bat_bu = {
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = tosa_bat_bu_props,
+ .num_properties = ARRAY_SIZE(tosa_bat_bu_props),
+- .get_property = tosa_bu_get_property,
++ .get_property = tosa_bat_get_property,
+ .external_power_changed = tosa_bat_external_power_changed,
+ },
+
+ .gpio_full = -1,
+ .gpio_charge_off = -1,
++
++ .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
++
+ .gpio_bat = TOSA_TC6393XB_BU_CHRG_ON,
+ .adc_bat = WM97XX_AUX_ID4,
++ .adc_bat_divider = 1266,
++
+ .gpio_temp = -1,
+ .adc_temp = -1,
++ .adc_temp_divider = -1,
+ };
+
+ static struct {
+@@ -326,13 +323,14 @@ static struct {
+ };
+
+ #ifdef CONFIG_PM
+-static int tosa_bat_suspend(struct device *dev, pm_message_t state)
++static int tosa_bat_suspend(struct platform_device *dev, pm_message_t state)
+ {
+ /* do nothing */
++ flush_scheduled_work();
+ return 0;
+ }
+
+-static int tosa_bat_resume(struct device *dev)
++static int tosa_bat_resume(struct platform_device *dev)
+ {
+ schedule_work(&bat_work);
+ return 0;
+@@ -342,7 +340,7 @@ static int tosa_bat_resume(struct device *dev)
+ #define tosa_bat_resume NULL
+ #endif
+
+-static int __devinit tosa_bat_probe(struct device *dev)
++static int __devinit tosa_bat_probe(struct platform_device *dev)
+ {
+ int ret;
+ int i;
+@@ -372,13 +370,13 @@ static int __devinit tosa_bat_probe(struct device *dev)
+
+ INIT_WORK(&bat_work, tosa_bat_work);
+
+- ret = power_supply_register(dev, &tosa_bat_main.psy);
++ ret = power_supply_register(&dev->dev, &tosa_bat_main.psy);
+ if (ret)
+ goto err_psy_reg_main;
+- ret = power_supply_register(dev, &tosa_bat_jacket.psy);
++ ret = power_supply_register(&dev->dev, &tosa_bat_jacket.psy);
+ if (ret)
+ goto err_psy_reg_jacket;
+- ret = power_supply_register(dev, &tosa_bat_bu.psy);
++ ret = power_supply_register(&dev->dev, &tosa_bat_bu.psy);
+ if (ret)
+ goto err_psy_reg_bu;
+
+@@ -413,7 +411,7 @@ err_gpio:
+ return ret;
+ }
+
+-static int __devexit tosa_bat_remove(struct device *dev)
++static int __devexit tosa_bat_remove(struct platform_device *dev)
+ {
+ int i;
+
+@@ -430,10 +428,9 @@ static int __devexit tosa_bat_remove(struct device *dev)
+ return 0;
+ }
+
+-static struct device_driver tosa_bat_driver = {
+- .name = "wm97xx-battery",
+- .bus = &wm97xx_bus_type,
+- .owner = THIS_MODULE,
++static struct platform_driver tosa_bat_driver = {
++ .driver.name = "wm97xx-battery",
++ .driver.owner = THIS_MODULE,
+ .probe = tosa_bat_probe,
+ .remove = __devexit_p(tosa_bat_remove),
+ .suspend = tosa_bat_suspend,
+@@ -442,12 +439,12 @@ static struct device_driver tosa_bat_driver = {
+
+ static int __init tosa_bat_init(void)
+ {
+- return driver_register(&tosa_bat_driver);
++ return platform_driver_register(&tosa_bat_driver);
+ }
+
+ static void __exit tosa_bat_exit(void)
+ {
+- driver_unregister(&tosa_bat_driver);
++ platform_driver_unregister(&tosa_bat_driver);
+ }
+
+ module_init(tosa_bat_init);
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0062-tosa-bat-fix-charging.patch b/recipes/linux/linux-rp-2.6.24/tosa/0062-tosa-bat-fix-charging.patch
new file mode 100644
index 0000000000..e3a6b74772
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0062-tosa-bat-fix-charging.patch
@@ -0,0 +1,78 @@
+From 0b9f80ab540b2e51f6e86f6a1adec67761f9368d Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 8 Feb 2008 00:50:03 +0300
+Subject: [PATCH 62/64] tosa-bat-fix-charging
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/power/tosa_battery.c | 28 +++++++++++++++++++++-------
+ 1 files changed, 21 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c
+index 008e791..2db9116 100644
+--- a/drivers/power/tosa_battery.c
++++ b/drivers/power/tosa_battery.c
+@@ -153,39 +153,53 @@ static irqreturn_t tosa_bat_full_isr(int irq, void *data)
+ return IRQ_HANDLED;
+ }
+
++static char *status_text[] = {
++ [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown",
++ [POWER_SUPPLY_STATUS_CHARGING] = "Charging",
++ [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging",
++ [POWER_SUPPLY_STATUS_NOT_CHARGING] = "Not charging",
++ [POWER_SUPPLY_STATUS_FULL] = "Full",
++};
++
+ static void tosa_bat_update(struct tosa_bat *bat)
+ {
+- int old = bat->status;
++ int old;
+ struct power_supply *psy = &bat->psy;
+
+ mutex_lock(&bat->work_lock);
+
++ old = bat->status;
++
+ if (bat->is_present && !bat->is_present(bat)) {
+- printk(KERN_DEBUG "%s not present\n", psy->name);
++ printk(KERN_NOTICE "%s not present\n", psy->name);
+ bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ bat->full_chrg = -1;
+ } else if (power_supply_am_i_supplied(psy)) {
++ if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
++ gpio_set_value(bat->gpio_charge_off, 0);
++ mdelay(15);
++ }
+ if (gpio_get_value(bat->gpio_full)) {
+- printk(KERN_DEBUG "%s full\n", psy->name);
+-
+ if (old == POWER_SUPPLY_STATUS_CHARGING || bat->full_chrg == -1)
+ bat->full_chrg = tosa_read_bat(bat);
+
+ gpio_set_value(bat->gpio_charge_off, 1);
+ bat->status = POWER_SUPPLY_STATUS_FULL;
+ } else {
+- printk(KERN_ERR "%s charge\n", psy->name);
+ gpio_set_value(bat->gpio_charge_off, 0);
+ bat->status = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ } else {
+- printk(KERN_ERR "%s discharge\n", psy->name);
+ gpio_set_value(bat->gpio_charge_off, 1);
+ bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+
+- if (old != bat->status)
++ if (old != bat->status) {
++ printk(KERN_NOTICE "%s %s -> %s\n", psy->name,
++ status_text[old],
++ status_text[bat->status]);
+ power_supply_changed(psy);
++ }
+
+ mutex_unlock(&bat->work_lock);
+ }
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0063-patch-tosa-bat-jacket-detect.patch b/recipes/linux/linux-rp-2.6.24/tosa/0063-patch-tosa-bat-jacket-detect.patch
new file mode 100644
index 0000000000..416cae44ec
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0063-patch-tosa-bat-jacket-detect.patch
@@ -0,0 +1,84 @@
+From 4ef7289137132959e3db5a1e77580ff9db185d90 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 8 Feb 2008 01:13:54 +0300
+Subject: [PATCH 63/64] patch tosa-bat-jacket-detect
+
+---
+ drivers/power/tosa_battery.c | 21 +++++++++++++++------
+ 1 files changed, 15 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c
+index 2db9116..70beed2 100644
+--- a/drivers/power/tosa_battery.c
++++ b/drivers/power/tosa_battery.c
+@@ -137,8 +137,7 @@ static int tosa_bat_get_property(struct power_supply *psy,
+ }
+
+ static bool tosa_jacket_bat_is_present(struct tosa_bat *bat) {
+- // FIXME
+- return 1;
++ return gpio_get_value(TOSA_GPIO_JACKET_DETECT) == 0;
+ }
+
+ static void tosa_bat_external_power_changed(struct power_supply *psy)
+@@ -146,9 +145,9 @@ static void tosa_bat_external_power_changed(struct power_supply *psy)
+ schedule_work(&bat_work);
+ }
+
+-static irqreturn_t tosa_bat_full_isr(int irq, void *data)
++static irqreturn_t tosa_bat_gpio_isr(int irq, void *data)
+ {
+- printk(KERN_ERR "bat_full irq: %d\n", gpio_get_value(irq_to_gpio(irq)));
++ printk(KERN_ERR "bat_gpio irq: %d\n", gpio_get_value(irq_to_gpio(irq)));
+ schedule_work(&bat_work);
+ return IRQ_HANDLED;
+ }
+@@ -334,6 +333,7 @@ static struct {
+ { TOSA_GPIO_BAT1_CRG, "jacket battery full", 0, 0 },
+ { TOSA_GPIO_BAT0_LOW, "main battery low", 0, 0 },
+ { TOSA_GPIO_BAT1_LOW, "jacket battery low", 0, 0 },
++ { TOSA_GPIO_JACKET_DETECT, "jacket detect", 0, 0 },
+ };
+
+ #ifdef CONFIG_PM
+@@ -395,19 +395,27 @@ static int __devinit tosa_bat_probe(struct platform_device *dev)
+ goto err_psy_reg_bu;
+
+ ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG),
+- tosa_bat_full_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ tosa_bat_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "main full", &tosa_bat_main);
+ if (ret)
+ goto err_req_main;
+
+ ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG),
+- tosa_bat_full_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ tosa_bat_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "jacket full", &tosa_bat_jacket);
++ if (ret)
++ goto err_req_jacket;
++
++ ret = request_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT),
++ tosa_bat_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ "jacket detect", &tosa_bat_jacket);
+ if (!ret) {
+ schedule_work(&bat_work);
+ return 0;
+ }
+
++ free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket);
++err_req_jacket:
+ free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
+ err_req_main:
+ power_supply_unregister(&tosa_bat_bu.psy);
+@@ -429,6 +437,7 @@ static int __devexit tosa_bat_remove(struct platform_device *dev)
+ {
+ int i;
+
++ free_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT), &tosa_bat_jacket);
+ free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket);
+ free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
+
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0064-Export-modes-via-sysfs.patch b/recipes/linux/linux-rp-2.6.24/tosa/0064-Export-modes-via-sysfs.patch
new file mode 100644
index 0000000000..eeb92cfdd5
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0064-Export-modes-via-sysfs.patch
@@ -0,0 +1,27 @@
+From feeee5d22c00d9d1e2e06eb5610740be238749b9 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Thu, 7 Feb 2008 22:27:38 +0300
+Subject: [PATCH 64/64] Export modes via sysfs
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/video/tmiofb.c | 3 +++
+ 1 files changed, 3 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
+index 6b963a1..9389a77 100644
+--- a/drivers/video/tmiofb.c
++++ b/drivers/video/tmiofb.c
+@@ -800,6 +800,9 @@ static int tmiofb_probe(struct platform_device *dev)
+ if (retval)
+ goto err_set_par;*/
+
++ fb_videomode_to_modelist(data->modes, data->num_modes,
++ &info->modelist);
++
+ retval = register_framebuffer(info);
+ if (retval < 0)
+ goto err_register_framebuffer;
+--
+1.5.3.8
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0065-wm97xx-core-fixes.patch b/recipes/linux/linux-rp-2.6.24/tosa/0065-wm97xx-core-fixes.patch
new file mode 100644
index 0000000000..5db0cc6ba0
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0065-wm97xx-core-fixes.patch
@@ -0,0 +1,49 @@
+From 2544412fc47dc13f4f3935cb4c2fd500d217e905 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 13 Feb 2008 02:00:15 +0300
+Subject: [PATCH] wm97xx-core fixes
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/input/touchscreen/wm97xx-core.c | 8 ++++----
+ include/linux/wm97xx.h | 1 -
+ 2 files changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
+index 840d9ff..4cbb9e5 100644
+--- a/drivers/input/touchscreen/wm97xx-core.c
++++ b/drivers/input/touchscreen/wm97xx-core.c
+@@ -596,7 +596,7 @@ static int wm97xx_probe(struct device *dev)
+ }
+ platform_set_drvdata(wm->battery_dev, wm);
+ wm->battery_dev->dev.parent = dev;
+- ret = platform_device_register(wm->battery_dev);
++ ret = platform_device_add(wm->battery_dev);
+ if (ret < 0)
+ goto batt_reg_err;
+
+@@ -609,7 +609,7 @@ static int wm97xx_probe(struct device *dev)
+ }
+ platform_set_drvdata(wm->touch_dev, wm);
+ wm->touch_dev->dev.parent = dev;
+- ret = platform_device_register(wm->touch_dev);
++ ret = platform_device_add(wm->touch_dev);
+ if (ret < 0)
+ goto touch_reg_err;
+
+@@ -619,10 +619,12 @@ static int wm97xx_probe(struct device *dev)
+ platform_device_put(wm->touch_dev);
+ touch_err:
+ platform_device_unregister(wm->battery_dev);
++ wm->battery_dev = NULL;
+ batt_reg_err:
+ platform_device_put(wm->battery_dev);
+ batt_err:
+ input_unregister_device(wm->input_dev);
++ wm->input_dev = NULL;
+ kfree(wm);
+ return ret;
+ }
+--
+1.5.4.1
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0066-tmiofb_probe-should-be-__devinit.patch b/recipes/linux/linux-rp-2.6.24/tosa/0066-tmiofb_probe-should-be-__devinit.patch
new file mode 100644
index 0000000000..42320be50b
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0066-tmiofb_probe-should-be-__devinit.patch
@@ -0,0 +1,26 @@
+From b6a63ad546cc26519a342f3fdd7b1def49ca33ee Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 13 Feb 2008 02:00:14 +0300
+Subject: [PATCH] tmiofb_probe should be __devinit
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/video/tmiofb.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
+index 9389a77..958ee8a 100644
+--- a/drivers/video/tmiofb.c
++++ b/drivers/video/tmiofb.c
+@@ -704,7 +704,7 @@ static irqreturn_t tmiofb_irq(int irq, void *__info)
+ return IRQ_HANDLED;
+ }
+
+-static int tmiofb_probe(struct platform_device *dev)
++static int __devinit tmiofb_probe(struct platform_device *dev)
+ {
+ struct mfd_cell *cell = mfd_get_cell(dev);
+ struct tmio_fb_data *data = cell->driver_data;
+--
+1.5.4.1
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0067-modeswitching.patch b/recipes/linux/linux-rp-2.6.24/tosa/0067-modeswitching.patch
new file mode 100644
index 0000000000..42b69d9377
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0067-modeswitching.patch
@@ -0,0 +1,225 @@
+From aded2e51a7a2113dae6431c858df1d95fb312851 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Wed, 13 Feb 2008 19:34:08 +0300
+Subject: [PATCH] modeswitching
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ arch/arm/mach-pxa/tosa.c | 33 +++++++++++++++++++++++++++++++-
+ drivers/mfd/tc6393xb.c | 2 +-
+ drivers/video/backlight/tosa_bl.c | 38 +++++++++++++++++++++++++++++++++---
+ drivers/video/tmiofb.c | 8 +++---
+ include/asm-arm/arch-pxa/tosa.h | 2 +
+ include/linux/mfd/tc6393xb.h | 2 +-
+ include/linux/mfd/tmio.h | 2 +-
+ 7 files changed, 75 insertions(+), 12 deletions(-)
+
+diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
+index 94c9915..6631de2 100644
+--- a/arch/arm/mach-pxa/tosa.c
++++ b/arch/arm/mach-pxa/tosa.c
+@@ -455,9 +455,40 @@ static struct fb_videomode tosa_tc6393xb_lcd_mode[] = {
+ }
+ };
+
++static DEFINE_SPINLOCK(tosa_lcd_mode_lock);
++
++static const struct fb_videomode *tosa_lcd_mode = &tosa_tc6393xb_lcd_mode[0];
++
++static int tosa_lcd_set_mode(struct platform_device *fb_dev,
++ const struct fb_videomode *mode)
++{
++ int rc;
++ unsigned long flags;
++
++ spin_lock_irqsave(&tosa_lcd_mode_lock, flags);
++ rc = tc6393xb_lcd_mode(fb_dev, mode);
++ if (!rc)
++ tosa_lcd_mode = mode;
++ spin_unlock_irqrestore(&tosa_lcd_mode_lock, flags);
++
++ return rc;
++}
++
++const struct fb_videomode *tosa_lcd_get_mode(void)
++{
++ unsigned long flags;
++ const struct fb_videomode *mode;
++
++ spin_lock_irqsave(&tosa_lcd_mode_lock, flags);
++ mode = tosa_lcd_mode;
++ spin_unlock_irqrestore(&tosa_lcd_mode_lock, flags);
++
++ return mode;
++}
++
+ static struct tmio_fb_data tosa_tc6393xb_fb_config = {
+ .lcd_set_power = tc6393xb_lcd_set_power,
+- .lcd_mode = tc6393xb_lcd_mode,
++ .lcd_mode = tosa_lcd_set_mode,
+ .num_modes = ARRAY_SIZE(tosa_tc6393xb_lcd_mode),
+ .modes = &tosa_tc6393xb_lcd_mode[0],
+ };
+diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
+index 9001687..21190f3 100644
+--- a/drivers/mfd/tc6393xb.c
++++ b/drivers/mfd/tc6393xb.c
+@@ -196,7 +196,7 @@ int tc6393xb_lcd_set_power(struct platform_device *fb, bool on)
+ EXPORT_SYMBOL(tc6393xb_lcd_set_power);
+
+ int tc6393xb_lcd_mode(struct platform_device *fb_dev,
+- struct fb_videomode *mode) {
++ const struct fb_videomode *mode) {
+ struct tc6393xb *tc6393xb =
+ platform_get_drvdata(to_platform_device(fb_dev->dev.parent));
+ struct tc6393xb_scr __iomem *scr = tc6393xb->scr;
+diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c
+index 9ef0bfb..45fc6ae 100644
+--- a/drivers/video/backlight/tosa_bl.c
++++ b/drivers/video/backlight/tosa_bl.c
+@@ -48,6 +48,9 @@ struct tosa_bl_data {
+ struct ssp_dev nssp_dev;
+ struct ssp_state nssp_state;
+
++ /* listen for mode changes */
++ struct notifier_block fb_notif;
++
+ struct backlight_device *bl_dev;
+ };
+
+@@ -103,14 +106,19 @@ static void tosa_lcd_tg_init(struct tosa_bl_data *data)
+ pxa_nssp_output(data, TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
+ }
+
+-static void tosa_lcd_tg_on(struct tosa_bl_data *data/*, const struct fb_videomode *mode*/)
++static void tosa_lcd_tg_on(struct tosa_bl_data *data)
+ {
+- const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
++ const struct fb_videomode *mode = tosa_lcd_get_mode();
++
++ int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
++
++ if (mode->yres == 640)
++ value |= TG_REG0_VQV;
+
+ tosa_lcd_tg_init(data);
+
+- dev_dbg(&data->bl_dev->dev, "tosa_lcd_on\n");
+- pxa_nssp_output(data, TG_PNLCTL, value | (/*mode->yres == 320 ? 0 : */ TG_REG0_VQV));
++ dev_dbg(&data->bl_dev->dev, "tosa_lcd_on: %04x (%d)\n", value, mode->yres);
++ pxa_nssp_output(data, TG_PNLCTL, value);
+
+ /* TG LCD pannel power up */
+ pxa_nssp_output(data, TG_PINICTL,0x4);
+@@ -173,6 +181,20 @@ static struct backlight_ops tosa_bl_ops = {
+ .update_status = tosa_bl_update_status,
+ };
+
++static int fb_notifier_callback(struct notifier_block *self,
++ unsigned long event, void *_data)
++{
++ struct tosa_bl_data *bl_data =
++ container_of(self, struct tosa_bl_data, fb_notif);
++
++ if (event != FB_EVENT_MODE_CHANGE && event != FB_EVENT_MODE_CHANGE_ALL)
++ return 0;
++
++ tosa_bl_update_status(bl_data->bl_dev);
++
++ return 0;
++}
++
+ static int tosa_bl_detect_client(struct i2c_adapter *adapter, int address,
+ int kind)
+ {
+@@ -238,9 +260,15 @@ static int tosa_bl_detect_client(struct i2c_adapter *adapter, int address,
+ data->bl_dev->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(data->bl_dev);
+
++ data->fb_notif.notifier_call = fb_notifier_callback;
++ err = fb_register_client(&data->fb_notif);
++ if (err)
++ goto err_fb_register;
+
+ return 0;
+
++err_fb_register:
++ backlight_device_unregister(data->bl_dev);
+ err_bl_register:
+ tosa_set_backlight(data, 0);
+ tosa_lcd_tg_off(data);
+@@ -265,6 +293,8 @@ static int tosa_bl_detach_client(struct i2c_client *client)
+ int err = 0;
+ struct tosa_bl_data *data = i2c_get_clientdata(client);
+
++ fb_unregister_client(&data->fb_notif);
++
+ backlight_device_unregister(data->bl_dev);
+
+ tosa_set_backlight(data, 0);
+diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
+index 958ee8a..cc75df9 100644
+--- a/drivers/video/tmiofb.c
++++ b/drivers/video/tmiofb.c
+@@ -618,17 +618,17 @@ static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+
+ static int tmiofb_set_par(struct fb_info *info)
+ {
+-/* struct fb_var_screeninfo *var = &info->var;
++ struct fb_var_screeninfo *var = &info->var;
+ struct fb_videomode *mode;
+
+ mode = tmiofb_find_mode(info, var);
+ if (!mode)
+ return -EINVAL;
+
+- if (info->mode == mode)
+- return 0;
++/* if (info->mode == mode)
++ return 0;*/
+
+- info->mode = mode; */
++ info->mode = mode;
+ info->fix.line_length = info->mode->xres * 2;
+
+ tmiofb_hw_mode(to_platform_device(info->device));
+diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h
+index 410fa9a..624c636 100644
+--- a/include/asm-arm/arch-pxa/tosa.h
++++ b/include/asm-arm/arch-pxa/tosa.h
+@@ -230,4 +230,6 @@ extern struct platform_device tosascoop_device;
+ #define TOSA_KEY_MAIL KEY_MAIL
+ #endif
+
++const struct fb_videomode *tosa_lcd_get_mode(void);
++
+ #endif /* _ASM_ARCH_TOSA_H_ */
+diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h
+index 97c4c7c..8ab9e91 100644
+--- a/include/linux/mfd/tc6393xb.h
++++ b/include/linux/mfd/tc6393xb.h
+@@ -53,7 +53,7 @@ struct tc6393xb_platform_data {
+
+ extern int tc6393xb_lcd_set_power(struct platform_device *fb_dev, bool on);
+ extern int tc6393xb_lcd_mode(struct platform_device *fb_dev,
+- struct fb_videomode *mode);
++ const struct fb_videomode *mode);
+
+ /*
+ * Relative to irq_base
+diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
+index b6d4aac..fe7ff2d 100644
+--- a/include/linux/mfd/tmio.h
++++ b/include/linux/mfd/tmio.h
+@@ -19,7 +19,7 @@ struct tmio_fb_data {
+ int (*lcd_set_power)(struct platform_device *fb_dev,
+ bool on);
+ int (*lcd_mode)(struct platform_device *fb_dev,
+- struct fb_videomode *mode);
++ const struct fb_videomode *mode);
+ int num_modes;
+ struct fb_videomode *modes;
+ };
+--
+1.5.4.1
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/0068-Preliminary-tosa-denoiser.patch b/recipes/linux/linux-rp-2.6.24/tosa/0068-Preliminary-tosa-denoiser.patch
new file mode 100644
index 0000000000..e90e3751c0
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/0068-Preliminary-tosa-denoiser.patch
@@ -0,0 +1,239 @@
+From f7dad1cd9c1bd3fce5d228e0b644a51baea50cd9 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dbaryshkov@gmail.com>
+Date: Fri, 15 Feb 2008 15:35:07 +0300
+Subject: [PATCH] Preliminary tosa denoiser
+
+Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
+---
+ drivers/input/touchscreen/Kconfig | 12 ++
+ drivers/input/touchscreen/Makefile | 1 +
+ drivers/input/touchscreen/tosa-wm97xx.c | 182 +++++++++++++++++++++++++++++++
+ 3 files changed, 195 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/input/touchscreen/tosa-wm97xx.c
+
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index 0be05a2..938aed2 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -210,6 +210,18 @@ config TOUCHSCREEN_WM97XX_MAINSTONE
+ To compile this driver as a module, choose M here: the
+ module will be called mainstone-wm97xx
+
++config TOUCHSCREEN_WM97XX_TOSA
++ tristate "WM97xx Tosa denoiser"
++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA
++ help
++ Say Y here for support for touchscreen denoising on
++ Sharl Zaurus SL-6000 (tosa) system.
++
++ If unsure, say N
++
++ To compile this driver as a module, choose M here: the
++ module will be called tosa-wm97xx
++
+ config TOUCHSCREEN_TOUCHWIN
+ tristate "Touchwin serial touchscreen"
+ select SERIO
+diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
+index d38156e..d86278b 100644
+--- a/drivers/input/touchscreen/Makefile
++++ b/drivers/input/touchscreen/Makefile
+@@ -23,6 +23,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
+ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
++obj-$(CONFIG_TOUCHSCREEN_WM97XX_TOSA) += tosa-wm97xx.o
+ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
+ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
+ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
+diff --git a/drivers/input/touchscreen/tosa-wm97xx.c b/drivers/input/touchscreen/tosa-wm97xx.c
+new file mode 100644
+index 0000000..8fd542b
+--- /dev/null
++++ b/drivers/input/touchscreen/tosa-wm97xx.c
+@@ -0,0 +1,182 @@
++/*
++ * tosa_ts.c -- Touchscreen driver for Sharp SL-6000 (Tosa).
++ *
++ * Copyright 2008 Dmitry Baryshkov
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Mike Arthur
++ * linux@wolfsonmicro.com
++ *
++ * 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.
++ *
++ * Revision history
++ * 1st Sep 2006 Initial version.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/wm97xx.h>
++#include <linux/fb.h>
++
++#include <asm/arch/tosa.h>
++#include <asm/gpio.h>
++
++static unsigned long hsync_time = 0;
++
++static void calc_hsync_time(const struct fb_videomode *mode)
++{
++ /* The 25 and 44 'magic numbers' are from Sharp's 2.4 patches */
++ if (mode->yres == 640) {
++ hsync_time = 25;
++ } else if (mode->yres == 320) {
++ hsync_time = 44;
++ } else {
++ printk(KERN_ERR "unknown video mode res specified: %dx%d!", mode->xres, mode->yres);
++ WARN_ON(1);
++ }
++
++ printk(KERN_ERR "tosa-wm97xx: using %lu hsync time\n", hsync_time);
++}
++
++static int fb_notifier_callback(struct notifier_block *self,
++ unsigned long event, void *_data)
++{
++ if (event != FB_EVENT_MODE_CHANGE && event != FB_EVENT_MODE_CHANGE_ALL)
++ return 0;
++
++ calc_hsync_time(tosa_lcd_get_mode());
++
++ return 0;
++}
++
++static void tosa_lcd_wait_hsync(void)
++{
++ /* Waits for a rising edge on the VGA line */
++ while (gpio_get_value(TOSA_GPIO_VGA_LINE) == 0);
++ while (gpio_get_value(TOSA_GPIO_VGA_LINE) != 0);
++}
++
++/* Taken from the Sharp 2.4 kernel code */
++#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a))
++#define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1))
++#define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(0))
++
++/* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait
++ * before sampling the Y axis of the touchscreen */
++static void tosa_lcd_sync_on(int adcsel)
++{
++ unsigned long timer1 = 0, timer2 = 0, wait_time = 0;
++ if (adcsel & WM97XX_ADCSEL_Y) {
++ CCNT_ON();
++ wait_time = hsync_time;
++
++ if (wait_time) {
++
++ /* wait for LCD rising edge */
++ tosa_lcd_wait_hsync();
++ /* get clock */
++ CCNT(timer1);
++ CCNT(timer2);
++
++ while ((timer2 - timer1) < wait_time) {
++ CCNT(timer2);
++ }
++ }
++ }
++}
++
++static void tosa_lcd_sync_off(int adcsel)
++{
++ if (adcsel & WM97XX_ADCSEL_Y)
++ CCNT_OFF();
++}
++
++static struct wm97xx_mach_ops tosa_mach_ops = {
++ .pre_sample = tosa_lcd_sync_on,
++ .post_sample = tosa_lcd_sync_off,
++};
++
++static int __devinit tosa_ts_probe(struct platform_device *dev) {
++ struct wm97xx *wm = platform_get_drvdata(dev);
++ struct notifier_block *notif;
++ int err = -ENOMEM;
++
++ notif = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
++ if (!notif)
++ goto err_alloc;
++
++ notif->notifier_call = fb_notifier_callback;
++
++ err = gpio_request(TOSA_GPIO_VGA_LINE, "hsync");
++ if (err)
++ goto err_gpio;
++
++ err = gpio_direction_input(TOSA_GPIO_VGA_LINE);
++ if (err)
++ goto err_gpio;
++
++ platform_set_drvdata(dev, notif);
++
++ err = fb_register_client(notif);
++ if (err)
++ goto err_register;
++
++ err = wm97xx_register_mach_ops(wm, &tosa_mach_ops);
++ if (err)
++ goto err_wm97xx;
++
++ calc_hsync_time(tosa_lcd_get_mode());
++
++ return 0;
++
++err_wm97xx:
++ fb_unregister_client(notif);
++err_register:
++ gpio_free(TOSA_GPIO_VGA_LINE);
++err_gpio:
++ kfree(notif);
++err_alloc:
++ return err;
++}
++
++
++static int __devexit tosa_ts_remove(struct platform_device *dev) {
++ struct wm97xx *wm = platform_get_drvdata(dev);
++
++ wm97xx_unregister_mach_ops(wm);
++
++ fb_unregister_client(platform_get_drvdata(dev));
++ gpio_free(TOSA_GPIO_VGA_LINE);
++ kfree(platform_get_drvdata(dev));
++
++ return 0;
++}
++
++static struct platform_driver tosa_ts_driver = {
++ .driver.name = "wm97xx-touch",
++ .driver.owner = THIS_MODULE,
++ .probe = tosa_ts_probe,
++ .remove = __devexit_p(tosa_ts_remove),
++};
++
++static int __init tosa_ts_init(void)
++{
++ return platform_driver_register(&tosa_ts_driver);
++}
++
++static void __exit tosa_ts_exit(void)
++{
++ platform_driver_unregister(&tosa_ts_driver);
++}
++
++module_init(tosa_ts_init);
++module_exit(tosa_ts_exit);
++
++/* Module information */
++MODULE_AUTHOR("Dmitry Baryshkov, Mike Arthur, mike@mikearthur.co.uk, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("Sharp SL6000 Tosa Touch Screen Denoiser");
++MODULE_LICENSE("GPL");
+--
+1.5.4.1
+
diff --git a/recipes/linux/linux-rp-2.6.24/tosa/tosa-bl-fixup.diff b/recipes/linux/linux-rp-2.6.24/tosa/tosa-bl-fixup.diff
new file mode 100644
index 0000000000..c4a23d1f49
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.24/tosa/tosa-bl-fixup.diff
@@ -0,0 +1,103 @@
+Index: linux-2.6.24/drivers/video/backlight/tosa_bl.c
+===================================================================
+--- linux-2.6.24.orig/drivers/video/backlight/tosa_bl.c 2008-11-15 22:59:51.592985003 +0300
++++ linux-2.6.24/drivers/video/backlight/tosa_bl.c 2008-11-18 04:08:13.021416618 +0300
+@@ -76,6 +76,8 @@ static void pxa_nssp_output(struct tosa_
+
+ static void tosa_set_backlight(struct tosa_bl_data *data, int brightness)
+ {
++ pr_debug("tosa_set_backlight\n");
++
+ /* SetBacklightDuty */
+ i2c_smbus_write_byte_data(&data->client, DAC_CH2, (unsigned char)brightness);
+
+@@ -91,7 +93,7 @@ static void tosa_set_backlight(struct to
+
+ static void tosa_lcd_tg_init(struct tosa_bl_data *data)
+ {
+- dev_dbg(&data->bl_dev->dev, "tosa_lcd_init\n");
++ pr_debug("tosa_lcd_init\n");
+
+ /* L3V On */
+ set_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393XB_L3V_ON);
+@@ -116,7 +118,7 @@ static void tosa_lcd_tg_on(struct tosa_b
+
+ tosa_lcd_tg_init(data);
+
+- dev_dbg(&data->bl_dev->dev, "tosa_lcd_on: %04x (%d)\n", value, mode->yres);
++ pr_debug("tosa_lcd_on: %04x (%d)\n", value, mode->yres);
+ pxa_nssp_output(data, TG_PNLCTL, value);
+
+ /* TG LCD pannel power up */
+@@ -129,12 +131,15 @@ static void tosa_lcd_tg_on(struct tosa_b
+
+ /* set common voltage */
+ i2c_smbus_write_byte_data(&data->client, DAC_CH1, data->comadj);
++
++ tosa_set_backlight(data, data->bl_dev->props.brightness);
++
+ }
+
+ static void tosa_lcd_tg_off(struct tosa_bl_data *data)
+ {
+ tosa_set_backlight(data, 0);
+- dev_dbg(&data->bl_dev->dev, "tosa_lcd_off\n");
++ pr_debug("tosa_lcd_off\n");
+ /* TG LCD VHSA off */
+ pxa_nssp_output(data, TG_PINICTL,0x4);
+ mdelay(50);
+@@ -158,8 +163,6 @@ static int tosa_bl_update_status(struct
+ struct tosa_bl_data *data = dev_get_drvdata(&dev->dev);
+ int new_power = max(props->power, props->fb_blank);
+
+- tosa_set_backlight(data, props->brightness);
+-
+ if (new_power)
+ tosa_lcd_tg_off(data);
+ else
+@@ -223,22 +226,26 @@ static int tosa_bl_detect_client(struct
+
+ err = gpio_request(TOSA_TC6393XB_BL_C20MA, "backlight");
+ if (err) {
+- dev_dbg(&data->bl_dev->dev, "Unable to request gpio!\n");
++ printk(KERN_ERR "tosa-bl; Unable to request gpio!\n");
+ goto err_gpio_bl;
+ }
+
+ err = gpio_request(TOSA_TC6393XB_TG_ON, "tg");
+ if (err) {
+- dev_dbg(&data->bl_dev->dev, "Unable to request gpio!\n");
++ printk(KERN_ERR "tosa-bl: Unable to request gpio!\n");
+ goto err_gpio_tg;
+ }
+
+ err = ssp_init(&data->nssp_dev,2,0);
+ if (err) {
+- dev_err(&data->bl_dev->dev, "Unable to register NSSP handler!\n");
++ printk(KERN_ERR "tosa-bl: Unable to register NSSP handler!\n");
+ goto err_ssp_init;
+ }
+
++ pxa_gpio_mode(GPIO83_NSSP_TX);
++ pxa_gpio_mode(GPIO81_NSSP_CLK_OUT);
++ pxa_gpio_mode(GPIO82_NSSP_FRM_OUT);
++
+ /* Tell the i2c layer a new client has arrived */
+ err = i2c_attach_client(client);
+ if (err)
+@@ -269,7 +276,6 @@ static int tosa_bl_detect_client(struct
+ err_fb_register:
+ backlight_device_unregister(data->bl_dev);
+ err_bl_register:
+- tosa_set_backlight(data, 0);
+ tosa_lcd_tg_off(data);
+
+ err = i2c_detach_client(client);
+@@ -296,7 +302,6 @@ static int tosa_bl_detach_client(struct
+
+ backlight_device_unregister(data->bl_dev);
+
+- tosa_set_backlight(data, 0);
+ tosa_lcd_tg_off(data);
+
+ /* Try to detach the client from i2c space */