summaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux/simpad
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux/simpad')
-rw-r--r--recipes/linux/linux/simpad/collie-kexec.patch13
-rw-r--r--recipes/linux/linux/simpad/connectplus-remove-ide-HACK.patch12
-rw-r--r--recipes/linux/linux/simpad/export_atags-r2.patch320
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.21-SIMpad-GPIO-MMC-mod.patch1650
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.21-SIMpad-battery-old-way-but-also-with-sysfs.patch571
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.21-SIMpad-cs3-simpad.patch184
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.21-SIMpad-mq200.patch2511
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.21-SIMpad-net-shared-irq.patch63
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.21-SIMpad-pcmcia.patch162
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.21-SIMpad-serial-gpio_keys-and-cs3-ro.patch332
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-switches.patch359
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-ts-supend-and-accuracy.patch121
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.21-SIMpad-usb-gadget.patch3329
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-GPIO-MMC-mod.patch1699
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-battery-old-way-but-also-with-sysfs.patch571
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-cs3-simpad.patch184
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-hostap_cs-shared-irq.patch106
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-mq200.patch2513
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-orinoco_cs-shared-irq.patch21
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-pcmcia.patch209
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-rtc-sa1100.patch68
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2358
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch1622
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch359
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-ts-supend-and-accuracy.patch121
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.27-SIMpad-GPIO-MMC-mod.patch1701
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.27-SIMpad-battery-old-way-but-also-with-sysfs.patch577
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.27-SIMpad-cs3-simpad.patch192
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.27-SIMpad-mq200.patch2640
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.27-SIMpad-pcmcia.patch226
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.27-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2358
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-switches.patch362
-rw-r--r--recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-ts-supend-and-accuracy.patch105
33 files changed, 23619 insertions, 0 deletions
diff --git a/recipes/linux/linux/simpad/collie-kexec.patch b/recipes/linux/linux/simpad/collie-kexec.patch
new file mode 100644
index 0000000000..2c8fa9352b
--- /dev/null
+++ b/recipes/linux/linux/simpad/collie-kexec.patch
@@ -0,0 +1,13 @@
+--- linux-2.6.20.4.orig/include/asm-arm/kexec.h 2007-04-10 19:16:39.000000000 +0200
++++ linux-2.6.20.4/include/asm-arm/kexec.h 2007-04-03 05:01:09.000000000 +0200
+@@ -8,8 +8,8 @@
+ /* Maximum address we can reach in physical address mode */
+ #define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL)
+ /* Maximum address we can use for the control code buffer */
+-#define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE
+-
++//#define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE
++#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL)
+ #define KEXEC_CONTROL_CODE_SIZE 4096
+
+ #define KEXEC_ARCH KEXEC_ARCH_ARM
diff --git a/recipes/linux/linux/simpad/connectplus-remove-ide-HACK.patch b/recipes/linux/linux/simpad/connectplus-remove-ide-HACK.patch
new file mode 100644
index 0000000000..4414b21191
--- /dev/null
+++ b/recipes/linux/linux/simpad/connectplus-remove-ide-HACK.patch
@@ -0,0 +1,12 @@
+Index: linux-2.6.13/drivers/ide/legacy/ide-cs.c
+===================================================================
+--- linux-2.6.13.orig/drivers/ide/legacy/ide-cs.c 2005-09-01 22:43:46.000000000 +0100
++++ linux-2.6.13/drivers/ide/legacy/ide-cs.c 2005-09-01 22:45:46.000000000 +0100
+@@ -488,7 +488,6 @@
+ PCMCIA_DEVICE_PROD_ID123("KODAK Picture Card ", "KODAK ", "V100K", 0x94a0d8f3, 0xe4fc3ea0, 0xe5e7eed4),
+ PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209),
+ PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e),
+- PCMCIA_MFC_DEVICE_PROD_ID12(1, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6),
+ PCMCIA_DEVICE_NULL,
+ };
+ MODULE_DEVICE_TABLE(pcmcia, ide_ids);
diff --git a/recipes/linux/linux/simpad/export_atags-r2.patch b/recipes/linux/linux/simpad/export_atags-r2.patch
new file mode 100644
index 0000000000..e6fd85cf20
--- /dev/null
+++ b/recipes/linux/linux/simpad/export_atags-r2.patch
@@ -0,0 +1,320 @@
+[PATCH] Export atags to userspace and allow kexec to use customised atags
+
+Currently, the atags used by kexec are fixed to the ones originally used
+to boot the kernel. This is less than ideal as changing the commandline,
+initrd and other options would be a useful feature.
+
+This patch exports the atags used for the current kernel to userspace
+through an "atags" file in procfs. The presence of the file is
+controlled by its own Kconfig option and cleans up several ifdef blocks
+into a separate file. The tags for the new kernel are assumed to be at
+a fixed location before the kernel image itself. The location of the
+tags used to boot the original kernel is unimportant and no longer
+saved.
+
+Based on a patch from Uli Luckas <u.luckas@road.de>
+
+Signed-off-by: Richard Purdie <rpurdie@rpsys.net>
+
+---
+ arch/arm/Kconfig | 7 +++
+ arch/arm/kernel/Makefile | 1
+ arch/arm/kernel/atags.c | 86 ++++++++++++++++++++++++++++++++++++++
+ arch/arm/kernel/atags.h | 5 ++
+ arch/arm/kernel/machine_kexec.c | 2
+ arch/arm/kernel/relocate_kernel.S | 30 +------------
+ arch/arm/kernel/setup.c | 32 --------------
+ include/asm-arm/kexec.h | 3 +
+ 8 files changed, 110 insertions(+), 56 deletions(-)
+
+Index: linux-2.6.23/arch/arm/kernel/atags.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/arch/arm/kernel/atags.c 2008-01-20 15:56:02.000000000 +0000
+@@ -0,0 +1,86 @@
++#include <linux/slab.h>
++#include <linux/kexec.h>
++#include <linux/proc_fs.h>
++#include <asm/setup.h>
++#include <asm/types.h>
++#include <asm/page.h>
++
++struct buffer {
++ size_t size;
++ char *data;
++};
++static struct buffer tags_buffer;
++
++static int
++read_buffer(char* page, char** start, off_t off, int count,
++ int* eof, void* data)
++{
++ struct buffer *buffer = (struct buffer *)data;
++
++ if (off >= buffer->size) {
++ *eof = 1;
++ return 0;
++ }
++
++ count = min((int) (buffer->size - off), count);
++
++ memcpy(page, &buffer->data[off], count);
++
++ return count;
++}
++
++
++static int
++create_proc_entries(void)
++{
++ struct proc_dir_entry* tags_entry;
++
++ tags_entry = create_proc_read_entry("atags", 0400, &proc_root, read_buffer, &tags_buffer);
++ if (!tags_entry)
++ return -ENOMEM;
++
++ return 0;
++}
++
++
++static char __initdata atags_copy_buf[KEXEC_BOOT_PARAMS_SIZE];
++static char __initdata *atags_copy;
++
++void __init save_atags(const struct tag *tags)
++{
++ atags_copy = atags_copy_buf;
++ memcpy(atags_copy, tags, KEXEC_BOOT_PARAMS_SIZE);
++}
++
++
++static int __init init_atags_procfs(void)
++{
++ struct tag *tag;
++ int error;
++
++ if (!atags_copy) {
++ printk(KERN_WARNING "Exporting ATAGs: No saved tags found\n");
++ return -EIO;
++ }
++
++ for (tag = (struct tag *) atags_copy; tag->hdr.size; tag = tag_next(tag))
++ ;
++
++ tags_buffer.size = ((char *) tag - atags_copy) + sizeof(tag->hdr);
++ tags_buffer.data = kmalloc(tags_buffer.size, GFP_KERNEL);
++ if (tags_buffer.data == NULL)
++ return -ENOMEM;
++ memcpy(tags_buffer.data, atags_copy, tags_buffer.size);
++
++ error = create_proc_entries();
++ if (error) {
++ printk(KERN_ERR "Exporting ATAGs: not enough memory\n");
++ kfree(tags_buffer.data);
++ tags_buffer.size = 0;
++ tags_buffer.data = NULL;
++ }
++
++ return error;
++}
++
++arch_initcall(init_atags_procfs);
+Index: linux-2.6.23/arch/arm/kernel/atags.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/arch/arm/kernel/atags.h 2008-01-20 15:56:02.000000000 +0000
+@@ -0,0 +1,5 @@
++#ifdef CONFIG_ATAGS_PROC
++extern void save_atags(struct tag *tags);
++#else
++static inline void save_atags(struct tag *tags) { }
++#endif
+Index: linux-2.6.23/arch/arm/kernel/Makefile
+===================================================================
+--- linux-2.6.23.orig/arch/arm/kernel/Makefile 2007-10-09 21:31:38.000000000 +0100
++++ linux-2.6.23/arch/arm/kernel/Makefile 2008-01-20 15:56:02.000000000 +0000
+@@ -19,6 +19,7 @@ obj-$(CONFIG_ISA_DMA) += dma-isa.o
+ obj-$(CONFIG_PCI) += bios32.o isa.o
+ obj-$(CONFIG_SMP) += smp.o
+ obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
++obj-$(CONFIG_ATAGS_PROC) += atags.o
+ obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
+
+ obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
+Index: linux-2.6.23/arch/arm/kernel/setup.c
+===================================================================
+--- linux-2.6.23.orig/arch/arm/kernel/setup.c 2008-01-20 15:55:43.000000000 +0000
++++ linux-2.6.23/arch/arm/kernel/setup.c 2008-01-20 15:56:02.000000000 +0000
+@@ -24,7 +24,6 @@
+ #include <linux/interrupt.h>
+ #include <linux/smp.h>
+ #include <linux/fs.h>
+-#include <linux/kexec.h>
+
+ #include <asm/cpu.h>
+ #include <asm/elf.h>
+@@ -39,6 +38,7 @@
+ #include <asm/mach/time.h>
+
+ #include "compat.h"
++#include "atags.h"
+
+ #ifndef MEM_SIZE
+ #define MEM_SIZE (16*1024*1024)
+@@ -784,23 +784,6 @@ static int __init customize_machine(void
+ }
+ arch_initcall(customize_machine);
+
+-#ifdef CONFIG_KEXEC
+-
+-/* Physical addr of where the boot params should be for this machine */
+-extern unsigned long kexec_boot_params_address;
+-
+-/* Physical addr of the buffer into which the boot params are copied */
+-extern unsigned long kexec_boot_params_copy;
+-
+-/* Pointer to the boot params buffer, for manipulation and display */
+-unsigned long kexec_boot_params;
+-EXPORT_SYMBOL(kexec_boot_params);
+-
+-/* The buffer itself - make sure it is sized correctly */
+-static unsigned long kexec_boot_params_buf[(KEXEC_BOOT_PARAMS_SIZE + 3) / 4];
+-
+-#endif
+-
+ void __init setup_arch(char **cmdline_p)
+ {
+ struct tag *tags = (struct tag *)&init_tags;
+@@ -819,18 +802,6 @@ void __init setup_arch(char **cmdline_p)
+ else if (mdesc->boot_params)
+ tags = phys_to_virt(mdesc->boot_params);
+
+-#ifdef CONFIG_KEXEC
+- kexec_boot_params_copy = virt_to_phys(kexec_boot_params_buf);
+- kexec_boot_params = (unsigned long)kexec_boot_params_buf;
+- if (__atags_pointer) {
+- kexec_boot_params_address = __atags_pointer;
+- memcpy((void *)kexec_boot_params, tags, KEXEC_BOOT_PARAMS_SIZE);
+- } else if (mdesc->boot_params) {
+- kexec_boot_params_address = mdesc->boot_params;
+- memcpy((void *)kexec_boot_params, tags, KEXEC_BOOT_PARAMS_SIZE);
+- }
+-#endif
+-
+ /*
+ * If we have the old style parameters, convert them to
+ * a tag list.
+@@ -846,6 +817,7 @@ void __init setup_arch(char **cmdline_p)
+ if (tags->hdr.tag == ATAG_CORE) {
+ if (meminfo.nr_banks != 0)
+ squash_mem_tags(tags);
++ save_atags(tags);
+ parse_tags(tags);
+ }
+
+Index: linux-2.6.23/arch/arm/Kconfig
+===================================================================
+--- linux-2.6.23.orig/arch/arm/Kconfig 2008-01-20 15:55:43.000000000 +0000
++++ linux-2.6.23/arch/arm/Kconfig 2008-01-20 15:58:52.000000000 +0000
+@@ -865,6 +865,13 @@ config KEXEC
+ initially work for you. It may help to enable device hotplugging
+ support.
+
++config ATAGS_PROC
++ bool "Export atags in procfs"
++ default n
++ help
++ Should the atags used to boot the kernel be exported in an "atags"
++ file in procfs. Useful with kexec.
++
+ endmenu
+
+ if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX )
+Index: linux-2.6.23/arch/arm/kernel/machine_kexec.c
+===================================================================
+--- linux-2.6.23.orig/arch/arm/kernel/machine_kexec.c 2007-10-09 21:31:38.000000000 +0100
++++ linux-2.6.23/arch/arm/kernel/machine_kexec.c 2008-01-20 15:56:03.000000000 +0000
+@@ -21,6 +21,7 @@ extern void setup_mm_for_reboot(char mod
+ extern unsigned long kexec_start_address;
+ extern unsigned long kexec_indirection_page;
+ extern unsigned long kexec_mach_type;
++extern unsigned long kexec_boot_atags;
+
+ /*
+ * Provide a dummy crash_notes definition while crash dump arrives to arm.
+@@ -62,6 +63,7 @@ void machine_kexec(struct kimage *image)
+ kexec_start_address = image->start;
+ kexec_indirection_page = page_list;
+ kexec_mach_type = machine_arch_type;
++ kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET;
+
+ /* copy our kernel relocation code to the control code page */
+ memcpy(reboot_code_buffer,
+Index: linux-2.6.23/arch/arm/kernel/relocate_kernel.S
+===================================================================
+--- linux-2.6.23.orig/arch/arm/kernel/relocate_kernel.S 2008-01-20 15:55:43.000000000 +0000
++++ linux-2.6.23/arch/arm/kernel/relocate_kernel.S 2008-01-20 15:56:03.000000000 +0000
+@@ -7,23 +7,6 @@
+ .globl relocate_new_kernel
+ relocate_new_kernel:
+
+- /* Move boot params back to where the kernel expects them */
+-
+- ldr r0,kexec_boot_params_address
+- teq r0,#0
+- beq 8f
+-
+- ldr r1,kexec_boot_params_copy
+- mov r6,#KEXEC_BOOT_PARAMS_SIZE/4
+-7:
+- ldr r5,[r1],#4
+- str r5,[r0],#4
+- subs r6,r6,#1
+- bne 7b
+-
+-8:
+- /* Boot params moved, now go on with the kernel */
+-
+ ldr r0,kexec_indirection_page
+ ldr r1,kexec_start_address
+
+@@ -67,7 +50,7 @@ relocate_new_kernel:
+ mov lr,r1
+ mov r0,#0
+ ldr r1,kexec_mach_type
+- ldr r2,kexec_boot_params_address
++ ldr r2,kexec_boot_atags
+ mov pc,lr
+
+ .globl kexec_start_address
+@@ -82,14 +65,9 @@ kexec_indirection_page:
+ kexec_mach_type:
+ .long 0x0
+
+- /* phy addr where new kernel will expect to find boot params */
+- .globl kexec_boot_params_address
+-kexec_boot_params_address:
+- .long 0x0
+-
+- /* phy addr where old kernel put a copy of orig boot params */
+- .globl kexec_boot_params_copy
+-kexec_boot_params_copy:
++ /* phy addr of the atags for the new kernel */
++ .globl kexec_boot_atags
++kexec_boot_atags:
+ .long 0x0
+
+ relocate_new_kernel_end:
+Index: linux-2.6.23/include/asm-arm/kexec.h
+===================================================================
+--- linux-2.6.23.orig/include/asm-arm/kexec.h 2008-01-20 15:55:57.000000000 +0000
++++ linux-2.6.23/include/asm-arm/kexec.h 2008-01-20 15:56:03.000000000 +0000
+@@ -16,6 +16,9 @@
+
+ #define KEXEC_BOOT_PARAMS_SIZE 1536
+
++#define KEXEC_ARM_ATAGS_OFFSET 0x1000
++#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
++
+ #ifndef __ASSEMBLY__
+
+ struct kimage;
diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-GPIO-MMC-mod.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-GPIO-MMC-mod.patch
new file mode 100644
index 0000000000..22f220de8a
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-GPIO-MMC-mod.patch
@@ -0,0 +1,1650 @@
+diff -uNr linux-2.6.21.vanilla/drivers/mmc/Kconfig linux-2.6.21/drivers/mmc/Kconfig
+--- linux-2.6.21.vanilla/drivers/mmc/Kconfig 2007-05-30 18:00:30.000000000 +0200
++++ linux-2.6.21/drivers/mmc/Kconfig 2007-05-30 18:26:18.000000000 +0200
+@@ -4,6 +4,12 @@
+
+ menu "MMC/SD Card support"
+
++config MMC_SPI_BLOCK
++ tristate "MMC support for SIMpad over GPIO"
++ help
++ Say Y here to enable MMC block device over GPIO
++ if you have done the MMC-Mod. For Module say M.
++
+ config MMC
+ tristate "MMC support"
+ help
+diff -uNr linux-2.6.21.vanilla/drivers/mmc/Makefile linux-2.6.21/drivers/mmc/Makefile
+--- linux-2.6.21.vanilla/drivers/mmc/Makefile 2007-05-30 18:00:30.000000000 +0200
++++ linux-2.6.21/drivers/mmc/Makefile 2007-05-30 18:26:18.000000000 +0200
+@@ -2,6 +2,8 @@
+ # Makefile for the kernel mmc device drivers.
+ #
+
++obj-$(CONFIG_MMC_SPI_BLOCK) += mmc_spi_block.o
++
+ #
+ # Core
+ #
+diff -uNr linux-2.6.21.vanilla/drivers/mmc/mmc_spi_block.c linux-2.6.21/drivers/mmc/mmc_spi_block.c
+--- linux-2.6.21.vanilla/drivers/mmc/mmc_spi_block.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/mmc/mmc_spi_block.c 2007-05-30 18:30:58.000000000 +0200
+@@ -0,0 +1,1618 @@
++/*
++ * Copyright (c) Cl�ent Ballabriga, 2005 - GPL
++ * Copyright (c) Guylhem Aznar, 2005 - GPL
++ *
++ * Please check http://externe.net/zaurus/simpad-bluetooth reference design first.
++ *
++ * Based on Madsuk/Rohde work on a MMC driver for the WRT54G.
++ *
++ * This is an ugly hack of a driver. I am surprised if it ever works!
++ * So please use a real driver or contribute one to the 2.4/2.6 mmc framework
++ *
++ * mrdata: ported to 2.6
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/errno.h>
++#include <linux/hdreg.h>
++#include <linux/kdev_t.h>
++#include <linux/blkdev.h>
++#include <linux/spinlock.h>
++#include <linux/time.h>
++#include <linux/delay.h>
++#include <linux/timer.h>
++
++#include <linux/platform_device.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/simpad.h>
++#include <asm/arch/gpio.h>
++
++static int major = 121;
++
++#define DEVICE_NAME "mmc_spi"
++
++static int hd_sizes[1<<6];
++static int hd_blocksizes[1<<6];
++static int hd_hardsectsizes[1<<6];
++static int hd_maxsect[1<<6];
++static struct hd_struct hd[1<<6];
++
++static struct gendisk *mmc_disk;
++
++static struct platform_device *mmc_dev; /* the one and only instance */
++
++static spinlock_t mmc_spi_lock;
++
++/*
++ * *******************************************************************
++ *
++ * This is the only configurable part.
++ *
++ * *******************************************************************
++ *
++ */
++
++// #define DEBUG 1
++// #define DEBUG_HD 1
++// #define CHECK_MEDIA_CHANGE // for developement ONLY, not working yet
++
++/* Let that include where it is or compilation fails on INIT_REQUEST/CURRENT */
++
++
++/*
++ * If you are using different GPIOs in your hardware hack, you must
++ * first make sure they are unused for other functions and then
++ * configure them here.
++ *
++ * On the simpad I use spare pins from the UART1 (internal serial port -> DECT 20-polig):
++ *
++ * Funktion PIN ## Original direction GPIO ## SPI function New direction SD/MMC
++ * - DCD PIN 08 (in) GPIO 23 DO - new name: DI -> MISO (in) PIN 7 Data Out
++ * - DTR PIN 11 (out) GPIO 07 CS (out) PIN 1 Chip Select
++ * - RI PIN 14 (in) GPIO 19 CLK (out) PIN 5 Clock
++ * - DSR PIN 16 (in) GPIO 06 DI - new name: DO -> MOSI (out) PIN 2 Data In
++ *
++ *
++ * SPI: MISO = Master In / Slave OUT MOSI = Master Out / Slave In
++ *
++ * Don't worry about in/out original function - the GPIOs will be
++ * reprogrammed.
++ */
++
++#define GPIO_SD_DI 23
++#define GPIO_SD_CS 7
++#define GPIO_SD_CLK 19
++#define GPIO_SD_DO 6
++
++// #define FAST_GPIO_SD_DI GPIO_GPIO23
++// #define FAST_GPIO_SD_CS GPIO_GPIO7
++// #define FAST_GPIO_SD_CLK GPIO_GPIO19
++// #define FAST_GPIO_SD_DO GPIO_GPIO6
++
++#define FAST_GPIO_SD_DI GPIO_UART1_DCD
++#define FAST_GPIO_SD_CS GPIO_UART1_DTR
++#define FAST_GPIO_SD_CLK GPIO_UART1_RI
++#define FAST_GPIO_SD_DO GPIO_UART1_DSR
++
++/*
++ * *******************************************************************
++ *
++ * Do not change anything below !
++ *
++ * *******************************************************************
++ *
++ */
++
++/* GPIO states */
++#define LOW 0
++#define HIGH 1
++
++#define INPUT 0
++#define OUTPUT 1
++
++#define PRESENT 1
++#define ABSENT 0
++
++typedef unsigned int uint32;
++typedef unsigned long u32_t;
++typedef unsigned short u16_t;
++typedef unsigned char u8_t;
++
++static struct timer_list mmc_timer;
++
++// static struct timeval s_zeit, e_zeit;
++
++/* start with no card */
++static int mmc_media_detect = 0;
++static int mmc_media_changed = 1;
++
++
++/////////////////////
++// prototypes
++static int mmc_open(struct inode *inode, struct file *filp);
++static int mmc_release(struct inode *inode, struct file *filp);
++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
++static void mmc_request(request_queue_t *q);
++
++
++/*
++ * *******************************************************************
++ *
++ * Begin GPIO hardware access functions.
++ *
++ * *******************************************************************
++ *
++ */
++
++#define gpio_read(a) ((GPLR & a) ? 1 : 0)
++#define gpio_write_high(a) GPSR = a
++#define gpio_write_low(a) GPCR = a
++
++/* set MMC_Chip_Select to HIGH (MMC/SD-Card inaktiv) */
++#define MMC_Disable() gpio_write_high( FAST_GPIO_SD_CS)
++
++/* set MMC_Chip_Select to LOW (MMC/SD-Card aktiv) */
++#define MMC_Enable() gpio_write_low( FAST_GPIO_SD_CS)
++
++/*
++ * *******************************************************************
++ *
++ * Begin SPI hardware access functions.
++ *
++ * *******************************************************************
++ *
++ */
++static int mmc_spi_media_detect(void)
++{
++// FIXME: add card detection/test by SPI
++
++ return 1;
++}
++
++static int mmc_spi_hardware_init(void)
++{
++ printk("\nmmc: GPIO init\n");
++
++ /* cut existing functions */
++ gpio_set_alternative_function(GPIO_SD_CLK, 0);
++ gpio_set_alternative_function(GPIO_SD_DI, 0);
++ gpio_set_alternative_function(GPIO_SD_DO, 0);
++ gpio_set_alternative_function(GPIO_SD_CS, 0);
++
++ /* remap directions and set state of spi pins */
++ gpio_direction_output(GPIO_SD_CLK, 0);
++ gpio_direction_input(GPIO_SD_DI);
++ gpio_direction_output(GPIO_SD_DO, 0);
++ gpio_direction_output(GPIO_SD_CS, 0);
++
++ printk("mmc: initialising MMC\n");
++
++ /* Start */
++ MMC_Disable();
++ gpio_write_low( FAST_GPIO_SD_CLK);
++ gpio_write_high( FAST_GPIO_SD_DO);
++ return 0;
++}
++
++/* return what has been read, write the parameter */
++/* Clockrate round about 1,2 MHz */
++
++static unsigned char mmc_spi_readwrite(unsigned char data_out)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++ if (data_out & i)
++ {
++ gpio_write_high( FAST_GPIO_SD_DO);
++ }
++ else
++ {
++ gpio_write_low( FAST_GPIO_SD_DO);
++ }
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ if (gpio_read( FAST_GPIO_SD_DI) == 1)
++ {
++ result |= i;
++ }
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ }
++
++ gpio_write_high( FAST_GPIO_SD_DO);
++
++ return (result);
++}
++
++/* return what has been read, write the parameter */
++/* Clockrate round 200 kHz */
++
++static unsigned char mmc_spi_readwrite_slow(unsigned char data_out)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++ if (data_out & i)
++ {
++ gpio_write_high( FAST_GPIO_SD_DO);
++ }
++ else
++ {
++ gpio_write_low( FAST_GPIO_SD_DO);
++ }
++
++ udelay(10);
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ udelay(10);
++
++ if (gpio_read( FAST_GPIO_SD_DI) == 1)
++ {
++ result |= i;
++ }
++
++ udelay(10);
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ udelay(10);
++
++ }
++
++ gpio_write_high( FAST_GPIO_SD_DO);
++
++ udelay(10);
++
++ // printk("Send Byte = 0x%2X Receive Byte = 0x%2X \n", data_out, result);
++
++ return (result);
++}
++
++/* return what has been read */
++
++static unsigned char mmc_spi_read_only(void)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ if (gpio_read( FAST_GPIO_SD_DI) == 1)
++ {
++ result |= i;
++ }
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ }
++
++ return (result);
++}
++
++/* write the parameter */
++/* Clockrate round about 3,6 MHz */
++
++static unsigned char mmc_spi_write_only(unsigned char data_out)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++
++ if (data_out & i)
++ {
++ gpio_write_high( FAST_GPIO_SD_DO);
++ }
++ else
++ {
++ gpio_write_low( FAST_GPIO_SD_DO);
++ }
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ }
++
++ gpio_write_high( FAST_GPIO_SD_DO);
++
++ return (result);
++}
++
++
++/**
++ * this function was contributed by: rcichielo from openwrt forums
++ *
++ * Comments added by Marc DENTY on 2007-03-20
++ *
++ * Sequence to read a card's "CID" bytes (name, serial number etc)
++ *
++ * Send: 4ah,00h,00h,00h,00h,00h - CMD10, no args, null CRC
++ * Read: xx - NCR Time
++ * Read: xx - Command Response (Should be 00h)
++ * Read: until FEh is received - Wait for Data token
++ * Read: yy * 16 - Get 16 bytes from CID
++ * Read: zz - Read CRC lo byte
++ * Read: zz - Read CRC hi byte
++ *
++ * Useful locations in the returned data packet:
++ *
++ * 03h-08h Manufacturers's name in ascii
++ * 0ah-0dh Card's 32 bit serial number
++ */
++/**
++ * Comments added by Cyril CATTIAUX on 2007-03-21
++ *
++ * CID format specification (from Sandisk SD Product Manual v1.9)
++ *
++ * cid[00 ] Manufacturer ID (unsigned byte)
++ * cid[01-02] OEM/Application ID (ASCII)
++ * cid[03-07] Product Name (ASCII)
++ * cid[08 ] Product Revistion (BCD coded number)
++ * cid[09-12] Serial Number (32-bit unsigned int)
++ * cid[13-14] Reserved(bit 12->15) - Manufacture Date(bit 0->11)
++ * cid[15 ] CRC7(bit 1->7) - Not used, allways 1 (bit 0)
++*/
++static int mmc_read_cid(unsigned char *cid)
++{
++ unsigned char result = 0;
++ int i;
++
++ MMC_Enable();
++
++ /* wait */
++ for (i = 0; i < 4; i++)
++ {
++ result=mmc_spi_readwrite(0xff);
++ }
++
++ /* issue CID (card identification data) read request */
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0x40 | 10);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x95);
++
++ for (i = 0; i < 8; i++)
++ {
++ result=mmc_spi_readwrite(0xff);
++
++ if(result == 0x00)
++ break;
++ }
++
++ if (result != 0x00) {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return(1);
++ }
++
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xfe) break;
++ }
++
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return(2);
++ }
++
++ for (i = 0; i < 16; i++) {
++ result = mmc_spi_readwrite(0xff);
++ cid[i] = result;
++ }
++
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0xff);
++
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++
++ return 0;
++}
++
++
++/**
++ * Comments added by Cyril CATTIAUX on 2007-03-21
++ *
++ * CID format specification (from Sandisk SD Product Manual v1.9)
++ *
++ * cid[00 ] Manufacturer ID (unsigned byte)
++ * cid[01-02] OEM/Application ID (ASCII)
++ * cid[03-07] Product Name (ASCII)
++ * cid[08 ] Product Revision (BCD coded 2 digit number)
++ * cid[09-12] Serial Number (32-bit unsigned int)
++ * cid[13-14] Manufacture Date(bit 0->11) (BCD coded 3 digit number YYM offset from 2000) - Reserved(bit 12->15)
++ * cid[15 ] Not used, allways 1 (bit 0) - CRC7(bit 1->7)
++*/
++static void mmc_show_cid_info(void)
++{
++ int i, result;
++ unsigned short tmps;
++ unsigned char cid[16];
++
++ char manufacturer_id;
++ char oem_id[3];
++ char product_name[6];
++ unsigned char product_revision_h, product_revision_l;
++ unsigned int product_sn;
++ unsigned short product_date_y;
++ unsigned char product_date_m;
++
++ result = mmc_read_cid(cid);
++
++ if (result == 0)
++ {
++ printk("mmc_init: MMC/SD Card ID: ");
++ for (i=0; i<16; i++) {
++ printk("%02X ", cid[i]);
++ }
++ manufacturer_id=cid[0];
++ strncpy(oem_id, &cid[1], 2);
++ oem_id[2]='\0';
++ strncpy(product_name, &cid[3], 5);
++ product_name[5]='\0';
++ product_revision_h=(cid[8] >> 4) & 0xf;
++ product_revision_l=cid[8] & 0xf;
++ product_sn=(cid[9]<<24) + (cid[10]<<16) + (cid[11]<<8) + cid[12];
++ tmps=((cid[13]<<8) + cid[14]) & 0x0fff;
++ product_date_y=2000 + (((tmps >> 8) & 0xf) * 10) + ((tmps >> 4) & 0xf);
++ product_date_m=tmps & 0xf;
++
++ printk("\nManufacturer ID : %02X\n", manufacturer_id);
++ printk("OEM/Application ID: %s\n", oem_id);
++ printk("Product name : %s\n", product_name);
++ printk("Product revision : %d.%d\n", product_revision_h, product_revision_l);
++ printk("Product SN : %08X\n", product_sn);
++ printk("Product Date : %d-%d\n", product_date_y, product_date_m);
++
++ } else {
++ printk("mmc_init: impossible to get card indentification info for reason code: %02x", result);
++ }
++}
++
++
++/*
++static int mmc_spi_hw_test(void)
++{
++ unsigned char result, k;
++
++ unsigned int i, j, t;
++
++ printk("mmc_spi_hw_test -> \n\n");
++ k = 0x55;
++ for ( i = 0 ; i < 5; i++) {
++
++ printk("\n0x%2X - ", k);
++ for ( j = 0 ; j < 8; j++ ) {
++ do_gettimeofday( &s_zeit );
++ result = mmc_spi_readwrite_slow(k);
++ do_gettimeofday( &e_zeit );
++
++ if ( result != k ) {
++ printk("!>ERROR<! Transfer = 0x%2X Receive = 0x%2X Trail = %d \n", k, result, j);
++ // i = 255; j = 1000;
++ }
++
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("Durchlauf: %i Versuch: %i von 8 -> Laufzeit: 0x%X s\n", i , j, t);
++ udelay(200);
++ }
++ printk("ready ");
++
++ // k++;
++ }
++ printk("ready ");
++ printk("\n\n");
++ return (0);
++}
++*/
++
++/*
++static int mmc_spi_speed_test(void)
++{
++ unsigned int i, j, k, l, t;
++
++ MMC_Disable();
++
++ for (k = 1; k < 6; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 5; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_readwrite(0xff);
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_readwrite: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ for (k = 1; k < 1; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 1; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_readwrite_slow(0xff);
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_readwrite_slow: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ for (k = 1; k < 6; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 5; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_read_only();
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_read_only: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ for (k = 1; k < 6; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 5; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_write_only(0xff);
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_write_only: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ return (1);
++
++}
++*/
++
++
++static int mmc_spi_card_init(void)
++{
++ unsigned char result = 0;
++ short i, j;
++
++// unsigned long flags;
++
++ // save_flags(flags);
++ // cli();
++
++/*
++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CS), gpio_getalt(&gp, GPIO_SD_CS));
++ printk("GPIO_SD_DI dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DI), gpio_getalt(&gp, GPIO_SD_DI));
++ printk("GPIO_SD_DO dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DO), gpio_getalt(&gp, GPIO_SD_DO));
++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CLK), gpio_getalt(&gp, GPIO_SD_CLK));
++*/
++
++ // printk("\nmmc: mmc_spi_hw_test() *START*\n");
++
++ // mmc_spi_hw_test();
++
++ printk("\nmmc: card init 1/2 (CMD0)\n");
++
++ for (j = 0; j < 10; j++)
++ {
++ MMC_Disable();
++
++ for (i = 0; i < 10; i++)
++ mmc_spi_readwrite_slow(0xff);
++
++ MMC_Enable();
++
++ mmc_spi_readwrite_slow(0xff);
++
++ mmc_spi_readwrite_slow(0x40);
++
++ for (i = 0; i < 4; i++) {
++
++ mmc_spi_readwrite_slow(0x00);
++
++ }
++
++ mmc_spi_readwrite_slow(0x95);
++
++ for (i = 0; i < 8; i++) {
++
++ result = mmc_spi_readwrite_slow(0xff);
++
++#ifdef DEBUG_HD
++ if (result != 0xff) {
++ if (result > 0x1F && result < 0x80)
++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result);
++ else
++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X\n", j, i, result);
++ }
++#endif
++ if (result == 0x01)
++ break;
++ }
++
++ if (result == 0x01)
++ break;
++
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++
++ mdelay(60);
++ }
++
++ if (result != 0x01) {
++
++ printk("mmc: card init 1/2 error: %d (CMD0) failed\n", result);
++ printk(" -> Hint: MMC/SD-Card realy (fully) inserted ?\n");
++
++ return (1);
++ }
++
++ printk("mmc: card init 1/2 (CMD0) success\n\n");
++
++ mdelay(1);
++
++ printk("mmc: card init 2/2 (CMD1)\n");
++ for (j = 0; j < 10; j++) {
++
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0x41);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // if (result >= 32 && result <= 127)
++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result);
++ // else
++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result);
++#endif
++ if (result == 0x00)
++ break;
++ }
++
++ mmc_spi_readwrite_slow(0xff);
++
++ if (result == 0x00) {
++ printk("mmc: card init 2/2 (CMD1) success\n\n");
++
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0x4d);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++ for (i = 0; i < 6; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // if (result > 31 && result < 128)
++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result);
++ // else
++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result);
++#endif
++ // if (result == 0x00)
++ // break;
++ }
++ // mdelay(60);
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++ // mdelay(10);
++
++ // restore_flags(flags);
++
++ return (0);
++ }
++ mdelay(60);
++ }
++ return (2);
++}
++
++
++static int mmc_spi_card_config(void)
++{
++ unsigned char result = 0;
++ short i, j;
++ unsigned char csd[32];
++ unsigned int c_size;
++ unsigned int c_size_mult;
++ unsigned int mult;
++ unsigned int read_bl_len;
++ unsigned int blocknr = 0;
++ unsigned int block_len = 0;
++ unsigned int size = 0;
++ unsigned char rd_buffer[528];
++// unsigned long flags;
++
++ MMC_Enable();
++
++ mmc_spi_readwrite_slow(0xff);
++ result = mmc_spi_readwrite_slow(0x51);
++ // mmc_spi_readwrite_slow(0x4A);
++ // mmc_spi_readwrite_slow(0x40+0x0D);
++ // mmc_spi_readwrite_slow(0x42);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++
++ // printk("mmc: (CMD17) response von 0x51 result = 0x%X\n", result);
++
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // printk("mmc: (CMD17) response (start token) result = 0x%X\n", result);
++#endif
++ if (result == 0x00)
++ break;
++ }
++ if (result != 0x00) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // restore_flags(flags);
++ // mmc_spi_readwrite_slow(0xff);
++ return (1);
++ }
++ // restore_flags(flags);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ rd_buffer[i] = result;
++#ifdef DEBUG_HD
++ /*
++ if (result >= 32 && result <= 127)
++ printk("mmc: CMD17 response (start token) result = 0x%X Zeichen = %c\n", result, result);
++ else
++ printk("mmc: CMD17 response (start token) result = 0x%X\n", result);
++ */
++#endif
++ // if (result == 0xfe)
++ // break;
++ }
++ /*
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0xff);
++ return(1);
++ }
++ */
++
++ for (i = 8; i < 520; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ rd_buffer[i] = result;
++ }
++ for (i = 0; i < 2; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ }
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++
++ printk("Buffer - Start\n");
++
++ for ( i = 0 ; i < 33 ; i++) {
++ printk("\r\n%4X - ", i*16);
++ for ( j = 0 ; j < 16 ; j++) {
++ if ( rd_buffer[i*16+j] < 16)
++ printk("0%X ", rd_buffer[i*16+j]);
++ else
++ printk("%2X ", rd_buffer[i*16+j]);
++ }
++ for ( j = 0 ; j < 16 ; j++) {
++ if ( rd_buffer[i*16+j] < ' ')
++ printk(".");
++ else
++ printk("%c", rd_buffer[i*16+j]);
++ }
++ }
++
++ printk("\nBuffer - Ende\n");
++
++ mmc_show_cid_info();
++
++ for(j = 0 ; j < 1; j++) {
++ MMC_Enable();
++
++ // mdelay(1);
++
++ // save_flags(flags);
++ // cli();
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0x49);
++ // mmc_spi_readwrite_slow(0x4A);
++ // mmc_spi_readwrite_slow(0x40+0x0D);
++ // mmc_spi_readwrite_slow(0x42);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // printk("mmc: (CMD9) response (start token) result = 0x%X\n", result);
++#endif
++ if (result == 0x00)
++ break;
++ }
++ // restore_flags(flags);
++ if (result != 0x00) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++ return (1);
++ }
++ for (i = 0; i < 22; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ if (result >= 32 && result <= 127)
++ printk("mmc: response (start token) result = 0x%X Zeichen = %c\n", result, result);
++ else
++ printk("mmc: response (start token) result = 0x%X\n", result);
++#endif
++ if (result == 0xfe)
++ break;
++ }
++ if (result == 0xfe)
++ break;
++
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++ }
++ mdelay(60);
++ }
++
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ printk("mmc: mmc card config (CMD9) failed result = 0x%X\n\n", result);
++ return (2);
++ }
++ for (i = 0; i < 16; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ csd[i] = result;
++ }
++ for (i = 0; i < 2; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ }
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++
++ if (result == 0x00)
++ return (3);
++
++ c_size = (csd[8] & 0xC0) + (csd[7] << 8) + ((csd[6] & 0x03) << 16);
++ c_size >>= 6;
++ c_size_mult = (csd[10] & 0x80) + ((csd[9] & 0x03) << 8);
++ c_size_mult >>= 7;
++ read_bl_len = csd[5] & 0x0f;
++ mult = 1;
++ mult <<= c_size_mult + 2;
++ blocknr = (c_size + 1) * mult;
++ block_len = 1;
++ block_len <<= read_bl_len;
++ size = block_len * blocknr;
++ size >>= 10;
++
++ for (i = 0; i < (1 << 6); i++) {
++ hd_blocksizes[i] = 1024;
++ hd_hardsectsizes[i] = block_len;
++ hd_maxsect[i] = 256;
++ }
++ hd_sizes[0] = size;
++ hd[0].nr_sects = blocknr;
++
++ printk("Size = %d, hardsectsize = %d, sectors = %d\n",
++ size, block_len, blocknr);
++
++ return 0;
++}
++
++
++/*
++ * *******************************************************************
++ *
++ * End of SPI hardware access functions.
++ *
++ * *******************************************************************
++ */
++
++
++static int mmc_spi_write_block(unsigned int dest_addr, unsigned char *data)
++{
++ unsigned int address;
++ unsigned char result = 0;
++ unsigned char ab0, ab1, ab2, ab3;
++ int i;
++
++ address = dest_addr;
++
++ ab3 = 0xff & (address >> 24);
++ ab2 = 0xff & (address >> 16);
++ ab1 = 0xff & (address >> 8);
++ ab0 = 0xff & address;
++
++ MMC_Enable();
++
++ mmc_spi_readwrite(0xff);
++
++ mmc_spi_readwrite(0x58);
++ mmc_spi_readwrite(ab3); /* msb */
++ mmc_spi_readwrite(ab2);
++ mmc_spi_readwrite(ab1);
++ mmc_spi_readwrite(ab0); /* lsb */
++ mmc_spi_readwrite(0xff);
++
++ for (i = 0; i < 8; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ {
++ break;
++ }
++ }
++
++ if (result != 0x00)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (1);
++ }
++
++ mmc_spi_readwrite(0xfe);
++
++ for (i = 0; i < 512; i += 32)
++ {
++ mmc_spi_write_only(data[i]);
++ mmc_spi_write_only(data[i+1]);
++ mmc_spi_write_only(data[i+2]);
++ mmc_spi_write_only(data[i+3]);
++ mmc_spi_write_only(data[i+4]);
++ mmc_spi_write_only(data[i+5]);
++ mmc_spi_write_only(data[i+6]);
++ mmc_spi_write_only(data[i+7]);
++ mmc_spi_write_only(data[i+8]);
++ mmc_spi_write_only(data[i+9]);
++ mmc_spi_write_only(data[i+10]);
++ mmc_spi_write_only(data[i+11]);
++ mmc_spi_write_only(data[i+12]);
++ mmc_spi_write_only(data[i+13]);
++ mmc_spi_write_only(data[i+14]);
++ mmc_spi_write_only(data[i+15]);
++ mmc_spi_write_only(data[i+16]);
++ mmc_spi_write_only(data[i+17]);
++ mmc_spi_write_only(data[i+18]);
++ mmc_spi_write_only(data[i+19]);
++ mmc_spi_write_only(data[i+20]);
++ mmc_spi_write_only(data[i+21]);
++ mmc_spi_write_only(data[i+22]);
++ mmc_spi_write_only(data[i+23]);
++ mmc_spi_write_only(data[i+24]);
++ mmc_spi_write_only(data[i+25]);
++ mmc_spi_write_only(data[i+26]);
++ mmc_spi_write_only(data[i+27]);
++ mmc_spi_write_only(data[i+28]);
++ mmc_spi_write_only(data[i+29]);
++ mmc_spi_write_only(data[i+30]);
++ mmc_spi_write_only(data[i+31]);
++ }
++
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0xff);
++
++ for (i = 0; i < 1000000; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xff)
++ {
++ break;
++ }
++ }
++
++ if (result != 0xff)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (3);
++ }
++
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (0);
++}
++
++static int mmc_spi_read_block(unsigned char *data, unsigned int src_addr)
++{
++ unsigned int address;
++ unsigned char result = 0;
++ unsigned char ab0, ab1, ab2, ab3;
++ unsigned long flags;
++ int i, j;
++
++ address = src_addr;
++
++ ab3 = 0xff & (address >> 24);
++ ab2 = 0xff & (address >> 16);
++ ab1 = 0xff & (address >> 8);
++ ab0 = 0xff & address;
++
++ MMC_Enable();
++
++ mmc_spi_readwrite(0xff);
++
++ mmc_spi_readwrite(0x51);
++ mmc_spi_readwrite(ab3); /* msb */
++ mmc_spi_readwrite(ab2);
++ mmc_spi_readwrite(ab1);
++ mmc_spi_readwrite(ab0); /* lsb */
++ mmc_spi_readwrite(0xff);
++
++ for (i = 0; i < 8; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ {
++ break;
++ }
++ }
++
++ if (result != 0x00)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (1);
++ }
++
++ for (i = 0; i < 100000; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xfe)
++ {
++ break;
++ }
++ }
++
++ if (result != 0xfe)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (2);
++ }
++
++ for (i = 0; i < 512; i += 32 )
++ {
++ data[i] = mmc_spi_read_only();
++ data[i+1] = mmc_spi_read_only();
++ data[i+2] = mmc_spi_read_only();
++ data[i+3] = mmc_spi_read_only();
++ data[i+4] = mmc_spi_read_only();
++ data[i+5] = mmc_spi_read_only();
++ data[i+6] = mmc_spi_read_only();
++ data[i+7] = mmc_spi_read_only();
++ data[i+8] = mmc_spi_read_only();
++ data[i+9] = mmc_spi_read_only();
++ data[i+10] = mmc_spi_read_only();
++ data[i+11] = mmc_spi_read_only();
++ data[i+12] = mmc_spi_read_only();
++ data[i+13] = mmc_spi_read_only();
++ data[i+14] = mmc_spi_read_only();
++ data[i+15] = mmc_spi_read_only();
++ data[i+16] = mmc_spi_read_only();
++ data[i+17] = mmc_spi_read_only();
++ data[i+18] = mmc_spi_read_only();
++ data[i+19] = mmc_spi_read_only();
++ data[i+20] = mmc_spi_read_only();
++ data[i+21] = mmc_spi_read_only();
++ data[i+22] = mmc_spi_read_only();
++ data[i+23] = mmc_spi_read_only();
++ data[i+24] = mmc_spi_read_only();
++ data[i+25] = mmc_spi_read_only();
++ data[i+26] = mmc_spi_read_only();
++ data[i+27] = mmc_spi_read_only();
++ data[i+28] = mmc_spi_read_only();
++ data[i+29] = mmc_spi_read_only();
++ data[i+30] = mmc_spi_read_only();
++ data[i+31] = mmc_spi_read_only();
++ }
++
++ result = mmc_spi_readwrite(0xff);
++ result = mmc_spi_readwrite(0xff);
++
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++
++ return (0);
++}
++
++/*
++static int mmc_revalidate(kdev_t dev)
++{
++ int target, max_p, start, i;
++
++ mmc_media_detect = mmc_spi_media_detect();
++
++ if (mmc_media_detect == 0)
++ {
++ return -ENODEV;
++ }
++
++ target = DEVICE_NR(dev);
++
++ max_p = hd_gendisk.max_p;
++ start = target << 6;
++ for (i = max_p - 1; i >= 0; i--)
++ {
++ int minor = start + i;
++ invalidate_device(MKDEV(MAJOR_NR, minor), 1);
++ hd_gendisk.part[minor].start_sect = 0;
++ hd_gendisk.part[minor].nr_sects = 0;
++ }
++
++ grok_partitions(&hd_gendisk, target, 1 << 6, hd_sizes[0] * 2);
++
++ return 0;
++}
++*/
++
++
++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
++ unsigned long arg)
++{
++ if (!inode || !inode->i_rdev)
++ return -EINVAL;
++
++ switch (cmd) {
++#if 0
++ case BLKGETSIZE:
++ return put_user(hd[MINOR(inode->i_rdev)].nr_sects,
++ (unsigned long *)arg);
++ case BLKGETSIZE64:
++ return put_user((u64) hd[MINOR(inode->i_rdev)].
++ nr_sects, (u64 *) arg);
++ case BLKRRPART:
++ if (!capable(CAP_SYS_ADMIN))
++ return -EACCES;
++
++ return mmc_revalidate(inode->i_rdev);
++#endif
++ case HDIO_GETGEO:
++ {
++ struct block_device *bdev = inode->i_bdev;
++ struct hd_geometry *loc, g;
++ loc = (struct hd_geometry *)arg;
++ if (!loc)
++ return -EINVAL;
++ memset(loc, 0, sizeof(struct hd_geometry));
++ g.heads = 4;
++ g.sectors = 16;
++ g.cylinders = get_capacity(bdev->bd_disk) / (4*16);
++ g.start = get_start_sect(bdev);
++ return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0;
++ }
++ default:
++ return -ENOTTY;
++ }
++}
++
++
++/*
++static int mmc_check_media_change(kdev_t dev)
++{
++ (void) dev;
++ if (mmc_media_changed == 1)
++ {
++ mmc_media_changed = 0;
++ return 1;
++ }
++ else
++ {
++ return 0;
++ }
++}
++*/
++
++
++/*
++static int mmc_init(void)
++{
++ int result;
++
++ result = mmc_spi_hardware_init();
++
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_spi_hardware_init\n", result);
++ return -1;
++ }
++
++
++ result = mmc_spi_hw_test();
++
++ if (result != 0)
++ {
++ printk("\n mmc: mmc_spi_hw_test i.O. \n\n");
++ return -1;
++ }
++
++
++ result = mmc_spi_speed_test();
++
++ if (result != 0)
++ {
++ printk("\n mmc: mmc_spi_speed_test i.O. \n\n");
++ return -1;
++ }
++
++
++ result = mmc_spi_card_init();
++ mdelay(50);
++ if (result != 0)
++ {
++ // Give it an extra shot
++ // result = mmc_spi_card_init();
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_card_init\n", result);
++ return -1;
++ }
++ }
++
++ result = mmc_spi_card_config();
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_card_config\n", result);
++ return -1;
++ }
++
++
++ return 0;
++}
++*/
++
++/*
++static void mmc_exit(void)
++{
++}
++*/
++
++
++/*
++static void mmc_check_media(void)
++{
++ int old_state, new_state;
++ int result;
++
++ old_state = mmc_media_detect;
++ new_state = mmc_spi_media_detect();
++
++ if (old_state != new_state)
++ {
++ mmc_media_changed = 1;
++ if (new_state == PRESENT)
++ {
++ result = mmc_init();
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_init\n", result);
++ }
++ else
++ {
++ mmc_exit();
++ }
++ }
++ }
++
++#ifdef CHECK_MEDIA_CHANGE
++ del_timer(&mmc_timer);
++ mmc_timer.expires = jiffies + 10*HZ;
++ add_timer(&mmc_timer);
++#endif
++
++}
++*/
++
++
++/* NB: There might be several requests in the queue, simply dequeuing only one
++ and not checking for more will cause a stall because the block subsystem
++ will not call this function again unless the queue is "plugged" which can
++ only happen if it runs empty... */
++static void mmc_spi_request(struct request_queue *q)
++{
++ struct request *req;
++ int ret;
++
++ unsigned int mmc_address;
++ unsigned char *buffer_address;
++ int nr_sectors;
++ int i;
++ int result, success;
++
++ if (blk_queue_plugged(q)) {
++ return;
++ }
++
++ spin_lock(&mmc_spi_lock);
++ for(;;) {
++ req = elv_next_request(q);
++ if (!req)
++ break;
++
++ if (!blk_fs_request(req)) {
++ printk("not a blk_fs_request\n");
++ spin_unlock(&mmc_spi_lock);
++ continue;
++ }
++
++ mmc_address = req->sector * hd_hardsectsizes[0];
++ buffer_address = req->buffer;
++ nr_sectors = req->current_nr_sectors;
++ success = 1;
++ if (rq_data_dir(req) == READ) {
++ spin_unlock_irq(q->queue_lock);
++ for (i = 0; i < nr_sectors; i++) {
++ result = mmc_spi_read_block(buffer_address, mmc_address);
++ if (unlikely(result < 0)) {
++ printk(KERN_ERR "mmi_spi_block: error reading block (%d)\n", result);
++ success = 0;
++ break;
++ }
++ mmc_address += hd_hardsectsizes[0];
++ buffer_address += hd_hardsectsizes[0];
++ }
++ spin_lock_irq(q->queue_lock);
++ } else {
++ spin_unlock_irq(q->queue_lock);
++ for (i = 0; i < nr_sectors; i++) {
++ result = mmc_spi_write_block(mmc_address, buffer_address);
++ if (unlikely(result < 0)) {
++ printk(KERN_ERR "mmi_spi_block: error writing block (%d)\n", result);
++ success = 0;
++ break;
++ }
++ mmc_address += hd_hardsectsizes[0];
++ buffer_address += hd_hardsectsizes[0];
++ }
++ spin_lock_irq(q->queue_lock);
++ }
++ ret = end_that_request_chunk(req, success, nr_sectors * hd_hardsectsizes[0]);
++ if (!ret) {
++ blkdev_dequeue_request(req);
++ end_that_request_last(req, 0);
++ }
++ }
++ spin_unlock(&mmc_spi_lock);
++}
++
++
++static int mmc_open(struct inode *inode, struct file *filp)
++{
++ // int device;
++ (void) filp;
++ mmc_media_detect = mmc_spi_media_detect();
++
++ if (mmc_media_detect == 0)
++ return -ENODEV;
++
++ return 0;
++}
++
++static int mmc_release(struct inode *inode, struct file *filp)
++{
++ return 0;
++}
++
++
++static struct block_device_operations mmc_spi_bdops = {
++ .open = mmc_open,
++ .release = mmc_release,
++ .ioctl = mmc_ioctl,
++ .owner = THIS_MODULE,
++#if 0
++ .check_media_change = mmc_check_media_change,
++ .revalidate = mmc_revalidate,
++#endif
++};
++
++static int detect_card(void)
++{
++ int result;
++
++ result = mmc_spi_card_init();
++ if (result != 0) {
++ // Give it an extra shot
++ result = mmc_spi_card_init();
++ if (result != 0) {
++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_init (%d)\n", result);
++ return -ENODEV;
++ }
++ }
++
++ result = mmc_spi_card_config();
++ // result = mmc_spi_speed_test();
++ if (result != 0) {
++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_config (%d)\n", result);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++/* Fills in the gendisk structure from the received card
++ data. */
++static int gendisk_init(struct device *dev, struct gendisk *gd)
++{
++ if (!gd)
++ return -EINVAL;
++
++ gd->major = major;
++ gd->first_minor = 0; /* only one device supported */
++ gd->fops = &mmc_spi_bdops;
++ gd->driverfs_dev = dev;
++
++ gd->queue = blk_init_queue(mmc_spi_request,NULL);
++
++ if (!gd->queue)
++ return -ENOMEM;
++
++ sprintf(gd->disk_name, "mmcblk");
++
++ blk_queue_hardsect_size(gd->queue, hd_hardsectsizes[0]);
++
++ set_capacity(gd, hd_sizes[0]<<1);
++
++ return 0;
++}
++
++static int gendisk_fini(struct gendisk *gd)
++{
++ BUG_ON(!gd);
++
++ if (gd->queue)
++ blk_cleanup_queue(gd->queue);
++
++ del_gendisk(gd);
++}
++
++/* platform driver device instance routines */
++static int mmc_spi_probe(struct platform_device *pdev)
++{
++ int result;
++ printk("$Id: mmc_spi_block.c,v 1.04 2007/05/25 23:27:16 mrdata Exp $\n");
++
++ result = mmc_spi_hardware_init();
++ if (result != 0) {
++ printk(KERN_ERR "mmc_spi_block: error in mmc_spi_hardware_init (%d)\n", result);
++ result = -ENODEV;
++ return result;
++ }
++
++ result = detect_card();
++ if (result < 0)
++ return result;
++
++ mmc_media_detect = 1;
++
++ result = register_blkdev(major, DEVICE_NAME);
++ if (result < 0)
++ return result;
++
++ if (!major)
++ major = result;
++
++ /* allow 8 partitions per device */
++ BUG_ON(mmc_disk!=NULL);
++ mmc_disk = alloc_disk(1 << 3);
++ if (!mmc_disk) {
++ result = -ENOMEM;
++ goto out;
++ }
++
++ result = gendisk_init(&pdev->dev, mmc_disk);
++ if (result < 0)
++ goto out;
++
++ add_disk(mmc_disk);
++
++ /*init_timer(&mmc_timer);
++ mmc_timer.expires = jiffies + HZ;
++ mmc_timer.function = (void *)mmc_check_media;
++ add_timer(&mmc_timer); */
++ return 0;
++
++out:
++ if (mmc_disk)
++ put_disk(mmc_disk);
++
++ unregister_blkdev(major, DEVICE_NAME);
++ return result;
++}
++
++static int mmc_spi_remove(struct platform_device *dev)
++{
++ int ret;
++
++ if (mmc_disk) {
++ gendisk_fini(mmc_disk);
++ put_disk(mmc_disk);
++ }
++
++ ret = unregister_blkdev(major, DEVICE_NAME);
++ return ret;
++}
++
++struct platform_driver mmc_spi_driver = {
++ .probe = mmc_spi_probe,
++ .remove = mmc_spi_remove,
++ .driver = {
++ .name = "mmc_spi",
++ .owner = THIS_MODULE,
++ },
++};
++
++
++/* module init/exit */
++static int __init mmc_block_spi_init(void)
++{
++ int ret;
++ spin_lock_init(&mmc_spi_lock);
++
++ ret = platform_driver_register(&mmc_spi_driver);
++ if (ret < 0)
++ return ret;
++
++ /* we just support one device */
++ mmc_dev = platform_device_register_simple("mmc_spi", -1, NULL, 0);
++ if (IS_ERR(mmc_dev))
++ return PTR_ERR(mmc_dev);
++
++ return 0;
++}
++
++
++static void __exit mmc_block_spi_exit(void)
++{
++ platform_driver_unregister(&mmc_spi_driver);
++ if (mmc_dev)
++ platform_device_unregister(mmc_dev);
++}
++
++
++module_init(mmc_block_spi_init);
++module_exit(mmc_block_spi_exit);
++
++MODULE_AUTHOR("Madsuk,Rohde,TaGana,Carsten Juttner <carjay@gmx.net>,Guylhem Aznar <mmc-driver@externe.net>,mrdata");
++MODULE_DESCRIPTION("Driver for MMC/SD-Cards in SPI mode by GPIO");
++MODULE_SUPPORTED_DEVICE("SIMpad");
++MODULE_LICENSE("GPL");
++
++module_param(major, int, 0444);
++MODULE_PARM_DESC(major, "specify the major device number for the MMC/SD SPI driver");
diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-battery-old-way-but-also-with-sysfs.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-battery-old-way-but-also-with-sysfs.patch
new file mode 100644
index 0000000000..ee18ff2bb0
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-battery-old-way-but-also-with-sysfs.patch
@@ -0,0 +1,571 @@
+diff -uNr linux-2.6.21.vanilla/drivers/mfd/Makefile linux-2.6.21/drivers/mfd/Makefile
+--- linux-2.6.21.vanilla/drivers/mfd/Makefile 2007-05-30 18:00:30.000000000 +0200
++++ linux-2.6.21/drivers/mfd/Makefile 2007-05-30 18:43:10.000000000 +0200
+@@ -12,3 +12,7 @@
+ ifeq ($(CONFIG_SA1100_ASSABET),y)
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
+ endif
++
++ifeq ($(CONFIG_SA1100_SIMPAD),y)
++obj-$(CONFIG_MCP_UCB1200) += ucb1x00-simpad.o
++endif
+diff -uNr linux-2.6.21.vanilla/drivers/mfd/ucb1x00-simpad.c linux-2.6.21/drivers/mfd/ucb1x00-simpad.c
+--- linux-2.6.21.vanilla/drivers/mfd/ucb1x00-simpad.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/mfd/ucb1x00-simpad.c 2007-05-30 18:35:48.000000000 +0200
+@@ -0,0 +1,226 @@
++/*
++ * linux/drivers/mfd/ucb1x00-simpad.c
++ *
++ * Copyright (C) 2001-2003 Russell King, All Rights Reserved.
++ * 2007/03/18 mrdata:
++ * - adapted ucb1x00-assabet.c
++ * - transfer ucb1x00-simpad.c from kernel24
++ * to new structur of kernel26
++ *
++ * 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.
++ *
++ * We handle the machine-specific bits of the UCB1x00 driver here.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/proc_fs.h>
++#include <linux/device.h>
++
++#include <linux/apm-emulation.h>
++
++#include <asm/dma.h>
++
++#include <asm/arch/simpad.h>
++#include <asm/arch-sa1100/simpad_pm.h>
++
++#include "ucb1x00.h"
++#include "ucb1x00-simpad.h"
++
++#define UCB1X00_ATTR(name,input,designation) \
++static ssize_t name##_show(struct class_device *dev, char *buf) \
++{ \
++ struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); \
++ int val; \
++ ucb1x00_adc_enable(ucb); \
++ val = ucb1x00_adc_read(ucb, input, UCB_NOSYNC); \
++ ucb1x00_adc_disable(ucb); \
++ return sprintf(buf, "%d\n", CALIBRATE_##designation(val)); \
++} \
++static CLASS_DEVICE_ATTR(name,0444,name##_show,NULL)
++
++UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1, BATTERY);
++UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD2, SUPPLY);
++UCB1X00_ATTR(icharger, UCB_ADC_INP_AD3, CHARGING);
++
++static struct ucb1x00 *ucb_alt;
++
++#define UCB1X00_WERT(name,input,designation) \
++static int ucb1x00_simpad_read_##name(struct ucb1x00 *ucb_alt) \
++{ \
++ int val; \
++ ucb1x00_adc_enable(ucb_alt); \
++ val = ucb1x00_adc_read(ucb_alt, input, UCB_NOSYNC); \
++ ucb1x00_adc_disable(ucb_alt); \
++ return CALIBRATE_##designation(val); \
++}
++
++UCB1X00_WERT(vbatt, UCB_ADC_INP_AD1, BATTERY);
++UCB1X00_WERT(vcharger, UCB_ADC_INP_AD2, SUPPLY);
++UCB1X00_WERT(icharger, UCB_ADC_INP_AD3, CHARGING);
++
++static int ucb1x00_simpad_add(struct ucb1x00_dev *dev)
++{
++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_vbatt);
++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_vcharger);
++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_icharger);
++ ucb_alt = dev->ucb;
++ return 0;
++}
++
++static void ucb1x00_simpad_remove(struct ucb1x00_dev *dev)
++{
++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_icharger);
++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vcharger);
++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vbatt);
++}
++
++static struct ucb1x00_driver ucb1x00_simpad_driver = {
++ .add = ucb1x00_simpad_add,
++ .remove = ucb1x00_simpad_remove,
++};
++
++static int __init ucb1x00_simpad_init(void)
++{
++ apm_get_power_status = simpad_apm_get_power_status;
++ return ucb1x00_register_driver(&ucb1x00_simpad_driver);
++}
++
++static void __exit ucb1x00_simpad_exit(void)
++{
++ apm_get_power_status = NULL;
++ ucb1x00_unregister_driver(&ucb1x00_simpad_driver);
++}
++
++/****************************************************************************/
++/* Functions exported for use by the kernel and kernel modules */
++/****************************************************************************/
++
++int simpad_get_battery(struct simpad_battery_apm *bstat)
++{
++ int icharger, vcharger, vbatt;
++
++ if ( ucb_alt ) {
++ icharger = ucb1x00_simpad_read_icharger( ucb_alt );
++ vcharger = ucb1x00_simpad_read_vcharger( ucb_alt );
++ vbatt = ucb1x00_simpad_read_vbatt( ucb_alt );
++ } else {
++ bstat->ac_status = SIMPAD_AC_STATUS_AC_UNKNOWN;
++ bstat->status = SIMPAD_BATT_STATUS_UNKNOWN;
++ bstat->percentage = 0x64; // lets say 100%
++ bstat->life = 330; // lets say a long time
++ return 0;
++ }
++
++ /* AC status */
++ bstat->ac_status = SIMPAD_AC_STATUS_AC_OFFLINE;
++ if ( vcharger>MIN_SUPPLY ) {
++ bstat->ac_status = SIMPAD_AC_STATUS_AC_ONLINE;
++ }
++
++ /* charging */
++ bstat->status = 0x0;
++ if ( icharger >= CHARGING_LED_LEVEL )
++ bstat->status = SIMPAD_BATT_STATUS_CHARGING;
++
++ if ( vbatt > BATT_LOW )
++ bstat->status |= SIMPAD_BATT_STATUS_HIGH;
++ else if ( vbatt < BATT_CRITICAL )
++ bstat->status |= SIMPAD_BATT_STATUS_CRITICAL;
++ else
++ bstat->status |= SIMPAD_BATT_STATUS_LOW;
++
++ if (bstat->status & SIMPAD_BATT_STATUS_CHARGING) {
++ if (icharger > CHARGING_MAX_LEVEL)
++ icharger = CHARGING_MAX_LEVEL;
++ if (icharger < CHARGING_LED_LEVEL)
++ icharger = CHARGING_LED_LEVEL;
++
++ bstat->percentage = 100 - 100 * (icharger - CHARGING_LED_LEVEL) /
++ (CHARGING_MAX_LEVEL - CHARGING_LED_LEVEL);
++ } else {
++ if (vbatt > BATT_FULL)
++ vbatt = BATT_FULL;
++ if (vbatt < BATT_EMPTY)
++ vbatt = BATT_EMPTY;
++
++ bstat->percentage = 100 * (vbatt - BATT_EMPTY) / (BATT_FULL - BATT_EMPTY);
++ }
++
++ /* let's assume: full load is 7h */
++ /* bstat->life = 420*bstat->percentage/100; */
++ /* mrdata: think, 4h is more realistic */
++ bstat->life = 240*(bstat->percentage)/100;
++
++#if 0
++ printk("get_battery: ac: %02x / ch: %02x / perc: %02x / life: %d \n",
++ bstat->ac_status, bstat->status,
++ bstat->percentage, bstat->life );
++#endif
++
++ return 0;
++}
++
++void simpad_apm_get_power_status(struct apm_power_info *info)
++{
++ struct simpad_battery_apm bstat;
++ unsigned char ac = APM_AC_UNKNOWN;
++ unsigned char level = APM_BATTERY_STATUS_UNKNOWN;
++ int status, result;
++
++ result = simpad_get_battery(&bstat);
++ if (result) {
++ printk("%s: unable to access battery information: result=%d\n", __FUNCTION__, result);
++ return;
++ }
++
++ switch (bstat.ac_status) {
++ case SIMPAD_AC_STATUS_AC_OFFLINE:
++ ac = APM_AC_OFFLINE;
++ break;
++ case SIMPAD_AC_STATUS_AC_ONLINE:
++ ac = APM_AC_ONLINE;
++ break;
++ case SIMPAD_AC_STATUS_AC_BACKUP:
++ ac = APM_AC_BACKUP;
++ break;
++ }
++
++ info->ac_line_status = ac;
++
++ status = bstat.status;
++ if (status & (SIMPAD_BATT_STATUS_CHARGING | SIMPAD_BATT_STATUS_CHARGE_MAIN))
++ level = APM_BATTERY_STATUS_CHARGING;
++ else if (status & (SIMPAD_BATT_STATUS_HIGH | SIMPAD_BATT_STATUS_FULL))
++ level = APM_BATTERY_STATUS_HIGH;
++ else if (status & SIMPAD_BATT_STATUS_LOW)
++ level = APM_BATTERY_STATUS_LOW;
++ else if (status & SIMPAD_BATT_STATUS_CRITICAL)
++ level = APM_BATTERY_STATUS_CRITICAL;
++
++ info->battery_status = level;
++ info->battery_flag = info->battery_status;
++
++ info->battery_life = bstat.percentage;
++
++ /* we have a dumb battery - so we know nothing */
++ info->time = bstat.life;
++ info->units = APM_UNITS_MINS;
++
++#if 0
++ printk("apm_get_power: ac: %02x / bs: %02x / bf: %02x / perc: %02x / life: %d\n",
++ info->ac_line_status, info->battery_status, info->battery_flag,
++ info->battery_life, info->time );
++#endif
++ return;
++}
++
++module_init(ucb1x00_simpad_init);
++module_exit(ucb1x00_simpad_exit);
++
++
++MODULE_AUTHOR("Juergen Messerer <juergen.messerer@freesurf.ch>");
++MODULE_DESCRIPTION("SIMpad noddy testing only example ADC driver");
++MODULE_LICENSE("GPL");
+diff -uNr linux-2.6.21.vanilla/drivers/mfd/ucb1x00-simpad.h linux-2.6.21/drivers/mfd/ucb1x00-simpad.h
+--- linux-2.6.21.vanilla/drivers/mfd/ucb1x00-simpad.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/mfd/ucb1x00-simpad.h 2007-05-30 18:35:48.000000000 +0200
+@@ -0,0 +1,86 @@
++/*
++ * linux/drivers/mfd/ucb1x00-simpad.h
++ *
++ * Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * 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.
++ */
++#ifndef UCB1300_SIMPAD_H
++#define UCB1300_SIMPAD_H
++
++/*
++ * Conversion from AD -> mV
++ * 7.5V = 1023 7.33137mV/Digit
++ *
++ * 400 Units == 9.7V
++ * a = ADC value
++ * 2007/03/24 mrdata:
++ * according UCB1300 datasheet ADC error max. 3 LSB
++ * 5-95% of full scale -> 3*7.33137mV = 21.99mV
++ * // old
++ * 21 = ADC error
++ * 12600 = Divident to get 2*7.3242
++ * 860 = Divider to get 2*7.3242
++ * 170 = Voltagedrop over
++ *
++ * // new
++ * 3 = ADC error
++ * 12610 = Divident to get 2*7.33139
++ * 860 = Divider to get 2*7.33139
++ * 170 = Voltagedrop over
++ */
++// #define CALIBRATE_BATTERY(a) ((((a + 21)*12600)/860) + 170)
++#define CALIBRATE_BATTERY(a) ((((a + 3)*12610)/860) + 170)
++
++/*
++ * We have two types of batteries a small and a large one
++ * To get the right value we to distinguish between those two
++ * 450 Units == 15 V
++ */
++#ifdef SMALL_BATTERY
++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 51)
++#define MIN_SUPPLY 8500 /* Less then 8.5V means no powersupply */
++#else
++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 45)
++//#define MIN_SUPPLY 14000 /* Less then 14V means no powersupply */
++#define MIN_SUPPLY 12000 /* Less then 12V means no powersupply */
++#endif
++
++/*
++ * Charging Current
++ * if value is >= 50 then charging is on
++ */
++// #define CALIBRATE_CHARGING(a) (((a) * 1000) / (152/4))
++#define CALIBRATE_CHARGING(a) (a)
++//#define CHARGING_LED_LEVEL 50
++
++#ifdef CONFIG_SA1100_SIMPAD_SINUSPAD
++
++// type small battery
++#define CHARGING_LED_LEVEL 12
++#define CHARGING_MAX_LEVEL 120
++#define BATT_FULL 8100
++#define BATT_LOW 7300
++#define BATT_CRITICAL 6700
++#define BATT_EMPTY 6400
++
++#else // CONFIG_SA1100_SIMPAD_SINUSPAD
++
++// type large battery
++// because of ADC error CHARGING_LED_LEVEL can changed
++// from 27 to 28
++#define CHARGING_LED_LEVEL 27
++#define CHARGING_MAX_LEVEL 265
++// BATT_FULL with SIMPAD_AC_STATUS_AC_OFFLINE
++#define BATT_FULL 8100
++#define BATT_LOW 7400
++#define BATT_CRITICAL 6800
++#define BATT_EMPTY 6500
++
++#endif // CONFIG_SA1100_SIMPAD_SINUSPAD
++
++// int simpad_get_battery(struct simpad_battery_apm *bstat);
++
++#endif
+diff -uNr linux-2.6.21.vanilla/include/asm-arm/arch-sa1100/simpad_pm.h linux-2.6.21/include/asm-arm/arch-sa1100/simpad_pm.h
+--- linux-2.6.21.vanilla/include/asm-arm/arch-sa1100/simpad_pm.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/include/asm-arm/arch-sa1100/simpad_pm.h 2007-05-30 18:36:07.000000000 +0200
+@@ -0,0 +1,236 @@
++/*
++* Abstraction interface for microcontroller connection to rest of system
++*
++* Copyright 2003 Peter Pregler
++* Copyright 2000,1 Compaq Computer Corporation.
++*
++* Use consistent with the GNU GPL is permitted,
++* provided that this copyright notice is
++* preserved in its entirety in all copies and derived works.
++*
++* COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
++* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
++* FITNESS FOR ANY PARTICULAR PURPOSE.
++*
++* Author: Peter Pregler (based on work for ipaq by Andrew Christian)
++*
++*/
++
++#ifndef __SIMPAD_HAL_H
++#define __SIMPAD_HAL_H
++
++#include <linux/apm-emulation.h>
++
++struct simpad_battery_apm {
++ unsigned char ac_status; /* line connected yes/no */
++ unsigned char status; /* battery loading yes/no */
++ unsigned char percentage; /* percentage loaded */
++ unsigned short life; /* life till empty */
++};
++
++extern void simpad_apm_get_power_status(struct apm_power_info *);
++
++// extern int simpad_get_battery(struct simpad_battery_apm *bstat);
++
++/* These should match the apm_bios.h definitions */
++#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00
++#define SIMPAD_AC_STATUS_AC_ONLINE 0x01
++#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */
++#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff
++
++
++/* These bitfields are rarely "or'd" together */
++#define SIMPAD_BATT_STATUS_HIGH 0x01
++#define SIMPAD_BATT_STATUS_LOW 0x02
++#define SIMPAD_BATT_STATUS_CRITICAL 0x04
++#define SIMPAD_BATT_STATUS_CHARGING 0x08
++#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10
++#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */
++#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */
++#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */
++#define SIMPAD_BATT_STATUS_NOBATT 0x80
++#define SIMPAD_BATT_STATUS_UNKNOWN 0xff
++
++#if 0 // FIXME
++#include <linux/simpad_ts.h>
++
++enum simpad_asset_type {
++ ASSET_TCHAR = 0,
++ ASSET_SHORT,
++ ASSET_LONG
++};
++
++#define TTYPE(_type) (((unsigned int)_type) << 8)
++#define TCHAR(_len) (TTYPE(ASSET_TCHAR) | (_len))
++#define TSHORT TTYPE(ASSET_SHORT)
++#define TLONG TTYPE(ASSET_LONG)
++#define ASSET(_type,_num) ((((unsigned int)_type)<<16) | (_num))
++
++#define ASSET_HM_VERSION ASSET( TCHAR(10), 0 ) /* 1.1, 1.2 */
++#define ASSET_SERIAL_NUMBER ASSET( TCHAR(40), 1 ) /* Unique iPAQ serial number */
++#define ASSET_MODULE_ID ASSET( TCHAR(20), 2 ) /* E.g., "iPAQ 3700" */
++#define ASSET_PRODUCT_REVISION ASSET( TCHAR(10), 3 ) /* 1.0, 2.0 */
++#define ASSET_PRODUCT_ID ASSET( TSHORT, 4 ) /* 2 = Palm-sized computer */
++#define ASSET_FRAME_RATE ASSET( TSHORT, 5 )
++#define ASSET_PAGE_MODE ASSET( TSHORT, 6 ) /* 0 = Flash memory */
++#define ASSET_COUNTRY_ID ASSET( TSHORT, 7 ) /* 0 = USA */
++#define ASSET_IS_COLOR_DISPLAY ASSET( TSHORT, 8 ) /* Boolean, 1 = yes */
++#define ASSET_ROM_SIZE ASSET( TSHORT, 9 ) /* 16, 32 */
++#define ASSET_RAM_SIZE ASSET( TSHORT, 10 ) /* 32768 */
++#define ASSET_HORIZONTAL_PIXELS ASSET( TSHORT, 11 ) /* 240 */
++#define ASSET_VERTICAL_PIXELS ASSET( TSHORT, 12 ) /* 320 */
++
++#define ASSET_TYPE(_asset) (((_asset)&0xff000000)>>24)
++#define ASSET_TCHAR_LEN(_asset) (((_asset)&0x00ff0000)>>16)
++#define ASSET_NUMBER(_asset) ((_asset)&0x0000ffff)
++
++#define MAX_TCHAR_LEN 40
++
++struct simpad_asset {
++ unsigned int type;
++ union {
++ unsigned char tchar[ MAX_TCHAR_LEN ];
++ unsigned short vshort;
++ unsigned long vlong;
++ } a;
++};
++
++/********************************************************************
++ * Interface to the hardware-type specific functions
++ *
++ * get_version Read the version number of the microcontroller on the option pack SPI bus
++ * spi_read Reads from the serial EEPROM memory on the option pack SPI bus
++ * spi_write Write to the serial EEPROM memory on the option pack SPI bus
++ * get_option_detect Returns whether or not an option pack is present
++ *
++ * get_thermal_sensor Return measured temperature of the unit, in units of 0.125 deg C
++ * set_notify_led Turns on, off, or blinks the Green LED
++ * read_light_sensor Returns the value of the front light sensor
++ * get_battery Returns the current voltage and charging state of all batteries
++ * audio_clock Sets the audio CODEC to run at a particular rate
++ * audio_power Turns on/off audio CODEC (internally calls audio_clock)
++ * audio_mute Mutes the audio CODEC
++ * asset_read Extracts PocketPC-style asset information from persistent memory
++ * backlight_control Adjusts the backlight level (only on/off for 3100)
++ *
++ *
++ * iPAQ 3100 only
++ * ==============
++ * codec_control Reset/mute/control level of 3100 audio codec
++ * contrast_control Adjusts the contrast level (only for 3100)
++ *
++ * iPAQ 3600, 3700 only
++ * ====================
++ * eeprom_read Reads from the asset information on the eeprom of a 3600 (deprecated)
++ * eeprom_write Writes to the asset information on the eeprom (deprecated)
++ *
++ * The interfaces to the EEPROM functions are maintained only because the simpad_ts driver has
++ * a deprecated ioctl call for them. They are used internally by the "asset_read" function.
++ *
++ * iPAQ 3800, 3900 only
++ * ====================
++ * set_ebat Tells enhanced PCMCIA sleeves that this iPAQ can handle
++ * a wider voltage range (applies to 3800, 3900)
++ *
++ *********************************************************************/
++
++struct simpad_hal_ops {
++ /* Functions provided by the underlying hardware */
++ int (*get_version)( struct simpad_ts_version * );
++ int (*eeprom_read)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*eeprom_write)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*get_thermal_sensor)( unsigned short * );
++ int (*set_notify_led)( unsigned char mode, unsigned char duration,
++ unsigned char ontime, unsigned char offtime );
++ int (*read_light_sensor)( unsigned char *result );
++ int (*get_battery)( struct simpad_battery * );
++ int (*spi_read)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*spi_write)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*codec_control)( unsigned char, unsigned char );
++ int (*get_option_detect)( int *result );
++ int (*audio_clock)( long samplerate );
++ int (*audio_power)( long samplerate );
++ int (*audio_mute)( int mute );
++ int (*asset_read)( struct simpad_asset *asset );
++ int (*set_ebat)( void );
++
++ /* Functions indirectly provided by the underlying hardware */
++ int (*backlight_control)( enum flite_pwr power, unsigned char level );
++ int (*contrast_control)( unsigned char level );
++
++ /* for module use counting */
++ struct module *owner;
++};
++
++/* Used by the device-specific hardware module to register itself */
++extern int simpad_hal_register_interface( struct simpad_hal_ops *ops );
++extern void simpad_hal_unregister_interface( struct simpad_hal_ops *ops );
++
++/*
++ * Calls into HAL from the device-specific hardware module
++ * These run at interrupt time
++ */
++extern void simpad_hal_keypress( unsigned char key );
++extern void simpad_hal_touchpanel( unsigned short x, unsigned short y, int down );
++extern void simpad_hal_option_detect( int present );
++
++/* Callbacks registered by device drivers */
++struct simpad_driver_ops {
++ void (*keypress)( unsigned char key );
++ void (*touchpanel)( unsigned short x, unsigned short y, int down );
++ void (*option_detect)( int present );
++};
++
++extern int simpad_hal_register_driver( struct simpad_driver_ops * );
++extern void simpad_hal_unregister_driver( struct simpad_driver_ops * );
++
++
++/* Calls into HAL from device drivers and other kernel modules */
++extern void simpad_get_flite( struct simpad_ts_backlight *bl );
++extern void simpad_get_contrast( unsigned char *contrast );
++extern int simpad_set_flite( enum flite_pwr pwr, unsigned char brightness );
++extern int simpad_set_contrast( unsigned char contrast );
++extern int simpad_toggle_frontlight( void );
++
++extern int simpad_apm_get_power_status(unsigned char *ac_line_status, unsigned char *battery_status,
++ unsigned char *battery_flag, unsigned char *battery_percentage,
++ unsigned short *battery_life);
++
++extern struct simpad_hal_ops *simpad_hal_ops;
++
++/* Do not use this macro in driver files - instead, use the inline functions defined below */
++#define CALL_HAL( f, args... ) \
++ { int __result = -EIO; \
++ if ( simpad_hal_ops && simpad_hal_ops->f ) { \
++ __MOD_INC_USE_COUNT(simpad_hal_ops->owner); \
++ __result = simpad_hal_ops->f(args); \
++ __MOD_DEC_USE_COUNT(simpad_hal_ops->owner); \
++ } \
++ return __result; }
++
++#define HFUNC static __inline__ int
++
++/* The eeprom_read/write address + len has a maximum value of 512. Both must be even numbers */
++HFUNC simpad_eeprom_read( u16 addr, u8 *data, u16 len ) CALL_HAL(eeprom_read,addr,data,len)
++HFUNC simpad_eeprom_write( u16 addr, u8 *data, u16 len) CALL_HAL(eeprom_write,addr,data,len)
++HFUNC simpad_spi_read( u8 addr, u8 *data, u16 len) CALL_HAL(spi_read,addr,data,len)
++HFUNC simpad_spi_write( u8 addr, u8 *data, u16 len) CALL_HAL(spi_write,addr,data,len)
++HFUNC simpad_get_version( struct simpad_ts_version *v ) CALL_HAL(get_version,v)
++HFUNC simpad_get_thermal_sensor( u16 *thermal ) CALL_HAL(get_thermal_sensor,thermal)
++HFUNC simpad_set_led( u8 mode, u8 dur, u8 ont, u8 offt ) CALL_HAL(set_notify_led, mode, dur, ont, offt)
++HFUNC simpad_get_light_sensor( u8 *result ) CALL_HAL(read_light_sensor,result)
++HFUNC simpad_get_battery( struct simpad_battery *bat ) CALL_HAL(get_battery,bat)
++HFUNC simpad_get_option_detect( int *result) CALL_HAL(get_option_detect,result)
++HFUNC simpad_audio_clock( long samplerate ) CALL_HAL(audio_clock,samplerate)
++HFUNC simpad_audio_power( long samplerate ) CALL_HAL(audio_power,samplerate)
++HFUNC simpad_audio_mute( int mute ) CALL_HAL(audio_mute,mute)
++HFUNC simpad_asset_read( struct simpad_asset *asset ) CALL_HAL(asset_read,asset)
++HFUNC simpad_set_ebat( void ) CALL_HAL(set_ebat)
++
++/* Don't use these functions directly - rather, call {get,set}_{flite,contrast} */
++ /* Functions indirectly provided by the underlying hardware */
++HFUNC simpad_backlight_control( enum flite_pwr p, u8 v ) CALL_HAL(backlight_control,p,v)
++HFUNC simpad_contrast_control( u8 level ) CALL_HAL(contrast_control,level)
++
++#endif
++#endif
diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-cs3-simpad.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-cs3-simpad.patch
new file mode 100644
index 0000000000..cfb9aad906
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-cs3-simpad.patch
@@ -0,0 +1,184 @@
+diff -uNr linux-2.6.21.vanilla/arch/arm/mach-sa1100/Makefile linux-2.6.21/arch/arm/mach-sa1100/Makefile
+--- linux-2.6.21.vanilla/arch/arm/mach-sa1100/Makefile 2007-05-29 21:34:59.000000000 +0200
++++ linux-2.6.21/arch/arm/mach-sa1100/Makefile 2007-05-30 17:44:04.000000000 +0200
+@@ -40,6 +40,7 @@
+ obj-$(CONFIG_SA1100_SHANNON) += shannon.o
+
+ obj-$(CONFIG_SA1100_SIMPAD) += simpad.o
++obj-$(CONFIG_SA1100_SIMPAD) += cs3-simpad.o
+ led-$(CONFIG_SA1100_SIMPAD) += leds-simpad.o
+
+ # LEDs support
+diff -uNr linux-2.6.21.vanilla/arch/arm/mach-sa1100/cs3-simpad.c linux-2.6.21/arch/arm/mach-sa1100/cs3-simpad.c
+--- linux-2.6.21.vanilla/arch/arm/mach-sa1100/cs3-simpad.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/arch/arm/mach-sa1100/cs3-simpad.c 2007-05-30 17:45:51.000000000 +0200
+@@ -0,0 +1,169 @@
++/*
++ * simpad-cs3.c
++ *
++ * This driver shows the GPIO states of the cs3 latch. You can also
++ * switch some GPIOS.
++ *
++ * (c) 2007 Bernhard Guillon <Bernhard.Guillon@OpenSIMpad.org>
++ *
++ * You may use this code as per GPL version 2
++ *
++ * Some parts are based on battery.c
++ *
++ * mrdata: -added cs3_ro support
++ * -added preprocessor stuff
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/device.h>
++
++#include <asm/arch/simpad.h>
++
++extern long get_cs3_ro(void);
++extern long get_cs3_shadow(void);
++extern void set_cs3_bit(int value);
++extern void clear_cs3_bit(int value);
++
++struct cs3 {
++ struct class_device class_dev;
++ const char *name;
++ char *id;
++ int type;
++};
++
++struct cs3 cs3 ={
++ .name = "latch_cs3",
++};
++
++
++#define CS3_STORE_ATTR(namek,nameg) \
++static ssize_t namek##_store (struct class_device *cdev, const char * buf, size_t count) \
++{ \
++ char val; \
++ if (sscanf(buf, "%c",&val) != 1) \
++ return -EINVAL; \
++ if (val == '1') \
++ set_cs3_bit(nameg); \
++ else if (val == '0') \
++ clear_cs3_bit(nameg); \
++ return strlen(buf); \
++}
++
++CS3_STORE_ATTR(display_on, DISPLAY_ON);
++CS3_STORE_ATTR(dect_power_on, DECT_POWER_ON);
++CS3_STORE_ATTR(irda_sd, IRDA_SD);
++CS3_STORE_ATTR(sd_mediaq, SD_MEDIAQ);
++CS3_STORE_ATTR(led2_on, LED2_ON);
++CS3_STORE_ATTR(irda_mode, IRDA_MODE);
++CS3_STORE_ATTR(reset_simcard, RESET_SIMCARD);
++
++
++#define CS3_ATTR(shadro,namek,nameg,mode,store) \
++static ssize_t namek##_show(struct class_device *class_dev, char *buf) \
++{ \
++ if (get_cs3_##shadro() & nameg ) \
++ return sprintf(buf, "1\n"); \
++ else \
++ return sprintf(buf, "0\n"); \
++} \
++static CLASS_DEVICE_ATTR(namek, mode, namek##_show, store)
++
++CS3_ATTR(shadow, vcc_5v_en, VCC_5V_EN, 0444, NULL);
++CS3_ATTR(shadow, vcc_3v_en, VCC_3V_EN, 0444, NULL);
++CS3_ATTR(shadow, en1, EN1, 0444, NULL);
++CS3_ATTR(shadow, en0, EN0, 0444, NULL);
++CS3_ATTR(shadow, display_on, DISPLAY_ON, 0664, display_on_store);
++CS3_ATTR(shadow, pcmcia_buff_dis, PCMCIA_BUFF_DIS, 0444, NULL);
++CS3_ATTR(shadow, mq_reset, MQ_RESET, 0444, NULL);
++CS3_ATTR(shadow, pcmcia_reset, PCMCIA_RESET, 0444, NULL);
++CS3_ATTR(shadow, dect_power_on, DECT_POWER_ON, 0664, dect_power_on_store);
++CS3_ATTR(shadow, irda_sd, IRDA_SD, 0664, irda_sd_store);
++CS3_ATTR(shadow, rs232_on, RS232_ON, 0444, NULL);
++CS3_ATTR(shadow, sd_mediaq, SD_MEDIAQ, 0664, sd_mediaq_store);
++CS3_ATTR(shadow, led2_on, LED2_ON, 0664, led2_on_store);
++CS3_ATTR(shadow, irda_mode, IRDA_MODE, 0664, irda_mode_store);
++CS3_ATTR(shadow, enable_5v, ENABLE_5V, 0444, NULL);
++CS3_ATTR(shadow, reset_simcard, RESET_SIMCARD, 0664, reset_simcard_store);
++CS3_ATTR(ro, pcmcia_bvd1, PCMCIA_BVD1, 0444, NULL);
++CS3_ATTR(ro, pcmcia_bvd2, PCMCIA_BVD2, 0444, NULL);
++CS3_ATTR(ro, pcmcia_vs1, PCMCIA_VS1, 0444, NULL);
++CS3_ATTR(ro, pcmcia_vs2, PCMCIA_VS2, 0444, NULL);
++CS3_ATTR(ro, lock_ind, LOCK_IND, 0444, NULL);
++CS3_ATTR(ro, charging_state, CHARGING_STATE, 0444, NULL);
++CS3_ATTR(ro, pcmcia_short, PCMCIA_SHORT, 0444, NULL);
++
++static struct class simpad_gpios_class = {
++ .name = "simpad",
++};
++
++#define create_entry_conditional(namek) \
++ rc = class_device_create_file(&cs3->class_dev, &class_device_attr_##namek); \
++ if (rc) goto out; \
++
++static int register_cs3_latch(struct cs3 *cs3)
++{
++ int rc = 0;
++ cs3->class_dev.class = &simpad_gpios_class;
++ strcpy(cs3->class_dev.class_id, cs3->name);
++ rc = class_device_register(&cs3->class_dev);
++ if(rc)
++ goto out;
++
++ create_entry_conditional(vcc_5v_en);
++ create_entry_conditional(vcc_3v_en);
++ create_entry_conditional(en1);
++ create_entry_conditional(en0);
++ create_entry_conditional(display_on);
++ create_entry_conditional(pcmcia_buff_dis);
++ create_entry_conditional(mq_reset);
++ create_entry_conditional(pcmcia_reset);
++ create_entry_conditional(dect_power_on);
++ create_entry_conditional(irda_sd);
++ create_entry_conditional(rs232_on);
++ create_entry_conditional(sd_mediaq);
++ create_entry_conditional(led2_on);
++ create_entry_conditional(irda_mode);
++ create_entry_conditional(enable_5v);
++ create_entry_conditional(reset_simcard);
++ create_entry_conditional(pcmcia_bvd1);
++ create_entry_conditional(pcmcia_bvd2);
++ create_entry_conditional(pcmcia_vs1);
++ create_entry_conditional(pcmcia_vs2);
++ create_entry_conditional(lock_ind);
++ create_entry_conditional(charging_state);
++ create_entry_conditional(pcmcia_short);
++
++out:
++ return rc;
++}
++
++static int __init simpad_gpios_class_init(void)
++{
++ int rc = 0;
++
++ rc = class_register(&simpad_gpios_class);
++
++ if(rc != 0)
++ {
++ printk(KERN_ERR "cs3 latch class failed to register properly\n");
++ return rc;
++ }
++
++ rc = register_cs3_latch(&cs3);
++ return rc;
++}
++
++static void __exit simpad_gpios_class_exit(void)
++{
++ class_unregister(&simpad_gpios_class);
++}
++
++module_init(simpad_gpios_class_init);
++module_exit(simpad_gpios_class_exit);
++
++MODULE_AUTHOR("Bernhard Guillon");
++MODULE_DESCRIPTION("CS3_latch driver");
++MODULE_LICENSE("GPL");
diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-mq200.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-mq200.patch
new file mode 100644
index 0000000000..5726779ad6
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-mq200.patch
@@ -0,0 +1,2511 @@
+diff -uNr linux-2.6.21.vanilla/drivers/video/Kconfig linux-2.6.21/drivers/video/Kconfig
+--- linux-2.6.21.vanilla/drivers/video/Kconfig 2007-05-01 16:40:48.000000000 +0200
++++ linux-2.6.21/drivers/video/Kconfig 2007-05-01 17:02:17.000000000 +0200
+@@ -139,7 +139,7 @@
+ This is particularly important to one driver, matroxfb. If
+ unsure, say N.
+
+-comment "Frame buffer hardware drivers"
++comment "Frambuffer hardware drivers"
+ depends on FB
+
+ config FB_CIRRUS
+@@ -1120,6 +1120,15 @@
+ ---help---
+ Driver for graphics boards with S3 Trio / S3 Virge chip.
+
++config FB_MQ200
++ bool "MQ200 Driver"
++ depends on (FB = y) && ARM && ARCH_SA1100
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ select FB_CFB_IMAGEBLIT
++ help
++ This is a MQ200 driver tested only on Siemens SIMpads.
++
+ config FB_SAVAGE
+ tristate "S3 Savage support"
+ depends on FB && PCI && EXPERIMENTAL
+diff -uNr linux-2.6.21.vanilla/drivers/video/Makefile linux-2.6.21/drivers/video/Makefile
+--- linux-2.6.21.vanilla/drivers/video/Makefile 2007-05-01 16:40:48.000000000 +0200
++++ linux-2.6.21/drivers/video/Makefile 2007-05-01 17:02:17.000000000 +0200
+@@ -29,6 +29,7 @@
+ obj-$(CONFIG_FB_PM2) += pm2fb.o
+ obj-$(CONFIG_FB_PM3) += pm3fb.o
+
++obj-$(CONFIG_FB_MQ200) += mq200/
+ obj-$(CONFIG_FB_MATROX) += matrox/
+ obj-$(CONFIG_FB_RIVA) += riva/ vgastate.o
+ obj-$(CONFIG_FB_NVIDIA) += nvidia/
+diff -uNr linux-2.6.21.vanilla/drivers/video/backlight/Kconfig linux-2.6.21/drivers/video/backlight/Kconfig
+--- linux-2.6.21.vanilla/drivers/video/backlight/Kconfig 2007-05-01 16:40:48.000000000 +0200
++++ linux-2.6.21/drivers/video/backlight/Kconfig 2007-05-01 17:02:17.000000000 +0200
+@@ -63,3 +63,20 @@
+ help
+ If you have a Frontpath ProGear say Y to enable the
+ backlight driver.
++
++config BACKLIGHT_SIMPAD
++ tristate "SIMpad MQ200 Backlight driver"
++ depends on SA1100_SIMPAD && BACKLIGHT_CLASS_DEVICE
++ default y
++ help
++ If you have a Siemens SIMpad say Y to enable the
++ backlight driver.
++
++config LCD_SIMPAD
++ tristate "SIMpad MQ200 LCD driver"
++ depends on SA1100_SIMPAD && LCD_CLASS_DEVICE
++ default y
++ help
++ If you have a Siemens SIMpad say Y to enable the
++ LCD driver.
++
+diff -uNr linux-2.6.21.vanilla/drivers/video/backlight/Makefile linux-2.6.21/drivers/video/backlight/Makefile
+--- linux-2.6.21.vanilla/drivers/video/backlight/Makefile 2007-05-01 16:40:48.000000000 +0200
++++ linux-2.6.21/drivers/video/backlight/Makefile 2007-05-01 17:02:17.000000000 +0200
+@@ -6,3 +6,5 @@
+ obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
+ obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
+ obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
++obj-$(CONFIG_BACKLIGHT_SIMPAD) += simpad_bl.o
++obj-$(CONFIG_LCD_SIMPAD) += simpad_lcd.o
+diff -uNr linux-2.6.21.vanilla/drivers/video/backlight/simpad_bl.c linux-2.6.21/drivers/video/backlight/simpad_bl.c
+--- linux-2.6.21.vanilla/drivers/video/backlight/simpad_bl.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/video/backlight/simpad_bl.c 2007-05-01 17:02:17.000000000 +0200
+@@ -0,0 +1,208 @@
++/*
++ * GPLv2 <zecke@handhelds.org
++ *
++ * Implementation of the backlight_driver for
++ * the mq200 framebuffer
++ *
++ * 2007/03/17 mrdata:
++ * - small changes simpad_bl_get_brightness()
++ * simpad_bl_set_brightness()
++ * - new function simpad_bl_update_status()
++ * - changed struct backlight_properties simpad_bl_props()
++ * to new one
++ * - changed __init simpad_bl_init() -> backlight_device_register
++ *
++ * 2007/03/24 mrdata
++ * - added .brightness=127 in
++ * struct backlight_properties simpad_bl_props()
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++#include <linux/fb.h>
++#include <linux/backlight.h>
++
++#include <asm/types.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++#include "../mq200/mq200_data.h"
++
++#define SIMPAD_BACKLIGHT_MASK 0x00a10044
++#define SIMPAD_DEFAULT_INTENSITY 127
++#define SIMPAD_MAX_INTENSITY 254
++#define REGISTER_BASE 0xf2e00000
++
++static int simpad_bl_suspended;
++static int current_intensity = 0;
++
++static void simpad_bl_send_intensity(struct backlight_device *bd)
++{
++ int intensity = bd->props.brightness;
++
++ union fp0fr fp0fr;
++ unsigned long dutyCycle, pwmcontrol;
++
++ if (intensity > SIMPAD_MAX_INTENSITY)
++ intensity = SIMPAD_MAX_INTENSITY;
++
++ if (bd->props.power != FB_BLANK_UNBLANK)
++ intensity = 0;
++
++ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
++ intensity = 0;
++
++ if (simpad_bl_suspended)
++ intensity = 0;
++
++ if (intensity != current_intensity)
++ {
++ /*
++ * Determine dutyCycle.
++ * Note: the lower the value, the brighter the display!
++ */
++
++ dutyCycle = SIMPAD_MAX_INTENSITY - intensity;
++
++ /*
++ * Configure PWM0 (source clock = oscillator clock, pwm always enabled,
++ * zero, clock pre-divider = 4) pwm frequency = 12.0kHz
++ */
++
++ fp0fr.whole = readl(FP0FR(REGISTER_BASE));
++ pwmcontrol = fp0fr.whole & 0xffff00ff;
++ fp0fr.whole &= 0xffffff00;
++ fp0fr.whole |= 0x00000044;
++ writel(fp0fr.whole, FP0FR(REGISTER_BASE));
++
++ /* Write to pwm duty cycle register. */
++ fp0fr.whole = dutyCycle << 8;
++ fp0fr.whole &= 0x0000ff00;
++ fp0fr.whole |= pwmcontrol;
++ writel(fp0fr.whole, FP0FR(REGISTER_BASE));
++
++ current_intensity = intensity;
++ }
++}
++
++
++#ifdef CONFIG_PM
++static int simpad_bl_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct backlight_device *bd = platform_get_drvdata(pdev);
++
++ simpad_bl_suspended = 1;
++ simpad_bl_send_intensity(bd);
++ return 0;
++}
++
++static int simpad_bl_resume(struct platform_device *pdev)
++{
++ struct backlight_device *bd = platform_get_drvdata(pdev);
++
++ simpad_bl_suspended = 0;
++ simpad_bl_send_intensity(bd);
++ return 0;
++}
++#else
++#define simpad_bl_suspend NULL
++#define simpad_bl_resume NULL
++#endif
++
++
++static int simpad_bl_set_intensity(struct backlight_device *bd)
++{
++ simpad_bl_send_intensity(bd);
++ return 0;
++}
++
++
++static int simpad_bl_get_intensity(struct backlight_device *bd)
++{
++ return current_intensity;
++}
++
++
++static struct backlight_ops simpad_bl_ops = {
++ .get_brightness = simpad_bl_get_intensity,
++ .update_status = simpad_bl_set_intensity,
++};
++
++
++static int __init simpad_bl_probe(struct platform_device *pdev)
++{
++ struct backlight_device *bd;
++
++ bd = backlight_device_register("simpad-mq200-bl", &pdev->dev, NULL, &simpad_bl_ops);
++
++ if (IS_ERR (bd))
++ return PTR_ERR (bd);
++
++ platform_set_drvdata(pdev, bd);
++
++ bd->props.max_brightness = SIMPAD_MAX_INTENSITY;
++ bd->props.brightness = SIMPAD_DEFAULT_INTENSITY;
++ simpad_bl_send_intensity(bd);
++
++ return 0;
++}
++
++
++static int simpad_bl_remove(struct platform_device *pdev)
++{
++ struct backlight_device *bd = platform_get_drvdata(pdev);
++
++ bd->props.brightness = 0;
++ bd->props.power = 0;
++ simpad_bl_send_intensity(bd);
++
++ backlight_device_unregister(bd);
++
++ return 0;
++}
++
++static struct platform_driver simpad_bl_driver = {
++ .probe = simpad_bl_probe,
++ .remove = simpad_bl_remove,
++ .suspend = simpad_bl_suspend,
++ .resume = simpad_bl_resume,
++ .driver = {
++ .name = "simpad-mq200-bl",
++ },
++};
++
++static struct platform_device *simpad_bl_device = NULL;
++
++static int __init simpad_bl_init(void)
++{
++ int ret;
++
++ ret = platform_driver_register(&simpad_bl_driver);
++ if (!ret) {
++ simpad_bl_device = platform_device_alloc("simpad-mq200-bl", -1);
++ if (!simpad_bl_device)
++ return -ENOMEM;
++
++ ret = platform_device_add(simpad_bl_device);
++
++ if (ret) {
++ platform_device_put(simpad_bl_device);
++ platform_driver_unregister(&simpad_bl_driver);
++ }
++ }
++ return ret;
++}
++
++static void __exit simpad_bl_exit(void)
++{
++ platform_device_unregister(simpad_bl_device);
++ platform_driver_unregister(&simpad_bl_driver);
++}
++
++
++module_init(simpad_bl_init);
++module_exit(simpad_bl_exit);
++MODULE_AUTHOR("Holger Hans Peter Freyther");
++MODULE_LICENSE("GPL");
+diff -uNr linux-2.6.21.vanilla/drivers/video/backlight/simpad_lcd.c linux-2.6.21/drivers/video/backlight/simpad_lcd.c
+--- linux-2.6.21.vanilla/drivers/video/backlight/simpad_lcd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/video/backlight/simpad_lcd.c 2007-05-01 17:02:17.000000000 +0200
+@@ -0,0 +1,170 @@
++/*
++ * GPLv2 <zecke@handhelds.org
++ *
++ * Implementation of the lcd_driver for the mq200 framebuffer
++ *
++ * 2007/03/24 mrdata:
++ * - added simpad_lcd_get_contrast()
++ * - added simpad_lcd_set_contrast()
++ * - modify struct lcd_properties simpad_lcd_props
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/fb.h>
++#include <linux/lcd.h>
++
++#include <asm/arch/simpad.h>
++#include <asm/hardware.h>
++
++extern long get_cs3_shadow(void);
++extern void set_cs3_bit(int);
++extern void clear_cs3_bit(int);
++
++#define UNUSED(x) x=x
++
++static int simpad_lcd_get_power(struct lcd_device* dev)
++{
++ UNUSED(dev);
++
++ return (get_cs3_shadow() & DISPLAY_ON) ? 0 : 4;
++}
++
++static int simpad_lcd_set_power(struct lcd_device* dev, int power)
++{
++ UNUSED(dev);
++
++ if( power == 4 )
++ clear_cs3_bit(DISPLAY_ON);
++ else
++ set_cs3_bit(DISPLAY_ON);
++
++ return 0;
++}
++
++static int simpad_lcd_get_contrast(struct lcd_device* dev)
++{
++ UNUSED(dev);
++
++ return 0;
++}
++
++static int simpad_lcd_set_contrast(struct lcd_device* dev, int contrast)
++{
++ UNUSED(dev);
++
++ UNUSED(contrast);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int simpad_lcd_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ UNUSED(state);
++ static int ret;
++
++ struct lcd_device* ld;
++
++ ld = platform_get_drvdata(pdev);
++
++ ret = simpad_lcd_set_power(ld, 4);
++
++ return ret;
++}
++
++static int simpad_lcd_resume(struct platform_device *pdev)
++{
++ struct lcd_device *ld;
++ static int ret;
++
++ ld = platform_get_drvdata(pdev);
++
++ ret = simpad_lcd_set_power(ld, 0);
++
++ return ret;
++}
++#else
++#define simpad_lcd_suspend NULL
++#define simpad_lcd_resume NULL
++#endif
++
++static struct lcd_properties simpad_lcd_props = {
++ .max_contrast = 0,
++};
++
++
++static struct lcd_ops simpad_lcd_ops = {
++ .get_power = simpad_lcd_get_power,
++ .set_power = simpad_lcd_set_power,
++ .get_contrast = simpad_lcd_get_contrast,
++ .set_contrast = simpad_lcd_set_contrast,
++};
++
++static int __init simpad_lcd_probe(struct platform_device *pdev)
++{
++ struct lcd_device *ld;
++
++ ld = lcd_device_register ("simpad-mq200-lcd", &pdev->dev, &simpad_lcd_ops);
++
++ if (IS_ERR(ld))
++ return PTR_ERR(ld);
++
++ platform_set_drvdata(pdev, ld);
++
++ ld->props.max_contrast = 0;
++
++ return 0;
++}
++
++static int simpad_lcd_remove(struct platform_device *pdev)
++{
++ struct lcd_device *ld = platform_get_drvdata(pdev);
++
++ lcd_device_unregister(ld);
++
++ return 0;
++}
++
++static struct platform_driver simpad_lcd_driver = {
++ .probe = simpad_lcd_probe,
++ .remove = simpad_lcd_remove,
++ .suspend = simpad_lcd_suspend,
++ .resume = simpad_lcd_resume,
++ .driver = {
++ .name = "simpad-mq200-lcd",
++ },
++};
++
++static struct platform_device *simpad_lcd_device = NULL;
++
++static int __init simpad_lcd_init(void)
++{
++ int ret;
++
++ ret = platform_driver_register(&simpad_lcd_driver);
++ if (!ret) {
++ simpad_lcd_device = platform_device_alloc("simpad-mq200-lcd", -1);
++ if (!simpad_lcd_device)
++ return -ENOMEM;
++
++ ret = platform_device_add(simpad_lcd_device);
++
++ if (ret) {
++ platform_device_put(simpad_lcd_device);
++ platform_driver_unregister(&simpad_lcd_driver);
++ }
++ }
++ return ret;
++}
++
++static void __exit simpad_lcd_exit(void) {
++ platform_driver_unregister(&simpad_lcd_driver);
++ platform_device_unregister(simpad_lcd_device);
++}
++
++module_init(simpad_lcd_init);
++module_exit(simpad_lcd_exit);
++MODULE_AUTHOR("Holger Hans Peter Freyther");
++MODULE_LICENSE("GPL");
+diff -uNr linux-2.6.21.vanilla/drivers/video/mq200/Makefile linux-2.6.21/drivers/video/mq200/Makefile
+--- linux-2.6.21.vanilla/drivers/video/mq200/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/video/mq200/Makefile 2007-05-01 17:02:17.000000000 +0200
+@@ -0,0 +1,6 @@
++# Makefile for mq200 video driver
++# 4 Aug 2003, Holger Hans Peter Freyther
++#
++
++obj-$(CONFIG_FB_MQ200) += mq_skeleton.o mq_external.o
++
+diff -uNr linux-2.6.21.vanilla/drivers/video/mq200/mq200_data.h linux-2.6.21/drivers/video/mq200/mq200_data.h
+--- linux-2.6.21.vanilla/drivers/video/mq200/mq200_data.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/video/mq200/mq200_data.h 2007-05-01 17:02:17.000000000 +0200
+@@ -0,0 +1,1120 @@
++/*
++ * From ucLinux mq200fb.c and mq200fb.h
++ *
++ * 2007/03/11 mrdata:
++ * insert registers for graphics controller 2 module
++ */
++
++#ifndef __MQ200_FB_H__
++#define __MQ200_FB_H__
++
++struct mq200_io_regions {
++ u32 fb_size; /* framebuffer size */
++ unsigned long phys_mmio_base; /* physical register memory base */
++ unsigned long virt_mmio_base; /* virtual start of registers */
++ unsigned long phys_fb_base; /* physical address of frame buffer */
++ unsigned long virt_fb_base; /* virtual start of the framebuffer */
++};
++
++#define MQ200_MONITOR_HORI_RES(info) info->monitor_info.horizontal_res
++#define MQ200_MONITOR_VERT_RES(info) info->monitor_info.vertical_res
++#define MQ200_MONITOR_DEPTH(info) info->monitor_info.depth
++#define MQ200_MONITOR_LINE_LENGTH(info) info->monitor_info.line_length
++
++struct mq200_monitor_info {
++ unsigned int horizontal_res;
++ unsigned int vertical_res;
++ unsigned int depth;
++ unsigned int refresh;
++ unsigned int line_length;
++ unsigned long flags;
++};
++
++
++/**
++ * Addresses of Module
++ */
++#define MQ200_FB_BASE (x) (x + 0x1800000) /* framebuffer */
++#define MQ200_FB_SIZE 0x200000 /* framebuffer size in bytes */
++#define MQ200_REGS_BASE(x) (x + 0x1e00000) /* start of registers area */
++#define MQ200_REGS_SIZE 0x200000 /* registers area size */
++
++#define PMU_OFFSET 0x00000 /* power management */
++#define CPU_OFFSET 0x02000 /* CPU interface */
++#define MIU_OFFSET 0x04000 /* memory controller */
++#define IN_OFFSET 0x08000 /* interrupt controller */
++#define GC_OFFSET 0x0a000 /* graphics controller 1&2 */
++#define GE_OFFSET 0x0c000 /* graphics engine */
++#define FPI_OFFSET 0x0e000 /* flat panel controller */
++#define CP1_OFFSET 0x10000 /* color palette 1 */
++#define DC_OFFSET 0x14000 /* device configuration */
++#define PCI_OFFSET 0x16000 /* PCI configuration */
++#define PSF_OFFSET 0x18000 /* ??? */
++
++
++/****
++ * Registers
++ */
++
++/* power management unit */
++#define PMR(addr) (addr + PCI_OFFSET + 0x40)/* power management
++ register */
++#define PMR_VALUE 0x06210001 /* expected read value of PMR register */
++#define PM00R(addr) (addr + PMU_OFFSET + 0x00) /* power management unit
++ configuration
++ register */
++#define PM01R(addr) (addr + PMU_OFFSET + 0x04) /* D1 state control */
++#define PM02R(addr) (addr + PMU_OFFSET + 0x08) /* d2 state control */
++#define PM06R(addr) (addr + PMU_OFFSET + 0x18) /* PLL 2 programming */
++#define PM07R(addr) (addr + PMU_OFFSET + 0x1c) /* PLL 3 programming */
++
++#define PMCSR(addr) (addr + PCI_OFFSET + 0x44) /* power management
++ control/status
++ register */
++
++/* memory interface unit */
++#define MM00R(addr) (addr + MIU_OFFSET + 0x00)/* MIU interface control
++ 0 */
++#define MM01R(addr) (addr + MIU_OFFSET + 0x04) /* MIU interface control
++ 1 */
++#define MM02R(addr) (addr + MIU_OFFSET + 0x08) /* memory interface
++ control 2 */
++#define MM03R(addr) (addr + MIU_OFFSET + 0x0c) /* memory interface
++ control 3 */
++#define MM04R(addr) (addr + MIU_OFFSET + 0x10) /* memory interface
++ control 4 */
++/* graphics controller 1 module */
++#define GC00R(addr) (addr + GC_OFFSET + 0x00) /* graphics controller 1
++ control */
++#define GC01R(addr) (addr + GC_OFFSET + 0x04) /* graphics controller
++ CRT control */
++#define GC02R(addr) (addr + GC_OFFSET + 0x08) /* horizontal display 1
++ control */
++#define GC03R(addr) (addr + GC_OFFSET + 0x0c) /* vertical display 1
++ control */
++#define GC04R(addr) (addr + GC_OFFSET + 0x10) /* horizontal sync 1
++ control */
++#define GC05R(addr) (addr + GC_OFFSET + 0x14) /* vertical sync 1
++ control */
++#define GC07R(addr) (addr + GC_OFFSET + 0x1c) /* vertical display 1
++ count */
++#define GC08R(addr) (addr + GC_OFFSET + 0x20) /* horizontal window 1
++ control */
++#define GC09R(addr) (addr + GC_OFFSET + 0x24) /* vertical window 1
++ control */
++#define GC0AR(addr) (addr + GC_OFFSET + 0x28) /* alternate horizontal
++ window 1 control */
++#define GC0BR(addr) (addr + GC_OFFSET + 0x2c) /* alternate vertical
++ window 1 control */
++#define GC0CR(addr) (addr + GC_OFFSET + 0x30) /* window 1
++ start address */
++#define GC0DR(addr) (addr + GC_OFFSET + 0x34) /* alternate window 1
++ start address */
++#define GC0ER(addr) (addr + GC_OFFSET + 0x38) /* alternate window 1
++ stride */
++#define GC0FR(addr) (addr + GC_OFFSET + 0x3c) /* alternate window 1
++ line size */
++#define GC10R(addr) (addr + GC_OFFSET + 0x40) /* hardware cursor 1
++ position */
++#define GC11R(addr) (addr + GC_OFFSET + 0x44) /* hardware cursor 1
++ start address and
++ offset */
++#define GC12R(addr) (addr + GC_OFFSET + 0x48) /* hardware cursor 1
++ foreground color */
++#define GC13R(addr) (addr + GC_OFFSET + 0x4c) /* hardware cursor 1
++ background color */
++
++/* graphics controller 2 module */
++#define GC20R(addr) (addr + GC_OFFSET + 0x80) /* graphics controller 2
++ control */
++#define GC21R(addr) (addr + GC_OFFSET + 0x84) /* graphics controller
++ CRC control */
++#define GC22R(addr) (addr + GC_OFFSET + 0x88) /* horizontal display 2
++ control */
++#define GC23R(addr) (addr + GC_OFFSET + 0x8c) /* vertical display 2
++ control */
++#define GC24R(addr) (addr + GC_OFFSET + 0x90) /* horizontal sync 2
++ control */
++#define GC25R(addr) (addr + GC_OFFSET + 0x94) /* vertical sync 2
++ control */
++#define GC27R(addr) (addr + GC_OFFSET + 0x9c) /* vertical display 2
++ count */
++#define GC28R(addr) (addr + GC_OFFSET + 0xa0) /* horizontal window 2
++ control */
++#define GC29R(addr) (addr + GC_OFFSET + 0xa4) /* vertical window 2
++ control */
++#define GC2AR(addr) (addr + GC_OFFSET + 0xa8) /* alternate horizontal
++ window 2 control */
++#define GC2BR(addr) (addr + GC_OFFSET + 0xac) /* alternate vertical
++ window 2 control */
++#define GC2CR(addr) (addr + GC_OFFSET + 0xb0) /* window 2
++ start address */
++#define GC2DR(addr) (addr + GC_OFFSET + 0xb4) /* alternate window 2
++ start address */
++#define GC2ER(addr) (addr + GC_OFFSET + 0xb8) /* alternate window 2
++ stride */
++#define GC2FR(addr) (addr + GC_OFFSET + 0xbc) /* alternate window 2
++ line size */
++#define GC30R(addr) (addr + GC_OFFSET + 0xc0) /* hardware cursor 2
++ position */
++#define GC31R(addr) (addr + GC_OFFSET + 0xc4) /* hardware cursor 2
++ start address and
++ offset */
++#define GC32R(addr) (addr + GC_OFFSET + 0xc8) /* hardware cursor 2
++ foreground color */
++#define GC33R(addr) (addr + GC_OFFSET + 0xcc) /* hardware cursor 2
++ background color */
++
++/* graphics engine */
++#define ROP_SRCCOPY 0xCC /* dest = source */
++#define ROP_SRCPAINT 0xEE /* dest = source OR dest */
++#define ROP_SRCAND 0x88 /* dest = source AND dest */
++#define ROP_SRCINVERT 0x66 /* dest = source XOR dest */
++#define ROP_SRCERASE 0x44 /* dest = source AND (NOT dest) */
++#define ROP_NOTSRCCOPY 0x33 /* dest = NOT source */
++#define ROP_NOTSRCERASE 0x11 /* dest = (NOT source) AND (NOT dest) */
++#define ROP_MERGECOPY 0xC0 /* dest = source AND pattern */
++#define ROP_MERGEPAINT 0xBB /* dest = (NOT source) OR dest */
++#define ROP_PATCOPY 0xF0 /* dest = pattern */
++#define ROP_PATPAINT 0xFB /* dest = DPSnoo */
++#define ROP_PATINVERT 0x5A /* dest = pattern XOR dest */
++#define ROP_DSTINVERT 0x55 /* dest = NOT dest */
++#define ROP_BLACKNESS 0x00 /* dest = BLACK */
++#define ROP_WHITENESS 0xFF /* dest = WHITE */
++
++#define GE00R(addr) (addr + GE_OFFSET + 0x00) /* primary drawing command
++ register */
++#define GE01R(addr) (addr + GE_OFFSET + 0x04) /* primary width and
++ height register */
++#define GE02R(addr) (addr + GE_OFFSET + 0x08) /* primary destination
++ address register */
++#define GE03R(addr) (addr + GE_OFFSET + 0x0c) /* primary source XY
++ register */
++#define GE04R(addr) (addr + GE_OFFSET + 0x10) /* primary color compare
++ register */
++#define GE05R(addr) (addr + GE_OFFSET + 0x14) /* primary clip left/top
++ register */
++#define GE06R(addr) (addr + GE_OFFSET + 0x18) /* primary clip
++ right/bottom register
++ */
++#define GE07R(addr) (addr + GE_OFFSET + 0x1c) /* primary source and
++ pattern offset
++ register */
++#define GE08R(addr) (addr + GE_OFFSET + 0x20) /* primary foreground
++ color
++ register/rectangle
++ fill register */
++#define GE09R(addr) (addr + GE_OFFSET + 0x24) /* source stride/offset
++ register */
++#define GE0AR(addr) (addr + GE_OFFSET + 0x28) /* destination stride
++ register and color
++ depth */
++#define GE0BR(addr) (addr + GE_OFFSET + 0x2c) /* image base address
++ register */
++#define GE40R(addr) (addr + GE_OFFSET + 0x100) /* mono pattern register
++ 0 */
++#define GE41R(addr) (addr + GE_OFFSET + 0x104) /* mono pattern register
++ 1 */
++#define GE42R(addr) (addr + GE_OFFSET + 0x108) /* foreground color
++ register */
++#define GE43R(addr) (addr + GE_OFFSET + 0x10c) /* background color
++ register */
++/* color palette */
++#define C1xxR(addr, regno) \
++ (addr + CP1_OFFSET + (regno) * 4) /* graphics controller color
++ palette 1 */
++/* device configuration */
++#define DC00R(addr) (addr + DC_OFFSET + 0x00) /* device configuration
++ register 0 */
++#define DC_RESET 0x4000
++/* PCI configuration space */
++#define PC00R(addr) (addr + PCI_OFFSET + 0x00)/* device ID/vendor ID
++ register */
++/* Flatpanel Control */
++#define FP00R(addr) (addr + FPI_OFFSET + 0x00) /* Flat Panel Control 0 */
++#define FP01R(addr) (addr + FPI_OFFSET + 0x04) /* Flat Panel Output Pin */
++#define FP02R(addr) (addr + FPI_OFFSET + 0x08) /* Flat Panel Gener Purpose
++ Outout Control Register */
++#define FP03R(addr) (addr + FPI_OFFSET + 0x0c) /* General Purpose I/O Port
++ Control Register */
++#define FP04R(addr) (addr + FPI_OFFSET + 0x10) /* STN Panel Control Register */
++#define FP05R(addr) (addr + FPI_OFFSET + 0x14) /* D-STN Half Frame Buffer
++ Control Register -By Guess */
++#define FP0FR(addr) (addr + FPI_OFFSET + 0x3c) /* Pulse Width Modulation
++ Control Register */
++#define FRCTL_PATTERN_COUNT 32
++#define FP10R(addr) (addr + FPI_OFFSET + 0x40) /* Frame-Rate Control Pattern
++ Register */
++#define FP11R(addr) (addr + FPI_OFFSET + 0x44)
++#define FP2FR(addr) (addr + FPI_OFFSET + 0xc0) /* Frame-Rate Control Weight
++ Registers */
++
++
++
++
++/* power management miscellaneous control */
++union pm00r {
++ struct {
++ u32 pll1_n_b5 :1; /* PLL 1 N parameter bit 5 is 0 */
++ u32 reserved_1 :1;
++ u32 pll2_enbl :1; /* PLL 2 enable */
++ u32 pll3_enbl :1; /* PLL 3 enable */
++ u32 reserved_2 :1;
++ u32 pwr_st_ctrl :1; /* power state status control */
++ u32 reserved_3 :2;
++
++ u32 ge_enbl :1; /* graphics engine enable */
++ u32 ge_bsy_gl :1; /* graphics engine force busy (global) */
++ u32 ge_bsy_lcl :1; /* graphics engine force busy (local) */
++ u32 ge_clock :2; /* graphics engine clock select */
++ u32 ge_cmd_fifo :1; /* graphics engine command FIFO reset */
++ u32 ge_src_fifo :1; /* graphics engine CPU source FIFO reset */
++ u32 miu_pwr_seq :1; /* memory interface unit power sequencing
++ enable */
++
++ u32 d3_mem_rfsh :1; /* D3 memory refresh */
++ u32 d4_mem_rfsh :1; /* D4 memory refresh */
++ u32 gpwr_intrvl :2; /* general power sequencing interval */
++ u32 fppwr_intrvl:2; /* flat panel power sequencing interval */
++ u32 gpwr_seq_ctr:1; /* general power sequencing interval control */
++ u32 pmu_tm :1; /* PMU test mode */
++
++ u32 pwr_state :2; /* power state (read only) */
++ u32 pwr_seq_st :1; /* power sequencing active status (read
++ only) */
++ u32 reserved_4 :5;
++ } part;
++ u32 whole;
++};
++
++/* D1 state control */
++union pm01r {
++ struct {
++ u32 osc_enbl :1; /* D1 oscillator enable */
++ u32 pll1_enbl :1; /* D1 PLL 1 enable */
++ u32 pll2_enbl :1; /* D1 PLL 2 enable */
++ u32 pll3_enbl :1; /* D1 PLL 3 enable */
++ u32 miu_enbl :1; /* D1 Memory Interface Unit (MIU) enable */
++ u32 mem_rfsh :1; /* D1 memory refresh enable */
++ u32 ge_enbl :1; /* D1 Graphics Engine (GE) enable */
++ u32 reserved_1 :1;
++
++ u32 crt_enbl :1; /* D1 CRT enable */
++ u32 fpd_enbl :1; /* D1 Flat Panel enable */
++ u32 reserved_2 :6;
++
++ u32 ctl1_enbl :1; /* D1 controller 1 enable */
++ u32 win1_enbl :1; /* D1 window 1 enable */
++ u32 awin1_enbl :1; /* D1 alternate window 1 enable */
++ u32 cur1_enbl :1; /* D1 cursor 1 enable */
++ u32 reserved_3 :4;
++
++ u32 ctl2_enbl :1; /* D1 controller 2 enable */
++ u32 win2_enbl :1; /* D1 window 2 enable */
++ u32 awin2_enbl :1; /* D1 alternate window 2 enable */
++ u32 cur2_enbl :1; /* D1 cursor 2 enable */
++ u32 reserved_4 :4;
++ } part;
++ u32 whole;
++};
++
++/* D2 state control */
++union pm02r {
++ struct {
++ u32 osc_enbl :1; /* D2 oscillator enable */
++ u32 pll1_enbl :1; /* D2 PLL 1 enable */
++ u32 pll2_enbl :1; /* D2 PLL 2 enable */
++ u32 pll3_enbl :1; /* D2 PLL 3 enable */
++ u32 miu_enbl :1; /* D2 Memory Interface Unit (MIU) enable */
++ u32 mem_rfsh :1; /* D2 memory refresh enable */
++ u32 ge_enbl :1; /* D2 Graphics Engine (GE) enable */
++ u32 reserved_1 :1;
++
++ u32 crt_enbl :1; /* D2 CRT enable */
++ u32 fpd_enbl :1; /* D2 Flat Panel enable */
++ u32 reserved_2 :6;
++
++ u32 ctl1_enbl :1; /* D2 controller 1 enable */
++ u32 win1_enbl :1; /* D2 window 1 enable */
++ u32 awin1_enbl :1; /* D2 alternate window 1 enable */
++ u32 cur1_enbl :1; /* D2 cursor 1 enable */
++ u32 reserved_3 :4;
++
++ u32 ctl2_enbl :1; /* D2 controller 2 enable */
++ u32 win2_enbl :1; /* D2 window 2 enable */
++ u32 awin2_enbl :1; /* D2 alternate window 2 enable */
++ u32 cur2_enbl :1; /* D2 cursor 2 enable */
++ u32 reserved_4 :4;
++ } part;
++ u32 whole;
++};
++
++/* PLL 2 programming */
++union pm06r {
++ struct {
++ u32 clk_src :1; /* PLL 2 reference clock source */
++ u32 bypass :1; /* PLL 2 bypass */
++ u32 reserved_1 :2;
++ u32 p_par :3; /* PLL 2 P parameter */
++ u32 reserved_2 :1;
++
++ u32 n_par :5; /* PLL 2 N parameter */
++ u32 reserved_3 :3;
++
++ u32 m_par :8; /* PLL 2 M parameter */
++
++ u32 reserved_4 :4;
++ u32 trim :4; /* PLL 2 trim value */
++ } part;
++ u32 whole;
++};
++
++/* PLL 3 programming */
++union pm07r {
++ struct {
++ u32 clk_src :1; /* PLL 3 reference clock source */
++ u32 bypass :1; /* PLL 3 bypass */
++ u32 reserved_1 :2;
++ u32 p_par :3; /* PLL 3 P parameter */
++ u32 reserved_2 :1;
++
++ u32 n_par :5; /* PLL 3 N parameter */
++ u32 reserved_3 :3;
++
++ u32 m_par :8; /* PLL 3 M parameter */
++
++ u32 reserved_4 :4;
++ u32 trim :4; /* PLL 3 trim value */
++ } part;
++ u32 whole;
++};
++
++
++
++/* MIU interface control 1 */
++union mm00r {
++ struct {
++ u32 miu_enbl :1; /* MIU enable bit */
++ u32 mr_dsbl :1; /* MIU reset disable bit */
++ u32 edr_dsbl :1; /* embedded DRAM reset disable bit */
++ u32 reserved_1 :29;
++ } part;
++ u32 whole;
++};
++
++/* MIU interface control 2 */
++union mm01r {
++ struct {
++ u32 mc_src :1; /* memory clock source */
++ u32 msr_enbl :1; /* memory slow refresh enable bit */
++ u32 pb_cpu :1; /* page break enable for CPU */
++ u32 pb_gc1 :1; /* page break enable for GC1 */
++ u32 pb_gc2 :1; /* page break enable for GC2 */
++ u32 pb_stn_r :1; /* page break enable for STN read */
++ u32 pb_stn_w :1; /* page break enable for STN write */
++ u32 pb_ge :1; /* page break enable for GE */
++ u32 reserved_1 :4;
++ u32 mr_interval :14; /* normal memory refresh time interval */
++ u32 reserved_2 :4;
++ u32 edarm_enbl :1; /* embedded DRAM auto-refresh mode enable */
++ u32 eds_enbl :1; /* EDRAM standby enable for EDRAM normal
++ mode operation */
++ } part;
++ u32 whole;
++};
++
++/* memory interface control 3 */
++union mm02r {
++ struct {
++ u32 bs_ :2;
++ u32 bs_stnr :2; /* burst count for STN read memory cycles */
++ u32 bs_stnw :2; /* burst count for STN write memroy cycles */
++ u32 bs_ge :2; /* burst count for graphics engine
++ read/write memroy cycles */
++ u32 bs_cpuw :2; /* burst count for CPU write memory cycles */
++ u32 fifo_gc1 :4; /* GC1 display refresh FIFO threshold */
++ u32 fifo_gc2 :4; /* GC2 display refresh FIFO threshold */
++ u32 fifo_stnr :4; /* STN read FIFO threshold */
++ u32 fifo_stnw :4; /* STN write FIFO threshold */
++ u32 fifo_ge_src :3; /* GE source read FIFO threshold */
++ u32 fifo_ge_dst :3; /* GE destination read FIFO threshold */
++ } part;
++ u32 whole;
++};
++
++/* memory interface control 4 */
++union mm03r {
++ struct {
++ u32 rd_late_req :1; /* read latency request */
++ u32 reserved_1 :31;
++ } part;
++ u32 whole;
++};
++
++/* memory interface control 5 */
++union mm04r {
++ struct {
++ u32 latency :3; /* EDRAM latency */
++ u32 dmm_cyc :1; /* enable for the dummy cycle insertion
++ between read and write cycles */
++ u32 pre_dmm_cyc :1; /* enable for the dummy cycle insertion
++ between read/write and precharge cycles
++ for the same bank */
++ u32 reserved_1 :3;
++ u32 bnk_act_cls :2; /* bank activate command to bank close
++ command timing interval control */
++ u32 bnk_act_rw :1; /* bank activate command to read/wirte
++ command timing interval control */
++ u32 bnk_cls_act :1; /* bank close command to bank activate
++ command timing interval control */
++ u32 trc :1; /* row cycle time */
++ u32 reserved_2 :3;
++ u32 delay_r :2; /* programmable delay for read clock */
++ u32 delay_m :2; /* programmable delay for internal memory
++ clock */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller 1 register */
++union gc00r {
++ struct {
++ u32 ctl_enbl :1; /* Controller 1 Enable */
++ u32 hc_reset :1; /* Horizontal Counter 1 Reset */
++ u32 vc_reset :1; /* Vertical Counter 1 Reset */
++ u32 iwin_enbl :1; /* Image Window 1 Enable */
++ u32 gcd :4; /* Graphics Color Depth (GCD) */
++
++ u32 hc_enbl :1; /* Hardware Cursor 1 Enable */
++ u32 reserved_1 :2;
++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */
++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */
++
++ u32 g1rclk_src :2; /* G1RCLK Source */
++ u32 tm0 :1; /* Test Mode 0 */
++ u32 tm1 :1; /* Test Mode 1 */
++ u32 fd :3; /* G1MCLK First Clock Divisor (FD1) */
++ u32 reserved_2 :1;
++
++ u32 sd :8; /* G1MCLK Second Clock Divisor (SD1) */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller CRT control */
++union gc01r {
++ struct {
++ u32 dac_enbl :2; /* CRT DAC enable */
++ u32 hsync_out :1; /* CRT HSYNC output during power down mode */
++ u32 vsync_out :1; /* CRT VSYNC output during power down mode */
++ u32 hsync_ctl :2; /* CRT HSYNC control */
++ u32 vsync_ctl :2; /* CRT VSYNC control */
++ /**/
++ u32 hsync_pol :1; /* CRT HSYNC polarity */
++ u32 vsync_pol :1; /* CRT VSYNC polarity */
++ u32 sync_p_enbl :1; /* sync pedestal enable */
++ u32 blnk_p_enbl :1; /* blank pedestal enable */
++ u32 c_sync_enbl :1; /* composite sync enable */
++ u32 vref_sel :1; /* VREF select */
++ u32 mn_sns_enbl :1; /* monitor sense enable */
++ u32 ct_out_enbl :1; /* constant output enable */
++ /**/
++ u32 dac_out_lvl :8; /* monitor sense DAC output level */
++ /**/
++ u32 blue_dac_r :1; /* blue DAC sense result */
++ u32 green_dac_r :1; /* green DAC sense result */
++ u32 red_dac_r :1; /* red DAC sense result */
++ u32 reserved_1 :1;
++ u32 mon_col_sel :1; /* mono/color monitor select */
++ u32 reserved_2 :3;
++ } part;
++ u32 whole;
++};
++
++/* horizontal display 1 control */
++union gc02r {
++ struct {
++ u32 hd1t :12; /* horizontal display 1 total */
++ u32 reserved_1 :4;
++
++ u32 hd1e :12; /* horizontal display 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 1 control */
++union gc03r {
++ struct {
++ u32 vd1t :12; /* vertical display 1 total */
++ u32 reserved_1 :4;
++
++ u32 vd1e :12; /* vertical display 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* horizontal sync 1 control */
++union gc04r {
++ struct {
++ u32 hs1s :12; /* horizontal sync 1 start */
++ u32 reserved_1 :4;
++
++ u32 hs1e :12; /* horizontal sync 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical sync 1 control */
++union gc05r {
++ struct {
++ u32 vs1s :12; /* vertical sync 1 start */
++ u32 reserved_1 :4;
++
++ u32 vs1e :12; /* vertical sync 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 1 count */
++union gc07r {
++ struct {
++ u32 vd_cnt :12; /* vertical display 1 count */
++ u32 reverved_1 :20;
++ } part;
++ u32 whole;
++};
++
++/* horizontal window 1 control */
++union gc08r {
++ struct {
++ u32 hw1s :12; /* horizontal window 1 start (HW1S) */
++ u32 reserved_1 :4;
++
++ u32 hw1w :12; /* horizontal window 1 width (HW1W) */
++ u32 w1ald :4; /* window 1 additional line data */
++ } part;
++ u32 whole;
++};
++
++/* vertical window 1 control */
++union gc09r {
++ struct {
++ u32 vw1s :12; /* vertical window 1 start */
++ u32 reserved_1 :4;
++ u32 vw1h :12; /* vertical window 1 height */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* window 1 start address */
++union gc0cr {
++ struct {
++ u32 w1sa :21; /* window 1 start address */
++ u32 reserved_1 :11;
++ } part;
++ u32 whole;
++};
++
++/* window 1 stride */
++union gc0er {
++ struct {
++ s16 w1st; /* window 1 stride */
++ s16 aw1st; /* alternate window 1 stride */
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 position */
++union gc10r {
++ struct {
++ u32 hc1s :12; /* horizontal cursor 1 start */
++ u32 reserved_1 :4;
++ u32 vc1s :12; /* vertical cursor 1 start */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 start address and offset */
++union gc11r {
++ struct {
++ u32 hc1sa :11; /* hardware cursor 1 start address */
++ u32 reserved_1 :5;
++ u32 hc1o :6; /* horizontal cursor 1 offset */
++ u32 reserved_2 :2;
++ u32 vc1o :6; /* vertical cursor 1 offset */
++ u32 reserved_3 :2;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 foreground color */
++union gc12r {
++ struct {
++ u32 hc1fc :24; /* hardware cursor 1 foreground color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 background color */
++union gc13r {
++ struct {
++ u32 hc1bc :24; /* hardware cursor 1 background color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++
++/* graphics controller 2 register */
++union gc20r {
++ struct {
++ u32 ctl_enbl :1; /* Controller 2 Enable */
++ u32 hc_reset :1; /* Horizontal Counter 2 Reset */
++ u32 vc_reset :1; /* Vertical Counter 2 Reset */
++ u32 iwin_enbl :1; /* Image Window 2 Enable */
++ u32 gcd :4; /* Graphics Color Depth (GCD) */
++
++ u32 hc_enbl :1; /* Hardware Cursor 2 Enable */
++ u32 reserved_1 :2;
++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */
++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */
++
++ u32 g2rclk_src :2; /* G2RCLK Source */
++ u32 tm0 :1; /* Test Mode 0 */
++ u32 tm1 :1; /* Test Mode 1 */
++ u32 fd :3; /* G2MCLK First Clock Divisor (FD1) */
++ u32 reserved_2 :1;
++
++ u32 sd :8; /* G2MCLK Second Clock Divisor (SD1) */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller CRC control */
++union gc21r {
++ struct {
++ u32 crc_enbl :1; /* CRC enable */
++ u32 vsync_wait :1; /* CRC input data control waitime of VSYNC */
++ u32 crc_o_sel :2; /* CRC output select */
++ u32 reserved_1 :4;
++ u32 crc_result :22; /* CRC result (read only) */
++ u32 reserved_2 :2;
++ } part;
++ u32 whole;
++};
++
++/* horizontal display 2 control */
++union gc22r {
++ struct {
++ u32 hd2t :12; /* horizontal display 2 total */
++ u32 reserved_1 :4;
++
++ u32 hd2e :12; /* horizontal display 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 2 control */
++union gc23r {
++ struct {
++ u32 vd2t :12; /* vertical display 2 total */
++ u32 reserved_1 :4;
++
++ u32 vd2e :12; /* vertical display 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* horizontal sync 2 control */
++union gc24r {
++ struct {
++ u32 hs2s :12; /* horizontal sync 2 start */
++ u32 reserved_1 :4;
++
++ u32 hs2e :12; /* horizontal sync 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical sync 2 control */
++union gc25r {
++ struct {
++ u32 vs2s :12; /* vertical sync 2 start */
++ u32 reserved_1 :4;
++
++ u32 vs2e :12; /* vertical sync 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 2 count */
++union gc27r {
++ struct {
++ u32 vd_cnt :12; /* vertical display 2 count */
++ u32 reverved_1 :20;
++ } part;
++ u32 whole;
++};
++
++/* horizontal window 2 control */
++union gc28r {
++ struct {
++ u32 hw2s :12; /* horizontal window 2 start (HW2S) */
++ u32 reserved_1 :4;
++
++ u32 hw2w :12; /* horizontal window 2 width (HW2W) */
++ u32 w2ald :4; /* window 2 additional line data */
++ } part;
++ u32 whole;
++};
++
++/* vertical window 2 control */
++union gc29r {
++ struct {
++ u32 vw2s :12; /* vertical window 2 start */
++ u32 reserved_1 :4;
++ u32 vw2h :12; /* vertical window 2 height */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* window 2 start address */
++union gc2cr {
++ struct {
++ u32 w2sa :21; /* window 2 start address */
++ u32 reserved_1 :11;
++ } part;
++ u32 whole;
++};
++
++/* window 2 stride */
++union gc2er {
++ struct {
++ s16 w2st; /* window 2 stride */
++ s16 aw2st; /* alternate window 2 stride */
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 position */
++union gc30r {
++ struct {
++ u32 hc2s :12; /* horizontal cursor 2 start */
++ u32 reserved_1 :4;
++ u32 vc2s :12; /* vertical cursor 2 start */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 start address and offset */
++union gc31r {
++ struct {
++ u32 hc2sa :11; /* hardware cursor 2 start address */
++ u32 reserved_1 :5;
++ u32 hc2o :6; /* horizontal cursor 2 offset */
++ u32 reserved_2 :2;
++ u32 vc2o :6; /* vertical cursor 2 offset */
++ u32 reserved_3 :2;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 foreground color */
++union gc32r {
++ struct {
++ u32 hc2fc :24; /* hardware cursor 2 foreground color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 background color */
++union gc33r {
++ struct {
++ u32 hc2bc :24; /* hardware cursor 2 background color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++
++/* primary drawing command register */
++union ge00r {
++ struct {
++ u32 rop :8; /* raster operation */
++ /**/
++ u32 cmd_typ :3; /* command type */
++ u32 x_dir :1; /* x direction */
++ u32 y_dir :1; /* y direction */
++ u32 src_mem :1; /* source memory */
++ u32 mon_src :1; /* mono source */
++ u32 mon_ptn :1; /* mono pattern */
++ /**/
++ u32 dst_trns_e :1; /* destination transparency enable */
++ u32 dst_trns_p :1; /* destination transparency polarity */
++ u32 mon_trns_e :1; /* mono source or mono pattern transparency
++ enable */
++ u32 mon_trns_p :1; /* mono transparency polarity */
++ u32 mod_sel :1; /* memory to screen or off screen to screen
++ mode select */
++ u32 alpha_sel :2; /* Alpha byte mask selection */
++ u32 sol_col :1; /* solid color */
++ /**/
++ u32 stride_eq :1; /* source stride is equal to destination
++ stride */
++ u32 rop2_sel :1; /* ROP2 code selection */
++ u32 clipping :1; /* enable clipping */
++ u32 auto_exec :1; /* auto execute */
++ u32 reserved_1 :4;
++ } part;
++ u32 whole;
++};
++
++/* primary width and height register */
++union ge01r {
++ struct {
++ u32 width :12; /* source/destination window width */
++ u32 reserved_1 :4;
++
++ u32 height :12; /* source/destination window height */
++ u32 reserved_2 :1;
++ u32 reserved_3 :3;
++ } bitblt;
++ struct {
++ u32 dm :17;
++ u32 axis_major :12;
++ u32 x_y :1; /* x-major or y-major */
++ u32 last_pix :1; /* decision to draw or not to draw the last
++ pixel of the line */
++ u32 reserved_1 :1;
++ } bresenham;
++ u32 whole;
++};
++
++/* primary destination address register */
++union ge02r {
++ struct {
++ u32 dst_x :12; /* destination x position */
++ u32 reserved_1 :1;
++ u32 h_offset :3; /* mono/color pattern horizontal offset */
++
++ u32 dst_y :12; /* destination y position */
++ u32 reserved_2 :1;
++ u32 v_offset :3; /* mono/color pattern vertical offset */
++ } window;
++ struct {
++ u32 x :12; /* starting x coordinate */
++ u32 dm :17; /* 17 bits major-axis delta */
++ u32 reserved_1 :3;
++ } line;
++ u32 whole;
++};
++
++/* source XY register/line draw starting Y coordinate and mintor axis delta */
++union ge03r {
++ struct {
++ u32 src_x :12; /* source X position */
++ u32 reserved_1 :4;
++
++ u32 src_y :12; /* source Y position */
++ u32 reserved_2 :4;
++ } window;
++ struct {
++ u32 start_y :12; /* starting Y coordinate */
++ u32 dn :17; /* 17 bits minor-axis delta */
++ u32 reserved_1 :3;
++ } line;
++ u32 whole;
++};
++
++/* clip left/top register */
++union ge05r {
++ struct {
++ u32 left :12; /* left edge of clipping rectangle */
++ u32 reserved_1 :4;
++
++ u32 top :12; /* top edge of clipping rectangle */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* source stride/offset register */
++union ge09r {
++ struct {
++ u32 src_strid :12; /* source line stride */
++ u32 reserved_1 :13;
++ u32 strt_bit :3; /* initial mono source bit offset */
++ u32 strt_byte :3; /* initial mono/color source byte offset */
++ u32 reserved_2 :1;
++ } line;
++ struct {
++ u32 strt_bit :5; /* initial mono source bit offset */
++ u32 reserved_1 :1;
++ u32 amount :10; /* number of 16 bytes amount that MIU need
++ to fetch from frame buffer */
++
++ u32 reserved_2 :9;
++ u32 bit_spc :7; /* bit space between lines */
++ } pack_mono;
++ struct {
++ u32 strt_bit :3; /* initial mono source bit offset */
++ u32 strt_byte :3; /* initial mono/color source byte offset */
++ u32 amount :10; /* number of 16 bytes amount that MIU need
++ to fetch from frame buffer */
++
++ u32 reserved_1 :9;
++ u32 bit_spc :3; /* bit space between lines */
++ u32 byt_spc :4; /* byte space between lines */
++ } pack_color;
++ u32 whole;
++};
++
++/* destination stride register and color depth */
++union ge0ar {
++ struct {
++ u32 dst_strid :12; /* destination line stride and color depth */
++ u32 reserved_1 :18;
++ u32 col_dpth :2; /* color depth */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller color pallete */
++union c1xxr {
++ struct {
++ u8 red; /* red color pallete */
++ u8 green; /* green/gray color pallete */
++ u8 blue; /* blue color palette */
++ u8 reserved_1;
++ } part;
++ u32 whole;
++};
++
++/* devicee configuration register 0 */
++union dc00r {
++ struct {
++ u32 osc_bypass :1; /* oscillator bypass */
++ u32 osc_enbl :1; /* oscillator enable */
++ u32 pll1_bypass :1; /* PLL1 bypass */
++ u32 pll1_enbl :1; /* PLL1 enable */
++ u32 pll1_p_par :3; /* PLL1 P parameter */
++ u32 cpu_div :1; /* CPU interface clock divisor */
++ u32 pll1_n_par :5; /* PLL1 N parameter */
++ u32 saisc :1; /* StrongARM interface synchronizer control */
++ u32 s_chp_reset :1; /* software chip reset */
++ u32 mem_enbl :1; /* memory standby enable */
++ u32 pll1_m_par :8; /* PLL 1 M parameter */
++ u32 osc_shaper :1; /* oscillator shaper disable */
++ u32 fast_pwr :1; /* fast power sequencing */
++ u32 osc_frq :2; /* oscillator frequency select */
++ u32 pll1_trim :4; /* PLL 1 trim value */
++ } part;
++ u32 whole;
++};
++
++/* device ID/vendor ID register */
++union pc00r {
++ struct {
++ u16 device; /* device ID */
++ u16 vendor; /* vendor ID */
++ } part;
++ u32 whole;
++};
++
++/* Flat Panel Control Register */
++union fp00r {
++ struct {
++ u32 flatp_enbl : 2; /* Flat Panel Interface Enable */
++ u32 flatp_type : 2; /* Flat Panel Type */
++ u32 mono : 1; /* Mono/Color Panel Select */
++ u32 flatp_intf : 3; /* Flat Panel Interface */
++ u32 dither_pat : 2; /* Dither Pattern */
++ u32 reserved : 2; /* Reserved Must Be 0*/
++ u32 dither_col : 3; /* Dither Base Color */
++ u32 alt_win_ctl: 1; /* Alternate Window Control */
++ u32 frc_ctl : 2; /* FRC Control */
++ u32 dither_adj1: 6; /* Dither Pattern Adjust 1 */
++ u32 dither_adj2: 3; /* Dither Pattern Adjust 2 */
++ u32 dither_adj3: 1; /* Dither Pattern Adjust 3 */
++ u32 test_mode0 : 1; /* Test Mode 0 */
++ u32 test_mode1 : 1; /* Test Mode 1 */
++ u32 test_mode2 : 1; /* Test Mode 2 */
++ u32 test_mode3 : 1; /* Test Mode 3 */
++ } part;
++ u32 whole;
++};
++
++union fp01r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp02r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp03r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp04r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp05r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp0fr {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++
++
++
++/****
++ * Others
++ */
++
++#define CHIPNAME "MQ-200"
++
++extern void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr);
++extern void mq200_external_setqmode(struct mq200_monitor_info*, unsigned long, spinlock_t *);
++extern void mq200_external_offdisplay(unsigned long);
++extern void mq200_external_ondisplay (unsigned long);
++extern int mq200_external_probe(unsigned long);
++
++
++
++#endif
+diff -uNr linux-2.6.21.vanilla/drivers/video/mq200/mq_external.c linux-2.6.21/drivers/video/mq200/mq_external.c
+--- linux-2.6.21.vanilla/drivers/video/mq200/mq_external.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/video/mq200/mq_external.c 2007-05-01 17:02:17.000000000 +0200
+@@ -0,0 +1,513 @@
++/*
++ * Copyright (C) 2005 Holger Hans Peter Freyther
++ *
++ * Based ON:
++ *
++ * linux/drivers/video/mq200fb.c -- MQ-200 for a frame buffer device
++ * based on linux/driver/video/pm2fb.c
++ *
++ * 2007/03/11 mrdata:
++ * bug found in gc1_reset(), renaming to gc1_gc2_reset()
++ * extend mq200_external_ondisplay() -> LCD for GC2 and CRT for GC1
++ *
++ * Copyright (C) 2000 Lineo, Japan
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive
++ * for more details.
++ */
++
++#include <asm/types.h>
++#include <asm/io.h>
++#include <linux/delay.h>
++#include <linux/spinlock.h>
++
++#include <asm/hardware.h>
++
++#include "mq200_data.h"
++
++
++#if 1
++#define PRINTK(args...) printk(args)
++#else
++#define PRINTK(args...)
++#endif
++
++
++/****
++ * power state transition to "state".
++ */
++static void
++power_state_transition(unsigned long register_base, int state)
++{
++ int i;
++ writel(state, PMCSR(register_base));
++ mdelay(300);
++ for (i = 1; ; i++) {
++ udelay(100);
++ if ((readl(PMCSR(register_base)) & 0x3) == state) {
++ break;
++ }
++ }
++}
++
++
++/****
++ * device configuration initialization.
++ */
++static void
++dc_reset(unsigned long register_base)
++{
++ union dc00r dc00r;
++
++ /* Reset First */
++ dc00r.whole = DC_RESET;
++ writel(dc00r.whole, DC00R(register_base));
++ mdelay(100);
++
++ dc00r.whole = 0xEF2082A;
++ writel(dc00r.whole, DC00R(register_base));
++ mdelay(300);
++ PRINTK(CHIPNAME ": DC00R = 0x%08X\n", readl(DC00R(register_base)));
++}
++
++
++/****
++ * initialize memory interface unit.
++ */
++static void
++miu_reset(unsigned long register_base)
++{
++ union mm00r mm00r;
++ union mm01r mm01r;
++ union mm02r mm02r;
++ union mm03r mm03r;
++ union mm04r mm04r;
++
++ /* MIU interface control 1 */
++ mm00r.whole = 0x4;
++ writel(mm00r.whole, MM00R(register_base));
++ mdelay(50);
++ writel(0, MM00R(register_base));
++ mdelay(50);
++
++ /* MIU interface control 2
++ * o PLL 1 output is used as memory clock source.
++ */
++ mm01r.whole = 0x4143e086;
++ writel(mm01r.whole, MM01R(register_base));
++
++ /* memory interface control 3 */
++ mm02r.whole = 0x6d6aabff;
++ writel(mm02r.whole, MM02R(register_base));
++
++ /* memory interface control 5 */
++ mm04r.whole = 0x10d;
++ writel(mm04r.whole, MM04R(register_base));
++
++ /* memory interface control 4 */
++ mm03r.whole = 0x1;
++ writel(mm03r.whole, MM03R(register_base));
++ mdelay(50);
++
++ /* MIU interface control 1 */
++ mm00r.whole = 0x3;
++ writel(mm00r.whole, MM00R(register_base));
++ mdelay(50);
++}
++
++/****
++ *
++ */
++static
++void fpctrl_reset(unsigned long addr)
++{
++ /*
++ * We're in D0 State, let us set the FPCTRL
++ */
++ union fp00r fp00r;
++ union fp01r fp01r;
++ union fp02r fp02r;
++ union fp03r fp03r;
++ union fp04r fp04r;
++ union fp0fr fp0fr;
++
++ fp00r.whole = 0x6320;
++ writel(fp00r.whole, FP00R(addr));
++
++ fp01r.whole = 0x20;
++ writel(fp01r.whole, FP01R(addr));
++
++ fp04r.whole = 0xBD0001;
++ writel(fp04r.whole, FP04R(addr));
++
++ /* Set Flat Panel General Purpose register first */
++ fp02r.whole = 0x0;
++ writel(fp02r.whole, FP02R(addr));
++
++ fp03r.whole = 0x0;
++ writel(fp03r.whole, FP03R(addr));
++
++ fp0fr.whole = 0xA16c44;
++ writel(fp0fr.whole, FP0FR(addr));
++
++ /* Set them again */
++ fp02r.whole = 0x0;
++ writel(fp02r.whole, FP02R(addr));
++
++ fp03r.whole = 0x0;
++ writel(fp03r.whole, FP03R(addr));
++}
++
++
++/****
++ * initialize power management unit.
++ */
++static void
++pmu_reset(unsigned long register_base)
++{
++ union pm00r pm00r;
++ union pm01r pm01r;
++ union pm02r pm02r;
++
++ /* power management miscellaneous control
++ * o GE is driven by PLL 1 clock.
++ */
++ pm00r.whole = 0xc0900;
++ writel(pm00r.whole, PM00R(register_base));
++
++ /* D1 state control */
++ pm01r.whole = 0x5000271;
++ writel(pm01r.whole, PM01R(register_base));
++
++ /* D2 state control */
++ pm02r.whole = 0x271;
++ writel(pm02r.whole, PM02R(register_base));
++}
++
++/****
++ * initialize graphics controller 1
++ * and graphics controller 2
++ */
++static void
++gc1_gc2_reset(unsigned long register_base, spinlock_t *lock )
++{
++ unsigned long flags;
++ union gc00r gc00r;
++ union gc01r gc01r;
++ union gc02r gc02r;
++ union gc03r gc03r;
++ union gc04r gc04r;
++ union gc05r gc05r;
++ union gc08r gc08r;
++ union gc09r gc09r;
++ union gc0cr gc0cr;
++ union gc0er gc0er;
++ union gc20r gc20r;
++ union gc22r gc22r;
++ union gc23r gc23r;
++ union gc24r gc24r;
++ union gc25r gc25r;
++ union gc28r gc28r;
++ union gc29r gc29r;
++ union gc2cr gc2cr;
++ union gc2er gc2er;
++
++ union pm00r pm00r;
++ union pm06r pm06r;
++ union pm06r pm07r;
++
++ spin_lock_irqsave(lock, flags);
++
++ /* alternate window 1 stride */
++ gc0er.whole = 0x640;
++ writel(gc0er.whole, GC0ER(register_base));
++
++ /* image window 1 start address */
++ gc0cr.whole = 0x0;
++ writel(gc0cr.whole, GC0CR(register_base));
++
++ /* alternate window 2 stride */
++ gc2er.whole = 0x640;
++ writel(gc0er.whole, GC2ER(register_base));
++
++ /* image window 2 start address */
++ gc2cr.whole = 0x0;
++ writel(gc2cr.whole, GC2CR(register_base));
++
++ /* read PM Register */
++ pm00r.whole = readl(PM00R(register_base));
++
++ /* horizontal window 1 control */
++ gc08r.whole = 0x131f0000;
++ writel(gc08r.whole, GC08R(register_base));
++
++ /* vertical window 1 control */
++ gc09r.whole = 0x12570000;
++ writel(gc09r.whole, GC09R(register_base));
++
++ /* horizontal display 1 control */
++ gc02r.whole = 0x320041e;
++ writel(gc02r.whole, GC02R(register_base));
++
++ /* vertical display 1 control */
++ gc03r.whole = 0x2570273;
++ writel(gc03r.whole, GC03R(register_base));
++
++ /* horizontal sync 1 control */
++ gc04r.whole = 0x3c70347;
++ writel(gc04r.whole, GC04R(register_base));
++
++ /* vertical sync 1 control */
++ gc05r.whole = 0x25d0259;
++ writel(gc05r.whole, GC05R(register_base));
++
++ /* graphics controller CRT control */
++ gc01r.whole = 0x800;
++ writel(gc01r.whole, GC01R(register_base));
++
++ /* PLL 2 programming */
++ pm06r.whole = 0xE90830;
++ writel(pm06r.whole, PM06R(register_base));
++
++ /* graphics controller 1 register
++ * o GC1 clock source is PLL 2.
++ * o hardware cursor is disabled.
++ */
++ gc00r.whole = 0x10000C8 | 0x20000;
++ writel(gc00r.whole, GC00R(register_base));
++
++#if 0
++ /* alternate horizontal window 1 control */
++ writel(0, GC0AR(register_base));
++
++ /* alternate vertical window 1 control */
++ writel(0, GC0BR(register_base));
++
++ /* window 1 start address */
++ writel(0x2004100, GC0CR(register_base));
++
++ /* alternate window 1 start address */
++ writel(0, GC0DR(register_base));
++
++ /* window 1 stride */
++ gc0er.whole = 0x5100048;
++ writel(gc0er.whole, GC0ER(register_base));
++
++ /* reserved register - ??? - */
++ writel(0x31f, GC0FR(register_base));
++#endif
++
++#if 0
++ /* hardware cursor 1 position */
++ writel(0, GC10R(register_base));
++
++ /* hardware cursor 1 start address and offset */
++ gc11r.whole = 0x5100048;
++ writel(gc11r.whole, GC11R(register_base));
++
++ /* hardware cursor 1 foreground color */
++ writel(0x00ffffff, GC12R(register_base));
++
++ /* hardware cursor 1 background color */
++ writel(0x00000000, GC13R(register_base));
++#endif
++
++ /* horizontal window 2 control */
++ gc28r.whole = 0x31f0000;
++ writel(gc28r.whole, GC28R(register_base));
++
++ /* vertical window 2 control */
++ gc29r.whole = 0x2570000;
++ writel(gc29r.whole, GC29R(register_base));
++
++ /* horizontal display 2 control */
++ gc22r.whole = 0x320041e;
++ writel(gc22r.whole, GC22R(register_base));
++
++ /* vertical display 2 control */
++ gc23r.whole = 0x2570273;
++ writel(gc23r.whole, GC23R(register_base));
++
++ /* horizontal sync 2 control */
++ gc24r.whole = 0x3c70347;
++ writel(gc24r.whole, GC24R(register_base));
++
++ /* vertical sync 2 control */
++ gc25r.whole = 0x25d0259;
++ writel(gc25r.whole, GC25R(register_base));
++
++ /* graphics controller CRT control */
++ gc01r.whole = 0x800;
++ writel(gc01r.whole, GC01R(register_base));
++
++ /* PLL 3 programming */
++ pm07r.whole = 0xE90830;
++ writel(pm07r.whole, PM07R(register_base));
++
++ /* graphics controller 2 register
++ * o GC2 clock source is PLL 3.
++ * o hardware cursor is disabled.
++ */
++ gc20r.whole = 0x10000C8 | 0x30000;
++ writel(gc20r.whole, GC20R(register_base));
++
++ /*
++ * Enable PLL2 and PLL3 in the PM Register
++ */
++ pm00r.part.pll2_enbl = 0x1;
++ pm00r.part.pll3_enbl = 0x1;
++ writel(pm00r.whole, PM00R(register_base));
++
++ spin_unlock_irqrestore(lock, flags);
++}
++
++
++/****
++ * initialize graphics engine.
++ */
++static void
++ge_reset(unsigned long register_base)
++{
++ /* drawing command register */
++ writel(0, GE00R(register_base));
++
++ /* promary width and height register */
++ writel(0, GE01R(register_base));
++
++ /* primary destination address register */
++ writel(0, GE02R(register_base));
++
++ /* primary source XY register */
++ writel(0, GE03R(register_base));
++
++ /* primary color compare register */
++ writel(0, GE04R(register_base));
++
++ /* primary clip left/top register */
++ writel(0, GE05R(register_base));
++
++ /* primary clip right/bottom register */
++ writel(0, GE06R(register_base));
++
++ /* primary source and pattern offset register */
++ writel(0, GE07R(register_base));
++
++ /* primary foreground color register/rectangle fill color depth */
++ writel(0, GE08R(register_base));
++
++ /* source stride/offset register */
++ writel(0, GE09R(register_base));
++
++ /* destination stride register and color depth */
++ writel(0, GE0AR(register_base));
++
++ /* image base address register */
++ writel(0, GE0BR(register_base));
++}
++
++/****
++ * initialize Color Palette 1.
++ */
++static void
++cp1_reset(unsigned long addr_info)
++{
++ int i;
++
++ for (i = 0; i < 256; i++)
++ writel(0, C1xxR(addr_info, i));
++}
++
++
++/*
++ * Below functions are called from the skeleton
++ */
++void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr)
++{
++ writel(color,C1xxR(addr,regno));
++}
++
++void mq200_external_setqmode(struct mq200_monitor_info* info,
++ unsigned long addr, spinlock_t *lock)
++{
++ dc_reset(addr); /* device configuration */
++
++ power_state_transition(addr, 0); /* transition to D0 state */
++
++ pmu_reset(addr); /* power management unit */
++
++ miu_reset(addr); /* memory interface unit */
++
++ ge_reset(addr); /* graphics engine */
++
++ fpctrl_reset(addr); /* reset the panel settings */
++
++ gc1_gc2_reset(addr, lock); /* graphics controller 1 and 2 */
++
++ cp1_reset(addr); /* color palette 1 */
++
++ mq200_external_ondisplay(addr); /* LCD and CRT */
++}
++
++void mq200_external_offdisplay(unsigned long addr)
++{
++ /*
++ * Move the MQ200 to D3 mode
++ */
++ power_state_transition(addr, 3);
++}
++
++/**
++ * to be called after mq200_external_setqmode
++ */
++void mq200_external_ondisplay (unsigned long addr)
++{
++ /*
++ * Set the framebuffer details
++ */
++ union gc00r gc00r;
++ union gc01r gc01r;
++ union gc20r gc20r;
++ union fp00r fp00r;
++
++ /* enable LCD for GC2 */
++ fp00r.whole = readl(FP00R(addr));
++ fp00r.whole &= 0xfffffffc;
++
++ gc20r.whole = readl(GC20R(addr));
++
++ if(!(gc20r.whole & 0x1)) {
++ gc20r.whole |= 0x1;
++ writel(gc20r.whole, GC20R(addr));
++ }
++
++ fp00r.whole |= 0x3;
++ writel(fp00r.whole, FP00R(addr));
++
++ /* enable CRT for GC1 */
++ gc00r.whole = readl(GC00R(addr));
++
++ if(!(gc00r.whole & 0x1)) {
++ gc00r.whole |= 0x1;
++ writel(gc00r.whole, GC00R(addr));
++ }
++
++ gc01r.whole = readl(GC01R(addr));
++ gc01r.whole &= 0xfffffffc;
++
++ gc01r.whole |= 0x1;
++ writel(gc01r.whole, GC01R(addr));
++
++}
++
++int mq200_external_probe(unsigned long addr)
++{
++ union pc00r pc00r;
++ if(readl(PMR(addr)) != PMR_VALUE)
++ return 0;
++
++ pc00r.whole = readl(PC00R(addr));
++ printk(KERN_INFO "mq200 video driver found Vendor: 0x%X Device: 0x%X\n",
++ pc00r.part.device, pc00r.part.vendor);
++ return 1;
++}
+diff -uNr linux-2.6.21.vanilla/drivers/video/mq200/mq_skeleton.c linux-2.6.21/drivers/video/mq200/mq_skeleton.c
+--- linux-2.6.21.vanilla/drivers/video/mq200/mq_skeleton.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/video/mq200/mq_skeleton.c 2007-05-01 17:02:17.000000000 +0200
+@@ -0,0 +1,398 @@
++/*
++ * Author: Holger Hans Peter Freyther
++ *
++ *
++ * This implements the frame buffer driver interface to communicate
++ * with the kernel.
++ * It uses the mq200 routines from the ucLinux driver from Lineo
++ *
++ * 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/autoconf.h>
++#include <linux/platform_device.h>
++#include <linux/module.h>
++#include <linux/fb.h>
++#include <linux/types.h>
++#include <linux/spinlock.h>
++
++#include "mq200_data.h"
++
++#if CONFIG_SA1100_SIMPAD
++/*
++ * Siemens SIMpad specefic data
++ */
++#include <asm/arch/simpad.h>
++#include <asm/arch/hardware.h>
++
++#define MQ200_REGIONS simpad_mq200_regions
++#define MQ200_MONITOR simpad_mq200_panel
++
++static struct mq200_io_regions simpad_mq200_regions = {
++ .fb_size = MQ200_FB_SIZE,
++ .phys_mmio_base = 0x4be00000,
++ .virt_mmio_base = 0xf2e00000,
++ .phys_fb_base = 0x4b800000,
++ .virt_fb_base = 0xf2800000,
++};
++
++static struct mq200_monitor_info simpad_mq200_panel = {
++ .horizontal_res = 800,
++ .vertical_res = 600,
++ .depth = 16,
++ .refresh = 60,
++ .line_length = 1600,
++ .flags = 0x00130004,
++};
++
++extern long get_cs3_shadow(void);
++extern void set_cs3_bit(int value);
++extern void clear_cs3_bit(int value);
++#endif
++
++
++
++struct mq200_info {
++ struct fb_info fb_info;
++ struct mq200_io_regions io_regions;
++ struct mq200_monitor_info monitor_info;
++
++ /* palette */
++ u32 pseudo_palette[17]; /* 16 colors + 1 in reserve not that well documented... */
++ spinlock_t lock;
++};
++
++
++
++static int mq200_blank( int blank_mode, struct fb_info *info )
++{
++#ifdef CONFIG_SA1100_SIMPAD
++ if(blank_mode ){
++ clear_cs3_bit(DISPLAY_ON);
++ }else {
++ set_cs3_bit(DISPLAY_ON);
++ }
++#endif
++ return 0;
++}
++
++
++static int mq200_check_var(struct fb_var_screeninfo *var,
++ struct fb_info *info )
++{ /* TODO do we need sanity checks here */
++ return 0;
++}
++
++
++static int mq200_set_par( struct fb_info *info )
++{
++ /* TODO set paraemeter */
++ return 0;
++}
++
++static int mq200_setcolreg(unsigned regno, unsigned red, unsigned green,
++ unsigned blue, unsigned transp,
++ struct fb_info *info )
++{
++ struct mq200_info *p;
++ unsigned long color;
++ u32* pal = info->pseudo_palette;
++
++ p = info->par;
++
++ if(regno > 255 )
++ return 1;
++
++ switch( info->var.bits_per_pixel ){
++ case 16:
++ pal[regno] =
++ ((red & 0xf800) >> 0) |
++ ((green & 0xf800) >> 5) | ((blue & 0xf800) >> 11);
++ break;
++ case 24:
++ pal[regno] =
++ ((red & 0xff00) << 8) |
++ ((green & 0xff00)) | ((blue & 0xff00) >> 8);
++ break;
++ case 32:
++ pal[regno] =
++ ((red & 0xff00) >> 8) |
++ ((green & 0xff00)) | ((blue & 0xff00) << 8);
++ break;
++ default:
++ break;
++ }
++
++ red &= 0xFF;
++ green &= 0xFF;
++ blue &= 0xFF;
++
++ color = red | (green << 8) | (blue << 16);
++ mq200_external_setpal(regno, color, p->io_regions.virt_mmio_base);
++
++ return 0;
++}
++
++
++static struct fb_ops mq200_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = mq200_check_var,
++ .fb_set_par = mq200_set_par,
++ .fb_setcolreg = mq200_setcolreg,
++#ifdef FB_SOFT_CURSOR
++ .fb_cursor = soft_cursor, /* FIXME use hardware cursor */
++#endif
++ .fb_fillrect = cfb_fillrect,
++ .fb_copyarea = cfb_copyarea,
++ .fb_imageblit = cfb_imageblit,
++ .fb_blank = mq200_blank,
++};
++
++
++/*********************************************************************
++ *
++ * Device driver and module init code
++ * this will register to the fb layer later
++ *
++ *********************************************************************/
++static void mq200_internal_init_color( struct fb_bitfield* red,
++ struct fb_bitfield* green,
++ struct fb_bitfield* blue,
++ int bpp )
++{
++ switch ( bpp )
++ {
++ case 16:
++ red->offset = 11;
++ green->offset = 5;
++ blue->offset = 0;
++
++ red->length = 5;
++ green->length = 6;
++ blue->length = 5;
++ break;
++ case 24:
++ red->offset = 16;
++ green->offset = 8;
++ blue->offset = 0;
++
++ red->length = 8;
++ green->length = 8;
++ blue->length = 8;
++ break;
++ case 32:
++ red->offset = 0;
++ green->offset = 8;
++ blue->offset = 16;
++
++ red->length = 8;
++ green->length = 8;
++ blue->length = 8;
++ case 8: /* fall through */
++ default:
++ red->offset = green->offset = blue->offset = 0;
++ red->length = green->length = blue->length = bpp;
++ break;
++ }
++
++}
++
++
++static struct mq200_info* __init mq200_internal_init_fbinfo(void)
++{
++ struct mq200_info *info = NULL;
++
++ info = (struct mq200_info*)kmalloc(sizeof(*info), GFP_KERNEL);
++ if(!info)
++ return NULL;
++
++ /*
++ * Initialize memory
++ */
++ memset(info, 0, sizeof(struct mq200_info) );
++ spin_lock_init(&info->lock);
++
++ /* set the base IO addresses */
++ info->io_regions = MQ200_REGIONS;
++ info->monitor_info = MQ200_MONITOR;
++
++ info->fb_info.screen_base = (char *)info->io_regions.virt_fb_base;
++
++ /* fb_fix_screeninfo filling */
++ strcpy(info->fb_info.fix.id, "MQ200_FB" );
++ info->fb_info.fix.smem_start = info->io_regions.phys_fb_base;
++ info->fb_info.fix.smem_len = info->io_regions.fb_size; /* - CURSOR_IMAGE */
++ info->fb_info.fix.mmio_start = info->io_regions.phys_mmio_base;
++ info->fb_info.fix.mmio_len = MQ200_REGS_SIZE;
++ info->fb_info.fix.type = FB_TYPE_PACKED_PIXELS;
++ info->fb_info.fix.accel = FB_ACCEL_NONE;
++ info->fb_info.fix.line_length = MQ200_MONITOR_LINE_LENGTH(info);
++
++ if(MQ200_MONITOR_DEPTH(info) <= 8 )
++ info->fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ else if( MQ200_MONITOR_DEPTH(info) >= 16 )
++ info->fb_info.fix.visual = FB_VISUAL_DIRECTCOLOR;
++ else
++ panic("Calling mq200 with wrong display data\n");
++
++ /* set the variable screen info */
++ info->fb_info.var.xres = MQ200_MONITOR_HORI_RES(info);
++ info->fb_info.var.yres = MQ200_MONITOR_VERT_RES(info);
++ info->fb_info.var.xres_virtual = MQ200_MONITOR_HORI_RES(info);
++ info->fb_info.var.yres_virtual = MQ200_MONITOR_VERT_RES(info);
++ info->fb_info.var.bits_per_pixel = MQ200_MONITOR_DEPTH(info);
++
++ mq200_internal_init_color(&info->fb_info.var.red,
++ &info->fb_info.var.green,
++ &info->fb_info.var.blue,
++ MQ200_MONITOR_DEPTH(info) );
++
++ info->fb_info.var.transp.length = info->fb_info.var.transp.offset = 0;
++ info->fb_info.var.height = info->fb_info.var.width = -1;
++
++ info->fb_info.var.vmode = FB_VMODE_NONINTERLACED;
++ info->fb_info.var.pixclock = 10000;
++ info->fb_info.var.left_margin = info->fb_info.var.right_margin = 16;
++ info->fb_info.var.upper_margin = info->fb_info.var.lower_margin = 16;
++ info->fb_info.var.hsync_len = info->fb_info.var.vsync_len = 8;
++
++ info->fb_info.var.nonstd = 0;
++ info->fb_info.var.activate = FB_ACTIVATE_NOW;
++ info->fb_info.var.accel_flags = 0;
++
++ return info;
++}
++
++
++extern void mq200_register_attributes(struct device* );
++/*
++ * gets called from the bus
++ * we will register our framebuffer from here
++ */
++static int __init mq200_probe(struct device *dev)
++{
++ struct mq200_info *info = NULL;
++ int retv= 0;
++
++ info = mq200_internal_init_fbinfo();
++ if(!mq200_external_probe(info->io_regions.virt_mmio_base))
++ goto error_out;
++
++ GPDR |= (1<<3);
++ GAFR &= ~(1<<3);
++ GPSR |= (1<<3);
++
++ mq200_external_setqmode(&info->monitor_info,
++ info->io_regions.virt_mmio_base,
++ &info->lock);
++
++ info->fb_info.fbops = &mq200_ops;
++ info->fb_info.flags = FBINFO_FLAG_DEFAULT;
++
++ mq200_check_var(&info->fb_info.var, &info->fb_info );
++
++ fb_alloc_cmap(&info->fb_info.cmap, 1 << MQ200_MONITOR_DEPTH(info), 0 );
++
++ info->fb_info.pseudo_palette = (void*)info->pseudo_palette;
++
++ /* save the pointer to the mq200 struct in var */
++ info->fb_info.par = info;
++
++ retv = register_framebuffer(&info->fb_info );
++ if(retv < 0)
++ goto error_out;
++
++
++ /* will get unset if retv != 0 */
++ dev_set_drvdata(dev, info );
++ return retv;
++
++/*
++ * Free the info and exit
++ */
++error_out:
++ kfree(info);
++ return -EINVAL;
++}
++
++#ifdef CONFIG_PM
++static struct mq200_info* get_mq200_info( struct device *dev)
++{
++ return dev_get_drvdata(dev);
++}
++
++static unsigned long get_mmio_base( struct device *dev )
++{
++ struct mq200_info *info = get_mq200_info(dev);
++ return info->io_regions.virt_mmio_base;
++}
++
++static struct mq200_monitor_info* get_monitor_info( struct device *dev)
++{
++ struct mq200_info *info = get_mq200_info(dev);
++ return &info->monitor_info;
++}
++
++static spinlock_t* get_spinlock( struct device *dev)
++{
++ return &get_mq200_info(dev)->lock;
++}
++
++/*
++ * FIXME: make sure we only call mq200_external_offdisplay only once
++ * a 2nd time will hang the kernel -zecke
++ *
++ * FIXME: save the content of the framebuffer inside dev->saved_state
++ * so on resume we can memcpy it back into the buffer and userspace
++ * does not need to redraw
++ *
++ * functions for suspending and resuming
++ */
++static int mq200_suspend(struct device *dev, pm_message_t state)
++{
++
++ mq200_external_offdisplay( get_mmio_base(dev) );
++ clear_cs3_bit(DISPLAY_ON);
++
++
++ return 0;
++}
++
++static int mq200_resume(struct device *dev)
++{
++ unsigned long mem = get_mmio_base(dev);
++ struct mq200_monitor_info *monitor = get_monitor_info(dev);
++ mq200_external_setqmode(monitor, mem, get_spinlock(dev) );
++
++
++ /*
++ * Set display on if it was on
++ */
++ set_cs3_bit(DISPLAY_ON);
++
++ return 0;
++}
++
++
++#endif
++
++
++static struct device_driver mq200fb_driver = {
++ .name = "simpad-mq200",
++ .bus = &platform_bus_type,
++ .probe = mq200_probe, /* will be called after we've registered the driver */
++ .suspend = mq200_suspend,
++ .resume = mq200_resume
++};
++
++int __devinit mq200_init(void)
++{
++ return driver_register(&mq200fb_driver);
++}
++
++module_init(mq200_init);
++MODULE_DESCRIPTION("MQ200 framebuffer driver");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Holger Hans Peter Freyther");
diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-net-shared-irq.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-net-shared-irq.patch
new file mode 100644
index 0000000000..e925781ccd
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-net-shared-irq.patch
@@ -0,0 +1,63 @@
+diff -uNr linux-2.6.21.vanilla/drivers/net/pcmcia/pcnet_cs.c linux-2.6.21/drivers/net/pcmcia/pcnet_cs.c
+--- linux-2.6.21.vanilla/drivers/net/pcmcia/pcnet_cs.c 2007-05-29 21:35:00.000000000 +0200
++++ linux-2.6.21/drivers/net/pcmcia/pcnet_cs.c 2007-05-30 17:51:21.000000000 +0200
+@@ -26,6 +26,8 @@
+ CCAE support. Drivers merged back together, and shared-memory
+ Socket EA support added, by Ken Raeburn, September 1995.
+
++ mrdata: -added shared irq
++
+ ======================================================================*/
+
+ #include <linux/kernel.h>
+@@ -254,7 +256,7 @@
+ info->p_dev = link;
+ link->priv = dev;
+
+- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
++ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+diff -uNr linux-2.6.21.vanilla/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.21/drivers/net/wireless/hostap/hostap_cs.c
+--- linux-2.6.21.vanilla/drivers/net/wireless/hostap/hostap_cs.c 2007-05-29 21:35:00.000000000 +0200
++++ linux-2.6.21/drivers/net/wireless/hostap/hostap_cs.c 2007-05-30 17:53:20.000000000 +0200
+@@ -1,3 +1,8 @@
++/*
++ *
++ * mrdata: -added shared irq
++ */
++
+ #define PRISM2_PCCARD
+
+ #include <linux/module.h>
+@@ -695,7 +700,7 @@
+ * irq structure is initialized.
+ */
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = prism2_interrupt;
+ link->irq.Instance = dev;
+diff -uNr linux-2.6.21.vanilla/drivers/net/wireless/orinoco_cs.c linux-2.6.21/drivers/net/wireless/orinoco_cs.c
+--- linux-2.6.21.vanilla/drivers/net/wireless/orinoco_cs.c 2007-05-29 21:35:00.000000000 +0200
++++ linux-2.6.21/drivers/net/wireless/orinoco_cs.c 2007-05-30 17:52:19.000000000 +0200
+@@ -8,6 +8,8 @@
+ * cards such as the 3Com AirConnect and Ericsson WLAN.
+ *
+ * Copyright notice & release notes in file orinoco.c
++ *
++ * mrdata: added shared irq
+ */
+
+ #define DRIVER_NAME "orinoco_cs"
+@@ -120,7 +122,7 @@
+ link->priv = dev;
+
+ /* Interrupt setup */
+- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = orinoco_interrupt;
+ link->irq.Instance = dev;
diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-pcmcia.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-pcmcia.patch
new file mode 100644
index 0000000000..6ff5e37dcf
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-pcmcia.patch
@@ -0,0 +1,162 @@
+diff -uNr linux-2.6.21.vanilla/drivers/pcmcia/cs.c linux-2.6.21/drivers/pcmcia/cs.c
+--- linux-2.6.21.vanilla/drivers/pcmcia/cs.c 2007-05-30 18:00:30.000000000 +0200
++++ linux-2.6.21/drivers/pcmcia/cs.c 2007-05-30 18:10:20.000000000 +0200
+@@ -10,6 +10,8 @@
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ * (C) 1999 David A. Hinds
++ *
++ * mrdata: -added suspend fix
+ */
+
+ #include <linux/module.h>
+@@ -60,6 +62,10 @@
+ INT_MODULE_PARM(unreset_delay, 10); /* centiseconds */
+ INT_MODULE_PARM(unreset_check, 10); /* centiseconds */
+ INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */
++// INT_MODULE_PARM(unreset_delay, 20); /* centiseconds */
++// INT_MODULE_PARM(unreset_check, 100); /* centiseconds */
++// INT_MODULE_PARM(unreset_limit, 300); /* unreset_check's */
++
+
+ /* Access speed for attribute memory windows */
+ INT_MODULE_PARM(cis_speed, 300); /* ns */
+@@ -365,6 +371,7 @@
+ skt->ops->set_socket(skt, &skt->socket);
+
+ msleep(unreset_delay * 10);
++
+ for (i = 0; i < unreset_limit; i++) {
+ skt->ops->get_status(skt, &status);
+
+@@ -430,7 +437,7 @@
+
+ msleep(initial_delay * 10);
+
+- for (i = 0; i < 100; i++) {
++ for (i = 0; i < 100; i++) {
+ skt->ops->get_status(skt, &status);
+ if (!(status & SS_DETECT))
+ return CS_NO_CARD;
+diff -uNr linux-2.6.21.vanilla/drivers/pcmcia/sa1100_generic.c linux-2.6.21/drivers/pcmcia/sa1100_generic.c
+--- linux-2.6.21.vanilla/drivers/pcmcia/sa1100_generic.c 2007-05-30 18:00:30.000000000 +0200
++++ linux-2.6.21/drivers/pcmcia/sa1100_generic.c 2007-05-30 18:00:40.000000000 +0200
+@@ -28,6 +28,9 @@
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
++ 2007 mrnice: added thesings changes from device_driver
++ to platform_driver - many thx to thesing
++
+ ======================================================================*/
+
+ #include <linux/module.h>
+@@ -81,13 +84,15 @@
+ return ret;
+ }
+
+-static struct device_driver sa11x0_pcmcia_driver = {
+- .probe = sa11x0_drv_pcmcia_probe,
+- .remove = soc_common_drv_pcmcia_remove,
+- .name = "sa11x0-pcmcia",
+- .bus = &platform_bus_type,
+- .suspend = pcmcia_socket_dev_suspend,
+- .resume = pcmcia_socket_dev_resume,
++static struct platform_driver sa11x0_pcmcia_driver = {
++ .driver = {
++ .name = "sa11x0-pcmcia",
++ .probe = sa11x0_drv_pcmcia_probe,
++ .remove = soc_common_drv_pcmcia_remove,
++ .suspend= pcmcia_socket_dev_suspend,
++ .resume = pcmcia_socket_dev_resume,
++ //.bus = &platform_bus_type,
++ },
+ };
+
+ /* sa11x0_pcmcia_init()
+@@ -100,7 +105,7 @@
+ */
+ static int __init sa11x0_pcmcia_init(void)
+ {
+- return driver_register(&sa11x0_pcmcia_driver);
++ return platform_driver_register(&sa11x0_pcmcia_driver);
+ }
+
+ /* sa11x0_pcmcia_exit()
+@@ -110,7 +115,7 @@
+ */
+ static void __exit sa11x0_pcmcia_exit(void)
+ {
+- driver_unregister(&sa11x0_pcmcia_driver);
++ platform_driver_unregister(&sa11x0_pcmcia_driver);
+ }
+
+ MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
+diff -uNr linux-2.6.21.vanilla/drivers/pcmcia/sa1100_simpad.c linux-2.6.21/drivers/pcmcia/sa1100_simpad.c
+--- linux-2.6.21.vanilla/drivers/pcmcia/sa1100_simpad.c 2007-05-30 18:00:30.000000000 +0200
++++ linux-2.6.21/drivers/pcmcia/sa1100_simpad.c 2007-05-30 18:08:53.000000000 +0200
+@@ -3,6 +3,8 @@
+ *
+ * PCMCIA implementation routines for simpad
+ *
++ * mrdata: -added cs3_ro stuff to get fix voltage issue
++ *
+ */
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+@@ -14,7 +16,8 @@
+ #include <asm/irq.h>
+ #include <asm/arch/simpad.h>
+ #include "sa1100_generic.h"
+-
++
++extern long get_cs3_ro(void);
+ extern long get_cs3_shadow(void);
+ extern void set_cs3_bit(int value);
+ extern void clear_cs3_bit(int value);
+@@ -25,7 +28,6 @@
+
+ static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+ {
+-
+ clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
+
+ skt->irq = IRQ_GPIO_CF_IRQ;
+@@ -47,21 +49,15 @@
+ struct pcmcia_state *state)
+ {
+ unsigned long levels = GPLR;
+- long cs3reg = get_cs3_shadow();
++ long cs3_ro_reg = get_cs3_ro();
+
+- state->detect=((levels & GPIO_CF_CD)==0)?1:0;
+- state->ready=(levels & GPIO_CF_IRQ)?1:0;
+- state->bvd1=1; /* Not available on Simpad. */
+- state->bvd2=1; /* Not available on Simpad. */
+- state->wrprot=0; /* Not available on Simpad. */
+-
+- if((cs3reg & 0x0c) == 0x0c) {
+- state->vs_3v=0;
+- state->vs_Xv=0;
+- } else {
+- state->vs_3v=1;
+- state->vs_Xv=0;
+- }
++ state->detect = ((levels & GPIO_CF_CD) == 0) ? 1 : 0 ;
++ state->ready = (levels & GPIO_CF_IRQ) ? 1 : 0 ;
++ state->bvd1 = (cs3_ro_reg & PCMCIA_BVD1) ? 1 : 0 ; /* alt: =1 Not available on Simpad. */
++ state->bvd2 = (cs3_ro_reg & PCMCIA_BVD2) ? 1 : 0 ; /* alt: =1 Not available on Simpad. */
++ state->wrprot = 0 ; /* Not available on Simpad. */
++ state->vs_3v = (cs3_ro_reg & PCMCIA_VS1) ? 0 : 1 ;
++ state->vs_Xv = (cs3_ro_reg & PCMCIA_VS2) ? 0 : 1 ;
+ }
+
+ static int
+@@ -104,6 +100,7 @@
+
+ static void simpad_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+ {
++ clear_cs3_bit(PCMCIA_RESET);
+ soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ }
+
diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-serial-gpio_keys-and-cs3-ro.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-serial-gpio_keys-and-cs3-ro.patch
new file mode 100644
index 0000000000..081afc8936
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-serial-gpio_keys-and-cs3-ro.patch
@@ -0,0 +1,332 @@
+diff -uNr linux-2.6.21.vanilla/arch/arm/mach-sa1100/simpad.c linux-2.6.21/arch/arm/mach-sa1100/simpad.c
+--- linux-2.6.21.vanilla/arch/arm/mach-sa1100/simpad.c 2007-05-30 18:00:29.000000000 +0200
++++ linux-2.6.21/arch/arm/mach-sa1100/simpad.c 2007-05-30 18:02:15.000000000 +0200
+@@ -1,5 +1,15 @@
+ /*
+ * linux/arch/arm/mach-sa1100/simpad.c
++ *
++ * 2007/04/11 mrdata:
++ * - insert simpad_uart_set_mctrl()
++ * simpad_uart_get_mctrl()
++ * - internal RS232/DECT/Bluetooth
++ * works again (based on 2.4 simpad-serial.patch)
++ * - added cs3_ro
++ *
++ * 2007/04/12 Bernhard Guillon:
++ * -added gpio_keys (based on h3000.c from hh.org)
+ */
+
+ #include <linux/module.h>
+@@ -9,6 +19,9 @@
+ #include <linux/proc_fs.h>
+ #include <linux/string.h>
+ #include <linux/pm.h>
++
++#include <linux/apm-emulation.h>
++
+ #include <linux/platform_device.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+@@ -27,12 +40,21 @@
+
+ #include <linux/serial_core.h>
+ #include <linux/ioport.h>
++#include <linux/input.h>
++#include <linux/gpio_keys.h>
+ #include <asm/io.h>
+
+ #include "generic.h"
+
++long cs3_ro;
+ long cs3_shadow;
+
++long get_cs3_ro(void)
++{
++ cs3_ro = *(CS3BUSTYPE *)(CS3_BASE);
++ return cs3_ro;
++}
++
+ long get_cs3_shadow(void)
+ {
+ return cs3_shadow;
+@@ -55,9 +77,12 @@
+ *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow;
+ }
+
++EXPORT_SYMBOL(get_cs3_ro);
++EXPORT_SYMBOL(get_cs3_shadow);
+ EXPORT_SYMBOL(set_cs3_bit);
+ EXPORT_SYMBOL(clear_cs3_bit);
+
++
+ static struct map_desc simpad_io_desc[] __initdata = {
+ { /* MQ200 */
+ .virtual = 0xf2800000,
+@@ -73,23 +98,71 @@
+ };
+
+
++static void simpad_uart_set_mctrl(struct uart_port *port, u_int mctrl)
++{
++ if (port->mapbase == _Ser1UTCR0) {
++ /* internal serial port (ttySA1, DECT/Bluetooth) */
++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART1_RTS;
++ else GPSR = GPIO_UART1_RTS;
++
++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART1_DTR;
++ else GPSR = GPIO_UART1_DTR;
++ }
++
++ else if (port->mapbase == _Ser3UTCR0) {
++ /* external serial port (ttySA0, RS232) */
++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART3_RTS;
++ else GPSR = GPIO_UART3_RTS;
++
++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART3_DTR;
++ else GPSR = GPIO_UART3_DTR;
++ }
++}
++
++
++static u_int simpad_uart_get_mctrl(struct uart_port *port)
++{
++ u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
++
++ if (port->mapbase == _Ser1UTCR0) {
++ /* internal serial port (ttySA1, DECT/Bluetooth) */
++ int gplr = GPLR;
++ if (gplr & GPIO_UART1_DCD) ret &= ~TIOCM_CD;
++ if (gplr & GPIO_UART1_CTS) ret &= ~TIOCM_CTS;
++ if (gplr & GPIO_UART1_DSR) ret &= ~TIOCM_DSR;
++ }
++
++ else if (port->mapbase == _Ser3UTCR0) {
++ /* external serial port (ttySA0, RS232) */
++ int gplr = GPLR;
++ if (gplr & GPIO_UART3_DCD) ret &= ~TIOCM_CD;
++ if (gplr & GPIO_UART3_CTS) ret &= ~TIOCM_CTS;
++ if (gplr & GPIO_UART3_DSR) ret &= ~TIOCM_DSR;
++ }
++ return ret;
++}
++
++
+ static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+ {
+- if (port->mapbase == (u_int)&Ser1UTCR0) {
+- if (state)
+- {
+- clear_cs3_bit(RS232_ON);
+- clear_cs3_bit(DECT_POWER_ON);
+- }else
+- {
+- set_cs3_bit(RS232_ON);
+- set_cs3_bit(DECT_POWER_ON);
+- }
+- }
++ if (port->mapbase == (u_int)&Ser3UTCR0) {
++ if (state)
++ {
++ clear_cs3_bit(RS232_ON);
++ /* clear_cs3_bit(DECT_POWER_ON); */
++ }else
++ {
++ set_cs3_bit(RS232_ON);
++ /* set_cs3_bit(DECT_POWER_ON); */
++ }
++ }
+ }
+
++
+ static struct sa1100_port_fns simpad_port_fns __initdata = {
+- .pm = simpad_uart_pm,
++ .set_mctrl = simpad_uart_set_mctrl,
++ .get_mctrl = simpad_uart_get_mctrl,
++ .pm = simpad_uart_pm,
+ };
+
+
+@@ -135,7 +208,6 @@
+ };
+
+
+-
+ static void __init simpad_map_io(void)
+ {
+ sa1100_map_io();
+@@ -144,13 +216,12 @@
+
+ set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON |
+ ENABLE_5V | RESET_SIMCARD | DECT_POWER_ON);
+-
+-
++
+ sa1100_register_uart_fns(&simpad_port_fns);
+ sa1100_register_uart(0, 3); /* serial interface */
+ sa1100_register_uart(1, 1); /* DECT */
+
+- // Reassign UART 1 pins
++ /* Reassign UART 1 pins */
+ GAFR |= GPIO_UART_TXD | GPIO_UART_RXD;
+ GPDR |= GPIO_UART_TXD | GPIO_LDD13 | GPIO_LDD15;
+ GPDR &= ~GPIO_UART_RXD;
+@@ -160,7 +231,6 @@
+ * Set up registers for sleep mode.
+ */
+
+-
+ PWER = PWER_GPIO0| PWER_RTC;
+ PGSR = 0x818;
+ PCFR = 0;
+@@ -171,9 +241,10 @@
+ sa11x0_set_mcp_data(&simpad_mcp_data);
+ }
+
++
+ static void simpad_power_off(void)
+ {
+- local_irq_disable(); // was cli
++ local_irq_disable(); /* was cli */
+ set_cs3(0x800); /* only SD_MEDIAQ */
+
+ /* disable internal oscillator, float CS lines */
+@@ -191,31 +262,52 @@
+ while(1);
+
+ local_irq_enable(); /* we won't ever call it */
++}
+
+
+-}
++/*
++ * gpio_keys
++*/
++
++static struct gpio_keys_button simpad_button_table[] = {
++ { KEY_POWER, IRQ_GPIO_POWER_BUTTON, 1, "power button" },
++};
++
++static struct gpio_keys_platform_data simpad_keys_data = {
++ .buttons = simpad_button_table,
++ .nbuttons = ARRAY_SIZE(simpad_button_table),
++};
++
++static struct platform_device simpad_keys = {
++ .name = "gpio-keys",
++ .dev = {
++ .platform_data = &simpad_keys_data,
++ },
++};
+
+
+ /*
+ * MediaQ Video Device
+ */
++
+ static struct platform_device simpad_mq200fb = {
+ .name = "simpad-mq200",
+ .id = 0,
+ };
+
++
+ static struct platform_device *devices[] __initdata = {
+- &simpad_mq200fb
++ &simpad_keys,
++ &simpad_mq200fb,
+ };
+
+
+-
+ static int __init simpad_init(void)
+ {
+ int ret;
+
+ pm_power_off = simpad_power_off;
+-
++
+ ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+ if(ret)
+ printk(KERN_WARNING "simpad: Unable to register mq200 framebuffer device");
+diff -uNr linux-2.6.21.vanilla/include/asm-arm/arch-sa1100/simpad.h linux-2.6.21/include/asm-arm/arch-sa1100/simpad.h
+--- linux-2.6.21.vanilla/include/asm-arm/arch-sa1100/simpad.h 2007-05-30 18:00:30.000000000 +0200
++++ linux-2.6.21/include/asm-arm/arch-sa1100/simpad.h 2007-05-30 18:02:56.000000000 +0200
+@@ -12,11 +12,12 @@
+ #define __ASM_ARCH_SIMPAD_H
+
+
+-#define GPIO_UART1_RTS GPIO_GPIO14
++#define GPIO_UART1_RTS GPIO_GPIO9
+ #define GPIO_UART1_DTR GPIO_GPIO7
+ #define GPIO_UART1_CTS GPIO_GPIO8
+ #define GPIO_UART1_DCD GPIO_GPIO23
+ #define GPIO_UART1_DSR GPIO_GPIO6
++#define GPIO_UART1_RI GPIO_GPIO19
+
+ #define GPIO_UART3_RTS GPIO_GPIO12
+ #define GPIO_UART3_DTR GPIO_GPIO16
+@@ -48,9 +49,9 @@
+ #define GPIO_SMART_CARD GPIO_GPIO10
+ #define IRQ_GPIO_SMARD_CARD IRQ_GPIO10
+
+-// CS3 Latch is write only, a shadow is necessary
++// CS3 Latch is write only 16-bit , a shadow is necessary
+
+-#define CS3BUSTYPE unsigned volatile long
++#define CS3BUSTYPE unsigned volatile long
+ #define CS3_BASE 0xf1000000
+
+ #define VCC_5V_EN 0x0001 // For 5V PCMCIA
+@@ -70,43 +71,17 @@
+ #define ENABLE_5V 0x4000 // Enable 5V circuit
+ #define RESET_SIMCARD 0x8000
+
+-#define RS232_ENABLE 0x0440
+-#define PCMCIAMASK 0x402f
++// CS3 Latch is readable only 8-bit interest
+
++#define PCMCIA_BVD1 0x0001
++#define PCMCIA_BVD2 0x0002
++#define PCMCIA_VS1 0x0004 // PCMCIA card voltage select
++#define PCMCIA_VS2 0x0008 // PCMCIA card voltage select, if both are in high state -> 5V PCMCIA card
++#define LOCK_IND 0x0010
++#define CHARGING_STATE 0x0020 // Ladestatus
++#define PCMCIA_SHORT 0x0040 // low active
+
+-struct simpad_battery {
+- unsigned char ac_status; /* line connected yes/no */
+- unsigned char status; /* battery loading yes/no */
+- unsigned char percentage; /* percentage loaded */
+- unsigned short life; /* life till empty */
+-};
+-
+-/* These should match the apm_bios.h definitions */
+-#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00
+-#define SIMPAD_AC_STATUS_AC_ONLINE 0x01
+-#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */
+-#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff
+-
+-/* These bitfields are rarely "or'd" together */
+-#define SIMPAD_BATT_STATUS_HIGH 0x01
+-#define SIMPAD_BATT_STATUS_LOW 0x02
+-#define SIMPAD_BATT_STATUS_CRITICAL 0x04
+-#define SIMPAD_BATT_STATUS_CHARGING 0x08
+-#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10
+-#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */
+-#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */
+-#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */
+-#define SIMPAD_BATT_STATUS_NOBATT 0x80
+-#define SIMPAD_BATT_STATUS_UNKNOWN 0xff
+-
+-extern int simpad_get_battery(struct simpad_battery* );
++#define RS232_ENABLE 0x0440
++#define PCMCIAMASK 0x402f
+
+ #endif // __ASM_ARCH_SIMPAD_H
+-
+-
+-
+-
+-
+-
+-
+-
diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-switches.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-switches.patch
new file mode 100644
index 0000000000..552bb8b223
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-switches.patch
@@ -0,0 +1,359 @@
+diff -uNr linux-2.6.21.vanilla/drivers/mfd/Kconfig linux-2.6.21/drivers/mfd/Kconfig
+--- linux-2.6.21.vanilla/drivers/mfd/Kconfig 2007-04-26 05:08:32.000000000 +0200
++++ linux-2.6.21/drivers/mfd/Kconfig 2007-05-29 12:56:32.000000000 +0200
+@@ -37,4 +37,7 @@
+ tristate "Touchscreen interface support"
+ depends on MCP_UCB1200 && INPUT
+
++config MCP_UCB1200_SWITCHES
++ tristate "SIMpad Switches support"
++ depends on MCP_UCB1200 && INPUT
+ endmenu
+diff -uNr linux-2.6.21.vanilla/drivers/mfd/Makefile linux-2.6.21/drivers/mfd/Makefile
+--- linux-2.6.21.vanilla/drivers/mfd/Makefile 2007-04-26 05:08:32.000000000 +0200
++++ linux-2.6.21/drivers/mfd/Makefile 2007-05-29 12:56:32.000000000 +0200
+@@ -8,7 +8,7 @@
+ obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
+ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
+-
++obj-$(CONFIG_MCP_UCB1200_SWITCHES) += ucb1x00-switches.o
+ ifeq ($(CONFIG_SA1100_ASSABET),y)
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
+ endif
+diff -Naur linux-2.6.21/drivers/mfd.old/ucb1x00-switches.c linux-2.6.21/drivers/mfd/ucb1x00-switches.c
+--- linux-2.6.21/drivers/mfd/ucb1x00-switches.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/mfd/ucb1x00-switches.c 2007-07-04 23:59:39.000000000 +0200
+@@ -0,0 +1,332 @@
++/*
++ * linux/drivers/mfd/ucb1x00-switches.c
++ *
++ * Copyright (C) 2007 Bernhard Guillon.
++ *
++ * 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.
++ *
++ * This driver is for the Switches of Siemens SIMpad (CL4,SL4,SLC), T-Sinus-Pad and
++ * Swisscom WP50 devices.
++ *
++ * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8.
++ *
++ * This driver is based on the 2.4 ucb1x00-switches, the 2.6 ucb1x00-assabet
++ * and the ucb1x00-ts driver.
++ *
++ * 2007/06/21 mrdata:
++ * - create new thread kswd() to handle irq_events for ucb1300-gpio's
++ * - found out, that not every key-press or key-release
++ * generate a irq_event
++ * -> establish key_state handling
++ * key_state, key_state_last <-> KEY_PRESS, KEY_RELEASE
++ * -> after irq_event polling the ucb1300-gpio's till all keys
++ * in key_state = KEY_RELEASE
++ *
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/device.h>
++#include <linux/sched.h>
++#include <linux/freezer.h>
++#include <linux/kthread.h>
++
++#include <asm/dma.h>
++
++#include "ucb1x00.h"
++
++#define KEY_PRESS 1
++#define KEY_RELEASE 0
++
++static int key [6] = { KEY_PROG1,KEY_PROG2,KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT };
++
++static unsigned short int key_state [6] = { 0, 0, 0, 0, 0, 0};
++static unsigned short int key_state_last [6] = { 1, 1, 1, 1, 1, 1};
++
++struct ucb1x00_switches {
++ struct input_dev *idev;
++ struct ucb1x00 *ucb;
++
++ wait_queue_head_t irq_wait;
++ struct task_struct *rtask;
++
++ int idx;
++
++ unsigned int valid:1;
++};
++
++static int ucb1x00_thread(void *_switches_id)
++{
++ unsigned short int this;
++ int idx_tmp;
++ int i;
++ struct ucb1x00_switches *switches = _switches_id;
++ struct input_dev *idev = switches->idev;
++ struct task_struct *tsk = current;
++ DECLARE_WAITQUEUE(wait, tsk);
++
++ add_wait_queue(&switches->irq_wait, &wait);
++
++ while (!kthread_should_stop())
++ {
++ signed long timeout;
++
++ if ((switches->idx >= 0) && (switches->idx <= 5) && (switches->valid == 1))
++ {
++ switches->valid = 0;
++
++ idx_tmp = switches->idx;
++
++ ucb1x00_enable(switches->ucb);
++
++ this = ~ucb1x00_io_read(switches->ucb);
++
++ ucb1x00_disable(switches->ucb);
++
++ if (key_state[idx_tmp] == KEY_RELEASE)
++ {
++ key_state_last[idx_tmp] = KEY_RELEASE;
++ key_state[idx_tmp] = KEY_PRESS;
++
++ input_report_key(idev, key[idx_tmp], KEY_PRESS);
++ input_sync(idev);
++ }
++
++ for (i = 0; i < 6; i++)
++ {
++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS))
++ {
++ key_state_last[i] = KEY_RELEASE;
++ key_state[i] = KEY_PRESS;
++
++ input_report_key(idev, key[i], KEY_PRESS);
++ input_sync(idev);
++ }
++ }
++
++ for(;;)
++ {
++ ucb1x00_enable(switches->ucb);
++ this = ~ucb1x00_io_read(switches->ucb);
++ ucb1x00_disable(switches->ucb);
++
++ for (i = 0; i < 6; i++)
++ {
++ if ((key_state[i] == KEY_PRESS) && (((this & (1 << i)) ? 1 : 0) == KEY_RELEASE))
++ {
++ key_state_last[i] = KEY_PRESS;
++ key_state[i] = KEY_RELEASE;
++
++ input_report_key(idev, key[i], KEY_RELEASE);
++ input_sync(idev);
++ }
++
++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS))
++ {
++ key_state_last[i] = KEY_RELEASE;
++ key_state[i] = KEY_PRESS;
++
++ input_report_key(idev, key[i], KEY_PRESS);
++ input_sync(idev);
++ }
++
++ }
++
++ // left loop, if no key press detect
++ if ((this | 0xff80) == 0xff80)
++ {
++ break;
++ }
++
++ set_task_state(tsk, TASK_INTERRUPTIBLE);
++
++ try_to_freeze();
++
++ timeout = HZ / 100;
++
++ schedule_timeout(timeout);
++ }
++ }
++
++ set_task_state(tsk, TASK_INTERRUPTIBLE);
++
++ try_to_freeze();
++
++ timeout = MAX_SCHEDULE_TIMEOUT;
++
++ schedule_timeout(timeout);
++ }
++
++ remove_wait_queue(&switches->irq_wait, &wait);
++
++ switches->rtask = NULL;
++
++ return 0;
++}
++
++
++static void ucb1x00_dev_irq(int idx, void *id)
++{
++ struct ucb1x00_switches *switches = id;
++
++ switches->idx = idx;
++ switches->valid = 1;
++
++ wake_up(&switches->irq_wait);
++}
++
++static int ucb1x00_switches_add(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_switches *switches;
++ struct input_dev *idev;
++ int err,i;
++
++ switches = kzalloc(sizeof(struct ucb1x00_switches), GFP_KERNEL);
++ idev = input_allocate_device();
++
++ if (!switches || !idev) {
++ err = -ENOMEM;
++ goto fail;
++ }
++
++ switches->ucb = dev->ucb;
++
++ idev->private = switches;
++ idev->name = "SIMpad Switches";
++ idev->id.product = switches->ucb->id;
++
++ __set_bit(EV_KEY, idev->evbit);
++ __set_bit(EV_REP, idev->evbit);
++ __set_bit(KEY_PROG1, idev->keybit);
++ __set_bit(KEY_PROG2, idev->keybit);
++ __set_bit(KEY_UP, idev->keybit);
++ __set_bit(KEY_DOWN, idev->keybit);
++ __set_bit(KEY_LEFT, idev->keybit);
++ __set_bit(KEY_RIGHT, idev->keybit);
++
++ err = input_register_device(idev);
++ if (err)
++ goto fail;
++ switches->idev = idev;
++ dev->priv = switches;
++
++ BUG_ON(switches->rtask);
++
++ init_waitqueue_head(&switches->irq_wait);
++
++ ucb1x00_enable(switches->ucb);
++
++ ucb1x00_io_set_dir(switches->ucb,
++ UCB_IO_0 | UCB_IO_1 | UCB_IO_2 |
++ UCB_IO_3 | UCB_IO_4 | UCB_IO_5,
++ UCB_IO_8 | UCB_IO_9);
++
++ ucb1x00_disable(switches->ucb);
++
++ for (i = 0; i < 6; ++i) {
++ ucb1x00_enable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ if (ucb1x00_hook_irq(switches->ucb, i, ucb1x00_dev_irq, switches) < 0) {
++ printk(KERN_ERR "unable to hook IRQ for "
++ "UCB1300 SWITCH_%d\n", i);
++ return -EBUSY;
++ }
++ }
++
++ switches->rtask = kthread_run(ucb1x00_thread, switches, "kswd");
++ if (!IS_ERR(switches->rtask))
++ {
++ return 0;
++ }
++ else
++ {
++ input_unregister_device(switches->idev);
++
++ for (i = 5; i >= 0; --i) {
++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ /* Only error conditions are ENOENT and EINVAL; silently
++ * ignore:
++ */
++ ucb1x00_free_irq(switches->ucb, i, NULL);
++ }
++ switches->rtask = NULL;
++ ucb1x00_disable(switches->ucb);
++ kfree(switches);
++
++ return -EFAULT;
++ }
++
++fail:
++ input_free_device(idev);
++ kfree(switches);
++ return err;
++
++}
++
++static void ucb1x00_switches_remove(struct ucb1x00_dev *dev)
++{
++ int i;
++ struct ucb1x00_switches *switches = dev->priv;
++
++ if (switches->rtask)
++ kthread_stop(switches->rtask);
++
++ switches->rtask = NULL;
++
++ input_unregister_device(switches->idev);
++
++ for (i = 5; i >= 0; --i) {
++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ /* Only error conditions are ENOENT and EINVAL; silently
++ * ignore:
++ */
++ ucb1x00_free_irq(switches->ucb, i, NULL);
++ }
++ ucb1x00_disable(switches->ucb);
++ kfree(switches);
++}
++
++#ifdef CONFIG_PM
++static int ucb1x00_switches_resume(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_switches *switches = dev->priv;
++
++ if (switches->rtask != NULL)
++ {
++ switches->valid = 0;
++ wake_up(&switches->irq_wait);
++
++ printk(KERN_DEBUG "ucb1x00-switches.c -> _switches_resume() kswd - restart *DONE*\n");
++ }
++ return 0;
++}
++#else
++#define ucb1x00_switches_resume NULL
++#endif
++
++static struct ucb1x00_driver ucb1x00_switches_driver = {
++ .add = ucb1x00_switches_add,
++ .remove = ucb1x00_switches_remove,
++ .resume = ucb1x00_switches_resume,
++};
++
++static int __init ucb1x00_switches_init(void)
++{
++ return ucb1x00_register_driver(&ucb1x00_switches_driver);
++}
++
++static void __exit ucb1x00_switches_exit(void)
++{
++ ucb1x00_unregister_driver(&ucb1x00_switches_driver);
++}
++
++module_init(ucb1x00_switches_init);
++module_exit(ucb1x00_switches_exit);
++
++MODULE_AUTHOR("Bernhard Guillon <Bernhard.Guillon@opensimpad.org>");
++MODULE_DESCRIPTION("UCB1x00 Switches driver for Siemens SIMpad");
++MODULE_LICENSE("GPL");
diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-ts-supend-and-accuracy.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-ts-supend-and-accuracy.patch
new file mode 100644
index 0000000000..8c2c89235f
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-ts-supend-and-accuracy.patch
@@ -0,0 +1,121 @@
+diff -uNr linux-2.6.21.vanilla/drivers/mfd/ucb1x00-ts.c linux-2.6.21/drivers/mfd/ucb1x00-ts.c
+--- linux-2.6.21.vanilla/drivers/mfd/ucb1x00-ts.c 2007-05-30 18:00:30.000000000 +0200
++++ linux-2.6.21/drivers/mfd/ucb1x00-ts.c 2007-05-30 18:14:42.000000000 +0200
+@@ -16,6 +16,10 @@
+ * It is important to note that the signal connected to the ADCSYNC
+ * pin should provide pulses even when the LCD is blanked, otherwise
+ * a pen touch needed to unblank the LCD will never be read.
++ *
++ * mrdata: -added some accuracy improvement based on thesings collie patch
++ * -added suspend fix
++ *
+ */
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+@@ -105,6 +109,8 @@
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+
++ udelay(55);
++
+ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+ }
+ }
+@@ -131,7 +137,7 @@
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+- udelay(55);
++ udelay(165);
+
+ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+ }
+@@ -159,7 +165,7 @@
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+- udelay(55);
++ udelay(165);
+
+ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
+ }
+@@ -225,13 +231,17 @@
+ signed long timeout;
+
+ ts->restart = 0;
+-
++
+ ucb1x00_adc_enable(ts->ucb);
+
+ x = ucb1x00_ts_read_xpos(ts);
++ ucb1x00_adc_disable(ts->ucb);
++ ucb1x00_adc_enable(ts->ucb);
+ y = ucb1x00_ts_read_ypos(ts);
++ ucb1x00_adc_disable(ts->ucb);
++ ucb1x00_adc_enable(ts->ucb);
+ p = ucb1x00_ts_read_pressure(ts);
+-
++
+ /*
+ * Switch back to interrupt mode.
+ */
+@@ -240,15 +250,19 @@
+
+ msleep(10);
+
++ if ((x < 60) || (y < 60)) {
++ p = 0;
++ }
++
+ ucb1x00_enable(ts->ucb);
+
+-
+ if (ucb1x00_ts_pen_down(ts)) {
++
+ set_task_state(tsk, TASK_INTERRUPTIBLE);
+
+ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING);
+ ucb1x00_disable(ts->ucb);
+-
++
+ /*
+ * If we spat out a valid sample set last time,
+ * spit out a "pen off" sample here.
+@@ -259,7 +273,9 @@
+ }
+
+ timeout = MAX_SCHEDULE_TIMEOUT;
++
+ } else {
++
+ ucb1x00_disable(ts->ucb);
+
+ /*
+@@ -278,6 +294,14 @@
+
+ try_to_freeze();
+
++ /*
++ * While suspend the ktsd-thread goes sleep -> try_to_freeze()
++ * While resume the ktsd-thread do wakup and must rune one time
++ * again to do a clean re-setup -> enable_irq: UCB_IRQ_TSPX
++ */
++ if(ts->restart)
++ timeout = HZ / 100;
++
+ schedule_timeout(timeout);
+ }
+
+@@ -360,8 +384,12 @@
+ * TS interrupt mode is set up again
+ * after sleep.
+ */
++
+ ts->restart = 1;
+ wake_up(&ts->irq_wait);
++
++ printk(KERN_INFO "ucb1x00-ts.c -> ucb1x00_ts_resume() ktsd - restart *DONE*\n");
++
+ }
+ return 0;
+ }
diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-usb-gadget.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-usb-gadget.patch
new file mode 100644
index 0000000000..00d062bd7d
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-usb-gadget.patch
@@ -0,0 +1,3329 @@
+diff -uNr linux-2.6.21.vanilla/drivers/usb/gadget/Kconfig linux-2.6.21/drivers/usb/gadget/Kconfig
+--- linux-2.6.21.vanilla/drivers/usb/gadget/Kconfig 2007-04-26 05:08:32.000000000 +0200
++++ linux-2.6.21/drivers/usb/gadget/Kconfig 2007-06-05 11:58:28.000000000 +0200
+@@ -205,6 +205,21 @@
+ depends on USB_GADGET_AT91
+ default USB_GADGET
+
++config USB_GADGET_SA1100
++ boolean "SA1100 USB Device Port"
++ depends on ARCH_SA1100
++ select USB_GADGET_SELECTED
++ help
++
++ Say "y" to link the driver statically, or "m" to build a
++ dynamically linked module called "sa1100_udc" and force all
++ gadget drivers to also be dynamically linked.
++
++config USB_SA1100
++ tristate
++ depends on USB_GADGET_SA1100
++ default USB_GADGET
++
+ config USB_GADGET_DUMMY_HCD
+ boolean "Dummy HCD (DEVELOPMENT)"
+ depends on (USB=y || (USB=m && USB_GADGET=m)) && EXPERIMENTAL
+diff -uNr linux-2.6.21.vanilla/drivers/usb/gadget/Makefile linux-2.6.21/drivers/usb/gadget/Makefile
+--- linux-2.6.21.vanilla/drivers/usb/gadget/Makefile 2007-04-26 05:08:32.000000000 +0200
++++ linux-2.6.21/drivers/usb/gadget/Makefile 2007-06-05 11:58:28.000000000 +0200
+@@ -8,6 +8,7 @@
+ obj-$(CONFIG_USB_OMAP) += omap_udc.o
+ obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
+ obj-$(CONFIG_USB_AT91) += at91_udc.o
++obj-$(CONFIG_USB_SA1100) += sa1100_udc.o
+
+ #
+ # USB gadget drivers
+diff -uNr linux-2.6.21.vanilla/drivers/usb/gadget/ether.c linux-2.6.21/drivers/usb/gadget/ether.c
+--- linux-2.6.21.vanilla/drivers/usb/gadget/ether.c 2007-04-26 05:08:32.000000000 +0200
++++ linux-2.6.21/drivers/usb/gadget/ether.c 2007-06-05 11:59:24.000000000 +0200
+@@ -1456,7 +1456,7 @@
+ goto done_set_intf;
+ }
+
+-#ifdef DEV_CONFIG_CDC
++//#ifdef DEV_CONFIG_CDC
+ switch (wIndex) {
+ case 0: /* control/master intf */
+ if (wValue != 0)
+@@ -1498,12 +1498,12 @@
+ value = 0;
+ break;
+ }
+-#else
++//#else
+ /* FIXME this is wrong, as is the assumption that
+ * all non-PXA hardware talks real CDC ...
+ */
+- dev_warn (&gadget->dev, "set_interface ignored!\n");
+-#endif /* DEV_CONFIG_CDC */
++// dev_warn (&gadget->dev, "set_interface ignored!\n");
++//#endif /* DEV_CONFIG_CDC */
+
+ done_set_intf:
+ spin_unlock (&dev->lock);
+diff -uNr linux-2.6.21.vanilla/drivers/usb/gadget/sa1100_udc.c linux-2.6.21/drivers/usb/gadget/sa1100_udc.c
+--- linux-2.6.21.vanilla/drivers/usb/gadget/sa1100_udc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/usb/gadget/sa1100_udc.c 2007-06-05 11:58:29.000000000 +0200
+@@ -0,0 +1,2591 @@
++/*
++ * SA1100 USB Device Controller (UDC) driver.
++ *
++ * Copyright (C) Compaq Computer Corporation, 1998, 1999
++ * Copyright (C) Extenex Corporation, 2001
++ * Copyright (C) David Brownell, 2003
++ * Copyright (C) Nick Bane, 2005, 2006
++ * Many fragments from pxa2xx_udc.c and mach-sa1100 driver with various
++ * GPL Copyright authors incl Russel king and Nicolas Pitre
++ * Port to 2.6.12 by N C Bane
++ *
++ * This file provides interrupt routing and overall coordination for the
++ * sa1100 USB endpoints: ep0, ep1out-bulk, ep2out-bulk, as well as device
++ * initialization and some parts of USB "Chapter 9" device behavior.
++ *
++ * It implements the "USB gadget controller" API, abstracting most hardware
++ * details so that drivers running on top of this API are mostly independent
++ * of hardware. A key exception is that ep0 logic needs to understand which
++ * endpoints a given controller has, and their capabilities. Also, hardware
++ * that doesn't fully support USB (like sa1100) may need workarounds in the
++ * protocols implemented by device functions.
++ *
++ * See linux/Documentation/arm/SA1100/SA1100_USB for more info, or the
++ * kerneldoc for the API exposed to gadget drivers.
++ *
++ */
++#define DEBUG 1
++#define VERBOSE 1
++
++//#define SA1100_USB_DEBUG
++#ifdef SA1100_USB_DEBUG
++static int sa1100_usb_debug=0;
++#endif
++
++#define NCB_DMA_FIX
++#ifdef NCB_DMA_FIX
++#include <linux/slab.h>
++#define SEND_BUFFER_SIZE 4096 /* this is probably a bit big */
++#define RECEIVE_BUFFER_SIZE 256 /* 64 may be all that is necessary */
++static char *send_buffer=NULL;
++static char *receive_buffer=NULL;
++#endif
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/ioport.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/smp_lock.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/list.h>
++#include <linux/interrupt.h>
++#include <linux/version.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++
++#include <asm/byteorder.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/dma.h>
++#include <asm/system.h>
++#include <asm/mach-types.h>
++#include <asm/unaligned.h>
++
++#include <linux/usb.h>
++#include <linux/usb_ch9.h>
++#include <linux/usb_gadget.h>
++
++#if CONFIG_PROC_FS
++#include <linux/proc_fs.h>
++#endif
++
++#if defined(CONFIG_SA1100_BALLOON)
++#include <asm/arch/balloon.h>
++#endif
++
++#define DRIVER_VERSION __DATE__
++
++#define DMA_ADDR_INVALID (~(dma_addr_t)0)
++
++
++static const char driver_name [] = "sa1100_udc";
++static const char driver_desc [] = "SA-1110 USB Device Controller";
++
++static const char ep0name [] = "ep0";
++
++#ifdef DEBUG
++static char *type_string (u8 bmAttributes)
++{
++ switch ( (bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
++ case USB_ENDPOINT_XFER_BULK: return "bulk";
++ //case USB_ENDPOINT_XFER_ISOC: return "iso";
++ case USB_ENDPOINT_XFER_INT: return "intr";
++ };
++ return "control";
++}
++#endif
++
++#include <linux/dma-mapping.h>
++struct usb_stats_t {
++ unsigned long ep0_fifo_write_failures;
++ unsigned long ep0_bytes_written;
++ unsigned long ep0_fifo_read_failures;
++ unsigned long ep0_bytes_read;
++};
++
++struct usb_info_t
++{
++ dma_regs_t *dmaregs_tx, *dmaregs_rx;
++ int state;
++ unsigned char address;
++ struct usb_stats_t stats;
++};
++
++enum { kError=-1, kEvSuspend=0, kEvReset=1,
++ kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 };
++int usbctl_next_state_on_event( int event ) {
++ return 0;
++}
++static struct usb_info_t usbd_info;
++
++/* receiver */
++void ep1_reset(void);
++void ep1_stall(void);
++int sa1100_usb_recv (struct usb_request *req, void (*callback) (int,int));
++
++/* xmitter */
++void ep2_reset(void);
++void ep2_stall(void);
++int sa1100_usb_send (struct usb_request *req, void (*callback) (int,int));
++
++/* UDC register utility functions */
++#define UDC_write(reg, val) { \
++ int i = 10000; \
++ do { \
++ (reg) = (val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: write %#x to %p (%#x) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while((reg) != (val)); \
++}
++
++#define UDC_set(reg, val) { \
++ int i = 10000; \
++ do { \
++ (reg) |= (val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: set %#x of %p (%#x) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while(!((reg) & (val))); \
++}
++
++#define UDC_clear(reg, val) { \
++ int i = 10000; \
++ do { \
++ (reg) &= ~(val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: clear %#x of %p (%#x) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while((reg) & (val)); \
++}
++
++#define UDC_flip(reg, val) { \
++ int i = 10000; \
++ (reg) = (val); \
++ do { \
++ (reg) = (val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: flip %#x of %p (%#x) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while(((reg) & (val))); \
++}
++
++#include "sa1100_udc.h"
++
++static struct sa1100_udc *the_controller;
++static void nuke (struct sa1100_ep *, int status);
++static void done (struct sa1100_ep *ep, struct sa1100_request *req, int status);
++static inline void ep0_idle (struct sa1100_udc *dev)
++{
++ dev->ep0state = EP0_IDLE;
++}
++
++// ep0 handlers
++
++// 1 == lots of trace noise, 0 = only "important' stuff
++//#define VERBOSITY 1
++
++#ifndef MIN
++#define MIN( a, b ) ((a)<(b)?(a):(b))
++#endif
++
++#if 1 && !defined( ASSERT )
++# define ASSERT(expr) \
++ if(!(expr)) { \
++ printk( "Assertion failed! %s,%s,%s,line=%d\n",\
++ #expr,__FILE__,__FUNCTION__,__LINE__); \
++ }
++#else
++# define ASSERT(expr)
++#endif
++
++#if VERBOSITY
++#define PRINTKD(fmt, args...) printk( fmt , ## args)
++#else
++#define PRINTKD(fmt, args...)
++#endif
++
++/* USB Device Requests */
++typedef struct
++{
++ __u8 bmRequestType;
++ __u8 bRequest;
++ __u16 wValue;
++ __u16 wIndex;
++ __u16 wLength;
++} usb_dev_request_t __attribute__ ((packed));
++
++/* other subroutines */
++unsigned int (*wrint)(void);
++void ep0_int_hndlr( void );
++static void ep0_queue(void *buf, unsigned int req, unsigned int act);
++static void write_fifo( void );
++static int read_fifo( struct usb_ctrlrequest * p );
++
++/* some voodo helpers 01Mar01ww */
++static void set_cs_bits( __u32 set_bits );
++static void set_de( void );
++static void set_ipr( void );
++static void set_ipr_and_de( void );
++static bool clear_opr( void );
++
++/***************************************************************************
++Inline Helpers
++***************************************************************************/
++
++/* Data extraction from usb_request_t fields */
++enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 };
++static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); }
++
++static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); }
++inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); }
++
++/* following is hook for self-powered flag in GET_STATUS. Some devices
++ .. might like to override and return real info */
++static inline bool self_powered_hook( void ) { return true; }
++
++#if VERBOSITY
++/* "pcs" == "print control status" */
++static inline void pcs( void )
++{
++ __u32 foo = Ser0UDCCS0;
++ printk( "%8.8X: %s %s %s %s\n",
++ foo,
++ foo & UDCCS0_SE ? "SE" : "",
++ foo & UDCCS0_OPR ? "OPR" : "",
++ foo & UDCCS0_IPR ? "IPR" : "",
++ foo & UDCCS0_SST ? "SST" : ""
++ );
++}
++static inline void preq( struct usb_ctrlrequest * pReq )
++{
++ static char * tnames[] = { "dev", "intf", "ep", "oth" };
++ static char * rnames[] = { "std", "class", "vendor", "???" };
++ char * psz;
++ switch( pReq->bRequest ) {
++ case USB_REQ_GET_STATUS: psz = "get stat"; break;
++ case USB_REQ_CLEAR_FEATURE: psz = "clr feat"; break;
++ case USB_REQ_SET_FEATURE: psz = "set feat"; break;
++ case USB_REQ_SET_ADDRESS: psz = "set addr"; break;
++ case USB_REQ_GET_DESCRIPTOR: psz = "get desc"; break;
++ case USB_REQ_SET_DESCRIPTOR: psz = "set desc"; break;
++ case USB_REQ_GET_CONFIGURATION: psz = "get cfg"; break;
++ case USB_REQ_SET_CONFIGURATION: psz = "set cfg"; break;
++ case USB_REQ_GET_INTERFACE: psz = "get intf"; break;
++ case USB_REQ_SET_INTERFACE: psz = "set intf"; break;
++ default: psz = "unknown"; break;
++ }
++ printk( "- [%s: %s req to %s. dir=%s]\n", psz,
++ rnames[ (pReq->bRequestType >> 5) & 3 ],
++ tnames[ pReq->bRequestType & 3 ],
++ ( pReq->bRequestType & 0x80 ) ? "in" : "out" );
++}
++
++static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req)
++{
++ printk("%s: bRequestType=0x%02x bRequest=0x%02x "
++ "wValue=0x%04x wIndex=0x%04x wLength=0x%04x\n",
++ prefix, req->bRequestType, req->bRequest,
++ le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex),
++ le16_to_cpu(req->wLength));
++}
++#else
++static inline void pcs( void ){}
++//static inline void preq( void ){}
++static inline void preq( void *x ){}
++static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) {}
++#endif
++
++/***************************************************************************
++Globals
++***************************************************************************/
++static const char pszMe[] = "usbep0: ";
++
++
++/* global write struct to keep write
++ ..state around across interrupts */
++static struct {
++ unsigned char *p;
++ int bytes_left;
++} wr;
++
++/***************************************************************************
++Public Interface
++***************************************************************************/
++
++/* reset received from HUB (or controller just went nuts and reset by itself!)
++ so udc core has been reset, track this state here */
++void
++ep0_reset(void)
++{
++ /* reset state machine */
++ wr.p = NULL;
++ wr.bytes_left = 0;
++ usbd_info.address=0;
++// needed? Ser0UDCAR = 0;
++}
++
++
++/* handle interrupt for endpoint zero */
++
++inline void ep0_clear_write(void) {
++ wr.p=NULL;
++ wr.bytes_left=0;
++}
++
++/* this is a config packet parser based on that from the updated HH 2.6 udc */
++static void
++ep0_read_packet( void )
++{
++ unsigned char status_buf[2]; /* returned in GET_STATUS */
++ struct usb_ctrlrequest req;
++ int request_type;
++ int n;
++ __u32 address;
++
++ /* reset previous count */
++ the_controller->ep0_req_len=-1;
++
++ /* read the setup request */
++ n = read_fifo( &req );
++ usbctl_dump_request("ep0_read_packet",&req);
++
++ if ( n != sizeof( req ) ) {
++ printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. "
++ " Stalling out...\n",
++ pszMe, sizeof( req ), n );
++ /* force stall, serviced out */
++ set_cs_bits( UDCCS0_FST | UDCCS0_SO );
++ goto sh_sb_end;
++ }
++
++ /* Is it a standard request? (not vendor or class request) */
++ request_type = type_code_from_request( req.bRequestType );
++ if ( request_type != 0 ) {
++ printk( "%ssetup begin: unsupported bRequestType: %d ignored\n",
++ pszMe, request_type );
++ set_cs_bits( UDCCS0_DE | UDCCS0_SO );
++ goto sh_sb_end;
++ }
++
++ /* save requested reply size */
++ the_controller->ep0_req_len=le16_to_cpu(req.wLength);
++ PRINTKD("%s: request length is %d\n",__FUNCTION__,the_controller->ep0_req_len);
++
++#if VERBOSITY
++ {
++ unsigned char * pdb = (unsigned char *) &req;
++ PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ",
++ pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7]
++ );
++ preq( &req );
++ }
++#endif
++
++ /* Handle it */
++ switch( req.bRequest ) {
++
++ /* This first bunch have no data phase */
++
++ case USB_REQ_SET_ADDRESS:
++ address = (__u32) (req.wValue & 0x7F);
++ /* when SO and DE sent, UDC will enter status phase and ack,
++ ..propagating new address to udc core. Next control transfer
++ ..will be on the new address. You can't see the change in a
++ ..read back of CAR until then. (about 250us later, on my box).
++ ..The original Intel driver sets S0 and DE and code to check
++ ..that address has propagated here. I tried this, but it
++ ..would only work sometimes! The rest of the time it would
++ ..never propagate and we'd spin forever. So now I just set
++ ..it and pray...
++ */
++ Ser0UDCAR = address;
++ usbd_info.address = address;
++ usbctl_next_state_on_event( kEvAddress );
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ printk( "%sI have been assigned address: %d\n", pszMe, address );
++ break;
++
++
++ case USB_REQ_SET_CONFIGURATION:
++ if ( req.wValue == 1 ) {
++ /* configured */
++ if (usbctl_next_state_on_event( kEvConfig ) != kError) {
++ /* (re)set the out and in max packet sizes */
++ PRINTKD( "%s: calling the_controller.driver->setup with SET_CONFIGURATION\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ __u32 in = __le16_to_cpu( the_controller->ep[1].ep.maxpacket );
++ __u32 out = __le16_to_cpu( the_controller->ep[2].ep.maxpacket );
++ Ser0UDCOMP = ( out - 1 );
++ Ser0UDCIMP = ( in - 1 );
++ // we are configured
++ usbd_info.state = USB_STATE_CONFIGURED;
++ // enable rx and tx interrupts
++ Ser0UDCCR &= ~(UDCCR_RIM | UDCCR_TIM);
++
++ printk( "%sConfigured (OMP=%8.8X IMP=%8.8X)\n", pszMe, out, in );
++ break;
++ }
++ } else if ( req.wValue == 0 ) {
++ /* de-configured */
++ if (usbctl_next_state_on_event( kEvDeConfig ) != kError )
++ printk( "%sDe-Configured\n", pszMe );
++ usbd_info.state = 0;
++ Ser0UDCCR |= UDCCR_RIM | UDCCR_TIM;
++ ep1_reset ();
++ ep2_reset ();
++ printk("%s: de-configured. Tx and Rx interrupts disabled. ep1 and ep2 reset\n",__FUNCTION__);
++ } else {
++ printk( "%ssetup phase: Unknown "
++ "\"set configuration\" data %d\n",
++ pszMe, req.wValue );
++ }
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ break;
++
++ case USB_REQ_CLEAR_FEATURE:
++ /* could check data length, direction...26Jan01ww */
++ if ( req.wValue == 0 ) { /* clearing ENDPOINT_HALT/STALL */
++ int ep = windex_to_ep_num( req.wIndex );
++ if ( ep == 1 ) {
++ printk( "%sclear feature \"endpoint halt\" "
++ " on receiver\n", pszMe );
++ ep1_reset();
++ }
++ else if ( ep == 2 ) {
++ printk( "%sclear feature \"endpoint halt\" "
++ "on xmitter\n", pszMe );
++ ep2_reset();
++ } else {
++ printk( "%sclear feature \"endpoint halt\" "
++ "on unsupported ep # %d\n",
++ pszMe, ep );
++ }
++ } else {
++ printk( "%sUnsupported feature selector (%d) "
++ "in clear feature. Ignored.\n" ,
++ pszMe, req.wValue );
++ }
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ break;
++
++ case USB_REQ_SET_FEATURE:
++ if ( req.wValue == 0 ) { /* setting ENDPOINT_HALT/STALL */
++ int ep = windex_to_ep_num( req.wValue );
++ if ( ep == 1 ) {
++ printk( "%set feature \"endpoint halt\" "
++ "on receiver\n", pszMe );
++ ep1_stall();
++ }
++ else if ( ep == 2 ) {
++ printk( "%sset feature \"endpoint halt\" "
++ " on xmitter\n", pszMe );
++ ep2_stall();
++ } else {
++ printk( "%sset feature \"endpoint halt\" "
++ "on unsupported ep # %d\n",
++ pszMe, ep );
++ }
++ }
++ else {
++ printk( "%sUnsupported feature selector "
++ "(%d) in set feature\n",
++ pszMe, req.wValue );
++ }
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ break;
++
++ /* The rest have a data phase that writes back to the host */
++ case USB_REQ_GET_STATUS:
++ /* return status bit flags */
++ status_buf[0] = status_buf[1] = 0;
++ n = request_target(req.bRequestType);
++ switch( n ) {
++ case kTargetDevice:
++ if ( self_powered_hook() )
++ status_buf[0] |= 1;
++ break;
++ case kTargetInterface:
++ break;
++ case kTargetEndpoint:
++ /* return stalled bit */
++ n = windex_to_ep_num( req.wIndex );
++ if ( n == 1 )
++ status_buf[0] |= (Ser0UDCCS1 & UDCCS1_FST) >> 4;
++ else if ( n == 2 )
++ status_buf[0] |= (Ser0UDCCS2 & UDCCS2_FST) >> 5;
++ else {
++ printk( "%sUnknown endpoint (%d) "
++ "in GET_STATUS\n", pszMe, n );
++ }
++ break;
++ default:
++ printk( "%sUnknown target (%d) in GET_STATUS\n",
++ pszMe, n );
++ /* fall thru */
++ break;
++ }
++ PRINTKD("%s: GET_STATUS writing %d\n",__FUNCTION__,req.wLength);
++ ep0_queue( status_buf, req.wLength, sizeof( status_buf ));
++ break;
++ case USB_REQ_GET_DESCRIPTOR:
++ PRINTKD( "%s: calling the_controller.driver->setup with GET_DESCRIPTOR\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ case USB_REQ_GET_CONFIGURATION:
++ PRINTKD( "%s: calling the_controller.driver->setup with GET_CONFIGURATION\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ case USB_REQ_GET_INTERFACE:
++ PRINTKD( "%s: calling the_controller->driver->setup with GET_INTERFACE\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ case USB_REQ_SET_INTERFACE:
++ PRINTKD( "%s: calling the_controller->driver->setup with SET_INTERFACE\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ default :
++ printk("%sunknown request 0x%x\n", pszMe, req.bRequest);
++ break;
++ } /* switch( bRequest ) */
++
++sh_sb_end:
++ return;
++
++}
++
++void
++ep0_int_hndlr( void )
++{
++ u32 cs_reg_in;
++
++ pcs();
++
++ cs_reg_in = Ser0UDCCS0;
++
++
++ /*
++ * If "setup end" has been set, the usb controller has terminated
++ * a setup transaction before we set DE. This happens during
++ * enumeration with some hosts. For example, the host will ask for
++ * our device descriptor and specify a return of 64 bytes. When we
++ * hand back the first 8, the host will know our max packet size
++ * and turn around and issue a new setup immediately. This causes
++ * the UDC to auto-ack the new setup and set SE. We must then
++ * "unload" (process) the new setup, which is what will happen
++ * after this preamble is finished executing.
++ */
++ if (cs_reg_in & UDCCS0_SE) {
++ PRINTKD("UDC: early termination of setup\n");
++
++ /*
++ * Clear setup end
++ */
++ set_cs_bits(UDCCS0_SSE);
++
++ /*
++ * Clear any pending write.
++ */
++ ep0_clear_write();
++ }
++
++ /*
++ * UDC sent a stall due to a protocol violation.
++ */
++ if (cs_reg_in & UDCCS0_SST) {
++// usb->ep0_stall_sent++;
++
++ PRINTKD("UDC: write_preamble: UDC sent stall\n");
++
++ /*
++ * Clear sent stall
++ */
++ set_cs_bits(UDCCS0_SST);
++
++ /*
++ * Clear any pending write.
++ */
++ ep0_clear_write();
++ }
++
++ switch (cs_reg_in & (UDCCS0_OPR | UDCCS0_IPR)) {
++ case UDCCS0_OPR | UDCCS0_IPR:
++ PRINTKD("UDC: write_preamble: see OPR. Stopping write to "
++ "handle new SETUP\n");
++
++ /*
++ * very rarely, you can get OPR and
++ * leftover IPR. Try to clear
++ */
++ UDC_clear(Ser0UDCCS0, UDCCS0_IPR);
++
++ /*
++ * Clear any pending write.
++ */
++ ep0_clear_write();
++
++ /*FALLTHROUGH*/
++ case UDCCS0_OPR:
++ /*
++ * A new setup request is pending. Handle
++ * it. Note that we don't try to read a
++ * packet if SE was set and OPR is clear.
++ */
++ ep0_read_packet();
++ break;
++
++ case 0:
++ // if data pending ...
++ if (wr.p) {
++ unsigned int cs_bits = 0;
++ if (wr.bytes_left != 0) {
++ /*
++ * More data to go
++ */
++ write_fifo();
++ // packet ready
++ cs_bits |= UDCCS0_IPR;
++ }
++
++ if (wr.bytes_left == 0) {
++ /*
++ * All data sent.
++ */
++ cs_bits |= wrint();
++ // a null packet may be following
++ if (!wrint)
++ ep0_clear_write();
++ }
++ set_cs_bits(cs_bits);
++ }
++ else
++ PRINTKD("%s: No data - probably an ACK\n",__FUNCTION__);
++ break;
++
++ case UDCCS0_IPR:
++ PRINTKD("UDC: IPR set, not writing\n");
++// usb->ep0_early_irqs++;
++ break;
++ }
++
++ pcs();
++ PRINTKD( "-end-\n" );
++}
++
++static unsigned int ep0_sh_write_data(void)
++{
++ /*
++ * If bytes left is zero, we are coming in on the
++ * interrupt after the last packet went out. And
++ * we know we don't have to empty packet this
++ * transfer so just set DE and we are done
++ */
++ PRINTKD("UDC: normal packet ended\n");
++ wrint=NULL;
++ return UDCCS0_DE;
++}
++
++static unsigned int ep0_sh_write_with_empty_packet(void)
++{
++ /*
++ * If bytes left is zero, we are coming in on the
++ * interrupt after the last packet went out.
++ * We must do short packet suff, so set DE and IPR
++ */
++ PRINTKD("UDC: short packet sent\n");
++ wrint=NULL;
++ return UDCCS0_IPR | UDCCS0_DE;
++}
++
++static unsigned int ep0_sh_write_data_then_empty_packet(void)
++{
++ PRINTKD("UDC: last packet full. Send empty packet next\n");
++ wrint=ep0_sh_write_with_empty_packet;
++ return 0;
++}
++
++static void ep0_queue(void *buf, unsigned int len, unsigned int req_len)
++{
++ __u32 cs_reg_bits = UDCCS0_IPR;
++
++ PRINTKD("a=%d r=%d\n", len, req_len);
++
++ if (len == 0) {
++ // no output packet to wait for
++ printk("%s: zero byte packet being queued. Setting DE and OPR end exiting\n",__FUNCTION__);
++ set_cs_bits(UDCCS0_DE | UDCCS0_SO);
++ return;
++ }
++
++ /*
++ * thou shalt not enter data phase until
++ * Out Packet Ready is clear
++ */
++ if (!clear_opr()) {
++ printk("UDC: SO did not clear OPR\n");
++ set_cs_bits(UDCCS0_DE | UDCCS0_SO);
++ return;
++ }
++
++ // note data to xmit stored
++ wr.p=buf;
++ wr.bytes_left=min(len, req_len);
++
++ // write the first block
++ write_fifo();
++
++ // done already?
++ if (wr.bytes_left == 0) {
++ /*
++ * out in one, so data end
++ */
++ cs_reg_bits |= UDCCS0_DE;
++ ep0_clear_write();
++ // rest is a shorter than expected reply?
++ } else if (len < req_len) {
++ /*
++ * we are going to short-change host
++ * so need nul to not stall
++ */
++ if (len % 8) {
++ PRINTKD("%s: %d more to go ending in a short packet.\n",__FUNCTION__,wr.bytes_left);
++ wrint=ep0_sh_write_with_empty_packet;
++ }
++ // unless we are on a packet boundary. Then send full packet plus null packet.
++ else {
++ PRINTKD("%s: %d more to go then add empty packet.\n",__FUNCTION__,wr.bytes_left);
++ wrint=ep0_sh_write_data_then_empty_packet;
++ }
++ } else {
++ /*
++ * we have as much or more than requested
++ */
++ PRINTKD("%s: %d more to go.\n",__FUNCTION__,wr.bytes_left);
++ wrint=ep0_sh_write_data;
++ }
++
++ /*
++ * note: IPR was set uncondtionally at start of routine
++ */
++ set_cs_bits(cs_reg_bits);
++}
++
++/*
++ * write_fifo()
++ * Stick bytes in the 8 bytes endpoint zero FIFO.
++ * This version uses a variety of tricks to make sure the bytes
++ * are written correctly. 1. The count register is checked to
++ * see if the byte went in, and the write is attempted again
++ * if not. 2. An overall counter is used to break out so we
++ * don't hang in those (rare) cases where the UDC reverses
++ * direction of the FIFO underneath us without notification
++ * (in response to host aborting a setup transaction early).
++ *
++ */
++static void write_fifo( void )
++{
++ int bytes_this_time = MIN( wr.bytes_left, 8 );
++ int bytes_written = 0;
++
++ PRINTKD( "WF=%d: ", bytes_this_time );
++
++ while( bytes_this_time-- ) {
++ unsigned int cwc;
++ int i;
++ PRINTKD( "%2.2X ", *wr.p );
++ cwc = Ser0UDCWC & 15;
++ i = 10;
++ do {
++ Ser0UDCD0 = *wr.p;
++ udelay( 20 ); /* voodo 28Feb01ww */
++ } while( (Ser0UDCWC &15) == cwc && --i );
++
++ if ( i == 0 ) {
++ printk( "%swrite_fifo: write failure\n", pszMe );
++ usbd_info.stats.ep0_fifo_write_failures++;
++ }
++
++ wr.p++;
++ bytes_written++;
++ }
++ wr.bytes_left -= bytes_written;
++
++ /* following propagation voodo so maybe caller writing IPR in
++ ..a moment might actually get it to stick 28Feb01ww */
++ udelay( 300 );
++
++ usbd_info.stats.ep0_bytes_written += bytes_written;
++ PRINTKD( "L=%d WCR=%8.8X\n", wr.bytes_left, Ser0UDCWC );
++}
++/*
++ * read_fifo()
++ * Read 1-8 bytes out of FIFO and put in request.
++ * Called to do the initial read of setup requests
++ * from the host. Return number of bytes read.
++ *
++ * Like write fifo above, this driver uses multiple
++ * reads checked agains the count register with an
++ * overall timeout.
++ *
++ */
++static int
++read_fifo( struct usb_ctrlrequest * request )
++{
++ int bytes_read = 0;
++ int fifo_count;
++
++ unsigned char * pOut = (unsigned char*) request;
++
++ fifo_count = ( Ser0UDCWC & 0xFF );
++
++ ASSERT( fifo_count <= 8 );
++ PRINTKD( "RF=%d ", fifo_count );
++
++ while( fifo_count-- ) {
++ unsigned int cwc;
++ int i;
++
++ cwc = Ser0UDCWC & 15;
++
++ i = 10;
++ do {
++ *pOut = (unsigned char) Ser0UDCD0;
++ udelay( 20 );
++ } while( ( Ser0UDCWC & 15 ) == cwc && --i );
++
++ if ( i == 0 ) {
++ printk( "%sread_fifo(): read failure\n", pszMe );
++ usbd_info.stats.ep0_fifo_read_failures++;
++ }
++ pOut++;
++ bytes_read++;
++ }
++
++ PRINTKD( "fc=%d\n", bytes_read );
++ usbd_info.stats.ep0_bytes_read++;
++ return bytes_read;
++}
++
++/* some voodo I am adding, since the vanilla macros just aren't doing it 1Mar01ww */
++
++#define ABORT_BITS ( UDCCS0_SST | UDCCS0_SE )
++#define OK_TO_WRITE (!( Ser0UDCCS0 & ABORT_BITS ))
++#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE)
++
++static void set_cs_bits( __u32 bits )
++{
++ if ( bits & ( UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST | UDCCS0_SST) )
++ Ser0UDCCS0 = bits;
++ else if ( (bits & BOTH_BITS) == BOTH_BITS )
++ set_ipr_and_de();
++ else if ( bits & UDCCS0_IPR )
++ set_ipr();
++ else if ( bits & UDCCS0_DE )
++ set_de();
++}
++
++static void set_de( void )
++{
++ int i = 1;
++ while( 1 ) {
++ if ( OK_TO_WRITE ) {
++ Ser0UDCCS0 |= UDCCS0_DE;
++ } else {
++ PRINTKD( "%sQuitting set DE because SST or SE set\n", pszMe );
++ break;
++ }
++ if ( Ser0UDCCS0 & UDCCS0_DE )
++ break;
++ udelay( i );
++ if ( ++i == 50 ) {
++ printk( "%sDangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8X)\n",
++ pszMe, UDCCS0_DE, Ser0UDCCS0 );
++ break;
++ }
++ }
++}
++
++static void set_ipr( void )
++{
++ int i = 1;
++ while( 1 ) {
++ if ( OK_TO_WRITE ) {
++ Ser0UDCCS0 |= UDCCS0_IPR;
++ } else {
++ PRINTKD( "%sQuitting set IPR because SST or SE set\n", pszMe );
++ break;
++ }
++ if ( Ser0UDCCS0 & UDCCS0_IPR )
++ break;
++ udelay( i );
++ if ( ++i == 50 ) {
++ printk( "%sDangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8X)\n",
++ pszMe, UDCCS0_IPR, Ser0UDCCS0 );
++ break;
++ }
++ }
++}
++
++static void set_ipr_and_de( void )
++{
++ int i = 1;
++ while( 1 ) {
++ if ( OK_TO_WRITE ) {
++ Ser0UDCCS0 |= BOTH_BITS;
++ } else {
++ PRINTKD( "%sQuitting set IPR/DE because SST or SE set\n", pszMe );
++ break;
++ }
++ if ( (Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS)
++ break;
++ udelay( i );
++ if ( ++i == 50 ) {
++ printk( "%sDangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8X)\n",
++ pszMe, UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0 );
++ break;
++ }
++ }
++}
++
++static bool clear_opr( void )
++{
++ int i = 10000;
++ bool is_clear;
++ do {
++ Ser0UDCCS0 = UDCCS0_SO;
++ is_clear = ! ( Ser0UDCCS0 & UDCCS0_OPR );
++ if ( i-- <= 0 ) {
++ printk( "%sclear_opr(): failed\n", pszMe );
++ break;
++ }
++ } while( ! is_clear );
++ return is_clear;
++}
++
++
++
++// ep1 handlers
++
++static char *ep1_buf;
++static int ep1_len;
++static void (*ep1_callback)(int flag, int size);
++static char *ep1_curdmabuf;
++static dma_addr_t ep1_curdmapos;
++static int ep1_curdmalen;
++static int ep1_remain;
++static int ep1_used;
++
++static dma_regs_t *dmaregs_rx = NULL;
++static int rx_pktsize;
++
++static int naking;
++
++static void
++ep1_start(void)
++{
++ sa1100_reset_dma(dmaregs_rx);
++ if (!ep1_curdmalen) {
++ ep1_curdmalen = rx_pktsize;
++ if (ep1_curdmalen > ep1_remain)
++ ep1_curdmalen = ep1_remain;
++ ep1_curdmapos = dma_map_single(NULL, ep1_curdmabuf, ep1_curdmalen,
++ DMA_FROM_DEVICE);
++ }
++
++ UDC_write( Ser0UDCOMP, ep1_curdmalen-1 );
++
++ sa1100_start_dma(dmaregs_rx, ep1_curdmapos, ep1_curdmalen);
++
++ if ( naking ) {
++ /* turn off NAK of OUT packets, if set */
++ UDC_flip( Ser0UDCCS1, UDCCS1_RPC );
++ naking = 0;
++ }
++}
++
++static void
++ep1_done(int flag)
++{
++ int size = ep1_len - ep1_remain;
++
++ if (!ep1_len)
++ return;
++ if (ep1_curdmalen)
++ dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen,
++ DMA_FROM_DEVICE);
++ ep1_len = ep1_curdmalen = 0;
++ if (ep1_callback)
++ ep1_callback(flag, size);
++}
++
++void
++ep1_state_change_notify( int new_state )
++{
++
++}
++
++void
++ep1_stall( void )
++{
++ /* SET_FEATURE force stall at UDC */
++ UDC_set( Ser0UDCCS1, UDCCS1_FST );
++}
++
++int
++ep1_init(dma_regs_t *dmaregs)
++{
++ dmaregs_rx = dmaregs;
++ sa1100_reset_dma(dmaregs_rx);
++ ep1_done(-EAGAIN);
++ return 0;
++}
++
++void
++ep1_reset(void)
++{
++ if (dmaregs_rx)
++ sa1100_reset_dma(dmaregs_rx);
++ UDC_clear(Ser0UDCCS1, UDCCS1_FST);
++ ep1_done(-EINTR);
++}
++
++void
++ep1_int_hndlr(int udcsr)
++{
++ dma_addr_t dma_addr;
++ unsigned int len;
++ int status = Ser0UDCCS1;
++
++ if ( naking ) printk( "%sEh? in ISR but naking = %d\n", "usbrx: ", naking );
++
++ if (status & UDCCS1_RPC) {
++
++ if (!ep1_curdmalen) {
++ printk("usb_recv: RPC for non-existent buffer\n");
++ naking=1;
++ return;
++ }
++
++ sa1100_stop_dma(dmaregs_rx);
++
++ if (status & UDCCS1_SST) {
++ printk("usb_recv: stall sent OMP=%d\n",Ser0UDCOMP);
++ UDC_flip(Ser0UDCCS1, UDCCS1_SST);
++ ep1_done(-EIO); // UDC aborted current transfer, so we do
++ return;
++ }
++
++ if (status & UDCCS1_RPE) {
++ printk("usb_recv: RPError %x\n", status);
++ UDC_flip(Ser0UDCCS1, UDCCS1_RPC);
++ ep1_done(-EIO);
++ return;
++ }
++
++ dma_addr=sa1100_get_dma_pos(dmaregs_rx);
++ dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen,
++ DMA_FROM_DEVICE);
++ len = dma_addr - ep1_curdmapos;
++#ifdef SA1100_USB_DEBUG
++ if (sa1100_usb_debug) {
++ int i;
++ printk("usb rx %d :\n ",len);
++ if (sa1100_usb_debug>1) {
++ for (i=0; i<len; i++) {
++ if ((i % 32)==31)
++ printk("\n ");
++// printk("%2.2x ",((char *)ep1_buf)[ep1_used+i]);
++ printk("%2.2x ",((char *)ep1_curdmapos)[i]);
++ }
++ }
++ printk("\n");
++ }
++#endif
++ if (len < ep1_curdmalen) {
++ char *buf = ep1_curdmabuf + len;
++ while (Ser0UDCCS1 & UDCCS1_RNE) {
++ if (len >= ep1_curdmalen) {
++ printk("usb_recv: too much data in fifo\n");
++ break;
++ }
++ *buf++ = Ser0UDCDR;
++ len++;
++ }
++ } else if (Ser0UDCCS1 & UDCCS1_RNE) {
++ printk("usb_recv: fifo screwed, shouldn't contain data\n");
++ len = 0;
++ }
++
++#if defined(NCB_DMA_FIX)
++// if (len && (ep1_buf != ep1_curdmabuf))
++// memcpy(ep1_buf,ep1_curdmabuf,len);
++ if (len)
++ memcpy(&(((unsigned char *)ep1_buf)[ep1_used]),ep1_curdmabuf,len);
++#endif
++
++ ep1_curdmalen = 0; /* dma unmap already done */
++ ep1_remain -= len;
++ ep1_used += len;
++// ep1_curdmabuf += len; // use same buffer again
++ naking = 1;
++//printk("%s: received %d, %d remaining\n",__FUNCTION__,len,ep1_remain);
++ if (len && (len == rx_pktsize))
++ ep1_start();
++ else
++ ep1_done((len) ? 0 : -EPIPE);
++ }
++ /* else, you can get here if we are holding NAK */
++}
++
++int
++sa1100_usb_recv(struct usb_request *req, void (*callback)(int flag, int size))
++{
++ unsigned long flags;
++ char *buf=req->buf;
++ int len=req->length;
++
++ if (ep1_len)
++ return -EBUSY;
++
++ local_irq_save(flags);
++ ep1_buf = buf;
++ ep1_len = len;
++ ep1_callback = callback;
++ ep1_remain = len;
++ ep1_used = 0;
++#ifdef NCB_DMA_FIX
++// if (((size_t)buf)&3)
++ if (1)
++ ep1_curdmabuf = receive_buffer;
++ else
++#else
++ ep1_curdmabuf = buf;
++#endif
++ ep1_curdmalen = 0;
++ ep1_start();
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++// ep2 handlers
++
++static char *ep2_buf;
++static int ep2_len;
++static void (*ep2_callback)(int status, int size);
++static dma_addr_t ep2_dma;
++static dma_addr_t ep2_curdmapos;
++static int ep2_curdmalen;
++static int ep2_remain;
++static dma_regs_t *dmaregs_tx = NULL;
++static int tx_pktsize;
++
++/* device state is changing, async */
++void
++ep2_state_change_notify( int new_state )
++{
++}
++
++/* set feature stall executing, async */
++void
++ep2_stall( void )
++{
++ UDC_set( Ser0UDCCS2, UDCCS2_FST ); /* force stall at UDC */
++}
++
++static void
++ep2_start(void)
++{
++ if (!ep2_len)
++ return;
++
++ ep2_curdmalen = tx_pktsize;
++ if (ep2_curdmalen > ep2_remain)
++ ep2_curdmalen = ep2_remain;
++
++ /* must do this _before_ queue buffer.. */
++ UDC_flip( Ser0UDCCS2,UDCCS2_TPC ); /* stop NAKing IN tokens */
++ UDC_write( Ser0UDCIMP, ep2_curdmalen-1 );
++
++#if 0
++ /* Remove if never seen...8Mar01ww */
++ {
++ int massive_attack = 20;
++ while ( Ser0UDCIMP != ep2_curdmalen-1 && massive_attack-- ) {
++ printk( "usbsnd: Oh no you don't! Let me spin..." );
++ udelay( 500 );
++ printk( "and try again...\n" );
++ UDC_write( Ser0UDCIMP, ep2_curdmalen-1 );
++ }
++ if ( massive_attack != 20 ) {
++ if ( Ser0UDCIMP != ep2_curdmalen-1 )
++ printk( "usbsnd: Massive attack FAILED :-( %d\n",
++ 20 - massive_attack );
++ else
++ printk( "usbsnd: Massive attack WORKED :-) %d\n",
++ 20 - massive_attack );
++ }
++ }
++ /* End remove if never seen... 8Mar01ww */
++#endif
++
++ Ser0UDCAR = usbd_info.address; // fighting stupid silicon bug
++ sa1100_start_dma(dmaregs_tx, ep2_curdmapos, ep2_curdmalen);
++}
++
++static void
++ep2_done(int flag)
++{
++ int size = ep2_len - ep2_remain;
++ if (ep2_len) {
++ dma_unmap_single(NULL, ep2_dma, ep2_len, DMA_TO_DEVICE);
++ ep2_len = 0;
++ if (ep2_callback)
++ ep2_callback(flag, size);
++ }
++}
++
++int
++ep2_init(dma_regs_t *dmaregs)
++{
++ dmaregs_tx = dmaregs;
++ sa1100_reset_dma(dmaregs_tx);
++ ep2_done(-EAGAIN);
++ return 0;
++}
++
++void
++ep2_reset(void)
++{
++ UDC_clear(Ser0UDCCS2, UDCCS2_FST);
++ if (dmaregs_tx)
++ sa1100_reset_dma(dmaregs_tx);
++ ep2_done(-EINTR);
++}
++
++void
++ep2_int_hndlr(int udcsr)
++{
++ int status = Ser0UDCCS2;
++
++ if (Ser0UDCAR != usbd_info.address) // check for stupid silicon bug.
++ Ser0UDCAR = usbd_info.address;
++
++ if (status & UDCCS2_TPC) {
++
++ UDC_flip(Ser0UDCCS2, UDCCS2_SST);
++
++ sa1100_reset_dma(dmaregs_tx);
++
++ if (status & (UDCCS2_TPE | UDCCS2_TUR)) {
++ printk("usb_send: transmit error %x\n", status);
++ ep2_done(-EIO);
++ } else {
++#if 1 // 22Feb01ww/Oleg
++ ep2_curdmapos += ep2_curdmalen;
++ ep2_remain -= ep2_curdmalen;
++#else
++ ep2_curdmapos += Ser0UDCIMP + 1; // this is workaround
++ ep2_remain -= Ser0UDCIMP + 1; // for case when setting of Ser0UDCIMP was failed
++#endif
++
++ if (ep2_remain != 0)
++ ep2_start();
++ else
++ ep2_done(0);
++ }
++ } else {
++ printk("usb_send: Not TPC: UDCCS2 = %x\n", status);
++ }
++}
++
++int
++sa1100_usb_send(struct usb_request *req, void (*callback)(int status, int size))
++{
++ char *buf=req->buf;
++ int len=req->length;
++ unsigned long flags;
++
++ if (usbd_info.state != USB_STATE_CONFIGURED) {
++ PRINTKD("%s: return -ENODEV\n",__FUNCTION__);
++ return -ENODEV;
++ }
++
++ if (ep2_len) {
++ PRINTKD("%s: return -EBUSY\n",__FUNCTION__);
++ return -EBUSY;
++ }
++
++ local_irq_save(flags);
++#ifdef NCB_DMA_FIX
++ // if misaligned, copy to aligned buffer
++// if (((size_t)buf)&3) {
++ if (1) {
++ PRINTKD("%s: copying %d bytes to send_buffer\n",__FUNCTION__,len);
++ memcpy(send_buffer,buf,len);
++ ep2_buf = send_buffer;
++ }
++ else
++#endif
++ ep2_buf = buf;
++
++ ep2_len = len;
++ ep2_dma = dma_map_single(NULL, ep2_buf, len,DMA_TO_DEVICE);
++ PRINTKD("%s: mapped dma to buffer(%p0\n",__FUNCTION__,buf);
++
++ ep2_callback = callback;
++ ep2_remain = len;
++ ep2_curdmapos = ep2_dma;
++
++ PRINTKD("%s: calling ep2_start\n",__FUNCTION__);
++ ep2_start();
++ local_irq_restore(flags);
++
++ return 0;
++}
++/*-------------------------------------------------------------------------*/
++
++static int
++sa1100_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
++{
++ struct sa1100_udc *dev;
++ struct sa1100_ep *ep;
++ u32 max;
++ int type;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!_ep || !desc || ep->desc || _ep->name == ep0name
++ || desc->bDescriptorType != USB_DT_ENDPOINT) {
++ PRINTKD("%s: _ep = %p, desc = %p\n",__FUNCTION__,_ep,desc);
++ if (_ep && desc)
++ PRINTKD("%s: ep->desc = %p, _ep->name = %s desc->bDescriptorType = %s\n",__FUNCTION__,ep->desc,_ep->name,
++ (desc->bDescriptorType == USB_DT_ENDPOINT) ? "USB_DT_ENDPOINT":"bad!!");
++ return -EINVAL;
++ }
++
++ dev = ep->dev;
++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++
++ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++ max = le16_to_cpu (desc->wMaxPacketSize);
++ switch (max) {
++ case 64: case 32:
++ /* note: maxpacket > 16 means DMA might overrun/underrun */
++ case 16: case 8:
++ break;
++ default:
++ if (type == USB_ENDPOINT_XFER_INT && max < 64)
++ break;
++ return -EDOM;
++ }
++
++ switch (type) {
++ case USB_ENDPOINT_XFER_BULK:
++ case USB_ENDPOINT_XFER_INT:
++ if (ep == &dev->ep[2]) {
++ if (desc->bEndpointAddress != (USB_DIR_IN|2) ) {
++ PRINTKD("%s: ep[2] has invalid endpoint\n",__FUNCTION__);
++ return -EINVAL;
++ }
++ tx_pktsize = max;
++ Ser0UDCOMP = max - 1;
++ PRINTKD("%s: ep2 max packet size is %d\n",__FUNCTION__,max);
++ break;
++ } else if (ep == &dev->ep[1]) {
++ if (desc->bEndpointAddress != (USB_DIR_OUT|1)) {
++ PRINTKD("%s: ep[1] has invalid endpoint\n",__FUNCTION__);
++ return -EINVAL;
++ }
++ rx_pktsize = max;
++ Ser0UDCIMP = max - 1;
++ PRINTKD("%s: ep1 max packet size is %d\n",__FUNCTION__,max);
++ break;
++ }
++ // FALLTHROUGH
++ default:
++ PRINTKD("%s: Invalid endpoint\n",__FUNCTION__);
++ return -EINVAL;
++ }
++
++ _ep->maxpacket = max;
++ ep->desc = desc;
++
++ DEBUG (dev, "enabled %s %s max %04x\n", _ep->name,
++ type_string (desc->bmAttributes), max);
++
++ return 0;
++}
++
++static int sa1100_disable (struct usb_ep *_ep)
++{
++ struct sa1100_ep *ep;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!_ep || !ep->desc || _ep->name == ep0name)
++ return -EINVAL;
++
++ nuke (ep, -ESHUTDOWN);
++
++ DEBUG (ep->dev, "disabled %s\n", _ep->name);
++
++ ep->desc = NULL;
++ ep->stopped = 1;
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static struct usb_request *
++sa1100_alloc_request (struct usb_ep *_ep, int gfp_flags)
++{
++ struct sa1100_request *req;
++
++ if (!_ep)
++ return 0;
++
++ req = kmalloc (sizeof *req, gfp_flags);
++ if (!req)
++ return 0;
++
++ memset (req, 0, sizeof *req);
++ req->req.dma = DMA_ADDR_INVALID;
++ INIT_LIST_HEAD (&req->queue);
++ return &req->req;
++}
++
++static void
++sa1100_free_request (struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct sa1100_request *req;
++
++#if 0 // NCB
++ struct sa1100_ep *ep;
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
++ return;
++ #endif
++
++ req = container_of (_req, struct sa1100_request, req);
++ WARN_ON (!list_empty (&req->queue));
++ kfree(req); //NCB - see pxa2xx_udc
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* allocate buffers this way to eliminate per-io buffer copies */
++
++static void *
++sa1100_alloc_buffer (
++ struct usb_ep *_ep,
++ unsigned bytes,
++ dma_addr_t *dma,
++ int gfp_flags
++)
++{
++ void *retval;
++#if 1 // NCB see pxa2xx_udc
++ retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM));
++ if (retval)
++// used in pxa .. *dma = virt_to_bus (retval);
++ *dma = virt_to_phys (retval);
++#else
++ struct sa1100_ep *ep;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!ep || (!ep->desc && _ep->name != ep0name))
++ return 0;
++
++ *dma = DMA_ADDR_INVALID;
++ retval = kmalloc (bytes, GFP_DMA | gfp_flags);
++ if (retval)
++ *dma = virt_to_phys (retval);
++ #endif
++ return retval;
++}
++
++static void
++sa1100_free_buffer (
++ struct usb_ep *_ep,
++ void *buf,
++ dma_addr_t dma,
++ unsigned bytes
++) {
++ kfree (buf);
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void
++done (struct sa1100_ep *ep, struct sa1100_request *req, int status)
++{
++ unsigned stopped = ep->stopped;
++
++
++ list_del_init (&req->queue);
++
++ if (req->req.status == -EINPROGRESS)
++ req->req.status = status;
++ else
++ status = req->req.status;
++
++ if (status && status != -ESHUTDOWN)
++ VDEBUG (ep->dev, "complete %s req %p stat %d len %u/%u\n",
++ ep->ep.name, &req->req, status,
++ req->req.actual, req->req.length);
++
++ /* don't modify queue heads during completion callback */
++ ep->stopped = 1;
++ PRINTKD("%s: calling complete on req\n",__FUNCTION__);
++ req->req.complete (&ep->ep, &req->req);
++ ep->stopped = stopped;
++}
++
++#if 0 // NCB
++void control_done (int value)
++{
++ struct list_head *entry, *tmp;
++ struct sa1100_request *req;
++
++ list_for_each_safe (entry, tmp, &the_controller.ep[0].queue) {
++ req = list_entry (entry, struct sa1100_request, queue);
++
++ /* HACK -- assume no control errors */
++ if (value == 0)
++ req->req.actual = req->req.length;
++#if 1
++ PRINTKD("%s: calling done with ep=%p, req=%p\n",__FUNCTION__,&the_udc.ep[0],req);
++ done (&the_udc.ep[0], req, value);
++#else
++ done (&the_udc.ep0, req, value);
++#endif
++ value = -EPROTO;
++ }
++}
++#endif
++
++/*-------------------------------------------------------------------------*/
++
++/* FIXME move away from the old non-queued api.
++ * - forces extra work on us
++ * - stores request state twice
++ * - doesn't let gadget driver handle dma mapping
++ * - status codes need mapping
++ */
++
++static int map_status (int status)
++{
++ switch (status) {
++ case 0:
++ case -EIO: /* ep[12]_int_handler */
++ return status;
++ case -EPIPE: /* ep1_int_handler */
++ return 0;
++ // case -EAGAIN: /* ep[12]_init */
++ // case -EINTR: /* ep[12]_reset */
++ default:
++ return -ESHUTDOWN;
++ }
++}
++
++static void tx_callback (int status, int size)
++{
++ struct sa1100_ep *ep = &the_controller->ep[2];
++ struct sa1100_request *req;
++
++// PRINTKD("%s: doing ...\n",__FUNCTION__);
++PRINTKD("%s: doing ... status=%d size=%d\n",__FUNCTION__,status,size);
++//PRINTKD("%s: doing ... status=%d\n",__FUNCTION__,status);
++ if (list_empty (&ep->queue)) {
++ if (status != -EAGAIN)
++ DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n",
++ ep->ep.name, status, size);
++// DEBUG (ep->dev, "%s, bogus tx callback %d\n",
++// ep->ep.name, status);
++else
++PRINTKD("%s: list empty.\n",__FUNCTION__);
++ return;
++ }
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ req->req.actual = size;
++ done (ep, req, map_status (status));
++
++PRINTKD("%s: ep->stopped=%s\n",__FUNCTION__,ep->stopped ? "true":"false");
++ if (ep->stopped || list_empty (&ep->queue))
++ return;
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++PRINTKD("%s: calling sa1100_usb_send\n",__FUNCTION__);
++ sa1100_usb_send (&req->req, tx_callback);
++}
++
++static void rx_callback (int status, int size)
++{
++ struct sa1100_ep *ep = &the_controller->ep[1];
++ struct sa1100_request *req;
++
++// PRINTKD("%s: doing ...\n",__FUNCTION__);
++PRINTKD("%s: doing ... status=%d\n",__FUNCTION__,status);
++ if (list_empty (&ep->queue)) {
++ if (status != -EAGAIN)
++ DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n",
++ ep->ep.name, status, size);
++ return;
++ }
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ req->req.actual = size;
++ done (ep, req, map_status (status));
++
++ if (ep->stopped || list_empty (&ep->queue))
++ return;
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ sa1100_usb_recv (&req->req, rx_callback);
++}
++
++
++static int
++sa1100_queue (struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
++{
++ struct sa1100_request *req;
++ struct sa1100_ep *ep;
++ struct sa1100_udc *dev;
++ unsigned long flags;
++
++ req = container_of (_req, struct sa1100_request, req);
++ if (!_req || !_req->complete || !_req->buf
++ || !list_empty (&req->queue))
++ return -EINVAL;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!_ep || (!ep->desc && _ep->name != ep0name))
++ return -EINVAL;
++ dev = ep->dev;
++
++ // handle ep0
++ if (_ep->name == ep0name) {
++ ep0_queue( _req->buf, _req->length, dev->ep0_req_len >=0 ? dev->ep0_req_len: _req->length );
++ return 0;
++ }
++
++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++
++ /* sa1100 udc can't write zlps */
++ if (ep == &dev->ep[2] && _req->length == 0)
++ return -ERANGE;
++
++ /* the old sa1100 api doesn't use 'unsigned' for lengths */
++ if (_req->length > INT_MAX)
++ return -ERANGE;
++
++#if 0
++ VDEBUG (dev, "%s queue req %p, len %d buf %p\n",
++ _ep->name, _req, _req->length, _req->buf);
++#endif
++
++ local_irq_save (flags);
++
++ _req->status = -EINPROGRESS;
++ _req->actual = 0;
++
++// NCB if (list_empty) {
++ if (list_empty (&ep->queue) && !ep->stopped) {
++ /* FIXME this does DMA mapping wrong. caller is allowed
++ * to provide buffers that don't need mapping, but this
++ * doesn't use them.
++ */
++ if (ep == &ep->dev->ep[2]) {
++ PRINTKD("%s: sa1100_usb_send buf %p length %d\n",__FUNCTION__,_req->buf,_req->length);
++ sa1100_usb_send (_req, tx_callback);
++ }
++ else if (ep == &ep->dev->ep[1]) {
++ PRINTKD("%s: sa1100_usb_recv buf %p length %d\n",__FUNCTION__,_req->buf,_req->length);
++ sa1100_usb_recv (_req, rx_callback);
++ }
++ /* ep0 rx/tx is handled separately */
++ }
++ list_add_tail (&req->queue, &ep->queue);
++
++ local_irq_restore (flags);
++
++ return 0;
++}
++
++/* dequeue ALL requests */
++static void nuke (struct sa1100_ep *ep, int status)
++{
++ struct sa1100_request *req;
++
++ /* called with irqs blocked */
++ ep->stopped = 1;
++ if (ep == &ep->dev->ep[1])
++ ep1_reset ();
++ else if (ep == &ep->dev->ep[2])
++ ep2_reset ();
++ while (!list_empty (&ep->queue)) {
++ req = list_entry (ep->queue.next,
++ struct sa1100_request,
++ queue);
++ done (ep, req, status);
++ }
++}
++
++/* dequeue JUST ONE request */
++static int sa1100_dequeue (struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct sa1100_ep *ep;
++ struct sa1100_request *req;
++ unsigned long flags;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ req = container_of (_req, struct sa1100_request, req);
++ if (!_ep || (!ep->desc && _ep->name != ep0name) || !_req)
++ return -EINVAL;
++
++ local_irq_save (flags);
++
++ /* make sure it's actually queued on this endpoint */
++ list_for_each_entry (req, &ep->queue, queue) {
++ if (&req->req == _req)
++ break;
++ }
++ if (&req->req != _req) {
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++
++#if 0
++//#ifdef USE_DMA
++ if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) {
++ cancel_dma(ep);
++ done(ep, req, -ECONNRESET);
++ /* restart i/o */
++ if (!list_empty(&ep->queue)) {
++ req = list_entry(ep->queue.next,
++ struct pxa2xx_request, queue);
++ kick_dma(ep, req);
++ }
++ } else
++//#endif
++#endif
++ done(ep, req, -ECONNRESET);
++
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int
++sa1100_set_halt (struct usb_ep *_ep, int value)
++{
++ struct sa1100_ep *ep;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!_ep || (!ep->desc && _ep->name != ep0name))
++ return -EINVAL;
++ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++ if ( (ep->desc->bmAttributes & 0x03) == USB_ENDPOINT_XFER_ISOC)
++ return -EINVAL;
++
++ VDEBUG (ep->dev, "%s %s halt\n", _ep->name, value ? "set" : "clear");
++
++ /* set/clear, then synch memory views with the device */
++ if (value) {
++ if (ep == &ep->dev->ep[1])
++ ep1_stall ();
++ else
++ ep2_stall ();
++ } else {
++ if (ep == &ep->dev->ep[1])
++ ep1_reset ();
++ else
++ ep2_reset ();
++ }
++
++ return 0;
++}
++
++static struct usb_ep_ops sa1100_ep_ops = {
++ .enable = sa1100_enable,
++ .disable = sa1100_disable,
++
++ .alloc_request = sa1100_alloc_request,
++ .free_request = sa1100_free_request,
++
++ .alloc_buffer = sa1100_alloc_buffer,
++ .free_buffer = sa1100_free_buffer,
++
++ .queue = sa1100_queue,
++ .dequeue = sa1100_dequeue,
++
++ .set_halt = sa1100_set_halt,
++ // .fifo_status = sa1100_fifo_status,
++ // .fifo_flush = sa1100_fifo_flush,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static int sa1100_get_frame (struct usb_gadget *_gadget)
++{
++ return -EOPNOTSUPP;
++}
++
++static int sa1100_wakeup (struct usb_gadget *_gadget)
++{
++ struct sa1100_udc *dev;
++
++ if (!_gadget)
++ return 0;
++ dev = container_of (_gadget, struct sa1100_udc, gadget);
++
++ // FIXME
++
++ return 0;
++}
++
++static const struct usb_gadget_ops sa1100_ops = {
++ .get_frame = sa1100_get_frame,
++ .wakeup = sa1100_wakeup,
++
++ // .set_selfpowered = sa1100_set_selfpowered,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static inline void enable_resume_mask_suspend (void)
++{
++ int i = 0;
++
++ while (1) {
++ Ser0UDCCR |= UDCCR_SUSIM; // mask future suspend events
++ udelay (i);
++ if ( (Ser0UDCCR & UDCCR_SUSIM) || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN (&the_controller, "%s Could not set SUSIM %8.8X\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++
++ i = 0;
++ while (1) {
++ Ser0UDCCR &= ~UDCCR_RESIM;
++ udelay (i);
++ if ( (Ser0UDCCR & UDCCR_RESIM) == 0
++ || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN (&the_controller, "%s Could not clear RESIM %8.8X\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++}
++
++static inline void enable_suspend_mask_resume (void)
++{
++ int i = 0;
++ while (1) {
++ Ser0UDCCR |= UDCCR_RESIM; // mask future resume events
++ udelay (i);
++ if (Ser0UDCCR & UDCCR_RESIM || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN (&the_controller, "%s could not set RESIM %8.8X\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++ i = 0;
++ while (1) {
++ Ser0UDCCR &= ~UDCCR_SUSIM;
++ udelay (i);
++ if ( (Ser0UDCCR & UDCCR_SUSIM) == 0
++ || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN (&the_controller, "%s Could not clear SUSIM %8.8X\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++}
++
++// HACK DEBUG 3Mar01ww
++// Well, maybe not, it really seems to help! 08Mar01ww
++static void core_kicker (void)
++{
++ u32 car = Ser0UDCAR;
++ u32 imp = Ser0UDCIMP;
++ u32 omp = Ser0UDCOMP;
++
++ UDC_set (Ser0UDCCR, UDCCR_UDD);
++ udelay (300);
++ UDC_clear (Ser0UDCCR, UDCCR_UDD);
++
++ Ser0UDCAR = car;
++ Ser0UDCIMP = imp;
++ Ser0UDCOMP = omp;
++}
++
++// NCB static void
++static irqreturn_t
++udc_int_hndlr (int irq, void *_dev, struct pt_regs *regs)
++{
++ struct sa1100_udc *dev = _dev;
++ u32 status = Ser0UDCSR;
++
++ u32 control = Ser0UDCCR;
++ PRINTKD("%s: status = 0x%x and control = 0x%x\n",__FUNCTION__,status,control);
++ /* ReSeT Interrupt Request - UDC has been reset */
++ if (status & UDCSR_RSTIR) {
++ PRINTKD("%s: processing UDCSR_RSTIR\n",__FUNCTION__);
++ if (usbctl_next_state_on_event (kEvReset) != kError) {
++ /* starting 20ms or so reset sequence now... */
++ INFO (dev, "Resetting\n");
++ ep0_reset (); // just set state to idle
++ ep1_reset (); // flush dma, clear false stall
++ ep2_reset (); // flush dma, clear false stall
++ }
++ // mask reset ints, they flood during sequence, enable
++ // suspend and resume
++ Ser0UDCCR |= UDCCR_REM; // mask reset
++ Ser0UDCCR &= ~ (UDCCR_SUSIM | UDCCR_RESIM); // enable suspend and resume
++ UDC_flip ( Ser0UDCSR, status); // clear all pending sources
++ PRINTKD("%s: setting USB_FULL_SPEED\n",__FUNCTION__);
++ dev->gadget.speed = USB_SPEED_FULL;
++ return IRQ_HANDLED; // NCB
++ }
++
++ /* else we have done something other than reset,
++ * so be sure reset enabled
++ */
++ UDC_clear (Ser0UDCCR, UDCCR_REM);
++
++ /* RESume Interrupt Request */
++ if (status & UDCSR_RESIR) {
++ struct usb_gadget_driver *driver = dev->driver;
++
++ PRINTKD("%s: processing UDCSR_RESIR\n",__FUNCTION__);
++ if (driver->resume)
++ driver->resume (&dev->gadget);
++ core_kicker ();
++ enable_suspend_mask_resume ();
++ }
++
++ /* SUSpend Interrupt Request */
++ if (status & UDCSR_SUSIR) {
++ struct usb_gadget_driver *driver = dev->driver;
++
++ PRINTKD("%s: processing UDCSR_SUSIR\n",__FUNCTION__);
++ if (driver->suspend)
++ driver->suspend (&dev->gadget);
++ enable_resume_mask_suspend ();
++ }
++
++ UDC_flip (Ser0UDCSR, status); // clear all pending sources
++
++ if (status & UDCSR_EIR)
++ ep0_int_hndlr ();
++
++
++ if (status & UDCSR_RIR) {
++ PRINTKD("%s: processing ep1_int_hndlr\n",__FUNCTION__);
++ ep1_int_hndlr (status);
++ }
++ if (status & UDCSR_TIR) {
++ PRINTKD("%s: processing ep2_int_hndlr\n",__FUNCTION__);
++ ep2_int_hndlr (status);
++ }
++
++ return IRQ_HANDLED; // NCB
++
++}
++
++/* soft_connect_hook ()
++ * Some devices have platform-specific circuitry to make USB
++ * not seem to be plugged in, even when it is. This allows
++ * software to control when a device 'appears' on the USB bus
++ * (after Linux has booted and this driver has loaded, for
++ * example). If you have such a circuit, control it here.
++ */
++#ifdef CONFIG_SA1100_EXTENEX1
++static void soft_connect_hook (int enable)
++{
++ if (machine_is_extenex1 ()) {
++ if (enable) {
++ PPDR |= PPC_USB_SOFT_CON;
++ PPSR |= PPC_USB_SOFT_CON;
++ } else {
++ PPSR &= ~PPC_USB_SOFT_CON;
++ PPDR &= ~PPC_USB_SOFT_CON;
++ }
++ }
++}
++#elif defined(CONFIG_SA1100_BALLOON)
++static void soft_connect_hook (int enable)
++{
++ if (machine_is_balloon()) {
++ if (enable)
++ balloon_udc_connect();
++ else
++ balloon_udc_disconnect();
++ }
++}
++#else
++#define soft_connect_hook(x) do { } while (0);
++#endif
++
++/* "function" sysfs attribute */
++static ssize_t
++show_function (struct device *_dev, char *buf)
++{
++ struct sa1100_udc *dev = dev_get_drvdata (_dev);
++
++ if (!dev->driver
++ || !dev->driver->function
++ || strlen (dev->driver->function) > PAGE_SIZE)
++ return 0;
++ return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
++}
++static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
++
++/* disable the UDC at the source */
++static void udc_disable (struct sa1100_udc *dev)
++{
++ soft_connect_hook (0);
++ UDC_set (Ser0UDCCR, UDCCR_UDD);
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++ ep0_idle(dev);
++}
++
++static void udc_reinit(struct sa1100_udc *dev) {
++
++ u32 i;
++
++ /* Initialize the gadget controller data structure */
++ INIT_LIST_HEAD (&dev->gadget.ep_list);
++ INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
++ ep0_idle(dev);
++ for ( i = 0 ; i < 3 ; i++) {
++ struct sa1100_ep *ep = &dev->ep[i];
++ if (i != 0)
++ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
++ ep->desc = NULL;
++ ep->stopped = 0;
++ INIT_LIST_HEAD(&ep->queue);
++ }
++}
++
++/* enable the udc at the source */
++static void udc_enable (struct sa1100_udc *dev)
++{
++ UDC_clear (Ser0UDCCR, UDCCR_UDD);
++ ep0_idle(dev);
++}
++
++static void ep0_start (struct sa1100_udc *dev)
++{
++ udc_enable (dev);
++ udelay (100);
++
++ /* clear stall - receiver seems to start stalled? 19Jan01ww */
++ /* also clear other stuff just to be thurough 22Feb01ww */
++ UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC );
++ UDC_clear(Ser0UDCCS2, UDCCS2_FST | UDCCS2_TPE | UDCCS2_TPC );
++
++ /* mask everything */
++ Ser0UDCCR = 0xFC;
++
++ /* flush DMA and fire through some -EAGAINs */
++ ep1_init (dev->ep[1].dmaregs);
++ ep2_init (dev->ep[2].dmaregs);
++
++ /* enable any platform specific hardware */
++ soft_connect_hook (1);
++
++ /* clear all top-level sources */
++ Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR |
++ UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR ;
++
++ /* EXERIMENT - a short line in the spec says toggling this
++ * bit diddles the internal state machine in the udc to
++ * expect a suspend
++ */
++ Ser0UDCCR |= UDCCR_RESIM;
++ /* END EXPERIMENT 10Feb01ww */
++
++ /* enable any platform specific hardware */
++ soft_connect_hook (1);
++
++ /* Enable interrupts. If you are unplugged you will immediately
++ * get a suspend interrupt. If you are plugged and have a soft
++ * connect-circuit, you will get a reset. If you are plugged
++ * without a soft-connect, I think you also get suspend. In short,
++ * start with suspend masked and everything else enabled
++ */
++ UDC_write( Ser0UDCCR, UDCCR_SUSIM );
++}
++
++
++/* when a driver is successfully registered, it will receive
++ * control requests including set_configuration (), which enables
++ * non-control requests. then usb traffic follows until a
++ * disconnect is reported. then a host may connect again, or
++ * the driver might get unbound.
++ */
++int usb_gadget_register_driver (struct usb_gadget_driver *driver)
++{
++ struct sa1100_udc *dev = the_controller;
++ int retval;
++
++ if (!driver
++ || !driver->bind
++ || !driver->unbind
++ || !driver->setup)
++ return -EINVAL;
++ if (!dev)
++ return -ENODEV;
++ if (dev->driver)
++ return -EBUSY;
++
++ /* hook up the driver ... */
++ dev->driver = driver;
++ dev->gadget.dev.driver = &driver->driver;
++
++// device_add (&dev->gadget.dev);
++ retval = driver->bind (&dev->gadget);
++ if (retval) {
++ DEBUG (dev, "bind to driver %s --> %d\n",
++ driver->driver.name, retval);
++ device_del(&dev->gadget.dev);
++ dev->driver = NULL;
++ dev->gadget.dev.driver = NULL;
++ return retval;
++ }
++ device_create_file(dev->dev, &dev_attr_function);
++
++ /* ... then enable host detection and ep0; and we're ready
++ * for set_configuration as well as eventual disconnect.
++ */
++ ep0_start (dev);
++
++ DEBUG (dev, "%s ready\n", driver->driver.name);
++
++ return 0;
++}
++EXPORT_SYMBOL (usb_gadget_register_driver);
++
++static void
++stop_activity (struct sa1100_udc *dev, struct usb_gadget_driver *driver)
++{
++ int i;
++
++ /* don't disconnect if it's not connected */
++ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
++ driver = NULL;
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++
++ /* mask everything */
++ Ser0UDCCR = 0xFC;
++
++ /* stop hardware; prevent new request submissions;
++ * and kill any outstanding requests.
++ */
++ for (i = 0; i < 3; i++) {
++ struct sa1100_ep *ep = &dev->ep[i];
++
++ ep->stopped = 1;
++ nuke(ep, -ESHUTDOWN);
++ }
++ udc_disable (dev);
++
++ /* report disconnect; the driver is already quiesced */
++ if (driver) {
++// spin_unlock (&dev->lock);
++ driver->disconnect (&dev->gadget);
++// spin_lock (&dev->lock);
++ }
++ /* re-init driver-visible data structures */
++ udc_reinit(dev);
++}
++
++int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
++{
++ struct sa1100_udc *dev = the_controller;
++
++ if (!dev)
++ return -ENODEV;
++ if (!driver || driver != dev->driver)
++ return -EINVAL;
++
++ local_irq_disable();
++ stop_activity (dev, driver);
++ local_irq_enable();
++ driver->unbind (&dev->gadget);
++ dev->driver = 0;
++
++//printk("%s: deleting device\n",__FUNCTION__);
++// device_del (&dev->gadget.dev);
++ device_remove_file(dev->dev, &dev_attr_function);
++
++ DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
++ return 0;
++}
++EXPORT_SYMBOL (usb_gadget_unregister_driver);
++
++
++/*-------------------------------------------------------------------------*/
++
++/*-------------------------------------------------------------------------*/
++
++//////////////////////////////////////////////////////////////////////////////
++// Proc Filesystem Support
++//////////////////////////////////////////////////////////////////////////////
++
++#if CONFIG_PROC_FS
++
++#define SAY(fmt,args...) p += sprintf (p, fmt, ## args)
++#define SAYV(num) p += sprintf (p, num_fmt, "Value", num)
++#define SAYC(label,yn) p += sprintf (p, yn_fmt, label, yn)
++#define SAYS(label,v) p += sprintf (p, cnt_fmt, label, v)
++
++static int usbctl_read_proc (char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ const char * num_fmt = "%25.25s: %8.8lX\n";
++ const char * cnt_fmt = "%25.25s: %lu\n";
++ const char * yn_fmt = "%25.25s: %s\n";
++ const char * yes = "YES";
++ const char * no = "NO";
++ unsigned long v;
++ char * p = page;
++ int len;
++
++ SAY ("SA1100 USB Controller Core\n");
++
++ SAYS ("ep0 bytes read", usbd_info.stats.ep0_bytes_read);
++ SAYS ("ep0 bytes written", usbd_info.stats.ep0_bytes_written);
++ SAYS ("ep0 FIFO read failures", usbd_info.stats.ep0_fifo_read_failures);
++ SAYS ("ep0 FIFO write failures", usbd_info.stats.ep0_fifo_write_failures);
++
++ SAY ("\n");
++
++ v = Ser0UDCAR;
++ SAY ("%25.25s: 0x%8.8lX - %ld\n", "Address Register", v, v);
++ v = Ser0UDCIMP;
++ SAY ("%25.25s: %ld (%8.8lX)\n", "IN max packet size", v+1, v);
++ v = Ser0UDCOMP;
++ SAY ("%25.25s: %ld (%8.8lX)\n", "OUT max packet size", v+1, v);
++
++ v = Ser0UDCCR;
++ SAY ("\nUDC Mask Register\n");
++ SAYV (v);
++ SAYC ("UDC Active", (v & UDCCR_UDA) ? yes : no);
++ SAYC ("Suspend interrupts masked", (v & UDCCR_SUSIM) ? yes : no);
++ SAYC ("Resume interrupts masked", (v & UDCCR_RESIM) ? yes : no);
++ SAYC ("Reset interrupts masked", (v & UDCCR_REM) ? yes : no);
++
++ v = Ser0UDCSR;
++ SAY ("\nUDC Interrupt Request Register\n");
++ SAYV (v);
++ SAYC ("Reset pending", (v & UDCSR_RSTIR) ? yes : no);
++ SAYC ("Suspend pending", (v & UDCSR_SUSIR) ? yes : no);
++ SAYC ("Resume pending", (v & UDCSR_RESIR) ? yes : no);
++ SAYC ("ep0 pending", (v & UDCSR_EIR) ? yes : no);
++ SAYC ("receiver pending", (v & UDCSR_RIR) ? yes : no);
++ SAYC ("tramsitter pending", (v & UDCSR_TIR) ? yes : no);
++
++#ifdef CONFIG_SA1100_EXTENEX1
++ SAYC ("\nSoft connect", (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden");
++#endif
++
++#if 1
++ SAY ("\nDMA Tx registers\n");
++ {
++ dma_regs_t *r=the_controller->ep[2].dmaregs;
++ SAY (" DDAR");
++ SAYV(r->DDAR);
++ SAY (" DCSR");
++ SAYV(r->RdDCSR);
++ SAY (" DBSA (address buf A) ");
++ SAYV(r->DBSA);
++ SAY (" DBTA (transfer count A) ");
++ SAYV(r->DBTA);
++ SAY (" DBSB (address buf B) ");
++ SAYV(r->DBSB);
++ SAY (" DBTB (transfer count B) ");
++ SAYV(r->DBTB);
++
++ }
++ SAY ("\nDMA Rx registers\n");
++ {
++ dma_regs_t *r=the_controller->ep[1].dmaregs;
++ SAY (" DDAR");
++ SAYV(r->DDAR);
++ SAY (" DCSR");
++ SAYV(r->RdDCSR);
++ SAY (" DBSA (address buf A) ");
++ SAYV(r->DBSA);
++ SAY (" DBTA (transfer count A) ");
++ SAYV(r->DBTA);
++ SAY (" DBSB (address buf B) ");
++ SAYV(r->DBSB);
++ SAY (" DBTB (transfer count B) ");
++ SAYV(r->DBTB);
++
++ }
++#endif
++#if 1
++ v = Ser0UDCCS0;
++ SAY ("\nUDC Endpoint Zero Status Register\n");
++ SAYV (v);
++ SAYC ("Out Packet Ready", (v & UDCCS0_OPR) ? yes : no);
++ SAYC ("In Packet Ready", (v & UDCCS0_IPR) ? yes : no);
++ SAYC ("Sent Stall", (v & UDCCS0_SST) ? yes : no);
++ SAYC ("Force Stall", (v & UDCCS0_FST) ? yes : no);
++ SAYC ("Data End", (v & UDCCS0_DE) ? yes : no);
++ SAYC ("Data Setup End", (v & UDCCS0_SE) ? yes : no);
++ SAYC ("Serviced (SO)", (v & UDCCS0_SO) ? yes : no);
++
++ v = Ser0UDCCS1;
++ SAY ("\nUDC Receiver Status Register\n");
++ SAYV (v);
++ SAYC ("Receive Packet Complete", (v & UDCCS1_RPC) ? yes : no);
++ SAYC ("Sent Stall", (v & UDCCS1_SST) ? yes : no);
++ SAYC ("Force Stall", (v & UDCCS1_FST) ? yes : no);
++ SAYC ("Receive Packet Error", (v & UDCCS1_RPE) ? yes : no);
++ SAYC ("Receive FIFO not empty", (v & UDCCS1_RNE) ? yes : no);
++
++ v = Ser0UDCCS2;
++ SAY ("\nUDC Transmitter Status Register\n");
++ SAYV (v);
++ SAYC ("FIFO has < 8 of 16 chars", (v & UDCCS2_TFS) ? yes : no);
++ SAYC ("Transmit Packet Complete", (v & UDCCS2_TPC) ? yes : no);
++ SAYC ("Transmit FIFO underrun", (v & UDCCS2_TUR) ? yes : no);
++ SAYC ("Transmit Packet Error", (v & UDCCS2_TPE) ? yes : no);
++ SAYC ("Sent Stall", (v & UDCCS2_SST) ? yes : no);
++ SAYC ("Force Stall", (v & UDCCS2_FST) ? yes : no);
++#endif
++
++ len = (p - page) - off;
++ if (len < 0)
++ len = 0;
++ *eof = (len <=count) ? 1 : 0;
++ *start = page + off;
++ return len;
++}
++
++static inline void register_proc_entry (void)
++{
++ create_proc_read_entry (driver_name, 0, NULL,
++ usbctl_read_proc, NULL);
++}
++
++static inline void unregister_proc_entry (void)
++{
++ remove_proc_entry (driver_name, NULL);
++}
++
++#else
++
++#define register_proc_entry() do {} while (0)
++#define unregister_proc_entry() do {} while (0)
++
++#endif /* CONFIG_PROC_FS */
++
++/*-------------------------------------------------------------------------*/
++
++MODULE_DESCRIPTION ("sa1100_udc");
++MODULE_AUTHOR ("Various");
++MODULE_LICENSE ("GPL");
++
++static struct sa1100_udc memory = {
++ .gadget = {
++ .ops = &sa1100_ops,
++ .ep0 = &memory.ep[0].ep,
++ .name = driver_name,
++/*
++ .dev = {
++ .bus_id = "gadget",
++ .release = nop_release,
++ },
++*/
++ },
++
++ /* control endpoint */
++ .ep[0] = {
++ .ep = {
++ .name = ep0name,
++ .ops = &sa1100_ep_ops,
++ .maxpacket = EP0_FIFO_SIZE,
++// .maxpacket = 8,
++ },
++ .dev = &memory,
++/*
++ .reg_udccs = &UDCCS0,
++ .reg_uddr = &UDDR0,
++*/
++ },
++
++ /* first group of endpoints */
++ .ep[1] = {
++ .ep = {
++ .name = "ep1out-bulk",
++ .ops = &sa1100_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++// .maxpacket = 64,
++ },
++ .dev = &memory,
++/*
++ .fifo_size = BULK_FIFO_SIZE,
++ .bEndpointAddress = USB_DIR_IN | 1,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .reg_udccs = &UDCCS1,
++ .reg_uddr = &UDDR1,
++ drcmr (25)
++*/
++ },
++ .ep[2] = {
++ .ep = {
++ .name = "ep2in-bulk",
++ .ops = &sa1100_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++// .maxpacket = 64,
++ },
++ .dev = &memory,
++/*
++ .fifo_size = BULK_FIFO_SIZE,
++ .bEndpointAddress = 2,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .reg_udccs = &UDCCS2,
++ .reg_ubcr = &UBCR2,
++ .reg_uddr = &UDDR2,
++ drcmr (26)
++*/
++ }
++};
++
++static int __init sa1100_udc_probe ( struct device *_dev)
++{
++ struct sa1100_udc *dev=&memory;
++ int retval = 0;
++
++ /* setup dev */
++ dev->dev = _dev;
++// dev->mach = _dev->platform_data;
++
++ device_initialize(&dev->gadget.dev);
++ dev->gadget.dev.parent = _dev;
++ dev->gadget.dev.dma_mask = _dev->dma_mask;
++
++ the_controller = dev;
++ dev_set_drvdata(_dev, dev);
++
++ /* controller stays disabled until gadget driver is bound */
++ udc_disable (dev);
++ udc_reinit(dev);
++
++// spin_lock_init(&the_udc.lock);
++ register_proc_entry ();
++
++ /* setup dma channels and IRQ */
++ retval = sa1100_request_dma(DMA_Ser0UDCRd, "USB receive",
++ NULL, NULL, &dev->ep[1].dmaregs);
++ if (retval) {
++ ERROR (dev, "couldn't get rx dma, err %d\n", retval);
++ goto err_rx_dma;
++ }
++ retval = sa1100_request_dma(DMA_Ser0UDCWr, "USB transmit",
++ NULL, NULL, &dev->ep[2].dmaregs);
++ if (retval) {
++ ERROR (dev, "couldn't get tx dma, err %d\n", retval);
++ goto err_tx_dma;
++ }
++ retval = request_irq (IRQ_Ser0UDC, udc_int_hndlr, SA_INTERRUPT,
++ driver_name, dev);
++ if (retval) {
++ ERROR (dev, "couldn't get irq, err %d\n", retval);
++ goto err_irq;
++ }
++
++ INFO (dev, "initialized, rx %p tx %p irq %d\n",
++ dev->ep[1].dmaregs, dev->ep[2].dmaregs, IRQ_Ser0UDC);
++ return 0;
++
++err_irq:
++ sa1100_free_dma (dev->ep[2].dmaregs);
++ usbd_info.dmaregs_rx = 0;
++err_tx_dma:
++ sa1100_free_dma (dev->ep[1].dmaregs);
++ usbd_info.dmaregs_tx = 0;
++err_rx_dma:
++ return retval;
++}
++
++static int __exit sa1100_udc_remove (struct device *_dev)
++{
++ struct sa1100_udc *dev = dev_get_drvdata(_dev);
++
++ udc_disable (dev);
++ unregister_proc_entry ();
++ usb_gadget_unregister_driver (dev->driver);
++ sa1100_free_dma (dev->ep[1].dmaregs);
++ sa1100_free_dma (dev->ep[2].dmaregs);
++ free_irq (IRQ_Ser0UDC, dev);
++ dev_set_drvdata(_dev,NULL);
++ the_controller = NULL;
++ return 0;
++}
++
++static struct device_driver udc_driver = {
++ .name = "sa11x0-udc",
++ .bus = &platform_bus_type,
++ .probe = sa1100_udc_probe,
++ .remove = __exit_p(sa1100_udc_remove),
++// .suspend = sa1100_udc_suspend,
++// .resume = sa1100_udc_resume,
++};
++
++static int __init udc_init(void)
++{
++ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
++#ifdef NCB_DMA_FIX
++ send_buffer = (char*) kmalloc( SEND_BUFFER_SIZE, GFP_KERNEL | GFP_DMA );
++ receive_buffer = (char*) kmalloc( RECEIVE_BUFFER_SIZE, GFP_KERNEL | GFP_DMA );
++#endif
++ return driver_register(&udc_driver);
++}
++module_init(udc_init);
++
++static void __exit udc_exit(void)
++{
++#ifdef NCB_DMA_FIX
++ if (send_buffer) {
++ kfree(send_buffer);
++ send_buffer=NULL;
++ }
++ if (receive_buffer) {
++ kfree(receive_buffer);
++ receive_buffer=NULL;
++ }
++#endif
++ driver_unregister(&udc_driver);
++}
++module_exit(udc_exit);
++
+diff -uNr linux-2.6.21.vanilla/drivers/usb/gadget/sa1100_udc.h linux-2.6.21/drivers/usb/gadget/sa1100_udc.h
+--- linux-2.6.21.vanilla/drivers/usb/gadget/sa1100_udc.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/drivers/usb/gadget/sa1100_udc.h 2007-06-05 11:58:29.000000000 +0200
+@@ -0,0 +1,107 @@
++/*
++ * internals of "new style" UDC controller
++ * <linux/usb_gadget.h> replaces ARM-specific "sa1100_usb.h".
++ */
++
++struct sa1100_ep {
++ struct usb_ep ep;
++ struct sa1100_udc *dev;
++ //unsigned long irqs;
++
++ const struct usb_endpoint_descriptor *desc;
++ struct list_head queue;
++ dma_regs_t *dmaregs;
++ unsigned stopped : 1;
++};
++
++struct sa1100_request {
++ struct usb_request req;
++ struct list_head queue;
++// NCB unsigned mapped : 1;
++};
++
++enum ep0_state {
++ EP0_IDLE,
++ EP0_IN_DATA_PHASE,
++ EP0_OUT_DATA_PHASE,
++ EP0_END_XFER,
++ EP0_STALL,
++};
++
++//#define EP0_FIFO_SIZE ((unsigned)16)
++#define EP0_FIFO_SIZE ((unsigned)8)
++#define BULK_FIFO_SIZE ((unsigned)64)
++//#define ISO_FIFO_SIZE ((unsigned)256)
++//#define INT_FIFO_SIZE ((unsigned)8)
++
++struct udc_stats {
++ struct ep0stats {
++ unsigned long ops;
++ unsigned long bytes;
++ } read, write;
++ unsigned long irqs;
++};
++
++struct sa1100_udc {
++ struct usb_gadget gadget;
++ struct usb_gadget_driver *driver;
++ struct device *dev;
++ enum ep0_state ep0state;
++ struct udc_stats stats;
++// NCB spinlock_t lock;
++// NCB dma_regs_t *dmaregs_tx, *dmaregs_rx;
++ unsigned got_irq : 1,
++ vbus : 1,
++ pullup : 1,
++ has_cfr : 1,
++ req_pending : 1,
++ req_std : 1,
++ req_config : 1;
++
++#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
++ struct timer_list timer;
++ u64 dma_mask;
++ unsigned char address;
++ struct sa1100_ep ep[3];
++ int ep0_req_len;
++};
++
++/*-------------------------------------------------------------------------*/
++
++#define xprintk(dev,level,fmt,args...) \
++ printk(level "%s: " fmt , driver_name , ## args)
++
++#ifdef DEBUG
++#undef DEBUG
++#define DEBUG(dev,fmt,args...) \
++ xprintk(dev , KERN_DEBUG , fmt , ## args)
++#else
++#define DEBUG(dev,fmt,args...) \
++ do { } while (0)
++#endif /* DEBUG */
++
++#ifdef VERBOSE
++#define VDEBUG DEBUG
++#else
++#define VDEBUG(dev,fmt,args...) \
++ do { } while (0)
++#endif /* VERBOSE */
++
++#define ERROR(dev,fmt,args...) \
++ xprintk(dev , KERN_ERR , fmt , ## args)
++#define WARN(dev,fmt,args...) \
++ xprintk(dev , KERN_WARNING , fmt , ## args)
++#define INFO(dev,fmt,args...) \
++ xprintk(dev , KERN_INFO , fmt , ## args)
++
++/*-------------------------------------------------------------------------*/
++
++#ifndef container_of
++#define container_of list_entry
++#endif
++
++#ifndef WARN_ON
++#define WARN_ON(x) do { } while (0)
++#endif
++
++
+diff -uNr linux-2.6.21.vanilla/include/linux/usb_ch9.h linux-2.6.21/include/linux/usb_ch9.h
+--- linux-2.6.21.vanilla/include/linux/usb_ch9.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.21/include/linux/usb_ch9.h 2007-06-05 11:58:29.000000000 +0200
+@@ -0,0 +1,555 @@
++/*
++ * This file holds USB constants and structures that are needed for USB
++ * device APIs. These are used by the USB device model, which is defined
++ * in chapter 9 of the USB 2.0 specification. Linux has several APIs in C
++ * that need these:
++ *
++ * - the master/host side Linux-USB kernel driver API;
++ * - the "usbfs" user space API; and
++ * - the Linux "gadget" slave/device/peripheral side driver API.
++ *
++ * USB 2.0 adds an additional "On The Go" (OTG) mode, which lets systems
++ * act either as a USB master/host or as a USB slave/device. That means
++ * the master and slave side APIs benefit from working well together.
++ *
++ * There's also "Wireless USB", using low power short range radios for
++ * peripheral interconnection but otherwise building on the USB framework.
++ */
++
++#ifndef __LINUX_USB_CH9_H
++#define __LINUX_USB_CH9_H
++
++#include <linux/types.h> /* __u8 etc */
++
++/*-------------------------------------------------------------------------*/
++
++/* CONTROL REQUEST SUPPORT */
++
++/*
++ * USB directions
++ *
++ * This bit flag is used in endpoint descriptors' bEndpointAddress field.
++ * It's also one of three fields in control requests bRequestType.
++ */
++#define USB_DIR_OUT 0 /* to device */
++#define USB_DIR_IN 0x80 /* to host */
++
++/*
++ * USB types, the second of three bRequestType fields
++ */
++#define USB_TYPE_MASK (0x03 << 5)
++#define USB_TYPE_STANDARD (0x00 << 5)
++#define USB_TYPE_CLASS (0x01 << 5)
++#define USB_TYPE_VENDOR (0x02 << 5)
++#define USB_TYPE_RESERVED (0x03 << 5)
++
++/*
++ * USB recipients, the third of three bRequestType fields
++ */
++#define USB_RECIP_MASK 0x1f
++#define USB_RECIP_DEVICE 0x00
++#define USB_RECIP_INTERFACE 0x01
++#define USB_RECIP_ENDPOINT 0x02
++#define USB_RECIP_OTHER 0x03
++
++/*
++ * Standard requests, for the bRequest field of a SETUP packet.
++ *
++ * These are qualified by the bRequestType field, so that for example
++ * TYPE_CLASS or TYPE_VENDOR specific feature flags could be retrieved
++ * by a GET_STATUS request.
++ */
++#define USB_REQ_GET_STATUS 0x00
++#define USB_REQ_CLEAR_FEATURE 0x01
++#define USB_REQ_SET_FEATURE 0x03
++#define USB_REQ_SET_ADDRESS 0x05
++#define USB_REQ_GET_DESCRIPTOR 0x06
++#define USB_REQ_SET_DESCRIPTOR 0x07
++#define USB_REQ_GET_CONFIGURATION 0x08
++#define USB_REQ_SET_CONFIGURATION 0x09
++#define USB_REQ_GET_INTERFACE 0x0A
++#define USB_REQ_SET_INTERFACE 0x0B
++#define USB_REQ_SYNCH_FRAME 0x0C
++
++#define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */
++#define USB_REQ_GET_ENCRYPTION 0x0E
++#define USB_REQ_SET_HANDSHAKE 0x0F
++#define USB_REQ_GET_HANDSHAKE 0x10
++#define USB_REQ_SET_CONNECTION 0x11
++#define USB_REQ_SET_SECURITY_DATA 0x12
++#define USB_REQ_GET_SECURITY_DATA 0x13
++#define USB_REQ_SET_WUSB_DATA 0x14
++#define USB_REQ_LOOPBACK_DATA_WRITE 0x15
++#define USB_REQ_LOOPBACK_DATA_READ 0x16
++#define USB_REQ_SET_INTERFACE_DS 0x17
++
++/*
++ * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and
++ * are read as a bit array returned by USB_REQ_GET_STATUS. (So there
++ * are at most sixteen features of each type.)
++ */
++#define USB_DEVICE_SELF_POWERED 0 /* (read only) */
++#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */
++#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */
++#define USB_DEVICE_BATTERY 2 /* (wireless) */
++#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */
++#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless)*/
++#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */
++#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */
++#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */
++
++#define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */
++
++
++/**
++ * struct usb_ctrlrequest - SETUP data for a USB device control request
++ * @bRequestType: matches the USB bmRequestType field
++ * @bRequest: matches the USB bRequest field
++ * @wValue: matches the USB wValue field (le16 byte order)
++ * @wIndex: matches the USB wIndex field (le16 byte order)
++ * @wLength: matches the USB wLength field (le16 byte order)
++ *
++ * This structure is used to send control requests to a USB device. It matches
++ * the different fields of the USB 2.0 Spec section 9.3, table 9-2. See the
++ * USB spec for a fuller description of the different fields, and what they are
++ * used for.
++ *
++ * Note that the driver for any interface can issue control requests.
++ * For most devices, interfaces don't coordinate with each other, so
++ * such requests may be made at any time.
++ */
++struct usb_ctrlrequest {
++ __u8 bRequestType;
++ __u8 bRequest;
++ __le16 wValue;
++ __le16 wIndex;
++ __le16 wLength;
++} __attribute__ ((packed));
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * STANDARD DESCRIPTORS ... as returned by GET_DESCRIPTOR, or
++ * (rarely) accepted by SET_DESCRIPTOR.
++ *
++ * Note that all multi-byte values here are encoded in little endian
++ * byte order "on the wire". But when exposed through Linux-USB APIs,
++ * they've been converted to cpu byte order.
++ */
++
++/*
++ * Descriptor types ... USB 2.0 spec table 9.5
++ */
++#define USB_DT_DEVICE 0x01
++#define USB_DT_CONFIG 0x02
++#define USB_DT_STRING 0x03
++#define USB_DT_INTERFACE 0x04
++#define USB_DT_ENDPOINT 0x05
++#define USB_DT_DEVICE_QUALIFIER 0x06
++#define USB_DT_OTHER_SPEED_CONFIG 0x07
++#define USB_DT_INTERFACE_POWER 0x08
++/* these are from a minor usb 2.0 revision (ECN) */
++#define USB_DT_OTG 0x09
++#define USB_DT_DEBUG 0x0a
++#define USB_DT_INTERFACE_ASSOCIATION 0x0b
++/* these are from the Wireless USB spec */
++#define USB_DT_SECURITY 0x0c
++#define USB_DT_KEY 0x0d
++#define USB_DT_ENCRYPTION_TYPE 0x0e
++#define USB_DT_BOS 0x0f
++#define USB_DT_DEVICE_CAPABILITY 0x10
++#define USB_DT_WIRELESS_ENDPOINT_COMP 0x11
++
++/* conventional codes for class-specific descriptors */
++#define USB_DT_CS_DEVICE 0x21
++#define USB_DT_CS_CONFIG 0x22
++#define USB_DT_CS_STRING 0x23
++#define USB_DT_CS_INTERFACE 0x24
++#define USB_DT_CS_ENDPOINT 0x25
++
++/* All standard descriptors have these 2 fields at the beginning */
++struct usb_descriptor_header {
++ __u8 bLength;
++ __u8 bDescriptorType;
++} __attribute__ ((packed));
++
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_DEVICE: Device descriptor */
++struct usb_device_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __le16 bcdUSB;
++ __u8 bDeviceClass;
++ __u8 bDeviceSubClass;
++ __u8 bDeviceProtocol;
++ __u8 bMaxPacketSize0;
++ __le16 idVendor;
++ __le16 idProduct;
++ __le16 bcdDevice;
++ __u8 iManufacturer;
++ __u8 iProduct;
++ __u8 iSerialNumber;
++ __u8 bNumConfigurations;
++} __attribute__ ((packed));
++
++#define USB_DT_DEVICE_SIZE 18
++
++
++/*
++ * Device and/or Interface Class codes
++ * as found in bDeviceClass or bInterfaceClass
++ * and defined by www.usb.org documents
++ */
++#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
++#define USB_CLASS_AUDIO 1
++#define USB_CLASS_COMM 2
++#define USB_CLASS_HID 3
++#define USB_CLASS_PHYSICAL 5
++#define USB_CLASS_STILL_IMAGE 6
++#define USB_CLASS_PRINTER 7
++#define USB_CLASS_MASS_STORAGE 8
++#define USB_CLASS_HUB 9
++#define USB_CLASS_CDC_DATA 0x0a
++#define USB_CLASS_CSCID 0x0b /* chip+ smart card */
++#define USB_CLASS_CONTENT_SEC 0x0d /* content security */
++#define USB_CLASS_VIDEO 0x0e
++#define USB_CLASS_WIRELESS_CONTROLLER 0xe0
++#define USB_CLASS_APP_SPEC 0xfe
++#define USB_CLASS_VENDOR_SPEC 0xff
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_CONFIG: Configuration descriptor information.
++ *
++ * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the
++ * descriptor type is different. Highspeed-capable devices can look
++ * different depending on what speed they're currently running. Only
++ * devices with a USB_DT_DEVICE_QUALIFIER have any OTHER_SPEED_CONFIG
++ * descriptors.
++ */
++struct usb_config_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __le16 wTotalLength;
++ __u8 bNumInterfaces;
++ __u8 bConfigurationValue;
++ __u8 iConfiguration;
++ __u8 bmAttributes;
++ __u8 bMaxPower;
++} __attribute__ ((packed));
++
++#define USB_DT_CONFIG_SIZE 9
++
++/* from config descriptor bmAttributes */
++#define USB_CONFIG_ATT_ONE (1 << 7) /* must be set */
++#define USB_CONFIG_ATT_SELFPOWER (1 << 6) /* self powered */
++#define USB_CONFIG_ATT_WAKEUP (1 << 5) /* can wakeup */
++#define USB_CONFIG_ATT_BATTERY (1 << 4) /* battery powered */
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_STRING: String descriptor */
++struct usb_string_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __le16 wData[1]; /* UTF-16LE encoded */
++} __attribute__ ((packed));
++
++/* note that "string" zero is special, it holds language codes that
++ * the device supports, not Unicode characters.
++ */
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_INTERFACE: Interface descriptor */
++struct usb_interface_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __u8 bInterfaceNumber;
++ __u8 bAlternateSetting;
++ __u8 bNumEndpoints;
++ __u8 bInterfaceClass;
++ __u8 bInterfaceSubClass;
++ __u8 bInterfaceProtocol;
++ __u8 iInterface;
++} __attribute__ ((packed));
++
++#define USB_DT_INTERFACE_SIZE 9
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_ENDPOINT: Endpoint descriptor */
++struct usb_endpoint_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __u8 bEndpointAddress;
++ __u8 bmAttributes;
++ __le16 wMaxPacketSize;
++ __u8 bInterval;
++
++ /* NOTE: these two are _only_ in audio endpoints. */
++ /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
++ __u8 bRefresh;
++ __u8 bSynchAddress;
++} __attribute__ ((packed));
++
++#define USB_DT_ENDPOINT_SIZE 7
++#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
++
++
++/*
++ * Endpoints
++ */
++#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
++#define USB_ENDPOINT_DIR_MASK 0x80
++
++#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
++#define USB_ENDPOINT_XFER_CONTROL 0
++#define USB_ENDPOINT_XFER_ISOC 1
++#define USB_ENDPOINT_XFER_BULK 2
++#define USB_ENDPOINT_XFER_INT 3
++#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
++
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */
++struct usb_qualifier_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __le16 bcdUSB;
++ __u8 bDeviceClass;
++ __u8 bDeviceSubClass;
++ __u8 bDeviceProtocol;
++ __u8 bMaxPacketSize0;
++ __u8 bNumConfigurations;
++ __u8 bRESERVED;
++} __attribute__ ((packed));
++
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_OTG (from OTG 1.0a supplement) */
++struct usb_otg_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __u8 bmAttributes; /* support for HNP, SRP, etc */
++} __attribute__ ((packed));
++
++/* from usb_otg_descriptor.bmAttributes */
++#define USB_OTG_SRP (1 << 0)
++#define USB_OTG_HNP (1 << 1) /* swap host/device roles */
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_DEBUG: for special highspeed devices, replacing serial console */
++struct usb_debug_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ /* bulk endpoints with 8 byte maxpacket */
++ __u8 bDebugInEndpoint;
++ __u8 bDebugOutEndpoint;
++};
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */
++struct usb_interface_assoc_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __u8 bFirstInterface;
++ __u8 bInterfaceCount;
++ __u8 bFunctionClass;
++ __u8 bFunctionSubClass;
++ __u8 bFunctionProtocol;
++ __u8 iFunction;
++} __attribute__ ((packed));
++
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_SECURITY: group of wireless security descriptors, including
++ * encryption types available for setting up a CC/association.
++ */
++struct usb_security_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __le16 wTotalLength;
++ __u8 bNumEncryptionTypes;
++};
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_KEY: used with {GET,SET}_SECURITY_DATA; only public keys
++ * may be retrieved.
++ */
++struct usb_key_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __u8 tTKID[3];
++ __u8 bReserved;
++ __u8 bKeyData[0];
++};
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_ENCRYPTION_TYPE: bundled in DT_SECURITY groups */
++struct usb_encryption_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __u8 bEncryptionType;
++#define USB_ENC_TYPE_UNSECURE 0
++#define USB_ENC_TYPE_WIRED 1 /* non-wireless mode */
++#define USB_ENC_TYPE_CCM_1 2 /* aes128/cbc session */
++#define USB_ENC_TYPE_RSA_1 3 /* rsa3072/sha1 auth */
++ __u8 bEncryptionValue; /* use in SET_ENCRYPTION */
++ __u8 bAuthKeyIndex;
++};
++
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_BOS: group of wireless capabilities */
++struct usb_bos_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __le16 wTotalLength;
++ __u8 bNumDeviceCaps;
++};
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_DEVICE_CAPABILITY: grouped with BOS */
++struct usb_dev_cap_header {
++ __u8 bLength;
++ __u8 bDescriptorType;
++ __u8 bDevCapabilityType;
++};
++
++#define USB_CAP_TYPE_WIRELESS_USB 1
++
++struct usb_wireless_cap_descriptor { /* Ultra Wide Band */
++ __u8 bLength;
++ __u8 bDescriptorType;
++ __u8 bDevCapabilityType;
++
++ __u8 bmAttributes;
++#define USB_WIRELESS_P2P_DRD (1 << 1)
++#define USB_WIRELESS_BEACON_MASK (3 << 2)
++#define USB_WIRELESS_BEACON_SELF (1 << 2)
++#define USB_WIRELESS_BEACON_DIRECTED (2 << 2)
++#define USB_WIRELESS_BEACON_NONE (3 << 2)
++ __le16 wPHYRates; /* bit rates, Mbps */
++#define USB_WIRELESS_PHY_53 (1 << 0) /* always set */
++#define USB_WIRELESS_PHY_80 (1 << 1)
++#define USB_WIRELESS_PHY_107 (1 << 2) /* always set */
++#define USB_WIRELESS_PHY_160 (1 << 3)
++#define USB_WIRELESS_PHY_200 (1 << 4) /* always set */
++#define USB_WIRELESS_PHY_320 (1 << 5)
++#define USB_WIRELESS_PHY_400 (1 << 6)
++#define USB_WIRELESS_PHY_480 (1 << 7)
++ __u8 bmTFITXPowerInfo; /* TFI power levels */
++ __u8 bmFFITXPowerInfo; /* FFI power levels */
++ __le16 bmBandGroup;
++ __u8 bReserved;
++};
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_DT_WIRELESS_ENDPOINT_COMP: companion descriptor associated with
++ * each endpoint descriptor for a wireless device
++ */
++struct usb_wireless_ep_comp_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __u8 bMaxBurst;
++ __u8 bMaxSequence;
++ __le16 wMaxStreamDelay;
++ __le16 wOverTheAirPacketSize;
++ __u8 bOverTheAirInterval;
++ __u8 bmCompAttributes;
++#define USB_ENDPOINT_SWITCH_MASK 0x03 /* in bmCompAttributes */
++#define USB_ENDPOINT_SWITCH_NO 0
++#define USB_ENDPOINT_SWITCH_SWITCH 1
++#define USB_ENDPOINT_SWITCH_SCALE 2
++};
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_REQ_SET_HANDSHAKE is a four-way handshake used between a wireless
++ * host and a device for connection set up, mutual authentication, and
++ * exchanging short lived session keys. The handshake depends on a CC.
++ */
++struct usb_handshake {
++ __u8 bMessageNumber;
++ __u8 bStatus;
++ __u8 tTKID[3];
++ __u8 bReserved;
++ __u8 CDID[16];
++ __u8 nonce[16];
++ __u8 MIC[8];
++};
++
++/*-------------------------------------------------------------------------*/
++
++/* USB_REQ_SET_CONNECTION modifies or revokes a connection context (CC).
++ * A CC may also be set up using non-wireless secure channels (including
++ * wired USB!), and some devices may support CCs with multiple hosts.
++ */
++struct usb_connection_context {
++ __u8 CHID[16]; /* persistent host id */
++ __u8 CDID[16]; /* device id (unique w/in host context) */
++ __u8 CK[16]; /* connection key */
++};
++
++/*-------------------------------------------------------------------------*/
++
++/* USB 2.0 defines three speeds, here's how Linux identifies them */
++
++enum usb_device_speed {
++ USB_SPEED_UNKNOWN = 0, /* enumerating */
++ USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */
++ USB_SPEED_HIGH, /* usb 2.0 */
++ USB_SPEED_VARIABLE, /* wireless (usb 2.5) */
++};
++
++enum usb_device_state {
++ /* NOTATTACHED isn't in the USB spec, and this state acts
++ * the same as ATTACHED ... but it's clearer this way.
++ */
++ USB_STATE_NOTATTACHED = 0,
++
++ /* chapter 9 and authentication (wireless) device states */
++ USB_STATE_ATTACHED,
++ USB_STATE_POWERED, /* wired */
++ USB_STATE_UNAUTHENTICATED, /* auth */
++ USB_STATE_RECONNECTING, /* auth */
++ USB_STATE_DEFAULT, /* limited function */
++ USB_STATE_ADDRESS,
++ USB_STATE_CONFIGURED, /* most functions */
++
++ USB_STATE_SUSPENDED
++
++ /* NOTE: there are actually four different SUSPENDED
++ * states, returning to POWERED, DEFAULT, ADDRESS, or
++ * CONFIGURED respectively when SOF tokens flow again.
++ */
++};
++
++#endif /* __LINUX_USB_CH9_H */
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-GPIO-MMC-mod.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-GPIO-MMC-mod.patch
new file mode 100644
index 0000000000..70a1555158
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-GPIO-MMC-mod.patch
@@ -0,0 +1,1699 @@
+diff -Nur linux-2.6.24.vanilla/drivers/mmc/card/Makefile linux-2.6.24/drivers/mmc/card/Makefile
+--- linux-2.6.24.vanilla/drivers/mmc/card/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/mmc/card/Makefile 2008-02-23 03:10:45.000000000 +0100
+@@ -6,8 +6,11 @@
+ EXTRA_CFLAGS += -DDEBUG
+ endif
+
++ifeq ($(CONFIG_SA1100_SIMPAD),y)
++# nothing to do
++else
+ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
+ mmc_block-objs := block.o queue.o
+
+ obj-$(CONFIG_SDIO_UART) += sdio_uart.o
+-
++endif
+diff -Nur linux-2.6.24.vanilla/drivers/mmc/core/Makefile linux-2.6.24/drivers/mmc/core/Makefile
+--- linux-2.6.24.vanilla/drivers/mmc/core/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/mmc/core/Makefile 2008-02-23 03:10:45.000000000 +0100
+@@ -6,9 +6,13 @@
+ EXTRA_CFLAGS += -DDEBUG
+ endif
+
++ifeq ($(CONFIG_SA1100_SIMPAD),y)
++# nothing to do
++else
+ obj-$(CONFIG_MMC) += mmc_core.o
+ mmc_core-y := core.o sysfs.o bus.o host.o \
+ mmc.o mmc_ops.o sd.o sd_ops.o \
+ sdio.o sdio_ops.o sdio_bus.o \
+ sdio_cis.o sdio_io.o sdio_irq.o
++endif
+
+diff -Nur linux-2.6.24.vanilla/drivers/mmc/host/Kconfig linux-2.6.24/drivers/mmc/host/Kconfig
+--- linux-2.6.24.vanilla/drivers/mmc/host/Kconfig 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/mmc/host/Kconfig 2008-02-23 03:10:45.000000000 +0100
+@@ -4,6 +4,7 @@
+
+ comment "MMC/SD Host Controller Drivers"
+
++
+ config MMC_ARMMMCI
+ tristate "ARM AMBA Multimedia Card Interface support"
+ depends on ARM_AMBA
+@@ -130,3 +131,9 @@
+
+ If unsure, or if your system has no SPI master driver, say N.
+
++config MMC_SPI_BLOCK
++ tristate "MMC/SD over GPIO (Software SPI) for SIMpad (EXPERIMENTAL)"
++ depends on SA1100_SIMPAD && EXPERIMENTAL
++ help
++ Say Y here to enable MMC block device over GPIO
++ if you have done the MMC-Mod. For Module say M.
+diff -Nur linux-2.6.24.vanilla/drivers/mmc/host/Makefile linux-2.6.24/drivers/mmc/host/Makefile
+--- linux-2.6.24.vanilla/drivers/mmc/host/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/mmc/host/Makefile 2008-02-23 03:10:45.000000000 +0100
+@@ -6,6 +6,9 @@
+ EXTRA_CFLAGS += -DDEBUG
+ endif
+
++ifeq ($(CONFIG_SA1100_SIMPAD),y)
++obj-$(CONFIG_MMC_SPI_BLOCK) += mmc_spi_block.o
++else
+ obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
+ obj-$(CONFIG_MMC_PXA) += pxamci.o
+ obj-$(CONFIG_MMC_IMX) += imxmmc.o
+@@ -17,4 +20,4 @@
+ obj-$(CONFIG_MMC_AT91) += at91_mci.o
+ obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
+ obj-$(CONFIG_MMC_SPI) += mmc_spi.o
+-
++endif
+diff -Nur linux-2.6.24.vanilla/drivers/mmc/host/mmc_spi_block.c linux-2.6.24/drivers/mmc/host/mmc_spi_block.c
+--- linux-2.6.24.vanilla/drivers/mmc/host/mmc_spi_block.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/mmc/host/mmc_spi_block.c 2008-02-23 03:10:45.000000000 +0100
+@@ -0,0 +1,1622 @@
++/*
++ * Copyright (c) Cl�ent Ballabriga, 2005 - GPL
++ * Copyright (c) Guylhem Aznar, 2005 - GPL
++ *
++ * Please check http://externe.net/zaurus/simpad-bluetooth reference design first.
++ *
++ * Based on Madsuk/Rohde work on a MMC driver for the WRT54G.
++ *
++ * This is an ugly hack of a driver. I am surprised if it ever works!
++ * So please use a real driver or contribute one to the 2.4/2.6 mmc framework
++ *
++ * mrdata: ported to 2.6
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/errno.h>
++#include <linux/hdreg.h>
++#include <linux/kdev_t.h>
++#include <linux/blkdev.h>
++#include <linux/spinlock.h>
++#include <linux/time.h>
++#include <linux/delay.h>
++#include <linux/timer.h>
++
++#include <linux/platform_device.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/simpad.h>
++#include <asm/arch/gpio.h>
++
++static int major = 121;
++
++#define DEVICE_NAME "mmc_spi"
++
++static int hd_sizes[1<<6];
++static int hd_blocksizes[1<<6];
++static int hd_hardsectsizes[1<<6];
++static int hd_maxsect[1<<6];
++static struct hd_struct hd[1<<6];
++
++static struct gendisk *mmc_disk;
++
++static struct platform_device *mmc_dev; /* the one and only instance */
++
++static spinlock_t mmc_spi_lock;
++
++/*
++ * *******************************************************************
++ *
++ * This is the only configurable part.
++ *
++ * *******************************************************************
++ *
++ */
++
++// #define DEBUG 1
++// #define DEBUG_HD 1
++// #define CHECK_MEDIA_CHANGE // for developement ONLY, not working yet
++
++/* Let that include where it is or compilation fails on INIT_REQUEST/CURRENT */
++
++
++/*
++ * If you are using different GPIOs in your hardware hack, you must
++ * first make sure they are unused for other functions and then
++ * configure them here.
++ *
++ * On the simpad I use spare pins from the UART1 (internal serial port -> DECT 20-polig):
++ *
++ * Funktion PIN ## Original direction GPIO ## SPI function New direction SD/MMC
++ * - DCD PIN 08 (in) GPIO 23 DO - new name: DI -> MISO (in) PIN 7 Data Out
++ * - DTR PIN 11 (out) GPIO 07 CS (out) PIN 1 Chip Select
++ * - RI PIN 14 (in) GPIO 19 CLK (out) PIN 5 Clock
++ * - DSR PIN 16 (in) GPIO 06 DI - new name: DO -> MOSI (out) PIN 2 Data In
++ *
++ *
++ * SPI: MISO = Master In / Slave OUT MOSI = Master Out / Slave In
++ *
++ * Don't worry about in/out original function - the GPIOs will be
++ * reprogrammed.
++ */
++
++#define GPIO_SD_DI 23
++#define GPIO_SD_CS 7
++#define GPIO_SD_CLK 19
++#define GPIO_SD_DO 6
++
++// #define FAST_GPIO_SD_DI GPIO_GPIO23
++// #define FAST_GPIO_SD_CS GPIO_GPIO7
++// #define FAST_GPIO_SD_CLK GPIO_GPIO19
++// #define FAST_GPIO_SD_DO GPIO_GPIO6
++
++#define FAST_GPIO_SD_DI GPIO_UART1_DCD
++#define FAST_GPIO_SD_CS GPIO_UART1_DTR
++#define FAST_GPIO_SD_CLK GPIO_UART1_RI
++#define FAST_GPIO_SD_DO GPIO_UART1_DSR
++
++/*
++ * *******************************************************************
++ *
++ * Do not change anything below !
++ *
++ * *******************************************************************
++ *
++ */
++
++/* GPIO states */
++#define LOW 0
++#define HIGH 1
++
++#define INPUT 0
++#define OUTPUT 1
++
++#define PRESENT 1
++#define ABSENT 0
++
++typedef unsigned int uint32;
++typedef unsigned long u32_t;
++typedef unsigned short u16_t;
++typedef unsigned char u8_t;
++
++// static struct timer_list mmc_timer;
++
++// static struct timeval s_zeit, e_zeit;
++
++/* start with no card */
++static int mmc_media_detect = 0;
++// static int mmc_media_changed = 1;
++
++
++/////////////////////
++// prototypes
++static int mmc_open(struct inode *inode, struct file *filp);
++static int mmc_release(struct inode *inode, struct file *filp);
++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
++static void mmc_spi_request(struct request_queue *q);
++
++
++/*
++ * *******************************************************************
++ *
++ * Begin GPIO hardware access functions.
++ *
++ * *******************************************************************
++ *
++ */
++
++#define gpio_read(a) ((GPLR & a) ? 1 : 0)
++#define gpio_write_high(a) GPSR = a
++#define gpio_write_low(a) GPCR = a
++
++/* set MMC_Chip_Select to HIGH (MMC/SD-Card inactiv) */
++#define MMC_Disable() gpio_write_high( FAST_GPIO_SD_CS)
++
++/* set MMC_Chip_Select to LOW (MMC/SD-Card activ) */
++#define MMC_Enable() gpio_write_low( FAST_GPIO_SD_CS)
++
++/*
++ * *******************************************************************
++ *
++ * Begin SPI hardware access functions.
++ *
++ * *******************************************************************
++ *
++ */
++static int mmc_spi_media_detect(void)
++{
++// FIXME: add card detection/test by SPI
++
++ return 1;
++}
++
++static int mmc_spi_hardware_init(void)
++{
++ printk("\nmmc: GPIO init\n");
++
++ /* cut existing functions */
++ gpio_set_alternative_function(GPIO_SD_CLK, 0);
++ gpio_set_alternative_function(GPIO_SD_DI, 0);
++ gpio_set_alternative_function(GPIO_SD_DO, 0);
++ gpio_set_alternative_function(GPIO_SD_CS, 0);
++
++ /* remap directions and set state of spi pins */
++ gpio_direction_output(GPIO_SD_CLK, 0);
++ gpio_direction_input(GPIO_SD_DI);
++ gpio_direction_output(GPIO_SD_DO, 0);
++ gpio_direction_output(GPIO_SD_CS, 0);
++
++ printk("mmc: initialising MMC\n");
++
++ /* Start */
++ MMC_Disable();
++ gpio_write_low( FAST_GPIO_SD_CLK);
++ gpio_write_high( FAST_GPIO_SD_DO);
++ return 0;
++}
++
++/* return what has been read, write the parameter */
++/* Clockrate round about 1,2 MHz */
++
++static unsigned char mmc_spi_readwrite(unsigned char data_out)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++ if (data_out & i)
++ {
++ gpio_write_high( FAST_GPIO_SD_DO);
++ }
++ else
++ {
++ gpio_write_low( FAST_GPIO_SD_DO);
++ }
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ if (gpio_read( FAST_GPIO_SD_DI) == 1)
++ {
++ result |= i;
++ }
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ }
++
++ gpio_write_high( FAST_GPIO_SD_DO);
++
++ return (result);
++}
++
++/* return what has been read, write the parameter */
++/* Clockrate round 200 kHz */
++
++static unsigned char mmc_spi_readwrite_slow(unsigned char data_out)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++ if (data_out & i)
++ {
++ gpio_write_high( FAST_GPIO_SD_DO);
++ }
++ else
++ {
++ gpio_write_low( FAST_GPIO_SD_DO);
++ }
++
++ udelay(10);
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ udelay(10);
++
++ if (gpio_read( FAST_GPIO_SD_DI) == 1)
++ {
++ result |= i;
++ }
++
++ udelay(10);
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ udelay(10);
++
++ }
++
++ gpio_write_high( FAST_GPIO_SD_DO);
++
++ udelay(10);
++
++ // printk("Send Byte = 0x%2X Receive Byte = 0x%2X \n", data_out, result);
++
++ return (result);
++}
++
++/* return what has been read */
++
++static unsigned char mmc_spi_read_only(void)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ if (gpio_read( FAST_GPIO_SD_DI) == 1)
++ {
++ result |= i;
++ }
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ }
++
++ return (result);
++}
++
++/* write the parameter */
++/* Clockrate round about 3,6 MHz */
++
++static unsigned char mmc_spi_write_only(unsigned char data_out)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++
++ if (data_out & i)
++ {
++ gpio_write_high( FAST_GPIO_SD_DO);
++ }
++ else
++ {
++ gpio_write_low( FAST_GPIO_SD_DO);
++ }
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ }
++
++ gpio_write_high( FAST_GPIO_SD_DO);
++
++ return (result);
++}
++
++
++/**
++ * this function was contributed by: rcichielo from openwrt forums
++ *
++ * Comments added by Marc DENTY on 2007-03-20
++ *
++ * Sequence to read a card's "CID" bytes (name, serial number etc)
++ *
++ * Send: 4ah,00h,00h,00h,00h,00h - CMD10, no args, null CRC
++ * Read: xx - NCR Time
++ * Read: xx - Command Response (Should be 00h)
++ * Read: until FEh is received - Wait for Data token
++ * Read: yy * 16 - Get 16 bytes from CID
++ * Read: zz - Read CRC lo byte
++ * Read: zz - Read CRC hi byte
++ *
++ * Useful locations in the returned data packet:
++ *
++ * 03h-08h Manufacturers's name in ascii
++ * 0ah-0dh Card's 32 bit serial number
++ */
++/**
++ * Comments added by Cyril CATTIAUX on 2007-03-21
++ *
++ * CID format specification (from Sandisk SD Product Manual v1.9)
++ *
++ * cid[00 ] Manufacturer ID (unsigned byte)
++ * cid[01-02] OEM/Application ID (ASCII)
++ * cid[03-07] Product Name (ASCII)
++ * cid[08 ] Product Revistion (BCD coded number)
++ * cid[09-12] Serial Number (32-bit unsigned int)
++ * cid[13-14] Reserved(bit 12->15) - Manufacture Date(bit 0->11)
++ * cid[15 ] CRC7(bit 1->7) - Not used, allways 1 (bit 0)
++*/
++static int mmc_read_cid(unsigned char *cid)
++{
++ unsigned char result = 0;
++ int i;
++
++ MMC_Enable();
++
++ /* wait */
++ for (i = 0; i < 4; i++)
++ {
++ result=mmc_spi_readwrite(0xff);
++ }
++
++ /* issue CID (card identification data) read request */
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0x40 | 10);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x95);
++
++ for (i = 0; i < 8; i++)
++ {
++ result=mmc_spi_readwrite(0xff);
++
++ if(result == 0x00)
++ break;
++ }
++
++ if (result != 0x00) {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return(1);
++ }
++
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xfe) break;
++ }
++
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return(2);
++ }
++
++ for (i = 0; i < 16; i++) {
++ result = mmc_spi_readwrite(0xff);
++ cid[i] = result;
++ }
++
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0xff);
++
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++
++ return 0;
++}
++
++
++/**
++ * Comments added by Cyril CATTIAUX on 2007-03-21
++ *
++ * CID format specification (from Sandisk SD Product Manual v1.9)
++ *
++ * cid[00 ] Manufacturer ID (unsigned byte)
++ * cid[01-02] OEM/Application ID (ASCII)
++ * cid[03-07] Product Name (ASCII)
++ * cid[08 ] Product Revision (BCD coded 2 digit number)
++ * cid[09-12] Serial Number (32-bit unsigned int)
++ * cid[13-14] Manufacture Date(bit 0->11) (BCD coded 3 digit number YYM offset from 2000) - Reserved(bit 12->15)
++ * cid[15 ] Not used, allways 1 (bit 0) - CRC7(bit 1->7)
++*/
++static void mmc_show_cid_info(void)
++{
++ int i, result;
++ unsigned short tmps;
++ unsigned char cid[16];
++
++ char manufacturer_id;
++ char oem_id[3];
++ char product_name[6];
++ unsigned char product_revision_h, product_revision_l;
++ unsigned int product_sn;
++ unsigned short product_date_y;
++ unsigned char product_date_m;
++
++ result = mmc_read_cid(cid);
++
++ if (result == 0)
++ {
++ printk("mmc_init: MMC/SD Card ID: ");
++ for (i=0; i<16; i++) {
++ printk("%02X ", cid[i]);
++ }
++ manufacturer_id=cid[0];
++ strncpy(oem_id, &cid[1], 2);
++ oem_id[2]='\0';
++ strncpy(product_name, &cid[3], 5);
++ product_name[5]='\0';
++ product_revision_h=(cid[8] >> 4) & 0xf;
++ product_revision_l=cid[8] & 0xf;
++ product_sn=(cid[9]<<24) + (cid[10]<<16) + (cid[11]<<8) + cid[12];
++ tmps=((cid[13]<<8) + cid[14]) & 0x0fff;
++ product_date_y=2000 + (((tmps >> 8) & 0xf) * 10) + ((tmps >> 4) & 0xf);
++ product_date_m=tmps & 0xf;
++
++ printk("\nManufacturer ID : %02X\n", manufacturer_id);
++ printk("OEM/Application ID: %s\n", oem_id);
++ printk("Product name : %s\n", product_name);
++ printk("Product revision : %d.%d\n", product_revision_h, product_revision_l);
++ printk("Product SN : %08X\n", product_sn);
++ printk("Product Date : %d-%d\n", product_date_y, product_date_m);
++
++ } else {
++ printk("mmc_init: impossible to get card indentification info for reason code: %02x", result);
++ }
++}
++
++
++/*
++static int mmc_spi_hw_test(void)
++{
++ unsigned char result, k;
++
++ unsigned int i, j, t;
++
++ printk("mmc_spi_hw_test -> \n\n");
++ k = 0x55;
++ for ( i = 0 ; i < 5; i++) {
++
++ printk("\n0x%2X - ", k);
++ for ( j = 0 ; j < 8; j++ ) {
++ do_gettimeofday( &s_zeit );
++ result = mmc_spi_readwrite_slow(k);
++ do_gettimeofday( &e_zeit );
++
++ if ( result != k ) {
++ printk("!>ERROR<! Transfer = 0x%2X Receive = 0x%2X Trail = %d \n", k, result, j);
++ // i = 255; j = 1000;
++ }
++
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("Durchlauf: %i Versuch: %i von 8 -> Laufzeit: 0x%X s\n", i , j, t);
++ udelay(200);
++ }
++ printk("ready ");
++
++ // k++;
++ }
++ printk("ready ");
++ printk("\n\n");
++ return (0);
++}
++*/
++
++/*
++static int mmc_spi_speed_test(void)
++{
++ unsigned int i, j, k, l, t;
++
++ MMC_Disable();
++
++ for (k = 1; k < 6; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 5; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_readwrite(0xff);
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_readwrite: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ for (k = 1; k < 1; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 1; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_readwrite_slow(0xff);
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_readwrite_slow: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ for (k = 1; k < 6; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 5; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_read_only();
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_read_only: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ for (k = 1; k < 6; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 5; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_write_only(0xff);
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_write_only: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ return (1);
++
++}
++*/
++
++
++static int mmc_spi_card_init(void)
++{
++ unsigned char result = 0;
++ short i, j;
++
++// unsigned long flags;
++
++ // save_flags(flags);
++ // cli();
++
++/*
++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CS), gpio_getalt(&gp, GPIO_SD_CS));
++ printk("GPIO_SD_DI dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DI), gpio_getalt(&gp, GPIO_SD_DI));
++ printk("GPIO_SD_DO dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DO), gpio_getalt(&gp, GPIO_SD_DO));
++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CLK), gpio_getalt(&gp, GPIO_SD_CLK));
++*/
++
++ // printk("\nmmc: mmc_spi_hw_test() *START*\n");
++
++ // mmc_spi_hw_test();
++
++ printk("\nmmc: card init 1/2 (CMD0)\n");
++
++ for (j = 0; j < 10; j++)
++ {
++ MMC_Disable();
++
++ for (i = 0; i < 10; i++)
++ mmc_spi_readwrite_slow(0xff);
++
++ MMC_Enable();
++
++ mmc_spi_readwrite_slow(0xff);
++
++ mmc_spi_readwrite_slow(0x40);
++
++ for (i = 0; i < 4; i++) {
++
++ mmc_spi_readwrite_slow(0x00);
++
++ }
++
++ mmc_spi_readwrite_slow(0x95);
++
++ for (i = 0; i < 8; i++) {
++
++ result = mmc_spi_readwrite_slow(0xff);
++
++#ifdef DEBUG_HD
++ if (result != 0xff) {
++ if (result > 0x1F && result < 0x80)
++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result);
++ else
++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X\n", j, i, result);
++ }
++#endif
++ if (result == 0x01)
++ break;
++ }
++
++ if (result == 0x01)
++ break;
++
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++
++ mdelay(60);
++ }
++
++ if (result != 0x01) {
++
++ printk("mmc: card init 1/2 error: %d (CMD0) failed\n", result);
++ printk(" -> Hint: MMC/SD-Card realy (fully) inserted ?\n");
++
++ return (1);
++ }
++
++ printk("mmc: card init 1/2 (CMD0) success\n\n");
++
++ mdelay(1);
++
++ printk("mmc: card init 2/2 (CMD1)\n");
++ for (j = 0; j < 10; j++) {
++
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0x41);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // if (result >= 32 && result <= 127)
++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result);
++ // else
++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result);
++#endif
++ if (result == 0x00)
++ break;
++ }
++
++ mmc_spi_readwrite_slow(0xff);
++
++ if (result == 0x00) {
++ printk("mmc: card init 2/2 (CMD1) success\n\n");
++
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0x4d);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++ for (i = 0; i < 6; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // if (result > 31 && result < 128)
++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result);
++ // else
++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result);
++#endif
++ // if (result == 0x00)
++ // break;
++ }
++ // mdelay(60);
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++ // mdelay(10);
++
++ // restore_flags(flags);
++
++ return (0);
++ }
++ mdelay(60);
++ }
++ return (2);
++}
++
++
++static int mmc_spi_card_config(void)
++{
++ unsigned char result = 0;
++ short i, j;
++ unsigned char csd[32];
++ unsigned int c_size;
++ unsigned int c_size_mult;
++ unsigned int mult;
++ unsigned int read_bl_len;
++ unsigned int blocknr = 0;
++ unsigned int block_len = 0;
++ unsigned int size = 0;
++ unsigned char rd_buffer[528];
++// unsigned long flags;
++
++ MMC_Enable();
++
++ mmc_spi_readwrite_slow(0xff);
++ result = mmc_spi_readwrite_slow(0x51);
++ // mmc_spi_readwrite_slow(0x4A);
++ // mmc_spi_readwrite_slow(0x40+0x0D);
++ // mmc_spi_readwrite_slow(0x42);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++
++ // printk("mmc: (CMD17) response von 0x51 result = 0x%X\n", result);
++
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // printk("mmc: (CMD17) response (start token) result = 0x%X\n", result);
++#endif
++ if (result == 0x00)
++ break;
++ }
++ if (result != 0x00) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // restore_flags(flags);
++ // mmc_spi_readwrite_slow(0xff);
++ return (1);
++ }
++ // restore_flags(flags);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ rd_buffer[i] = result;
++#ifdef DEBUG_HD
++ /*
++ if (result >= 32 && result <= 127)
++ printk("mmc: CMD17 response (start token) result = 0x%X Zeichen = %c\n", result, result);
++ else
++ printk("mmc: CMD17 response (start token) result = 0x%X\n", result);
++ */
++#endif
++ // if (result == 0xfe)
++ // break;
++ }
++ /*
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0xff);
++ return(1);
++ }
++ */
++
++ for (i = 8; i < 520; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ rd_buffer[i] = result;
++ }
++ for (i = 0; i < 2; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ }
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++
++ printk("Buffer - Start\n");
++
++ for ( i = 0 ; i < 33 ; i++) {
++ printk("\r\n%4X - ", i*16);
++ for ( j = 0 ; j < 16 ; j++) {
++ if ( rd_buffer[i*16+j] < 16)
++ printk("0%X ", rd_buffer[i*16+j]);
++ else
++ printk("%2X ", rd_buffer[i*16+j]);
++ }
++ for ( j = 0 ; j < 16 ; j++) {
++ if ( rd_buffer[i*16+j] < ' ')
++ printk(".");
++ else
++ printk("%c", rd_buffer[i*16+j]);
++ }
++ }
++
++ printk("\nBuffer - Ende\n");
++
++ mmc_show_cid_info();
++
++ for(j = 0 ; j < 1; j++) {
++ MMC_Enable();
++
++ // mdelay(1);
++
++ // save_flags(flags);
++ // cli();
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0x49);
++ // mmc_spi_readwrite_slow(0x4A);
++ // mmc_spi_readwrite_slow(0x40+0x0D);
++ // mmc_spi_readwrite_slow(0x42);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // printk("mmc: (CMD9) response (start token) result = 0x%X\n", result);
++#endif
++ if (result == 0x00)
++ break;
++ }
++ // restore_flags(flags);
++ if (result != 0x00) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++ return (1);
++ }
++ for (i = 0; i < 22; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ if (result >= 32 && result <= 127)
++ printk("mmc: response (start token) result = 0x%X Zeichen = %c\n", result, result);
++ else
++ printk("mmc: response (start token) result = 0x%X\n", result);
++#endif
++ if (result == 0xfe)
++ break;
++ }
++ if (result == 0xfe)
++ break;
++
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++ }
++ mdelay(60);
++ }
++
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ printk("mmc: mmc card config (CMD9) failed result = 0x%X\n\n", result);
++ return (2);
++ }
++ for (i = 0; i < 16; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ csd[i] = result;
++ }
++ for (i = 0; i < 2; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ }
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++
++ if (result == 0x00)
++ return (3);
++
++ c_size = (csd[8] & 0xC0) + (csd[7] << 8) + ((csd[6] & 0x03) << 16);
++ c_size >>= 6;
++ c_size_mult = (csd[10] & 0x80) + ((csd[9] & 0x03) << 8);
++ c_size_mult >>= 7;
++ read_bl_len = csd[5] & 0x0f;
++ mult = 1;
++ mult <<= c_size_mult + 2;
++ blocknr = (c_size + 1) * mult;
++ block_len = 1;
++ block_len <<= read_bl_len;
++ size = block_len * blocknr;
++ size >>= 10;
++
++ for (i = 0; i < (1 << 6); i++) {
++ hd_blocksizes[i] = 1024;
++ hd_hardsectsizes[i] = block_len;
++ hd_maxsect[i] = 256;
++ }
++ hd_sizes[0] = size;
++ hd[0].nr_sects = blocknr;
++
++ printk("Size = %d, hardsectsize = %d, sectors = %d\n",
++ size, block_len, blocknr);
++
++ return 0;
++}
++
++
++/*
++ * *******************************************************************
++ *
++ * End of SPI hardware access functions.
++ *
++ * *******************************************************************
++ */
++
++
++static int mmc_spi_write_block(unsigned int dest_addr, unsigned char *data)
++{
++ unsigned int address;
++ unsigned char result = 0;
++ unsigned char ab0, ab1, ab2, ab3;
++ int i;
++
++ address = dest_addr;
++
++ ab3 = 0xff & (address >> 24);
++ ab2 = 0xff & (address >> 16);
++ ab1 = 0xff & (address >> 8);
++ ab0 = 0xff & address;
++
++ MMC_Enable();
++
++ mmc_spi_readwrite(0xff);
++
++ mmc_spi_readwrite(0x58);
++ mmc_spi_readwrite(ab3); /* msb */
++ mmc_spi_readwrite(ab2);
++ mmc_spi_readwrite(ab1);
++ mmc_spi_readwrite(ab0); /* lsb */
++ mmc_spi_readwrite(0xff);
++
++ for (i = 0; i < 8; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ {
++ break;
++ }
++ }
++
++ if (result != 0x00)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (1);
++ }
++
++ mmc_spi_readwrite(0xfe);
++
++ for (i = 0; i < 512; i += 32)
++ {
++ mmc_spi_write_only(data[i]);
++ mmc_spi_write_only(data[i+1]);
++ mmc_spi_write_only(data[i+2]);
++ mmc_spi_write_only(data[i+3]);
++ mmc_spi_write_only(data[i+4]);
++ mmc_spi_write_only(data[i+5]);
++ mmc_spi_write_only(data[i+6]);
++ mmc_spi_write_only(data[i+7]);
++ mmc_spi_write_only(data[i+8]);
++ mmc_spi_write_only(data[i+9]);
++ mmc_spi_write_only(data[i+10]);
++ mmc_spi_write_only(data[i+11]);
++ mmc_spi_write_only(data[i+12]);
++ mmc_spi_write_only(data[i+13]);
++ mmc_spi_write_only(data[i+14]);
++ mmc_spi_write_only(data[i+15]);
++ mmc_spi_write_only(data[i+16]);
++ mmc_spi_write_only(data[i+17]);
++ mmc_spi_write_only(data[i+18]);
++ mmc_spi_write_only(data[i+19]);
++ mmc_spi_write_only(data[i+20]);
++ mmc_spi_write_only(data[i+21]);
++ mmc_spi_write_only(data[i+22]);
++ mmc_spi_write_only(data[i+23]);
++ mmc_spi_write_only(data[i+24]);
++ mmc_spi_write_only(data[i+25]);
++ mmc_spi_write_only(data[i+26]);
++ mmc_spi_write_only(data[i+27]);
++ mmc_spi_write_only(data[i+28]);
++ mmc_spi_write_only(data[i+29]);
++ mmc_spi_write_only(data[i+30]);
++ mmc_spi_write_only(data[i+31]);
++ }
++
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0xff);
++
++ for (i = 0; i < 1000000; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xff)
++ {
++ break;
++ }
++ }
++
++ if (result != 0xff)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (3);
++ }
++
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (0);
++}
++
++static int mmc_spi_read_block(unsigned char *data, unsigned int src_addr)
++{
++ unsigned int address;
++ unsigned char result = 0;
++ unsigned char ab0, ab1, ab2, ab3;
++ // unsigned long flags;
++ // int i, j;
++ int i;
++
++ address = src_addr;
++
++ ab3 = 0xff & (address >> 24);
++ ab2 = 0xff & (address >> 16);
++ ab1 = 0xff & (address >> 8);
++ ab0 = 0xff & address;
++
++ MMC_Enable();
++
++ mmc_spi_readwrite(0xff);
++
++ mmc_spi_readwrite(0x51);
++ mmc_spi_readwrite(ab3); /* msb */
++ mmc_spi_readwrite(ab2);
++ mmc_spi_readwrite(ab1);
++ mmc_spi_readwrite(ab0); /* lsb */
++ mmc_spi_readwrite(0xff);
++
++ for (i = 0; i < 8; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ {
++ break;
++ }
++ }
++
++ if (result != 0x00)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (1);
++ }
++
++ for (i = 0; i < 100000; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xfe)
++ {
++ break;
++ }
++ }
++
++ if (result != 0xfe)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (2);
++ }
++
++ for (i = 0; i < 512; i += 32 )
++ {
++ data[i] = mmc_spi_read_only();
++ data[i+1] = mmc_spi_read_only();
++ data[i+2] = mmc_spi_read_only();
++ data[i+3] = mmc_spi_read_only();
++ data[i+4] = mmc_spi_read_only();
++ data[i+5] = mmc_spi_read_only();
++ data[i+6] = mmc_spi_read_only();
++ data[i+7] = mmc_spi_read_only();
++ data[i+8] = mmc_spi_read_only();
++ data[i+9] = mmc_spi_read_only();
++ data[i+10] = mmc_spi_read_only();
++ data[i+11] = mmc_spi_read_only();
++ data[i+12] = mmc_spi_read_only();
++ data[i+13] = mmc_spi_read_only();
++ data[i+14] = mmc_spi_read_only();
++ data[i+15] = mmc_spi_read_only();
++ data[i+16] = mmc_spi_read_only();
++ data[i+17] = mmc_spi_read_only();
++ data[i+18] = mmc_spi_read_only();
++ data[i+19] = mmc_spi_read_only();
++ data[i+20] = mmc_spi_read_only();
++ data[i+21] = mmc_spi_read_only();
++ data[i+22] = mmc_spi_read_only();
++ data[i+23] = mmc_spi_read_only();
++ data[i+24] = mmc_spi_read_only();
++ data[i+25] = mmc_spi_read_only();
++ data[i+26] = mmc_spi_read_only();
++ data[i+27] = mmc_spi_read_only();
++ data[i+28] = mmc_spi_read_only();
++ data[i+29] = mmc_spi_read_only();
++ data[i+30] = mmc_spi_read_only();
++ data[i+31] = mmc_spi_read_only();
++ }
++
++ result = mmc_spi_readwrite(0xff);
++ result = mmc_spi_readwrite(0xff);
++
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++
++ return (0);
++}
++
++/*
++static int mmc_revalidate(kdev_t dev)
++{
++ int target, max_p, start, i;
++
++ mmc_media_detect = mmc_spi_media_detect();
++
++ if (mmc_media_detect == 0)
++ {
++ return -ENODEV;
++ }
++
++ target = DEVICE_NR(dev);
++
++ max_p = hd_gendisk.max_p;
++ start = target << 6;
++ for (i = max_p - 1; i >= 0; i--)
++ {
++ int minor = start + i;
++ invalidate_device(MKDEV(MAJOR_NR, minor), 1);
++ hd_gendisk.part[minor].start_sect = 0;
++ hd_gendisk.part[minor].nr_sects = 0;
++ }
++
++ grok_partitions(&hd_gendisk, target, 1 << 6, hd_sizes[0] * 2);
++
++ return 0;
++}
++*/
++
++
++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
++ unsigned long arg)
++{
++ if (!inode || !inode->i_rdev)
++ return -EINVAL;
++
++ switch (cmd) {
++#if 0
++ case BLKGETSIZE:
++ return put_user(hd[MINOR(inode->i_rdev)].nr_sects,
++ (unsigned long *)arg);
++ case BLKGETSIZE64:
++ return put_user((u64) hd[MINOR(inode->i_rdev)].
++ nr_sects, (u64 *) arg);
++ case BLKRRPART:
++ if (!capable(CAP_SYS_ADMIN))
++ return -EACCES;
++
++ return mmc_revalidate(inode->i_rdev);
++#endif
++ case HDIO_GETGEO:
++ {
++ struct block_device *bdev = inode->i_bdev;
++ struct hd_geometry *loc, g;
++ loc = (struct hd_geometry *)arg;
++ if (!loc)
++ return -EINVAL;
++ memset(loc, 0, sizeof(struct hd_geometry));
++ g.heads = 4;
++ g.sectors = 16;
++ g.cylinders = get_capacity(bdev->bd_disk) / (4*16);
++ g.start = get_start_sect(bdev);
++ return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0;
++ }
++ default:
++ return -ENOTTY;
++ }
++}
++
++
++/*
++static int mmc_check_media_change(kdev_t dev)
++{
++ (void) dev;
++ if (mmc_media_changed == 1)
++ {
++ mmc_media_changed = 0;
++ return 1;
++ }
++ else
++ {
++ return 0;
++ }
++}
++*/
++
++
++/*
++static int mmc_init(void)
++{
++ int result;
++
++ result = mmc_spi_hardware_init();
++
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_spi_hardware_init\n", result);
++ return -1;
++ }
++
++
++ result = mmc_spi_hw_test();
++
++ if (result != 0)
++ {
++ printk("\n mmc: mmc_spi_hw_test i.O. \n\n");
++ return -1;
++ }
++
++
++ result = mmc_spi_speed_test();
++
++ if (result != 0)
++ {
++ printk("\n mmc: mmc_spi_speed_test i.O. \n\n");
++ return -1;
++ }
++
++
++ result = mmc_spi_card_init();
++ mdelay(50);
++ if (result != 0)
++ {
++ // Give it an extra shot
++ // result = mmc_spi_card_init();
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_card_init\n", result);
++ return -1;
++ }
++ }
++
++ result = mmc_spi_card_config();
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_card_config\n", result);
++ return -1;
++ }
++
++
++ return 0;
++}
++*/
++
++/*
++static void mmc_exit(void)
++{
++}
++*/
++
++
++/*
++static void mmc_check_media(void)
++{
++ int old_state, new_state;
++ int result;
++
++ old_state = mmc_media_detect;
++ new_state = mmc_spi_media_detect();
++
++ if (old_state != new_state)
++ {
++ mmc_media_changed = 1;
++ if (new_state == PRESENT)
++ {
++ result = mmc_init();
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_init\n", result);
++ }
++ else
++ {
++ mmc_exit();
++ }
++ }
++ }
++
++#ifdef CHECK_MEDIA_CHANGE
++ del_timer(&mmc_timer);
++ mmc_timer.expires = jiffies + 10*HZ;
++ add_timer(&mmc_timer);
++#endif
++
++}
++*/
++
++
++/* NB: There might be several requests in the queue, simply dequeuing only one
++ and not checking for more will cause a stall because the block subsystem
++ will not call this function again unless the queue is "plugged" which can
++ only happen if it runs empty... */
++static void mmc_spi_request(struct request_queue *q)
++{
++ struct request *req;
++ int ret;
++
++ unsigned int mmc_address;
++ unsigned char *buffer_address;
++ int nr_sectors;
++ int i;
++ int result, success;
++
++ if (blk_queue_plugged(q)) {
++ return;
++ }
++
++ spin_lock(&mmc_spi_lock);
++ for(;;) {
++ req = elv_next_request(q);
++ if (!req)
++ break;
++
++ if (!blk_fs_request(req)) {
++ printk("not a blk_fs_request\n");
++ spin_unlock(&mmc_spi_lock);
++ continue;
++ }
++
++ mmc_address = req->sector * hd_hardsectsizes[0];
++ buffer_address = req->buffer;
++ nr_sectors = req->current_nr_sectors;
++ success = 1;
++ if (rq_data_dir(req) == READ) {
++ spin_unlock_irq(q->queue_lock);
++ for (i = 0; i < nr_sectors; i++) {
++ result = mmc_spi_read_block(buffer_address, mmc_address);
++ if (unlikely(result < 0)) {
++ printk(KERN_ERR "mmi_spi_block: error reading block (%d)\n", result);
++ success = 0;
++ break;
++ }
++ mmc_address += hd_hardsectsizes[0];
++ buffer_address += hd_hardsectsizes[0];
++ }
++ spin_lock_irq(q->queue_lock);
++ } else {
++ spin_unlock_irq(q->queue_lock);
++ for (i = 0; i < nr_sectors; i++) {
++ result = mmc_spi_write_block(mmc_address, buffer_address);
++ if (unlikely(result < 0)) {
++ printk(KERN_ERR "mmi_spi_block: error writing block (%d)\n", result);
++ success = 0;
++ break;
++ }
++ mmc_address += hd_hardsectsizes[0];
++ buffer_address += hd_hardsectsizes[0];
++ }
++ spin_lock_irq(q->queue_lock);
++ }
++ ret = end_that_request_chunk(req, success, nr_sectors * hd_hardsectsizes[0]);
++ if (!ret) {
++ blkdev_dequeue_request(req);
++ end_that_request_last(req, 0);
++ }
++ }
++ spin_unlock(&mmc_spi_lock);
++}
++
++
++static int mmc_open(struct inode *inode, struct file *filp)
++{
++ // int device;
++ (void) filp;
++ mmc_media_detect = mmc_spi_media_detect();
++
++ if (mmc_media_detect == 0)
++ return -ENODEV;
++
++ return 0;
++}
++
++static int mmc_release(struct inode *inode, struct file *filp)
++{
++ return 0;
++}
++
++
++static struct block_device_operations mmc_spi_bdops = {
++ .open = mmc_open,
++ .release = mmc_release,
++ .ioctl = mmc_ioctl,
++ .owner = THIS_MODULE,
++#if 0
++ .check_media_change = mmc_check_media_change,
++ .revalidate = mmc_revalidate,
++#endif
++};
++
++static int detect_card(void)
++{
++ int result;
++
++ result = mmc_spi_card_init();
++ if (result != 0) {
++ // Give it an extra shot
++ result = mmc_spi_card_init();
++ if (result != 0) {
++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_init (%d)\n", result);
++ return -ENODEV;
++ }
++ }
++
++ result = mmc_spi_card_config();
++ // result = mmc_spi_speed_test();
++ if (result != 0) {
++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_config (%d)\n", result);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++/* Fills in the gendisk structure from the received card
++ data. */
++static int gendisk_init(struct device *dev, struct gendisk *gd)
++{
++ if (!gd)
++ return -EINVAL;
++
++ gd->major = major;
++ gd->first_minor = 0; /* only one device supported */
++ gd->fops = &mmc_spi_bdops;
++ gd->driverfs_dev = dev;
++
++ gd->queue = blk_init_queue(mmc_spi_request,NULL);
++
++ if (!gd->queue)
++ return -ENOMEM;
++
++ sprintf(gd->disk_name, "mmcblk");
++
++ blk_queue_hardsect_size(gd->queue, hd_hardsectsizes[0]);
++
++ set_capacity(gd, hd_sizes[0]<<1);
++
++ return 0;
++}
++
++static int gendisk_fini(struct gendisk *gd)
++{
++ BUG_ON(!gd);
++
++ if (gd->queue)
++ blk_cleanup_queue(gd->queue);
++
++ del_gendisk(gd);
++
++ return 0;
++}
++
++/* platform driver device instance routines */
++static int mmc_spi_probe(struct platform_device *pdev)
++{
++ int result;
++ printk("$Id: mmc_spi_block.c,v 1.05 2008/01/04 01:02:10 mrdata Exp $\n");
++
++ result = mmc_spi_hardware_init();
++ if (result != 0) {
++ printk(KERN_ERR "mmc_spi_block: error in mmc_spi_hardware_init (%d)\n", result);
++ result = -ENODEV;
++ return result;
++ }
++
++ result = detect_card();
++ if (result < 0)
++ return result;
++
++ mmc_media_detect = 1;
++
++ result = register_blkdev(major, DEVICE_NAME);
++ if (result < 0)
++ return result;
++
++ if (!major)
++ major = result;
++
++ /* allow 8 partitions per device */
++ BUG_ON(mmc_disk!=NULL);
++ mmc_disk = alloc_disk(1 << 3);
++ if (!mmc_disk) {
++ result = -ENOMEM;
++ goto out;
++ }
++
++ result = gendisk_init(&pdev->dev, mmc_disk);
++ if (result < 0)
++ goto out;
++
++ add_disk(mmc_disk);
++
++ /*init_timer(&mmc_timer);
++ mmc_timer.expires = jiffies + HZ;
++ mmc_timer.function = (void *)mmc_check_media;
++ add_timer(&mmc_timer); */
++ return 0;
++
++out:
++ if (mmc_disk)
++ put_disk(mmc_disk);
++
++ unregister_blkdev(major, DEVICE_NAME);
++ return result;
++}
++
++static int mmc_spi_remove(struct platform_device *dev)
++{
++ // int ret;
++
++ if (mmc_disk) {
++ gendisk_fini(mmc_disk);
++ put_disk(mmc_disk);
++ }
++
++ // ret = unregister_blkdev(major, DEVICE_NAME);
++ unregister_blkdev(major, DEVICE_NAME);
++ return 0;
++}
++
++struct platform_driver mmc_spi_driver = {
++ .probe = mmc_spi_probe,
++ .remove = mmc_spi_remove,
++ .driver = {
++ .name = "mmc_spi_block",
++ .owner = THIS_MODULE,
++ },
++};
++
++
++/* module init/exit */
++static int __init mmc_spi_block_init(void)
++{
++ int ret;
++ spin_lock_init(&mmc_spi_lock);
++
++ ret = platform_driver_register(&mmc_spi_driver);
++ if (ret < 0)
++ return ret;
++
++ /* we just support one device */
++ mmc_dev = platform_device_register_simple("mmc_spi_block", -1, NULL, 0);
++ if (IS_ERR(mmc_dev))
++ return PTR_ERR(mmc_dev);
++
++ return 0;
++}
++
++
++static void __exit mmc_spi_block_exit(void)
++{
++ platform_driver_unregister(&mmc_spi_driver);
++ if (mmc_dev)
++ platform_device_unregister(mmc_dev);
++}
++
++
++module_init(mmc_spi_block_init);
++module_exit(mmc_spi_block_exit);
++
++MODULE_AUTHOR("Madsuk,Rohde,TaGana,Carsten Juttner <carjay@gmx.net>,Guylhem Aznar <mmc-driver@externe.net>,mrdata");
++MODULE_DESCRIPTION("Driver for MMC/SD-Cards in SPI mode over GPIO (Software SPI)");
++MODULE_SUPPORTED_DEVICE("SIMpad");
++MODULE_LICENSE("GPL");
++
++module_param(major, int, 0444);
++MODULE_PARM_DESC(major, "specify the major device number for the MMC/SD SPI driver");
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-battery-old-way-but-also-with-sysfs.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-battery-old-way-but-also-with-sysfs.patch
new file mode 100644
index 0000000000..35f192ada2
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-battery-old-way-but-also-with-sysfs.patch
@@ -0,0 +1,571 @@
+diff -Nur linux-2.6.24.vanilla/drivers/mfd/Makefile linux-2.6.24/drivers/mfd/Makefile
+--- linux-2.6.24.vanilla/drivers/mfd/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/mfd/Makefile 2008-02-20 21:27:39.000000000 +0100
+@@ -12,3 +12,7 @@
+ ifeq ($(CONFIG_SA1100_ASSABET),y)
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
+ endif
++
++ifeq ($(CONFIG_SA1100_SIMPAD),y)
++obj-$(CONFIG_MCP_UCB1200) += ucb1x00-simpad.o
++endif
+diff -Nur linux-2.6.24.vanilla/drivers/mfd/ucb1x00-simpad.c linux-2.6.24/drivers/mfd/ucb1x00-simpad.c
+--- linux-2.6.24.vanilla/drivers/mfd/ucb1x00-simpad.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/mfd/ucb1x00-simpad.c 2008-02-20 21:27:39.000000000 +0100
+@@ -0,0 +1,226 @@
++/*
++ * linux/drivers/mfd/ucb1x00-simpad.c
++ *
++ * Copyright (C) 2001-2003 Russell King, All Rights Reserved.
++ * 2007/03/18 mrdata:
++ * - adapted ucb1x00-assabet.c
++ * - transfer ucb1x00-simpad.c from kernel24
++ * to new structur of kernel26
++ *
++ * 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.
++ *
++ * We handle the machine-specific bits of the UCB1x00 driver here.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/proc_fs.h>
++#include <linux/device.h>
++
++#include <linux/apm-emulation.h>
++
++#include <asm/dma.h>
++
++#include <asm/arch/simpad.h>
++#include <asm/arch-sa1100/simpad_pm.h>
++
++#include "ucb1x00.h"
++#include "ucb1x00-simpad.h"
++
++#define UCB1X00_ATTR(name,input,designation) \
++static ssize_t name##_show(struct class_device *dev, char *buf) \
++{ \
++ struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); \
++ int val; \
++ ucb1x00_adc_enable(ucb); \
++ val = ucb1x00_adc_read(ucb, input, UCB_NOSYNC); \
++ ucb1x00_adc_disable(ucb); \
++ return sprintf(buf, "%d\n", CALIBRATE_##designation(val)); \
++} \
++static CLASS_DEVICE_ATTR(name,0444,name##_show,NULL)
++
++UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1, BATTERY);
++UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD2, SUPPLY);
++UCB1X00_ATTR(icharger, UCB_ADC_INP_AD3, CHARGING);
++
++static struct ucb1x00 *ucb_alt;
++
++#define UCB1X00_WERT(name,input,designation) \
++static int ucb1x00_simpad_read_##name(struct ucb1x00 *ucb_alt) \
++{ \
++ int val; \
++ ucb1x00_adc_enable(ucb_alt); \
++ val = ucb1x00_adc_read(ucb_alt, input, UCB_NOSYNC); \
++ ucb1x00_adc_disable(ucb_alt); \
++ return CALIBRATE_##designation(val); \
++}
++
++UCB1X00_WERT(vbatt, UCB_ADC_INP_AD1, BATTERY);
++UCB1X00_WERT(vcharger, UCB_ADC_INP_AD2, SUPPLY);
++UCB1X00_WERT(icharger, UCB_ADC_INP_AD3, CHARGING);
++
++static int ucb1x00_simpad_add(struct ucb1x00_dev *dev)
++{
++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_vbatt);
++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_vcharger);
++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_icharger);
++ ucb_alt = dev->ucb;
++ return 0;
++}
++
++static void ucb1x00_simpad_remove(struct ucb1x00_dev *dev)
++{
++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_icharger);
++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vcharger);
++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vbatt);
++}
++
++static struct ucb1x00_driver ucb1x00_simpad_driver = {
++ .add = ucb1x00_simpad_add,
++ .remove = ucb1x00_simpad_remove,
++};
++
++static int __init ucb1x00_simpad_init(void)
++{
++ apm_get_power_status = simpad_apm_get_power_status;
++ return ucb1x00_register_driver(&ucb1x00_simpad_driver);
++}
++
++static void __exit ucb1x00_simpad_exit(void)
++{
++ apm_get_power_status = NULL;
++ ucb1x00_unregister_driver(&ucb1x00_simpad_driver);
++}
++
++/****************************************************************************/
++/* Functions exported for use by the kernel and kernel modules */
++/****************************************************************************/
++
++int simpad_get_battery(struct simpad_battery_apm *bstat)
++{
++ int icharger, vcharger, vbatt;
++
++ if ( ucb_alt ) {
++ icharger = ucb1x00_simpad_read_icharger( ucb_alt );
++ vcharger = ucb1x00_simpad_read_vcharger( ucb_alt );
++ vbatt = ucb1x00_simpad_read_vbatt( ucb_alt );
++ } else {
++ bstat->ac_status = SIMPAD_AC_STATUS_AC_UNKNOWN;
++ bstat->status = SIMPAD_BATT_STATUS_UNKNOWN;
++ bstat->percentage = 0x64; // lets say 100%
++ bstat->life = 330; // lets say a long time
++ return 0;
++ }
++
++ /* AC status */
++ bstat->ac_status = SIMPAD_AC_STATUS_AC_OFFLINE;
++ if ( vcharger>MIN_SUPPLY ) {
++ bstat->ac_status = SIMPAD_AC_STATUS_AC_ONLINE;
++ }
++
++ /* charging */
++ bstat->status = 0x0;
++ if ( icharger >= CHARGING_LED_LEVEL )
++ bstat->status = SIMPAD_BATT_STATUS_CHARGING;
++
++ if ( vbatt > BATT_LOW )
++ bstat->status |= SIMPAD_BATT_STATUS_HIGH;
++ else if ( vbatt < BATT_CRITICAL )
++ bstat->status |= SIMPAD_BATT_STATUS_CRITICAL;
++ else
++ bstat->status |= SIMPAD_BATT_STATUS_LOW;
++
++ if (bstat->status & SIMPAD_BATT_STATUS_CHARGING) {
++ if (icharger > CHARGING_MAX_LEVEL)
++ icharger = CHARGING_MAX_LEVEL;
++ if (icharger < CHARGING_LED_LEVEL)
++ icharger = CHARGING_LED_LEVEL;
++
++ bstat->percentage = 100 - 100 * (icharger - CHARGING_LED_LEVEL) /
++ (CHARGING_MAX_LEVEL - CHARGING_LED_LEVEL);
++ } else {
++ if (vbatt > BATT_FULL)
++ vbatt = BATT_FULL;
++ if (vbatt < BATT_EMPTY)
++ vbatt = BATT_EMPTY;
++
++ bstat->percentage = 100 * (vbatt - BATT_EMPTY) / (BATT_FULL - BATT_EMPTY);
++ }
++
++ /* let's assume: full load is 7h */
++ /* bstat->life = 420*bstat->percentage/100; */
++ /* mrdata: think, 4h is more realistic */
++ bstat->life = 240*(bstat->percentage)/100;
++
++#if 0
++ printk("get_battery: ac: %02x / ch: %02x / perc: %02x / life: %d \n",
++ bstat->ac_status, bstat->status,
++ bstat->percentage, bstat->life );
++#endif
++
++ return 0;
++}
++
++void simpad_apm_get_power_status(struct apm_power_info *info)
++{
++ struct simpad_battery_apm bstat;
++ unsigned char ac = APM_AC_UNKNOWN;
++ unsigned char level = APM_BATTERY_STATUS_UNKNOWN;
++ int status, result;
++
++ result = simpad_get_battery(&bstat);
++ if (result) {
++ printk("%s: unable to access battery information: result=%d\n", __FUNCTION__, result);
++ return;
++ }
++
++ switch (bstat.ac_status) {
++ case SIMPAD_AC_STATUS_AC_OFFLINE:
++ ac = APM_AC_OFFLINE;
++ break;
++ case SIMPAD_AC_STATUS_AC_ONLINE:
++ ac = APM_AC_ONLINE;
++ break;
++ case SIMPAD_AC_STATUS_AC_BACKUP:
++ ac = APM_AC_BACKUP;
++ break;
++ }
++
++ info->ac_line_status = ac;
++
++ status = bstat.status;
++ if (status & (SIMPAD_BATT_STATUS_CHARGING | SIMPAD_BATT_STATUS_CHARGE_MAIN))
++ level = APM_BATTERY_STATUS_CHARGING;
++ else if (status & (SIMPAD_BATT_STATUS_HIGH | SIMPAD_BATT_STATUS_FULL))
++ level = APM_BATTERY_STATUS_HIGH;
++ else if (status & SIMPAD_BATT_STATUS_LOW)
++ level = APM_BATTERY_STATUS_LOW;
++ else if (status & SIMPAD_BATT_STATUS_CRITICAL)
++ level = APM_BATTERY_STATUS_CRITICAL;
++
++ info->battery_status = level;
++ info->battery_flag = info->battery_status;
++
++ info->battery_life = bstat.percentage;
++
++ /* we have a dumb battery - so we know nothing */
++ info->time = bstat.life;
++ info->units = APM_UNITS_MINS;
++
++#if 0
++ printk("apm_get_power: ac: %02x / bs: %02x / bf: %02x / perc: %02x / life: %d\n",
++ info->ac_line_status, info->battery_status, info->battery_flag,
++ info->battery_life, info->time );
++#endif
++ return;
++}
++
++module_init(ucb1x00_simpad_init);
++module_exit(ucb1x00_simpad_exit);
++
++
++MODULE_AUTHOR("Juergen Messerer <juergen.messerer@freesurf.ch>");
++MODULE_DESCRIPTION("SIMpad noddy testing only example ADC driver");
++MODULE_LICENSE("GPL");
+diff -Nur linux-2.6.24.vanilla/drivers/mfd/ucb1x00-simpad.h linux-2.6.24/drivers/mfd/ucb1x00-simpad.h
+--- linux-2.6.24.vanilla/drivers/mfd/ucb1x00-simpad.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/mfd/ucb1x00-simpad.h 2008-02-20 21:27:39.000000000 +0100
+@@ -0,0 +1,86 @@
++/*
++ * linux/drivers/mfd/ucb1x00-simpad.h
++ *
++ * Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * 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.
++ */
++#ifndef UCB1300_SIMPAD_H
++#define UCB1300_SIMPAD_H
++
++/*
++ * Conversion from AD -> mV
++ * 7.5V = 1023 7.33137mV/Digit
++ *
++ * 400 Units == 9.7V
++ * a = ADC value
++ * 2007/03/24 mrdata:
++ * according UCB1300 datasheet ADC error max. 3 LSB
++ * 5-95% of full scale -> 3*7.33137mV = 21.99mV
++ * // old
++ * 21 = ADC error
++ * 12600 = Divident to get 2*7.3242
++ * 860 = Divider to get 2*7.3242
++ * 170 = Voltagedrop over
++ *
++ * // new
++ * 3 = ADC error
++ * 12610 = Divident to get 2*7.33139
++ * 860 = Divider to get 2*7.33139
++ * 170 = Voltagedrop over
++ */
++// #define CALIBRATE_BATTERY(a) ((((a + 21)*12600)/860) + 170)
++#define CALIBRATE_BATTERY(a) ((((a + 3)*12610)/860) + 170)
++
++/*
++ * We have two types of batteries a small and a large one
++ * To get the right value we to distinguish between those two
++ * 450 Units == 15 V
++ */
++#ifdef SMALL_BATTERY
++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 51)
++#define MIN_SUPPLY 8500 /* Less then 8.5V means no powersupply */
++#else
++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 45)
++//#define MIN_SUPPLY 14000 /* Less then 14V means no powersupply */
++#define MIN_SUPPLY 12000 /* Less then 12V means no powersupply */
++#endif
++
++/*
++ * Charging Current
++ * if value is >= 50 then charging is on
++ */
++// #define CALIBRATE_CHARGING(a) (((a) * 1000) / (152/4))
++#define CALIBRATE_CHARGING(a) (a)
++//#define CHARGING_LED_LEVEL 50
++
++#ifdef CONFIG_SA1100_SIMPAD_SINUSPAD
++
++// type small battery
++#define CHARGING_LED_LEVEL 12
++#define CHARGING_MAX_LEVEL 120
++#define BATT_FULL 8100
++#define BATT_LOW 7300
++#define BATT_CRITICAL 6700
++#define BATT_EMPTY 6400
++
++#else // CONFIG_SA1100_SIMPAD_SINUSPAD
++
++// type large battery
++// because of ADC error CHARGING_LED_LEVEL can changed
++// from 27 to 28
++#define CHARGING_LED_LEVEL 27
++#define CHARGING_MAX_LEVEL 265
++// BATT_FULL with SIMPAD_AC_STATUS_AC_OFFLINE
++#define BATT_FULL 8100
++#define BATT_LOW 7400
++#define BATT_CRITICAL 6800
++#define BATT_EMPTY 6500
++
++#endif // CONFIG_SA1100_SIMPAD_SINUSPAD
++
++// int simpad_get_battery(struct simpad_battery_apm *bstat);
++
++#endif
+diff -Nur linux-2.6.24.vanilla/include/asm-arm/arch-sa1100/simpad_pm.h linux-2.6.24/include/asm-arm/arch-sa1100/simpad_pm.h
+--- linux-2.6.24.vanilla/include/asm-arm/arch-sa1100/simpad_pm.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/include/asm-arm/arch-sa1100/simpad_pm.h 2008-02-20 21:27:39.000000000 +0100
+@@ -0,0 +1,236 @@
++/*
++* Abstraction interface for microcontroller connection to rest of system
++*
++* Copyright 2003 Peter Pregler
++* Copyright 2000,1 Compaq Computer Corporation.
++*
++* Use consistent with the GNU GPL is permitted,
++* provided that this copyright notice is
++* preserved in its entirety in all copies and derived works.
++*
++* COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
++* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
++* FITNESS FOR ANY PARTICULAR PURPOSE.
++*
++* Author: Peter Pregler (based on work for ipaq by Andrew Christian)
++*
++*/
++
++#ifndef __SIMPAD_HAL_H
++#define __SIMPAD_HAL_H
++
++#include <linux/apm-emulation.h>
++
++struct simpad_battery_apm {
++ unsigned char ac_status; /* line connected yes/no */
++ unsigned char status; /* battery loading yes/no */
++ unsigned char percentage; /* percentage loaded */
++ unsigned short life; /* life till empty */
++};
++
++extern void simpad_apm_get_power_status(struct apm_power_info *);
++
++// extern int simpad_get_battery(struct simpad_battery_apm *bstat);
++
++/* These should match the apm_bios.h definitions */
++#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00
++#define SIMPAD_AC_STATUS_AC_ONLINE 0x01
++#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */
++#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff
++
++
++/* These bitfields are rarely "or'd" together */
++#define SIMPAD_BATT_STATUS_HIGH 0x01
++#define SIMPAD_BATT_STATUS_LOW 0x02
++#define SIMPAD_BATT_STATUS_CRITICAL 0x04
++#define SIMPAD_BATT_STATUS_CHARGING 0x08
++#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10
++#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */
++#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */
++#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */
++#define SIMPAD_BATT_STATUS_NOBATT 0x80
++#define SIMPAD_BATT_STATUS_UNKNOWN 0xff
++
++#if 0 // FIXME
++#include <linux/simpad_ts.h>
++
++enum simpad_asset_type {
++ ASSET_TCHAR = 0,
++ ASSET_SHORT,
++ ASSET_LONG
++};
++
++#define TTYPE(_type) (((unsigned int)_type) << 8)
++#define TCHAR(_len) (TTYPE(ASSET_TCHAR) | (_len))
++#define TSHORT TTYPE(ASSET_SHORT)
++#define TLONG TTYPE(ASSET_LONG)
++#define ASSET(_type,_num) ((((unsigned int)_type)<<16) | (_num))
++
++#define ASSET_HM_VERSION ASSET( TCHAR(10), 0 ) /* 1.1, 1.2 */
++#define ASSET_SERIAL_NUMBER ASSET( TCHAR(40), 1 ) /* Unique iPAQ serial number */
++#define ASSET_MODULE_ID ASSET( TCHAR(20), 2 ) /* E.g., "iPAQ 3700" */
++#define ASSET_PRODUCT_REVISION ASSET( TCHAR(10), 3 ) /* 1.0, 2.0 */
++#define ASSET_PRODUCT_ID ASSET( TSHORT, 4 ) /* 2 = Palm-sized computer */
++#define ASSET_FRAME_RATE ASSET( TSHORT, 5 )
++#define ASSET_PAGE_MODE ASSET( TSHORT, 6 ) /* 0 = Flash memory */
++#define ASSET_COUNTRY_ID ASSET( TSHORT, 7 ) /* 0 = USA */
++#define ASSET_IS_COLOR_DISPLAY ASSET( TSHORT, 8 ) /* Boolean, 1 = yes */
++#define ASSET_ROM_SIZE ASSET( TSHORT, 9 ) /* 16, 32 */
++#define ASSET_RAM_SIZE ASSET( TSHORT, 10 ) /* 32768 */
++#define ASSET_HORIZONTAL_PIXELS ASSET( TSHORT, 11 ) /* 240 */
++#define ASSET_VERTICAL_PIXELS ASSET( TSHORT, 12 ) /* 320 */
++
++#define ASSET_TYPE(_asset) (((_asset)&0xff000000)>>24)
++#define ASSET_TCHAR_LEN(_asset) (((_asset)&0x00ff0000)>>16)
++#define ASSET_NUMBER(_asset) ((_asset)&0x0000ffff)
++
++#define MAX_TCHAR_LEN 40
++
++struct simpad_asset {
++ unsigned int type;
++ union {
++ unsigned char tchar[ MAX_TCHAR_LEN ];
++ unsigned short vshort;
++ unsigned long vlong;
++ } a;
++};
++
++/********************************************************************
++ * Interface to the hardware-type specific functions
++ *
++ * get_version Read the version number of the microcontroller on the option pack SPI bus
++ * spi_read Reads from the serial EEPROM memory on the option pack SPI bus
++ * spi_write Write to the serial EEPROM memory on the option pack SPI bus
++ * get_option_detect Returns whether or not an option pack is present
++ *
++ * get_thermal_sensor Return measured temperature of the unit, in units of 0.125 deg C
++ * set_notify_led Turns on, off, or blinks the Green LED
++ * read_light_sensor Returns the value of the front light sensor
++ * get_battery Returns the current voltage and charging state of all batteries
++ * audio_clock Sets the audio CODEC to run at a particular rate
++ * audio_power Turns on/off audio CODEC (internally calls audio_clock)
++ * audio_mute Mutes the audio CODEC
++ * asset_read Extracts PocketPC-style asset information from persistent memory
++ * backlight_control Adjusts the backlight level (only on/off for 3100)
++ *
++ *
++ * iPAQ 3100 only
++ * ==============
++ * codec_control Reset/mute/control level of 3100 audio codec
++ * contrast_control Adjusts the contrast level (only for 3100)
++ *
++ * iPAQ 3600, 3700 only
++ * ====================
++ * eeprom_read Reads from the asset information on the eeprom of a 3600 (deprecated)
++ * eeprom_write Writes to the asset information on the eeprom (deprecated)
++ *
++ * The interfaces to the EEPROM functions are maintained only because the simpad_ts driver has
++ * a deprecated ioctl call for them. They are used internally by the "asset_read" function.
++ *
++ * iPAQ 3800, 3900 only
++ * ====================
++ * set_ebat Tells enhanced PCMCIA sleeves that this iPAQ can handle
++ * a wider voltage range (applies to 3800, 3900)
++ *
++ *********************************************************************/
++
++struct simpad_hal_ops {
++ /* Functions provided by the underlying hardware */
++ int (*get_version)( struct simpad_ts_version * );
++ int (*eeprom_read)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*eeprom_write)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*get_thermal_sensor)( unsigned short * );
++ int (*set_notify_led)( unsigned char mode, unsigned char duration,
++ unsigned char ontime, unsigned char offtime );
++ int (*read_light_sensor)( unsigned char *result );
++ int (*get_battery)( struct simpad_battery * );
++ int (*spi_read)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*spi_write)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*codec_control)( unsigned char, unsigned char );
++ int (*get_option_detect)( int *result );
++ int (*audio_clock)( long samplerate );
++ int (*audio_power)( long samplerate );
++ int (*audio_mute)( int mute );
++ int (*asset_read)( struct simpad_asset *asset );
++ int (*set_ebat)( void );
++
++ /* Functions indirectly provided by the underlying hardware */
++ int (*backlight_control)( enum flite_pwr power, unsigned char level );
++ int (*contrast_control)( unsigned char level );
++
++ /* for module use counting */
++ struct module *owner;
++};
++
++/* Used by the device-specific hardware module to register itself */
++extern int simpad_hal_register_interface( struct simpad_hal_ops *ops );
++extern void simpad_hal_unregister_interface( struct simpad_hal_ops *ops );
++
++/*
++ * Calls into HAL from the device-specific hardware module
++ * These run at interrupt time
++ */
++extern void simpad_hal_keypress( unsigned char key );
++extern void simpad_hal_touchpanel( unsigned short x, unsigned short y, int down );
++extern void simpad_hal_option_detect( int present );
++
++/* Callbacks registered by device drivers */
++struct simpad_driver_ops {
++ void (*keypress)( unsigned char key );
++ void (*touchpanel)( unsigned short x, unsigned short y, int down );
++ void (*option_detect)( int present );
++};
++
++extern int simpad_hal_register_driver( struct simpad_driver_ops * );
++extern void simpad_hal_unregister_driver( struct simpad_driver_ops * );
++
++
++/* Calls into HAL from device drivers and other kernel modules */
++extern void simpad_get_flite( struct simpad_ts_backlight *bl );
++extern void simpad_get_contrast( unsigned char *contrast );
++extern int simpad_set_flite( enum flite_pwr pwr, unsigned char brightness );
++extern int simpad_set_contrast( unsigned char contrast );
++extern int simpad_toggle_frontlight( void );
++
++extern int simpad_apm_get_power_status(unsigned char *ac_line_status, unsigned char *battery_status,
++ unsigned char *battery_flag, unsigned char *battery_percentage,
++ unsigned short *battery_life);
++
++extern struct simpad_hal_ops *simpad_hal_ops;
++
++/* Do not use this macro in driver files - instead, use the inline functions defined below */
++#define CALL_HAL( f, args... ) \
++ { int __result = -EIO; \
++ if ( simpad_hal_ops && simpad_hal_ops->f ) { \
++ __MOD_INC_USE_COUNT(simpad_hal_ops->owner); \
++ __result = simpad_hal_ops->f(args); \
++ __MOD_DEC_USE_COUNT(simpad_hal_ops->owner); \
++ } \
++ return __result; }
++
++#define HFUNC static __inline__ int
++
++/* The eeprom_read/write address + len has a maximum value of 512. Both must be even numbers */
++HFUNC simpad_eeprom_read( u16 addr, u8 *data, u16 len ) CALL_HAL(eeprom_read,addr,data,len)
++HFUNC simpad_eeprom_write( u16 addr, u8 *data, u16 len) CALL_HAL(eeprom_write,addr,data,len)
++HFUNC simpad_spi_read( u8 addr, u8 *data, u16 len) CALL_HAL(spi_read,addr,data,len)
++HFUNC simpad_spi_write( u8 addr, u8 *data, u16 len) CALL_HAL(spi_write,addr,data,len)
++HFUNC simpad_get_version( struct simpad_ts_version *v ) CALL_HAL(get_version,v)
++HFUNC simpad_get_thermal_sensor( u16 *thermal ) CALL_HAL(get_thermal_sensor,thermal)
++HFUNC simpad_set_led( u8 mode, u8 dur, u8 ont, u8 offt ) CALL_HAL(set_notify_led, mode, dur, ont, offt)
++HFUNC simpad_get_light_sensor( u8 *result ) CALL_HAL(read_light_sensor,result)
++HFUNC simpad_get_battery( struct simpad_battery *bat ) CALL_HAL(get_battery,bat)
++HFUNC simpad_get_option_detect( int *result) CALL_HAL(get_option_detect,result)
++HFUNC simpad_audio_clock( long samplerate ) CALL_HAL(audio_clock,samplerate)
++HFUNC simpad_audio_power( long samplerate ) CALL_HAL(audio_power,samplerate)
++HFUNC simpad_audio_mute( int mute ) CALL_HAL(audio_mute,mute)
++HFUNC simpad_asset_read( struct simpad_asset *asset ) CALL_HAL(asset_read,asset)
++HFUNC simpad_set_ebat( void ) CALL_HAL(set_ebat)
++
++/* Don't use these functions directly - rather, call {get,set}_{flite,contrast} */
++ /* Functions indirectly provided by the underlying hardware */
++HFUNC simpad_backlight_control( enum flite_pwr p, u8 v ) CALL_HAL(backlight_control,p,v)
++HFUNC simpad_contrast_control( u8 level ) CALL_HAL(contrast_control,level)
++
++#endif
++#endif
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-cs3-simpad.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-cs3-simpad.patch
new file mode 100644
index 0000000000..51232d55cd
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-cs3-simpad.patch
@@ -0,0 +1,184 @@
+diff -uNr linux-2.6.24.vanilla/arch/arm/mach-sa1100/Makefile linux-2.6.24/arch/arm/mach-sa1100/Makefile
+--- linux-2.6.24.vanilla/arch/arm/mach-sa1100/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/arch/arm/mach-sa1100/Makefile 2008-02-20 20:49:57.000000000 +0100
+@@ -41,6 +41,7 @@
+ obj-$(CONFIG_SA1100_SHANNON) += shannon.o
+
+ obj-$(CONFIG_SA1100_SIMPAD) += simpad.o
++obj-$(CONFIG_SA1100_SIMPAD) += cs3-simpad.o
+ led-$(CONFIG_SA1100_SIMPAD) += leds-simpad.o
+
+ # LEDs support
+diff -uNr linux-2.6.24.vanilla/arch/arm/mach-sa1100/cs3-simpad.c linux-2.6.24/arch/arm/mach-sa1100/cs3-simpad.c
+--- linux-2.6.24.vanilla/arch/arm/mach-sa1100/cs3-simpad.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/arch/arm/mach-sa1100/cs3-simpad.c 2008-02-20 20:49:57.000000000 +0100
+@@ -0,0 +1,169 @@
++/*
++ * simpad-cs3.c
++ *
++ * This driver shows the GPIO states of the cs3 latch. You can also
++ * switch some GPIOS.
++ *
++ * (c) 2007 Bernhard Guillon <Bernhard.Guillon@OpenSIMpad.org>
++ *
++ * You may use this code as per GPL version 2
++ *
++ * Some parts are based on battery.c
++ *
++ * mrdata: -added cs3_ro support
++ * -added preprocessor stuff
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/device.h>
++
++#include <asm/arch/simpad.h>
++
++extern long get_cs3_ro(void);
++extern long get_cs3_shadow(void);
++extern void set_cs3_bit(int value);
++extern void clear_cs3_bit(int value);
++
++struct cs3 {
++ struct class_device class_dev;
++ const char *name;
++ char *id;
++ int type;
++};
++
++struct cs3 cs3 ={
++ .name = "latch_cs3",
++};
++
++
++#define CS3_STORE_ATTR(namek,nameg) \
++static ssize_t namek##_store (struct class_device *cdev, const char * buf, size_t count) \
++{ \
++ char val; \
++ if (sscanf(buf, "%c",&val) != 1) \
++ return -EINVAL; \
++ if (val == '1') \
++ set_cs3_bit(nameg); \
++ else if (val == '0') \
++ clear_cs3_bit(nameg); \
++ return strlen(buf); \
++}
++
++CS3_STORE_ATTR(display_on, DISPLAY_ON);
++CS3_STORE_ATTR(dect_power_on, DECT_POWER_ON);
++CS3_STORE_ATTR(irda_sd, IRDA_SD);
++CS3_STORE_ATTR(sd_mediaq, SD_MEDIAQ);
++CS3_STORE_ATTR(led2_on, LED2_ON);
++CS3_STORE_ATTR(irda_mode, IRDA_MODE);
++CS3_STORE_ATTR(reset_simcard, RESET_SIMCARD);
++
++
++#define CS3_ATTR(shadro,namek,nameg,mode,store) \
++static ssize_t namek##_show(struct class_device *class_dev, char *buf) \
++{ \
++ if (get_cs3_##shadro() & nameg ) \
++ return sprintf(buf, "1\n"); \
++ else \
++ return sprintf(buf, "0\n"); \
++} \
++static CLASS_DEVICE_ATTR(namek, mode, namek##_show, store)
++
++CS3_ATTR(shadow, vcc_5v_en, VCC_5V_EN, 0444, NULL);
++CS3_ATTR(shadow, vcc_3v_en, VCC_3V_EN, 0444, NULL);
++CS3_ATTR(shadow, en1, EN1, 0444, NULL);
++CS3_ATTR(shadow, en0, EN0, 0444, NULL);
++CS3_ATTR(shadow, display_on, DISPLAY_ON, 0664, display_on_store);
++CS3_ATTR(shadow, pcmcia_buff_dis, PCMCIA_BUFF_DIS, 0444, NULL);
++CS3_ATTR(shadow, mq_reset, MQ_RESET, 0444, NULL);
++CS3_ATTR(shadow, pcmcia_reset, PCMCIA_RESET, 0444, NULL);
++CS3_ATTR(shadow, dect_power_on, DECT_POWER_ON, 0664, dect_power_on_store);
++CS3_ATTR(shadow, irda_sd, IRDA_SD, 0664, irda_sd_store);
++CS3_ATTR(shadow, rs232_on, RS232_ON, 0444, NULL);
++CS3_ATTR(shadow, sd_mediaq, SD_MEDIAQ, 0664, sd_mediaq_store);
++CS3_ATTR(shadow, led2_on, LED2_ON, 0664, led2_on_store);
++CS3_ATTR(shadow, irda_mode, IRDA_MODE, 0664, irda_mode_store);
++CS3_ATTR(shadow, enable_5v, ENABLE_5V, 0444, NULL);
++CS3_ATTR(shadow, reset_simcard, RESET_SIMCARD, 0664, reset_simcard_store);
++CS3_ATTR(ro, pcmcia_bvd1, PCMCIA_BVD1, 0444, NULL);
++CS3_ATTR(ro, pcmcia_bvd2, PCMCIA_BVD2, 0444, NULL);
++CS3_ATTR(ro, pcmcia_vs1, PCMCIA_VS1, 0444, NULL);
++CS3_ATTR(ro, pcmcia_vs2, PCMCIA_VS2, 0444, NULL);
++CS3_ATTR(ro, lock_ind, LOCK_IND, 0444, NULL);
++CS3_ATTR(ro, charging_state, CHARGING_STATE, 0444, NULL);
++CS3_ATTR(ro, pcmcia_short, PCMCIA_SHORT, 0444, NULL);
++
++static struct class simpad_gpios_class = {
++ .name = "simpad",
++};
++
++#define create_entry_conditional(namek) \
++ rc = class_device_create_file(&cs3->class_dev, &class_device_attr_##namek); \
++ if (rc) goto out; \
++
++static int register_cs3_latch(struct cs3 *cs3)
++{
++ int rc = 0;
++ cs3->class_dev.class = &simpad_gpios_class;
++ strcpy(cs3->class_dev.class_id, cs3->name);
++ rc = class_device_register(&cs3->class_dev);
++ if(rc)
++ goto out;
++
++ create_entry_conditional(vcc_5v_en);
++ create_entry_conditional(vcc_3v_en);
++ create_entry_conditional(en1);
++ create_entry_conditional(en0);
++ create_entry_conditional(display_on);
++ create_entry_conditional(pcmcia_buff_dis);
++ create_entry_conditional(mq_reset);
++ create_entry_conditional(pcmcia_reset);
++ create_entry_conditional(dect_power_on);
++ create_entry_conditional(irda_sd);
++ create_entry_conditional(rs232_on);
++ create_entry_conditional(sd_mediaq);
++ create_entry_conditional(led2_on);
++ create_entry_conditional(irda_mode);
++ create_entry_conditional(enable_5v);
++ create_entry_conditional(reset_simcard);
++ create_entry_conditional(pcmcia_bvd1);
++ create_entry_conditional(pcmcia_bvd2);
++ create_entry_conditional(pcmcia_vs1);
++ create_entry_conditional(pcmcia_vs2);
++ create_entry_conditional(lock_ind);
++ create_entry_conditional(charging_state);
++ create_entry_conditional(pcmcia_short);
++
++out:
++ return rc;
++}
++
++static int __init simpad_gpios_class_init(void)
++{
++ int rc = 0;
++
++ rc = class_register(&simpad_gpios_class);
++
++ if(rc != 0)
++ {
++ printk(KERN_ERR "cs3 latch class failed to register properly\n");
++ return rc;
++ }
++
++ rc = register_cs3_latch(&cs3);
++ return rc;
++}
++
++static void __exit simpad_gpios_class_exit(void)
++{
++ class_unregister(&simpad_gpios_class);
++}
++
++module_init(simpad_gpios_class_init);
++module_exit(simpad_gpios_class_exit);
++
++MODULE_AUTHOR("Bernhard Guillon");
++MODULE_DESCRIPTION("CS3_latch driver");
++MODULE_LICENSE("GPL");
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-hostap_cs-shared-irq.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-hostap_cs-shared-irq.patch
new file mode 100644
index 0000000000..758ddae7f7
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-hostap_cs-shared-irq.patch
@@ -0,0 +1,106 @@
+diff -Nur linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.24/drivers/net/wireless/hostap/hostap_cs.c
+--- linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_cs.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/net/wireless/hostap/hostap_cs.c 2008-02-28 12:00:27.000000000 +0100
+@@ -1,3 +1,8 @@
++/*
++ *
++ * mrdata: -added shared irq
++ */
++
+ #define PRISM2_PCCARD
+
+ #include <linux/module.h>
+@@ -35,7 +40,7 @@
+ module_param(ignore_cis_vcc, int, 0444);
+ MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry");
+
+-
++int activar=0;
+ /* struct local_info::hw_priv */
+ struct hostap_cs_priv {
+ dev_node_t node;
+@@ -499,11 +504,13 @@
+
+ PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info);
+ p_dev->conf.IntType = INT_MEMORY_AND_IO;
+-
++
++ activar=0;
+ ret = prism2_config(p_dev);
+ if (ret) {
+ PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n");
+ }
++ activar=1;
+
+ return ret;
+ }
+@@ -693,7 +700,7 @@
+ * irq structure is initialized.
+ */
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = prism2_interrupt;
+ link->irq.Instance = dev;
+diff -Nur linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_hw.c linux-2.6.24/drivers/net/wireless/hostap/hostap_hw.c
+--- linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_hw.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/net/wireless/hostap/hostap_hw.c 2008-02-28 12:00:27.000000000 +0100
+@@ -54,6 +54,7 @@
+ #include "hostap.h"
+ #include "hostap_ap.h"
+
++extern int activar;
+
+ /* #define final_version */
+
+@@ -1497,6 +1498,8 @@
+ if (local->hw_downloading)
+ return 1;
+
++ activar=1;
++
+ if (prism2_hw_init(dev, initial)) {
+ return local->no_pri ? 0 : 1;
+ }
+@@ -2630,8 +2633,15 @@
+ int events = 0;
+ u16 ev;
+
+- iface = netdev_priv(dev);
+- local = iface->local;
++
++ // Todos los parametros de entrada son correctos (no son nulos). De momento esta es la unica forma que conozco de detectar el problema.
++ if (!activar) {
++ printk("hostap_hw.c: INTERRUPT BEFORE DEVICE INIT!\n");
++ return IRQ_HANDLED;
++ }
++
++ iface = netdev_priv(dev);
++ local = iface->local;
+
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0);
+
+diff -Nur linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_pci.c linux-2.6.24/drivers/net/wireless/hostap/hostap_pci.c
+--- linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_pci.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/net/wireless/hostap/hostap_pci.c 2008-02-28 12:00:27.000000000 +0100
+@@ -19,6 +19,7 @@
+
+ #include "hostap_wlan.h"
+
++int activar=1;
+
+ static char *dev_info = "hostap_pci";
+
+diff -Nur linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_plx.c linux-2.6.24/drivers/net/wireless/hostap/hostap_plx.c
+--- linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_plx.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/net/wireless/hostap/hostap_plx.c 2008-02-28 12:00:27.000000000 +0100
+@@ -21,7 +21,7 @@
+ #include <asm/io.h>
+
+ #include "hostap_wlan.h"
+-
++int activar=1;
+
+ static char *dev_info = "hostap_plx";
+
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-mq200.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-mq200.patch
new file mode 100644
index 0000000000..ac9fdfbfcf
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-mq200.patch
@@ -0,0 +1,2513 @@
+diff -Nur linux-2.6.24.vanilla/drivers/video/Kconfig linux-2.6.24/drivers/video/Kconfig
+--- linux-2.6.24.vanilla/drivers/video/Kconfig 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/video/Kconfig 2008-02-20 21:51:22.000000000 +0100
+@@ -198,7 +198,7 @@
+ This is particularly important to one driver, matroxfb. If
+ unsure, say N.
+
+-comment "Frame buffer hardware drivers"
++comment "Frambuffer hardware drivers"
+ depends on FB
+
+ config FB_CIRRUS
+@@ -1389,6 +1389,15 @@
+ ---help---
+ Driver for graphics boards with S3 Trio / S3 Virge chip.
+
++config FB_MQ200
++ bool "MQ200 Driver"
++ depends on (FB = y) && ARM && ARCH_SA1100
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ select FB_CFB_IMAGEBLIT
++ help
++ This is a MQ200 driver tested only on Siemens SIMpads.
++
+ config FB_SAVAGE
+ tristate "S3 Savage support"
+ depends on FB && PCI && EXPERIMENTAL
+diff -Nur linux-2.6.24.vanilla/drivers/video/Makefile linux-2.6.24/drivers/video/Makefile
+--- linux-2.6.24.vanilla/drivers/video/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/video/Makefile 2008-02-20 21:51:22.000000000 +0100
+@@ -35,6 +35,7 @@
+ obj-$(CONFIG_FB_PM2) += pm2fb.o
+ obj-$(CONFIG_FB_PM3) += pm3fb.o
+
++obj-$(CONFIG_FB_MQ200) += mq200/
+ obj-$(CONFIG_FB_MATROX) += matrox/
+ obj-$(CONFIG_FB_RIVA) += riva/
+ obj-$(CONFIG_FB_NVIDIA) += nvidia/
+diff -Nur linux-2.6.24.vanilla/drivers/video/backlight/Kconfig linux-2.6.24/drivers/video/backlight/Kconfig
+--- linux-2.6.24.vanilla/drivers/video/backlight/Kconfig 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/video/backlight/Kconfig 2008-02-20 21:51:22.000000000 +0100
+@@ -90,3 +90,20 @@
+ help
+ If you have a Intel LE80578 (Carillo Ranch) say Y to enable the
+ backlight driver.
++
++config BACKLIGHT_SIMPAD
++ tristate "SIMpad MQ200 Backlight driver"
++ depends on SA1100_SIMPAD && BACKLIGHT_CLASS_DEVICE
++ default y
++ help
++ If you have a Siemens SIMpad say Y to enable the
++ backlight driver.
++
++config LCD_SIMPAD
++ tristate "SIMpad MQ200 LCD driver"
++ depends on SA1100_SIMPAD && LCD_CLASS_DEVICE
++ default y
++ help
++ If you have a Siemens SIMpad say Y to enable the
++ LCD driver.
++
+diff -Nur linux-2.6.24.vanilla/drivers/video/backlight/Makefile linux-2.6.24/drivers/video/backlight/Makefile
+--- linux-2.6.24.vanilla/drivers/video/backlight/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/video/backlight/Makefile 2008-02-20 21:51:22.000000000 +0100
+@@ -9,3 +9,5 @@
+ obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
+ obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
+ obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
++obj-$(CONFIG_BACKLIGHT_SIMPAD) += simpad_bl.o
++obj-$(CONFIG_LCD_SIMPAD) += simpad_lcd.o
+diff -Nur linux-2.6.24.vanilla/drivers/video/backlight/simpad_bl.c linux-2.6.24/drivers/video/backlight/simpad_bl.c
+--- linux-2.6.24.vanilla/drivers/video/backlight/simpad_bl.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/video/backlight/simpad_bl.c 2008-02-20 21:51:22.000000000 +0100
+@@ -0,0 +1,208 @@
++/*
++ * GPLv2 <zecke@handhelds.org
++ *
++ * Implementation of the backlight_driver for
++ * the mq200 framebuffer
++ *
++ * 2007/03/17 mrdata:
++ * - small changes simpad_bl_get_brightness()
++ * simpad_bl_set_brightness()
++ * - new function simpad_bl_update_status()
++ * - changed struct backlight_properties simpad_bl_props()
++ * to new one
++ * - changed __init simpad_bl_init() -> backlight_device_register
++ *
++ * 2007/03/24 mrdata
++ * - added .brightness=127 in
++ * struct backlight_properties simpad_bl_props()
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++#include <linux/fb.h>
++#include <linux/backlight.h>
++
++#include <asm/types.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++#include "../mq200/mq200_data.h"
++
++#define SIMPAD_BACKLIGHT_MASK 0x00a10044
++#define SIMPAD_DEFAULT_INTENSITY 127
++#define SIMPAD_MAX_INTENSITY 254
++#define REGISTER_BASE 0xf2e00000
++
++static int simpad_bl_suspended;
++static int current_intensity = 0;
++
++static void simpad_bl_send_intensity(struct backlight_device *bd)
++{
++ int intensity = bd->props.brightness;
++
++ union fp0fr fp0fr;
++ unsigned long dutyCycle, pwmcontrol;
++
++ if (intensity > SIMPAD_MAX_INTENSITY)
++ intensity = SIMPAD_MAX_INTENSITY;
++
++ if (bd->props.power != FB_BLANK_UNBLANK)
++ intensity = 0;
++
++ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
++ intensity = 0;
++
++ if (simpad_bl_suspended)
++ intensity = 0;
++
++ if (intensity != current_intensity)
++ {
++ /*
++ * Determine dutyCycle.
++ * Note: the lower the value, the brighter the display!
++ */
++
++ dutyCycle = SIMPAD_MAX_INTENSITY - intensity;
++
++ /*
++ * Configure PWM0 (source clock = oscillator clock, pwm always enabled,
++ * zero, clock pre-divider = 4) pwm frequency = 12.0kHz
++ */
++
++ fp0fr.whole = readl(FP0FR(REGISTER_BASE));
++ pwmcontrol = fp0fr.whole & 0xffff00ff;
++ fp0fr.whole &= 0xffffff00;
++ fp0fr.whole |= 0x00000044;
++ writel(fp0fr.whole, FP0FR(REGISTER_BASE));
++
++ /* Write to pwm duty cycle register. */
++ fp0fr.whole = dutyCycle << 8;
++ fp0fr.whole &= 0x0000ff00;
++ fp0fr.whole |= pwmcontrol;
++ writel(fp0fr.whole, FP0FR(REGISTER_BASE));
++
++ current_intensity = intensity;
++ }
++}
++
++
++#ifdef CONFIG_PM
++static int simpad_bl_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct backlight_device *bd = platform_get_drvdata(pdev);
++
++ simpad_bl_suspended = 1;
++ simpad_bl_send_intensity(bd);
++ return 0;
++}
++
++static int simpad_bl_resume(struct platform_device *pdev)
++{
++ struct backlight_device *bd = platform_get_drvdata(pdev);
++
++ simpad_bl_suspended = 0;
++ simpad_bl_send_intensity(bd);
++ return 0;
++}
++#else
++#define simpad_bl_suspend NULL
++#define simpad_bl_resume NULL
++#endif
++
++
++static int simpad_bl_set_intensity(struct backlight_device *bd)
++{
++ simpad_bl_send_intensity(bd);
++ return 0;
++}
++
++
++static int simpad_bl_get_intensity(struct backlight_device *bd)
++{
++ return current_intensity;
++}
++
++
++static struct backlight_ops simpad_bl_ops = {
++ .get_brightness = simpad_bl_get_intensity,
++ .update_status = simpad_bl_set_intensity,
++};
++
++
++static int __init simpad_bl_probe(struct platform_device *pdev)
++{
++ struct backlight_device *bd;
++
++ bd = backlight_device_register("simpad-mq200-bl", &pdev->dev, NULL, &simpad_bl_ops);
++
++ if (IS_ERR (bd))
++ return PTR_ERR (bd);
++
++ platform_set_drvdata(pdev, bd);
++
++ bd->props.max_brightness = SIMPAD_MAX_INTENSITY;
++ bd->props.brightness = SIMPAD_DEFAULT_INTENSITY;
++ simpad_bl_send_intensity(bd);
++
++ return 0;
++}
++
++
++static int simpad_bl_remove(struct platform_device *pdev)
++{
++ struct backlight_device *bd = platform_get_drvdata(pdev);
++
++ bd->props.brightness = 0;
++ bd->props.power = 0;
++ simpad_bl_send_intensity(bd);
++
++ backlight_device_unregister(bd);
++
++ return 0;
++}
++
++static struct platform_driver simpad_bl_driver = {
++ .probe = simpad_bl_probe,
++ .remove = simpad_bl_remove,
++ .suspend = simpad_bl_suspend,
++ .resume = simpad_bl_resume,
++ .driver = {
++ .name = "simpad-mq200-bl",
++ },
++};
++
++static struct platform_device *simpad_bl_device = NULL;
++
++static int __init simpad_bl_init(void)
++{
++ int ret;
++
++ ret = platform_driver_register(&simpad_bl_driver);
++ if (!ret) {
++ simpad_bl_device = platform_device_alloc("simpad-mq200-bl", -1);
++ if (!simpad_bl_device)
++ return -ENOMEM;
++
++ ret = platform_device_add(simpad_bl_device);
++
++ if (ret) {
++ platform_device_put(simpad_bl_device);
++ platform_driver_unregister(&simpad_bl_driver);
++ }
++ }
++ return ret;
++}
++
++static void __exit simpad_bl_exit(void)
++{
++ platform_device_unregister(simpad_bl_device);
++ platform_driver_unregister(&simpad_bl_driver);
++}
++
++
++module_init(simpad_bl_init);
++module_exit(simpad_bl_exit);
++MODULE_AUTHOR("Holger Hans Peter Freyther");
++MODULE_LICENSE("GPL");
+diff -Nur linux-2.6.24.vanilla/drivers/video/backlight/simpad_lcd.c linux-2.6.24/drivers/video/backlight/simpad_lcd.c
+--- linux-2.6.24.vanilla/drivers/video/backlight/simpad_lcd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/video/backlight/simpad_lcd.c 2008-02-20 21:51:22.000000000 +0100
+@@ -0,0 +1,172 @@
++/*
++ * GPLv2 <zecke@handhelds.org
++ *
++ * Implementation of the lcd_driver for the mq200 framebuffer
++ *
++ * 2007/03/24 mrdata:
++ * - added simpad_lcd_get_contrast()
++ * - added simpad_lcd_set_contrast()
++ * - modify struct lcd_properties simpad_lcd_props
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/fb.h>
++#include <linux/lcd.h>
++
++#include <asm/arch/simpad.h>
++#include <asm/hardware.h>
++
++extern long get_cs3_shadow(void);
++extern void set_cs3_bit(int);
++extern void clear_cs3_bit(int);
++
++#define UNUSED(x) x=x
++
++static int simpad_lcd_get_power(struct lcd_device* dev)
++{
++ UNUSED(dev);
++
++ return (get_cs3_shadow() & DISPLAY_ON) ? 0 : 4;
++}
++
++static int simpad_lcd_set_power(struct lcd_device* dev, int power)
++{
++ UNUSED(dev);
++
++ if( power == 4 )
++ clear_cs3_bit(DISPLAY_ON);
++ else
++ set_cs3_bit(DISPLAY_ON);
++
++ return 0;
++}
++
++static int simpad_lcd_get_contrast(struct lcd_device* dev)
++{
++ UNUSED(dev);
++
++ return 0;
++}
++
++static int simpad_lcd_set_contrast(struct lcd_device* dev, int contrast)
++{
++ UNUSED(dev);
++
++ UNUSED(contrast);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int simpad_lcd_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ static int ret;
++ struct lcd_device* ld;
++
++ UNUSED(state);
++
++ ld = platform_get_drvdata(pdev);
++
++ ret = simpad_lcd_set_power(ld, 4);
++
++ return ret;
++}
++
++static int simpad_lcd_resume(struct platform_device *pdev)
++{
++ struct lcd_device *ld;
++ static int ret;
++
++ ld = platform_get_drvdata(pdev);
++
++ ret = simpad_lcd_set_power(ld, 0);
++
++ return ret;
++}
++#else
++#define simpad_lcd_suspend NULL
++#define simpad_lcd_resume NULL
++#endif
++
++
++/*FIXME
++static struct lcd_properties simpad_lcd_props = {
++ .max_contrast = 0,
++};
++*/
++
++static struct lcd_ops simpad_lcd_ops = {
++ .get_power = simpad_lcd_get_power,
++ .set_power = simpad_lcd_set_power,
++ .get_contrast = simpad_lcd_get_contrast,
++ .set_contrast = simpad_lcd_set_contrast,
++};
++
++static int __init simpad_lcd_probe(struct platform_device *pdev)
++{
++ struct lcd_device *ld;
++
++ ld = lcd_device_register ("simpad-mq200-lcd", &pdev->dev, NULL, &simpad_lcd_ops);
++
++ if (IS_ERR(ld))
++ return PTR_ERR(ld);
++
++ platform_set_drvdata(pdev, ld);
++
++ ld->props.max_contrast = 0;
++
++ return 0;
++}
++
++static int simpad_lcd_remove(struct platform_device *pdev)
++{
++ struct lcd_device *ld = platform_get_drvdata(pdev);
++
++ lcd_device_unregister(ld);
++
++ return 0;
++}
++
++static struct platform_driver simpad_lcd_driver = {
++ .probe = simpad_lcd_probe,
++ .remove = simpad_lcd_remove,
++ .suspend = simpad_lcd_suspend,
++ .resume = simpad_lcd_resume,
++ .driver = {
++ .name = "simpad-mq200-lcd",
++ },
++};
++
++static struct platform_device *simpad_lcd_device = NULL;
++
++static int __init simpad_lcd_init(void)
++{
++ int ret;
++
++ ret = platform_driver_register(&simpad_lcd_driver);
++ if (!ret) {
++ simpad_lcd_device = platform_device_alloc("simpad-mq200-lcd", -1);
++ if (!simpad_lcd_device)
++ return -ENOMEM;
++
++ ret = platform_device_add(simpad_lcd_device);
++
++ if (ret) {
++ platform_device_put(simpad_lcd_device);
++ platform_driver_unregister(&simpad_lcd_driver);
++ }
++ }
++ return ret;
++}
++
++static void __exit simpad_lcd_exit(void) {
++ platform_driver_unregister(&simpad_lcd_driver);
++ platform_device_unregister(simpad_lcd_device);
++}
++
++module_init(simpad_lcd_init);
++module_exit(simpad_lcd_exit);
++MODULE_AUTHOR("Holger Hans Peter Freyther");
++MODULE_LICENSE("GPL");
+diff -Nur linux-2.6.24.vanilla/drivers/video/mq200/Makefile linux-2.6.24/drivers/video/mq200/Makefile
+--- linux-2.6.24.vanilla/drivers/video/mq200/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/video/mq200/Makefile 2008-02-20 21:51:22.000000000 +0100
+@@ -0,0 +1,6 @@
++# Makefile for mq200 video driver
++# 4 Aug 2003, Holger Hans Peter Freyther
++#
++
++obj-$(CONFIG_FB_MQ200) += mq_skeleton.o mq_external.o
++
+diff -Nur linux-2.6.24.vanilla/drivers/video/mq200/mq200_data.h linux-2.6.24/drivers/video/mq200/mq200_data.h
+--- linux-2.6.24.vanilla/drivers/video/mq200/mq200_data.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/video/mq200/mq200_data.h 2008-02-20 21:51:22.000000000 +0100
+@@ -0,0 +1,1120 @@
++/*
++ * From ucLinux mq200fb.c and mq200fb.h
++ *
++ * 2007/03/11 mrdata:
++ * insert registers for graphics controller 2 module
++ */
++
++#ifndef __MQ200_FB_H__
++#define __MQ200_FB_H__
++
++struct mq200_io_regions {
++ u32 fb_size; /* framebuffer size */
++ unsigned long phys_mmio_base; /* physical register memory base */
++ unsigned long virt_mmio_base; /* virtual start of registers */
++ unsigned long phys_fb_base; /* physical address of frame buffer */
++ unsigned long virt_fb_base; /* virtual start of the framebuffer */
++};
++
++#define MQ200_MONITOR_HORI_RES(info) info->monitor_info.horizontal_res
++#define MQ200_MONITOR_VERT_RES(info) info->monitor_info.vertical_res
++#define MQ200_MONITOR_DEPTH(info) info->monitor_info.depth
++#define MQ200_MONITOR_LINE_LENGTH(info) info->monitor_info.line_length
++
++struct mq200_monitor_info {
++ unsigned int horizontal_res;
++ unsigned int vertical_res;
++ unsigned int depth;
++ unsigned int refresh;
++ unsigned int line_length;
++ unsigned long flags;
++};
++
++
++/**
++ * Addresses of Module
++ */
++#define MQ200_FB_BASE (x) (x + 0x1800000) /* framebuffer */
++#define MQ200_FB_SIZE 0x200000 /* framebuffer size in bytes */
++#define MQ200_REGS_BASE(x) (x + 0x1e00000) /* start of registers area */
++#define MQ200_REGS_SIZE 0x200000 /* registers area size */
++
++#define PMU_OFFSET 0x00000 /* power management */
++#define CPU_OFFSET 0x02000 /* CPU interface */
++#define MIU_OFFSET 0x04000 /* memory controller */
++#define IN_OFFSET 0x08000 /* interrupt controller */
++#define GC_OFFSET 0x0a000 /* graphics controller 1&2 */
++#define GE_OFFSET 0x0c000 /* graphics engine */
++#define FPI_OFFSET 0x0e000 /* flat panel controller */
++#define CP1_OFFSET 0x10000 /* color palette 1 */
++#define DC_OFFSET 0x14000 /* device configuration */
++#define PCI_OFFSET 0x16000 /* PCI configuration */
++#define PSF_OFFSET 0x18000 /* ??? */
++
++
++/****
++ * Registers
++ */
++
++/* power management unit */
++#define PMR(addr) (addr + PCI_OFFSET + 0x40)/* power management
++ register */
++#define PMR_VALUE 0x06210001 /* expected read value of PMR register */
++#define PM00R(addr) (addr + PMU_OFFSET + 0x00) /* power management unit
++ configuration
++ register */
++#define PM01R(addr) (addr + PMU_OFFSET + 0x04) /* D1 state control */
++#define PM02R(addr) (addr + PMU_OFFSET + 0x08) /* d2 state control */
++#define PM06R(addr) (addr + PMU_OFFSET + 0x18) /* PLL 2 programming */
++#define PM07R(addr) (addr + PMU_OFFSET + 0x1c) /* PLL 3 programming */
++
++#define PMCSR(addr) (addr + PCI_OFFSET + 0x44) /* power management
++ control/status
++ register */
++
++/* memory interface unit */
++#define MM00R(addr) (addr + MIU_OFFSET + 0x00)/* MIU interface control
++ 0 */
++#define MM01R(addr) (addr + MIU_OFFSET + 0x04) /* MIU interface control
++ 1 */
++#define MM02R(addr) (addr + MIU_OFFSET + 0x08) /* memory interface
++ control 2 */
++#define MM03R(addr) (addr + MIU_OFFSET + 0x0c) /* memory interface
++ control 3 */
++#define MM04R(addr) (addr + MIU_OFFSET + 0x10) /* memory interface
++ control 4 */
++/* graphics controller 1 module */
++#define GC00R(addr) (addr + GC_OFFSET + 0x00) /* graphics controller 1
++ control */
++#define GC01R(addr) (addr + GC_OFFSET + 0x04) /* graphics controller
++ CRT control */
++#define GC02R(addr) (addr + GC_OFFSET + 0x08) /* horizontal display 1
++ control */
++#define GC03R(addr) (addr + GC_OFFSET + 0x0c) /* vertical display 1
++ control */
++#define GC04R(addr) (addr + GC_OFFSET + 0x10) /* horizontal sync 1
++ control */
++#define GC05R(addr) (addr + GC_OFFSET + 0x14) /* vertical sync 1
++ control */
++#define GC07R(addr) (addr + GC_OFFSET + 0x1c) /* vertical display 1
++ count */
++#define GC08R(addr) (addr + GC_OFFSET + 0x20) /* horizontal window 1
++ control */
++#define GC09R(addr) (addr + GC_OFFSET + 0x24) /* vertical window 1
++ control */
++#define GC0AR(addr) (addr + GC_OFFSET + 0x28) /* alternate horizontal
++ window 1 control */
++#define GC0BR(addr) (addr + GC_OFFSET + 0x2c) /* alternate vertical
++ window 1 control */
++#define GC0CR(addr) (addr + GC_OFFSET + 0x30) /* window 1
++ start address */
++#define GC0DR(addr) (addr + GC_OFFSET + 0x34) /* alternate window 1
++ start address */
++#define GC0ER(addr) (addr + GC_OFFSET + 0x38) /* alternate window 1
++ stride */
++#define GC0FR(addr) (addr + GC_OFFSET + 0x3c) /* alternate window 1
++ line size */
++#define GC10R(addr) (addr + GC_OFFSET + 0x40) /* hardware cursor 1
++ position */
++#define GC11R(addr) (addr + GC_OFFSET + 0x44) /* hardware cursor 1
++ start address and
++ offset */
++#define GC12R(addr) (addr + GC_OFFSET + 0x48) /* hardware cursor 1
++ foreground color */
++#define GC13R(addr) (addr + GC_OFFSET + 0x4c) /* hardware cursor 1
++ background color */
++
++/* graphics controller 2 module */
++#define GC20R(addr) (addr + GC_OFFSET + 0x80) /* graphics controller 2
++ control */
++#define GC21R(addr) (addr + GC_OFFSET + 0x84) /* graphics controller
++ CRC control */
++#define GC22R(addr) (addr + GC_OFFSET + 0x88) /* horizontal display 2
++ control */
++#define GC23R(addr) (addr + GC_OFFSET + 0x8c) /* vertical display 2
++ control */
++#define GC24R(addr) (addr + GC_OFFSET + 0x90) /* horizontal sync 2
++ control */
++#define GC25R(addr) (addr + GC_OFFSET + 0x94) /* vertical sync 2
++ control */
++#define GC27R(addr) (addr + GC_OFFSET + 0x9c) /* vertical display 2
++ count */
++#define GC28R(addr) (addr + GC_OFFSET + 0xa0) /* horizontal window 2
++ control */
++#define GC29R(addr) (addr + GC_OFFSET + 0xa4) /* vertical window 2
++ control */
++#define GC2AR(addr) (addr + GC_OFFSET + 0xa8) /* alternate horizontal
++ window 2 control */
++#define GC2BR(addr) (addr + GC_OFFSET + 0xac) /* alternate vertical
++ window 2 control */
++#define GC2CR(addr) (addr + GC_OFFSET + 0xb0) /* window 2
++ start address */
++#define GC2DR(addr) (addr + GC_OFFSET + 0xb4) /* alternate window 2
++ start address */
++#define GC2ER(addr) (addr + GC_OFFSET + 0xb8) /* alternate window 2
++ stride */
++#define GC2FR(addr) (addr + GC_OFFSET + 0xbc) /* alternate window 2
++ line size */
++#define GC30R(addr) (addr + GC_OFFSET + 0xc0) /* hardware cursor 2
++ position */
++#define GC31R(addr) (addr + GC_OFFSET + 0xc4) /* hardware cursor 2
++ start address and
++ offset */
++#define GC32R(addr) (addr + GC_OFFSET + 0xc8) /* hardware cursor 2
++ foreground color */
++#define GC33R(addr) (addr + GC_OFFSET + 0xcc) /* hardware cursor 2
++ background color */
++
++/* graphics engine */
++#define ROP_SRCCOPY 0xCC /* dest = source */
++#define ROP_SRCPAINT 0xEE /* dest = source OR dest */
++#define ROP_SRCAND 0x88 /* dest = source AND dest */
++#define ROP_SRCINVERT 0x66 /* dest = source XOR dest */
++#define ROP_SRCERASE 0x44 /* dest = source AND (NOT dest) */
++#define ROP_NOTSRCCOPY 0x33 /* dest = NOT source */
++#define ROP_NOTSRCERASE 0x11 /* dest = (NOT source) AND (NOT dest) */
++#define ROP_MERGECOPY 0xC0 /* dest = source AND pattern */
++#define ROP_MERGEPAINT 0xBB /* dest = (NOT source) OR dest */
++#define ROP_PATCOPY 0xF0 /* dest = pattern */
++#define ROP_PATPAINT 0xFB /* dest = DPSnoo */
++#define ROP_PATINVERT 0x5A /* dest = pattern XOR dest */
++#define ROP_DSTINVERT 0x55 /* dest = NOT dest */
++#define ROP_BLACKNESS 0x00 /* dest = BLACK */
++#define ROP_WHITENESS 0xFF /* dest = WHITE */
++
++#define GE00R(addr) (addr + GE_OFFSET + 0x00) /* primary drawing command
++ register */
++#define GE01R(addr) (addr + GE_OFFSET + 0x04) /* primary width and
++ height register */
++#define GE02R(addr) (addr + GE_OFFSET + 0x08) /* primary destination
++ address register */
++#define GE03R(addr) (addr + GE_OFFSET + 0x0c) /* primary source XY
++ register */
++#define GE04R(addr) (addr + GE_OFFSET + 0x10) /* primary color compare
++ register */
++#define GE05R(addr) (addr + GE_OFFSET + 0x14) /* primary clip left/top
++ register */
++#define GE06R(addr) (addr + GE_OFFSET + 0x18) /* primary clip
++ right/bottom register
++ */
++#define GE07R(addr) (addr + GE_OFFSET + 0x1c) /* primary source and
++ pattern offset
++ register */
++#define GE08R(addr) (addr + GE_OFFSET + 0x20) /* primary foreground
++ color
++ register/rectangle
++ fill register */
++#define GE09R(addr) (addr + GE_OFFSET + 0x24) /* source stride/offset
++ register */
++#define GE0AR(addr) (addr + GE_OFFSET + 0x28) /* destination stride
++ register and color
++ depth */
++#define GE0BR(addr) (addr + GE_OFFSET + 0x2c) /* image base address
++ register */
++#define GE40R(addr) (addr + GE_OFFSET + 0x100) /* mono pattern register
++ 0 */
++#define GE41R(addr) (addr + GE_OFFSET + 0x104) /* mono pattern register
++ 1 */
++#define GE42R(addr) (addr + GE_OFFSET + 0x108) /* foreground color
++ register */
++#define GE43R(addr) (addr + GE_OFFSET + 0x10c) /* background color
++ register */
++/* color palette */
++#define C1xxR(addr, regno) \
++ (addr + CP1_OFFSET + (regno) * 4) /* graphics controller color
++ palette 1 */
++/* device configuration */
++#define DC00R(addr) (addr + DC_OFFSET + 0x00) /* device configuration
++ register 0 */
++#define DC_RESET 0x4000
++/* PCI configuration space */
++#define PC00R(addr) (addr + PCI_OFFSET + 0x00)/* device ID/vendor ID
++ register */
++/* Flatpanel Control */
++#define FP00R(addr) (addr + FPI_OFFSET + 0x00) /* Flat Panel Control 0 */
++#define FP01R(addr) (addr + FPI_OFFSET + 0x04) /* Flat Panel Output Pin */
++#define FP02R(addr) (addr + FPI_OFFSET + 0x08) /* Flat Panel Gener Purpose
++ Outout Control Register */
++#define FP03R(addr) (addr + FPI_OFFSET + 0x0c) /* General Purpose I/O Port
++ Control Register */
++#define FP04R(addr) (addr + FPI_OFFSET + 0x10) /* STN Panel Control Register */
++#define FP05R(addr) (addr + FPI_OFFSET + 0x14) /* D-STN Half Frame Buffer
++ Control Register -By Guess */
++#define FP0FR(addr) (addr + FPI_OFFSET + 0x3c) /* Pulse Width Modulation
++ Control Register */
++#define FRCTL_PATTERN_COUNT 32
++#define FP10R(addr) (addr + FPI_OFFSET + 0x40) /* Frame-Rate Control Pattern
++ Register */
++#define FP11R(addr) (addr + FPI_OFFSET + 0x44)
++#define FP2FR(addr) (addr + FPI_OFFSET + 0xc0) /* Frame-Rate Control Weight
++ Registers */
++
++
++
++
++/* power management miscellaneous control */
++union pm00r {
++ struct {
++ u32 pll1_n_b5 :1; /* PLL 1 N parameter bit 5 is 0 */
++ u32 reserved_1 :1;
++ u32 pll2_enbl :1; /* PLL 2 enable */
++ u32 pll3_enbl :1; /* PLL 3 enable */
++ u32 reserved_2 :1;
++ u32 pwr_st_ctrl :1; /* power state status control */
++ u32 reserved_3 :2;
++
++ u32 ge_enbl :1; /* graphics engine enable */
++ u32 ge_bsy_gl :1; /* graphics engine force busy (global) */
++ u32 ge_bsy_lcl :1; /* graphics engine force busy (local) */
++ u32 ge_clock :2; /* graphics engine clock select */
++ u32 ge_cmd_fifo :1; /* graphics engine command FIFO reset */
++ u32 ge_src_fifo :1; /* graphics engine CPU source FIFO reset */
++ u32 miu_pwr_seq :1; /* memory interface unit power sequencing
++ enable */
++
++ u32 d3_mem_rfsh :1; /* D3 memory refresh */
++ u32 d4_mem_rfsh :1; /* D4 memory refresh */
++ u32 gpwr_intrvl :2; /* general power sequencing interval */
++ u32 fppwr_intrvl:2; /* flat panel power sequencing interval */
++ u32 gpwr_seq_ctr:1; /* general power sequencing interval control */
++ u32 pmu_tm :1; /* PMU test mode */
++
++ u32 pwr_state :2; /* power state (read only) */
++ u32 pwr_seq_st :1; /* power sequencing active status (read
++ only) */
++ u32 reserved_4 :5;
++ } part;
++ u32 whole;
++};
++
++/* D1 state control */
++union pm01r {
++ struct {
++ u32 osc_enbl :1; /* D1 oscillator enable */
++ u32 pll1_enbl :1; /* D1 PLL 1 enable */
++ u32 pll2_enbl :1; /* D1 PLL 2 enable */
++ u32 pll3_enbl :1; /* D1 PLL 3 enable */
++ u32 miu_enbl :1; /* D1 Memory Interface Unit (MIU) enable */
++ u32 mem_rfsh :1; /* D1 memory refresh enable */
++ u32 ge_enbl :1; /* D1 Graphics Engine (GE) enable */
++ u32 reserved_1 :1;
++
++ u32 crt_enbl :1; /* D1 CRT enable */
++ u32 fpd_enbl :1; /* D1 Flat Panel enable */
++ u32 reserved_2 :6;
++
++ u32 ctl1_enbl :1; /* D1 controller 1 enable */
++ u32 win1_enbl :1; /* D1 window 1 enable */
++ u32 awin1_enbl :1; /* D1 alternate window 1 enable */
++ u32 cur1_enbl :1; /* D1 cursor 1 enable */
++ u32 reserved_3 :4;
++
++ u32 ctl2_enbl :1; /* D1 controller 2 enable */
++ u32 win2_enbl :1; /* D1 window 2 enable */
++ u32 awin2_enbl :1; /* D1 alternate window 2 enable */
++ u32 cur2_enbl :1; /* D1 cursor 2 enable */
++ u32 reserved_4 :4;
++ } part;
++ u32 whole;
++};
++
++/* D2 state control */
++union pm02r {
++ struct {
++ u32 osc_enbl :1; /* D2 oscillator enable */
++ u32 pll1_enbl :1; /* D2 PLL 1 enable */
++ u32 pll2_enbl :1; /* D2 PLL 2 enable */
++ u32 pll3_enbl :1; /* D2 PLL 3 enable */
++ u32 miu_enbl :1; /* D2 Memory Interface Unit (MIU) enable */
++ u32 mem_rfsh :1; /* D2 memory refresh enable */
++ u32 ge_enbl :1; /* D2 Graphics Engine (GE) enable */
++ u32 reserved_1 :1;
++
++ u32 crt_enbl :1; /* D2 CRT enable */
++ u32 fpd_enbl :1; /* D2 Flat Panel enable */
++ u32 reserved_2 :6;
++
++ u32 ctl1_enbl :1; /* D2 controller 1 enable */
++ u32 win1_enbl :1; /* D2 window 1 enable */
++ u32 awin1_enbl :1; /* D2 alternate window 1 enable */
++ u32 cur1_enbl :1; /* D2 cursor 1 enable */
++ u32 reserved_3 :4;
++
++ u32 ctl2_enbl :1; /* D2 controller 2 enable */
++ u32 win2_enbl :1; /* D2 window 2 enable */
++ u32 awin2_enbl :1; /* D2 alternate window 2 enable */
++ u32 cur2_enbl :1; /* D2 cursor 2 enable */
++ u32 reserved_4 :4;
++ } part;
++ u32 whole;
++};
++
++/* PLL 2 programming */
++union pm06r {
++ struct {
++ u32 clk_src :1; /* PLL 2 reference clock source */
++ u32 bypass :1; /* PLL 2 bypass */
++ u32 reserved_1 :2;
++ u32 p_par :3; /* PLL 2 P parameter */
++ u32 reserved_2 :1;
++
++ u32 n_par :5; /* PLL 2 N parameter */
++ u32 reserved_3 :3;
++
++ u32 m_par :8; /* PLL 2 M parameter */
++
++ u32 reserved_4 :4;
++ u32 trim :4; /* PLL 2 trim value */
++ } part;
++ u32 whole;
++};
++
++/* PLL 3 programming */
++union pm07r {
++ struct {
++ u32 clk_src :1; /* PLL 3 reference clock source */
++ u32 bypass :1; /* PLL 3 bypass */
++ u32 reserved_1 :2;
++ u32 p_par :3; /* PLL 3 P parameter */
++ u32 reserved_2 :1;
++
++ u32 n_par :5; /* PLL 3 N parameter */
++ u32 reserved_3 :3;
++
++ u32 m_par :8; /* PLL 3 M parameter */
++
++ u32 reserved_4 :4;
++ u32 trim :4; /* PLL 3 trim value */
++ } part;
++ u32 whole;
++};
++
++
++
++/* MIU interface control 1 */
++union mm00r {
++ struct {
++ u32 miu_enbl :1; /* MIU enable bit */
++ u32 mr_dsbl :1; /* MIU reset disable bit */
++ u32 edr_dsbl :1; /* embedded DRAM reset disable bit */
++ u32 reserved_1 :29;
++ } part;
++ u32 whole;
++};
++
++/* MIU interface control 2 */
++union mm01r {
++ struct {
++ u32 mc_src :1; /* memory clock source */
++ u32 msr_enbl :1; /* memory slow refresh enable bit */
++ u32 pb_cpu :1; /* page break enable for CPU */
++ u32 pb_gc1 :1; /* page break enable for GC1 */
++ u32 pb_gc2 :1; /* page break enable for GC2 */
++ u32 pb_stn_r :1; /* page break enable for STN read */
++ u32 pb_stn_w :1; /* page break enable for STN write */
++ u32 pb_ge :1; /* page break enable for GE */
++ u32 reserved_1 :4;
++ u32 mr_interval :14; /* normal memory refresh time interval */
++ u32 reserved_2 :4;
++ u32 edarm_enbl :1; /* embedded DRAM auto-refresh mode enable */
++ u32 eds_enbl :1; /* EDRAM standby enable for EDRAM normal
++ mode operation */
++ } part;
++ u32 whole;
++};
++
++/* memory interface control 3 */
++union mm02r {
++ struct {
++ u32 bs_ :2;
++ u32 bs_stnr :2; /* burst count for STN read memory cycles */
++ u32 bs_stnw :2; /* burst count for STN write memroy cycles */
++ u32 bs_ge :2; /* burst count for graphics engine
++ read/write memroy cycles */
++ u32 bs_cpuw :2; /* burst count for CPU write memory cycles */
++ u32 fifo_gc1 :4; /* GC1 display refresh FIFO threshold */
++ u32 fifo_gc2 :4; /* GC2 display refresh FIFO threshold */
++ u32 fifo_stnr :4; /* STN read FIFO threshold */
++ u32 fifo_stnw :4; /* STN write FIFO threshold */
++ u32 fifo_ge_src :3; /* GE source read FIFO threshold */
++ u32 fifo_ge_dst :3; /* GE destination read FIFO threshold */
++ } part;
++ u32 whole;
++};
++
++/* memory interface control 4 */
++union mm03r {
++ struct {
++ u32 rd_late_req :1; /* read latency request */
++ u32 reserved_1 :31;
++ } part;
++ u32 whole;
++};
++
++/* memory interface control 5 */
++union mm04r {
++ struct {
++ u32 latency :3; /* EDRAM latency */
++ u32 dmm_cyc :1; /* enable for the dummy cycle insertion
++ between read and write cycles */
++ u32 pre_dmm_cyc :1; /* enable for the dummy cycle insertion
++ between read/write and precharge cycles
++ for the same bank */
++ u32 reserved_1 :3;
++ u32 bnk_act_cls :2; /* bank activate command to bank close
++ command timing interval control */
++ u32 bnk_act_rw :1; /* bank activate command to read/wirte
++ command timing interval control */
++ u32 bnk_cls_act :1; /* bank close command to bank activate
++ command timing interval control */
++ u32 trc :1; /* row cycle time */
++ u32 reserved_2 :3;
++ u32 delay_r :2; /* programmable delay for read clock */
++ u32 delay_m :2; /* programmable delay for internal memory
++ clock */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller 1 register */
++union gc00r {
++ struct {
++ u32 ctl_enbl :1; /* Controller 1 Enable */
++ u32 hc_reset :1; /* Horizontal Counter 1 Reset */
++ u32 vc_reset :1; /* Vertical Counter 1 Reset */
++ u32 iwin_enbl :1; /* Image Window 1 Enable */
++ u32 gcd :4; /* Graphics Color Depth (GCD) */
++
++ u32 hc_enbl :1; /* Hardware Cursor 1 Enable */
++ u32 reserved_1 :2;
++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */
++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */
++
++ u32 g1rclk_src :2; /* G1RCLK Source */
++ u32 tm0 :1; /* Test Mode 0 */
++ u32 tm1 :1; /* Test Mode 1 */
++ u32 fd :3; /* G1MCLK First Clock Divisor (FD1) */
++ u32 reserved_2 :1;
++
++ u32 sd :8; /* G1MCLK Second Clock Divisor (SD1) */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller CRT control */
++union gc01r {
++ struct {
++ u32 dac_enbl :2; /* CRT DAC enable */
++ u32 hsync_out :1; /* CRT HSYNC output during power down mode */
++ u32 vsync_out :1; /* CRT VSYNC output during power down mode */
++ u32 hsync_ctl :2; /* CRT HSYNC control */
++ u32 vsync_ctl :2; /* CRT VSYNC control */
++ /**/
++ u32 hsync_pol :1; /* CRT HSYNC polarity */
++ u32 vsync_pol :1; /* CRT VSYNC polarity */
++ u32 sync_p_enbl :1; /* sync pedestal enable */
++ u32 blnk_p_enbl :1; /* blank pedestal enable */
++ u32 c_sync_enbl :1; /* composite sync enable */
++ u32 vref_sel :1; /* VREF select */
++ u32 mn_sns_enbl :1; /* monitor sense enable */
++ u32 ct_out_enbl :1; /* constant output enable */
++ /**/
++ u32 dac_out_lvl :8; /* monitor sense DAC output level */
++ /**/
++ u32 blue_dac_r :1; /* blue DAC sense result */
++ u32 green_dac_r :1; /* green DAC sense result */
++ u32 red_dac_r :1; /* red DAC sense result */
++ u32 reserved_1 :1;
++ u32 mon_col_sel :1; /* mono/color monitor select */
++ u32 reserved_2 :3;
++ } part;
++ u32 whole;
++};
++
++/* horizontal display 1 control */
++union gc02r {
++ struct {
++ u32 hd1t :12; /* horizontal display 1 total */
++ u32 reserved_1 :4;
++
++ u32 hd1e :12; /* horizontal display 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 1 control */
++union gc03r {
++ struct {
++ u32 vd1t :12; /* vertical display 1 total */
++ u32 reserved_1 :4;
++
++ u32 vd1e :12; /* vertical display 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* horizontal sync 1 control */
++union gc04r {
++ struct {
++ u32 hs1s :12; /* horizontal sync 1 start */
++ u32 reserved_1 :4;
++
++ u32 hs1e :12; /* horizontal sync 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical sync 1 control */
++union gc05r {
++ struct {
++ u32 vs1s :12; /* vertical sync 1 start */
++ u32 reserved_1 :4;
++
++ u32 vs1e :12; /* vertical sync 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 1 count */
++union gc07r {
++ struct {
++ u32 vd_cnt :12; /* vertical display 1 count */
++ u32 reverved_1 :20;
++ } part;
++ u32 whole;
++};
++
++/* horizontal window 1 control */
++union gc08r {
++ struct {
++ u32 hw1s :12; /* horizontal window 1 start (HW1S) */
++ u32 reserved_1 :4;
++
++ u32 hw1w :12; /* horizontal window 1 width (HW1W) */
++ u32 w1ald :4; /* window 1 additional line data */
++ } part;
++ u32 whole;
++};
++
++/* vertical window 1 control */
++union gc09r {
++ struct {
++ u32 vw1s :12; /* vertical window 1 start */
++ u32 reserved_1 :4;
++ u32 vw1h :12; /* vertical window 1 height */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* window 1 start address */
++union gc0cr {
++ struct {
++ u32 w1sa :21; /* window 1 start address */
++ u32 reserved_1 :11;
++ } part;
++ u32 whole;
++};
++
++/* window 1 stride */
++union gc0er {
++ struct {
++ s16 w1st; /* window 1 stride */
++ s16 aw1st; /* alternate window 1 stride */
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 position */
++union gc10r {
++ struct {
++ u32 hc1s :12; /* horizontal cursor 1 start */
++ u32 reserved_1 :4;
++ u32 vc1s :12; /* vertical cursor 1 start */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 start address and offset */
++union gc11r {
++ struct {
++ u32 hc1sa :11; /* hardware cursor 1 start address */
++ u32 reserved_1 :5;
++ u32 hc1o :6; /* horizontal cursor 1 offset */
++ u32 reserved_2 :2;
++ u32 vc1o :6; /* vertical cursor 1 offset */
++ u32 reserved_3 :2;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 foreground color */
++union gc12r {
++ struct {
++ u32 hc1fc :24; /* hardware cursor 1 foreground color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 background color */
++union gc13r {
++ struct {
++ u32 hc1bc :24; /* hardware cursor 1 background color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++
++/* graphics controller 2 register */
++union gc20r {
++ struct {
++ u32 ctl_enbl :1; /* Controller 2 Enable */
++ u32 hc_reset :1; /* Horizontal Counter 2 Reset */
++ u32 vc_reset :1; /* Vertical Counter 2 Reset */
++ u32 iwin_enbl :1; /* Image Window 2 Enable */
++ u32 gcd :4; /* Graphics Color Depth (GCD) */
++
++ u32 hc_enbl :1; /* Hardware Cursor 2 Enable */
++ u32 reserved_1 :2;
++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */
++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */
++
++ u32 g2rclk_src :2; /* G2RCLK Source */
++ u32 tm0 :1; /* Test Mode 0 */
++ u32 tm1 :1; /* Test Mode 1 */
++ u32 fd :3; /* G2MCLK First Clock Divisor (FD1) */
++ u32 reserved_2 :1;
++
++ u32 sd :8; /* G2MCLK Second Clock Divisor (SD1) */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller CRC control */
++union gc21r {
++ struct {
++ u32 crc_enbl :1; /* CRC enable */
++ u32 vsync_wait :1; /* CRC input data control waitime of VSYNC */
++ u32 crc_o_sel :2; /* CRC output select */
++ u32 reserved_1 :4;
++ u32 crc_result :22; /* CRC result (read only) */
++ u32 reserved_2 :2;
++ } part;
++ u32 whole;
++};
++
++/* horizontal display 2 control */
++union gc22r {
++ struct {
++ u32 hd2t :12; /* horizontal display 2 total */
++ u32 reserved_1 :4;
++
++ u32 hd2e :12; /* horizontal display 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 2 control */
++union gc23r {
++ struct {
++ u32 vd2t :12; /* vertical display 2 total */
++ u32 reserved_1 :4;
++
++ u32 vd2e :12; /* vertical display 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* horizontal sync 2 control */
++union gc24r {
++ struct {
++ u32 hs2s :12; /* horizontal sync 2 start */
++ u32 reserved_1 :4;
++
++ u32 hs2e :12; /* horizontal sync 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical sync 2 control */
++union gc25r {
++ struct {
++ u32 vs2s :12; /* vertical sync 2 start */
++ u32 reserved_1 :4;
++
++ u32 vs2e :12; /* vertical sync 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 2 count */
++union gc27r {
++ struct {
++ u32 vd_cnt :12; /* vertical display 2 count */
++ u32 reverved_1 :20;
++ } part;
++ u32 whole;
++};
++
++/* horizontal window 2 control */
++union gc28r {
++ struct {
++ u32 hw2s :12; /* horizontal window 2 start (HW2S) */
++ u32 reserved_1 :4;
++
++ u32 hw2w :12; /* horizontal window 2 width (HW2W) */
++ u32 w2ald :4; /* window 2 additional line data */
++ } part;
++ u32 whole;
++};
++
++/* vertical window 2 control */
++union gc29r {
++ struct {
++ u32 vw2s :12; /* vertical window 2 start */
++ u32 reserved_1 :4;
++ u32 vw2h :12; /* vertical window 2 height */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* window 2 start address */
++union gc2cr {
++ struct {
++ u32 w2sa :21; /* window 2 start address */
++ u32 reserved_1 :11;
++ } part;
++ u32 whole;
++};
++
++/* window 2 stride */
++union gc2er {
++ struct {
++ s16 w2st; /* window 2 stride */
++ s16 aw2st; /* alternate window 2 stride */
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 position */
++union gc30r {
++ struct {
++ u32 hc2s :12; /* horizontal cursor 2 start */
++ u32 reserved_1 :4;
++ u32 vc2s :12; /* vertical cursor 2 start */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 start address and offset */
++union gc31r {
++ struct {
++ u32 hc2sa :11; /* hardware cursor 2 start address */
++ u32 reserved_1 :5;
++ u32 hc2o :6; /* horizontal cursor 2 offset */
++ u32 reserved_2 :2;
++ u32 vc2o :6; /* vertical cursor 2 offset */
++ u32 reserved_3 :2;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 foreground color */
++union gc32r {
++ struct {
++ u32 hc2fc :24; /* hardware cursor 2 foreground color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 background color */
++union gc33r {
++ struct {
++ u32 hc2bc :24; /* hardware cursor 2 background color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++
++/* primary drawing command register */
++union ge00r {
++ struct {
++ u32 rop :8; /* raster operation */
++ /**/
++ u32 cmd_typ :3; /* command type */
++ u32 x_dir :1; /* x direction */
++ u32 y_dir :1; /* y direction */
++ u32 src_mem :1; /* source memory */
++ u32 mon_src :1; /* mono source */
++ u32 mon_ptn :1; /* mono pattern */
++ /**/
++ u32 dst_trns_e :1; /* destination transparency enable */
++ u32 dst_trns_p :1; /* destination transparency polarity */
++ u32 mon_trns_e :1; /* mono source or mono pattern transparency
++ enable */
++ u32 mon_trns_p :1; /* mono transparency polarity */
++ u32 mod_sel :1; /* memory to screen or off screen to screen
++ mode select */
++ u32 alpha_sel :2; /* Alpha byte mask selection */
++ u32 sol_col :1; /* solid color */
++ /**/
++ u32 stride_eq :1; /* source stride is equal to destination
++ stride */
++ u32 rop2_sel :1; /* ROP2 code selection */
++ u32 clipping :1; /* enable clipping */
++ u32 auto_exec :1; /* auto execute */
++ u32 reserved_1 :4;
++ } part;
++ u32 whole;
++};
++
++/* primary width and height register */
++union ge01r {
++ struct {
++ u32 width :12; /* source/destination window width */
++ u32 reserved_1 :4;
++
++ u32 height :12; /* source/destination window height */
++ u32 reserved_2 :1;
++ u32 reserved_3 :3;
++ } bitblt;
++ struct {
++ u32 dm :17;
++ u32 axis_major :12;
++ u32 x_y :1; /* x-major or y-major */
++ u32 last_pix :1; /* decision to draw or not to draw the last
++ pixel of the line */
++ u32 reserved_1 :1;
++ } bresenham;
++ u32 whole;
++};
++
++/* primary destination address register */
++union ge02r {
++ struct {
++ u32 dst_x :12; /* destination x position */
++ u32 reserved_1 :1;
++ u32 h_offset :3; /* mono/color pattern horizontal offset */
++
++ u32 dst_y :12; /* destination y position */
++ u32 reserved_2 :1;
++ u32 v_offset :3; /* mono/color pattern vertical offset */
++ } window;
++ struct {
++ u32 x :12; /* starting x coordinate */
++ u32 dm :17; /* 17 bits major-axis delta */
++ u32 reserved_1 :3;
++ } line;
++ u32 whole;
++};
++
++/* source XY register/line draw starting Y coordinate and mintor axis delta */
++union ge03r {
++ struct {
++ u32 src_x :12; /* source X position */
++ u32 reserved_1 :4;
++
++ u32 src_y :12; /* source Y position */
++ u32 reserved_2 :4;
++ } window;
++ struct {
++ u32 start_y :12; /* starting Y coordinate */
++ u32 dn :17; /* 17 bits minor-axis delta */
++ u32 reserved_1 :3;
++ } line;
++ u32 whole;
++};
++
++/* clip left/top register */
++union ge05r {
++ struct {
++ u32 left :12; /* left edge of clipping rectangle */
++ u32 reserved_1 :4;
++
++ u32 top :12; /* top edge of clipping rectangle */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* source stride/offset register */
++union ge09r {
++ struct {
++ u32 src_strid :12; /* source line stride */
++ u32 reserved_1 :13;
++ u32 strt_bit :3; /* initial mono source bit offset */
++ u32 strt_byte :3; /* initial mono/color source byte offset */
++ u32 reserved_2 :1;
++ } line;
++ struct {
++ u32 strt_bit :5; /* initial mono source bit offset */
++ u32 reserved_1 :1;
++ u32 amount :10; /* number of 16 bytes amount that MIU need
++ to fetch from frame buffer */
++
++ u32 reserved_2 :9;
++ u32 bit_spc :7; /* bit space between lines */
++ } pack_mono;
++ struct {
++ u32 strt_bit :3; /* initial mono source bit offset */
++ u32 strt_byte :3; /* initial mono/color source byte offset */
++ u32 amount :10; /* number of 16 bytes amount that MIU need
++ to fetch from frame buffer */
++
++ u32 reserved_1 :9;
++ u32 bit_spc :3; /* bit space between lines */
++ u32 byt_spc :4; /* byte space between lines */
++ } pack_color;
++ u32 whole;
++};
++
++/* destination stride register and color depth */
++union ge0ar {
++ struct {
++ u32 dst_strid :12; /* destination line stride and color depth */
++ u32 reserved_1 :18;
++ u32 col_dpth :2; /* color depth */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller color pallete */
++union c1xxr {
++ struct {
++ u8 red; /* red color pallete */
++ u8 green; /* green/gray color pallete */
++ u8 blue; /* blue color palette */
++ u8 reserved_1;
++ } part;
++ u32 whole;
++};
++
++/* devicee configuration register 0 */
++union dc00r {
++ struct {
++ u32 osc_bypass :1; /* oscillator bypass */
++ u32 osc_enbl :1; /* oscillator enable */
++ u32 pll1_bypass :1; /* PLL1 bypass */
++ u32 pll1_enbl :1; /* PLL1 enable */
++ u32 pll1_p_par :3; /* PLL1 P parameter */
++ u32 cpu_div :1; /* CPU interface clock divisor */
++ u32 pll1_n_par :5; /* PLL1 N parameter */
++ u32 saisc :1; /* StrongARM interface synchronizer control */
++ u32 s_chp_reset :1; /* software chip reset */
++ u32 mem_enbl :1; /* memory standby enable */
++ u32 pll1_m_par :8; /* PLL 1 M parameter */
++ u32 osc_shaper :1; /* oscillator shaper disable */
++ u32 fast_pwr :1; /* fast power sequencing */
++ u32 osc_frq :2; /* oscillator frequency select */
++ u32 pll1_trim :4; /* PLL 1 trim value */
++ } part;
++ u32 whole;
++};
++
++/* device ID/vendor ID register */
++union pc00r {
++ struct {
++ u16 device; /* device ID */
++ u16 vendor; /* vendor ID */
++ } part;
++ u32 whole;
++};
++
++/* Flat Panel Control Register */
++union fp00r {
++ struct {
++ u32 flatp_enbl : 2; /* Flat Panel Interface Enable */
++ u32 flatp_type : 2; /* Flat Panel Type */
++ u32 mono : 1; /* Mono/Color Panel Select */
++ u32 flatp_intf : 3; /* Flat Panel Interface */
++ u32 dither_pat : 2; /* Dither Pattern */
++ u32 reserved : 2; /* Reserved Must Be 0*/
++ u32 dither_col : 3; /* Dither Base Color */
++ u32 alt_win_ctl: 1; /* Alternate Window Control */
++ u32 frc_ctl : 2; /* FRC Control */
++ u32 dither_adj1: 6; /* Dither Pattern Adjust 1 */
++ u32 dither_adj2: 3; /* Dither Pattern Adjust 2 */
++ u32 dither_adj3: 1; /* Dither Pattern Adjust 3 */
++ u32 test_mode0 : 1; /* Test Mode 0 */
++ u32 test_mode1 : 1; /* Test Mode 1 */
++ u32 test_mode2 : 1; /* Test Mode 2 */
++ u32 test_mode3 : 1; /* Test Mode 3 */
++ } part;
++ u32 whole;
++};
++
++union fp01r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp02r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp03r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp04r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp05r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp0fr {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++
++
++
++/****
++ * Others
++ */
++
++#define CHIPNAME "MQ-200"
++
++extern void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr);
++extern void mq200_external_setqmode(struct mq200_monitor_info*, unsigned long, spinlock_t *);
++extern void mq200_external_offdisplay(unsigned long);
++extern void mq200_external_ondisplay (unsigned long);
++extern int mq200_external_probe(unsigned long);
++
++
++
++#endif
+diff -Nur linux-2.6.24.vanilla/drivers/video/mq200/mq_external.c linux-2.6.24/drivers/video/mq200/mq_external.c
+--- linux-2.6.24.vanilla/drivers/video/mq200/mq_external.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/video/mq200/mq_external.c 2008-02-20 21:51:22.000000000 +0100
+@@ -0,0 +1,513 @@
++/*
++ * Copyright (C) 2005 Holger Hans Peter Freyther
++ *
++ * Based ON:
++ *
++ * linux/drivers/video/mq200fb.c -- MQ-200 for a frame buffer device
++ * based on linux/driver/video/pm2fb.c
++ *
++ * 2007/03/11 mrdata:
++ * bug found in gc1_reset(), renaming to gc1_gc2_reset()
++ * extend mq200_external_ondisplay() -> LCD for GC2 and CRT for GC1
++ *
++ * Copyright (C) 2000 Lineo, Japan
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive
++ * for more details.
++ */
++
++#include <asm/types.h>
++#include <asm/io.h>
++#include <linux/delay.h>
++#include <linux/spinlock.h>
++
++#include <asm/hardware.h>
++
++#include "mq200_data.h"
++
++
++#if 1
++#define PRINTK(args...) printk(args)
++#else
++#define PRINTK(args...)
++#endif
++
++
++/****
++ * power state transition to "state".
++ */
++static void
++power_state_transition(unsigned long register_base, int state)
++{
++ int i;
++ writel(state, PMCSR(register_base));
++ mdelay(300);
++ for (i = 1; ; i++) {
++ udelay(100);
++ if ((readl(PMCSR(register_base)) & 0x3) == state) {
++ break;
++ }
++ }
++}
++
++
++/****
++ * device configuration initialization.
++ */
++static void
++dc_reset(unsigned long register_base)
++{
++ union dc00r dc00r;
++
++ /* Reset First */
++ dc00r.whole = DC_RESET;
++ writel(dc00r.whole, DC00R(register_base));
++ mdelay(100);
++
++ dc00r.whole = 0xEF2082A;
++ writel(dc00r.whole, DC00R(register_base));
++ mdelay(300);
++ PRINTK(CHIPNAME ": DC00R = 0x%08X\n", readl(DC00R(register_base)));
++}
++
++
++/****
++ * initialize memory interface unit.
++ */
++static void
++miu_reset(unsigned long register_base)
++{
++ union mm00r mm00r;
++ union mm01r mm01r;
++ union mm02r mm02r;
++ union mm03r mm03r;
++ union mm04r mm04r;
++
++ /* MIU interface control 1 */
++ mm00r.whole = 0x4;
++ writel(mm00r.whole, MM00R(register_base));
++ mdelay(50);
++ writel(0, MM00R(register_base));
++ mdelay(50);
++
++ /* MIU interface control 2
++ * o PLL 1 output is used as memory clock source.
++ */
++ mm01r.whole = 0x4143e086;
++ writel(mm01r.whole, MM01R(register_base));
++
++ /* memory interface control 3 */
++ mm02r.whole = 0x6d6aabff;
++ writel(mm02r.whole, MM02R(register_base));
++
++ /* memory interface control 5 */
++ mm04r.whole = 0x10d;
++ writel(mm04r.whole, MM04R(register_base));
++
++ /* memory interface control 4 */
++ mm03r.whole = 0x1;
++ writel(mm03r.whole, MM03R(register_base));
++ mdelay(50);
++
++ /* MIU interface control 1 */
++ mm00r.whole = 0x3;
++ writel(mm00r.whole, MM00R(register_base));
++ mdelay(50);
++}
++
++/****
++ *
++ */
++static
++void fpctrl_reset(unsigned long addr)
++{
++ /*
++ * We're in D0 State, let us set the FPCTRL
++ */
++ union fp00r fp00r;
++ union fp01r fp01r;
++ union fp02r fp02r;
++ union fp03r fp03r;
++ union fp04r fp04r;
++ union fp0fr fp0fr;
++
++ fp00r.whole = 0x6320;
++ writel(fp00r.whole, FP00R(addr));
++
++ fp01r.whole = 0x20;
++ writel(fp01r.whole, FP01R(addr));
++
++ fp04r.whole = 0xBD0001;
++ writel(fp04r.whole, FP04R(addr));
++
++ /* Set Flat Panel General Purpose register first */
++ fp02r.whole = 0x0;
++ writel(fp02r.whole, FP02R(addr));
++
++ fp03r.whole = 0x0;
++ writel(fp03r.whole, FP03R(addr));
++
++ fp0fr.whole = 0xA16c44;
++ writel(fp0fr.whole, FP0FR(addr));
++
++ /* Set them again */
++ fp02r.whole = 0x0;
++ writel(fp02r.whole, FP02R(addr));
++
++ fp03r.whole = 0x0;
++ writel(fp03r.whole, FP03R(addr));
++}
++
++
++/****
++ * initialize power management unit.
++ */
++static void
++pmu_reset(unsigned long register_base)
++{
++ union pm00r pm00r;
++ union pm01r pm01r;
++ union pm02r pm02r;
++
++ /* power management miscellaneous control
++ * o GE is driven by PLL 1 clock.
++ */
++ pm00r.whole = 0xc0900;
++ writel(pm00r.whole, PM00R(register_base));
++
++ /* D1 state control */
++ pm01r.whole = 0x5000271;
++ writel(pm01r.whole, PM01R(register_base));
++
++ /* D2 state control */
++ pm02r.whole = 0x271;
++ writel(pm02r.whole, PM02R(register_base));
++}
++
++/****
++ * initialize graphics controller 1
++ * and graphics controller 2
++ */
++static void
++gc1_gc2_reset(unsigned long register_base, spinlock_t *lock )
++{
++ unsigned long flags;
++ union gc00r gc00r;
++ union gc01r gc01r;
++ union gc02r gc02r;
++ union gc03r gc03r;
++ union gc04r gc04r;
++ union gc05r gc05r;
++ union gc08r gc08r;
++ union gc09r gc09r;
++ union gc0cr gc0cr;
++ union gc0er gc0er;
++ union gc20r gc20r;
++ union gc22r gc22r;
++ union gc23r gc23r;
++ union gc24r gc24r;
++ union gc25r gc25r;
++ union gc28r gc28r;
++ union gc29r gc29r;
++ union gc2cr gc2cr;
++ union gc2er gc2er;
++
++ union pm00r pm00r;
++ union pm06r pm06r;
++ union pm06r pm07r;
++
++ spin_lock_irqsave(lock, flags);
++
++ /* alternate window 1 stride */
++ gc0er.whole = 0x640;
++ writel(gc0er.whole, GC0ER(register_base));
++
++ /* image window 1 start address */
++ gc0cr.whole = 0x0;
++ writel(gc0cr.whole, GC0CR(register_base));
++
++ /* alternate window 2 stride */
++ gc2er.whole = 0x640;
++ writel(gc0er.whole, GC2ER(register_base));
++
++ /* image window 2 start address */
++ gc2cr.whole = 0x0;
++ writel(gc2cr.whole, GC2CR(register_base));
++
++ /* read PM Register */
++ pm00r.whole = readl(PM00R(register_base));
++
++ /* horizontal window 1 control */
++ gc08r.whole = 0x131f0000;
++ writel(gc08r.whole, GC08R(register_base));
++
++ /* vertical window 1 control */
++ gc09r.whole = 0x12570000;
++ writel(gc09r.whole, GC09R(register_base));
++
++ /* horizontal display 1 control */
++ gc02r.whole = 0x320041e;
++ writel(gc02r.whole, GC02R(register_base));
++
++ /* vertical display 1 control */
++ gc03r.whole = 0x2570273;
++ writel(gc03r.whole, GC03R(register_base));
++
++ /* horizontal sync 1 control */
++ gc04r.whole = 0x3c70347;
++ writel(gc04r.whole, GC04R(register_base));
++
++ /* vertical sync 1 control */
++ gc05r.whole = 0x25d0259;
++ writel(gc05r.whole, GC05R(register_base));
++
++ /* graphics controller CRT control */
++ gc01r.whole = 0x800;
++ writel(gc01r.whole, GC01R(register_base));
++
++ /* PLL 2 programming */
++ pm06r.whole = 0xE90830;
++ writel(pm06r.whole, PM06R(register_base));
++
++ /* graphics controller 1 register
++ * o GC1 clock source is PLL 2.
++ * o hardware cursor is disabled.
++ */
++ gc00r.whole = 0x10000C8 | 0x20000;
++ writel(gc00r.whole, GC00R(register_base));
++
++#if 0
++ /* alternate horizontal window 1 control */
++ writel(0, GC0AR(register_base));
++
++ /* alternate vertical window 1 control */
++ writel(0, GC0BR(register_base));
++
++ /* window 1 start address */
++ writel(0x2004100, GC0CR(register_base));
++
++ /* alternate window 1 start address */
++ writel(0, GC0DR(register_base));
++
++ /* window 1 stride */
++ gc0er.whole = 0x5100048;
++ writel(gc0er.whole, GC0ER(register_base));
++
++ /* reserved register - ??? - */
++ writel(0x31f, GC0FR(register_base));
++#endif
++
++#if 0
++ /* hardware cursor 1 position */
++ writel(0, GC10R(register_base));
++
++ /* hardware cursor 1 start address and offset */
++ gc11r.whole = 0x5100048;
++ writel(gc11r.whole, GC11R(register_base));
++
++ /* hardware cursor 1 foreground color */
++ writel(0x00ffffff, GC12R(register_base));
++
++ /* hardware cursor 1 background color */
++ writel(0x00000000, GC13R(register_base));
++#endif
++
++ /* horizontal window 2 control */
++ gc28r.whole = 0x31f0000;
++ writel(gc28r.whole, GC28R(register_base));
++
++ /* vertical window 2 control */
++ gc29r.whole = 0x2570000;
++ writel(gc29r.whole, GC29R(register_base));
++
++ /* horizontal display 2 control */
++ gc22r.whole = 0x320041e;
++ writel(gc22r.whole, GC22R(register_base));
++
++ /* vertical display 2 control */
++ gc23r.whole = 0x2570273;
++ writel(gc23r.whole, GC23R(register_base));
++
++ /* horizontal sync 2 control */
++ gc24r.whole = 0x3c70347;
++ writel(gc24r.whole, GC24R(register_base));
++
++ /* vertical sync 2 control */
++ gc25r.whole = 0x25d0259;
++ writel(gc25r.whole, GC25R(register_base));
++
++ /* graphics controller CRT control */
++ gc01r.whole = 0x800;
++ writel(gc01r.whole, GC01R(register_base));
++
++ /* PLL 3 programming */
++ pm07r.whole = 0xE90830;
++ writel(pm07r.whole, PM07R(register_base));
++
++ /* graphics controller 2 register
++ * o GC2 clock source is PLL 3.
++ * o hardware cursor is disabled.
++ */
++ gc20r.whole = 0x10000C8 | 0x30000;
++ writel(gc20r.whole, GC20R(register_base));
++
++ /*
++ * Enable PLL2 and PLL3 in the PM Register
++ */
++ pm00r.part.pll2_enbl = 0x1;
++ pm00r.part.pll3_enbl = 0x1;
++ writel(pm00r.whole, PM00R(register_base));
++
++ spin_unlock_irqrestore(lock, flags);
++}
++
++
++/****
++ * initialize graphics engine.
++ */
++static void
++ge_reset(unsigned long register_base)
++{
++ /* drawing command register */
++ writel(0, GE00R(register_base));
++
++ /* promary width and height register */
++ writel(0, GE01R(register_base));
++
++ /* primary destination address register */
++ writel(0, GE02R(register_base));
++
++ /* primary source XY register */
++ writel(0, GE03R(register_base));
++
++ /* primary color compare register */
++ writel(0, GE04R(register_base));
++
++ /* primary clip left/top register */
++ writel(0, GE05R(register_base));
++
++ /* primary clip right/bottom register */
++ writel(0, GE06R(register_base));
++
++ /* primary source and pattern offset register */
++ writel(0, GE07R(register_base));
++
++ /* primary foreground color register/rectangle fill color depth */
++ writel(0, GE08R(register_base));
++
++ /* source stride/offset register */
++ writel(0, GE09R(register_base));
++
++ /* destination stride register and color depth */
++ writel(0, GE0AR(register_base));
++
++ /* image base address register */
++ writel(0, GE0BR(register_base));
++}
++
++/****
++ * initialize Color Palette 1.
++ */
++static void
++cp1_reset(unsigned long addr_info)
++{
++ int i;
++
++ for (i = 0; i < 256; i++)
++ writel(0, C1xxR(addr_info, i));
++}
++
++
++/*
++ * Below functions are called from the skeleton
++ */
++void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr)
++{
++ writel(color,C1xxR(addr,regno));
++}
++
++void mq200_external_setqmode(struct mq200_monitor_info* info,
++ unsigned long addr, spinlock_t *lock)
++{
++ dc_reset(addr); /* device configuration */
++
++ power_state_transition(addr, 0); /* transition to D0 state */
++
++ pmu_reset(addr); /* power management unit */
++
++ miu_reset(addr); /* memory interface unit */
++
++ ge_reset(addr); /* graphics engine */
++
++ fpctrl_reset(addr); /* reset the panel settings */
++
++ gc1_gc2_reset(addr, lock); /* graphics controller 1 and 2 */
++
++ cp1_reset(addr); /* color palette 1 */
++
++ mq200_external_ondisplay(addr); /* LCD and CRT */
++}
++
++void mq200_external_offdisplay(unsigned long addr)
++{
++ /*
++ * Move the MQ200 to D3 mode
++ */
++ power_state_transition(addr, 3);
++}
++
++/**
++ * to be called after mq200_external_setqmode
++ */
++void mq200_external_ondisplay (unsigned long addr)
++{
++ /*
++ * Set the framebuffer details
++ */
++ union gc00r gc00r;
++ union gc01r gc01r;
++ union gc20r gc20r;
++ union fp00r fp00r;
++
++ /* enable LCD for GC2 */
++ fp00r.whole = readl(FP00R(addr));
++ fp00r.whole &= 0xfffffffc;
++
++ gc20r.whole = readl(GC20R(addr));
++
++ if(!(gc20r.whole & 0x1)) {
++ gc20r.whole |= 0x1;
++ writel(gc20r.whole, GC20R(addr));
++ }
++
++ fp00r.whole |= 0x3;
++ writel(fp00r.whole, FP00R(addr));
++
++ /* enable CRT for GC1 */
++ gc00r.whole = readl(GC00R(addr));
++
++ if(!(gc00r.whole & 0x1)) {
++ gc00r.whole |= 0x1;
++ writel(gc00r.whole, GC00R(addr));
++ }
++
++ gc01r.whole = readl(GC01R(addr));
++ gc01r.whole &= 0xfffffffc;
++
++ gc01r.whole |= 0x1;
++ writel(gc01r.whole, GC01R(addr));
++
++}
++
++int mq200_external_probe(unsigned long addr)
++{
++ union pc00r pc00r;
++ if(readl(PMR(addr)) != PMR_VALUE)
++ return 0;
++
++ pc00r.whole = readl(PC00R(addr));
++ printk(KERN_INFO "mq200 video driver found Vendor: 0x%X Device: 0x%X\n",
++ pc00r.part.device, pc00r.part.vendor);
++ return 1;
++}
+diff -Nur linux-2.6.24.vanilla/drivers/video/mq200/mq_skeleton.c linux-2.6.24/drivers/video/mq200/mq_skeleton.c
+--- linux-2.6.24.vanilla/drivers/video/mq200/mq_skeleton.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/video/mq200/mq_skeleton.c 2008-02-20 21:51:22.000000000 +0100
+@@ -0,0 +1,398 @@
++/*
++ * Author: Holger Hans Peter Freyther
++ *
++ *
++ * This implements the frame buffer driver interface to communicate
++ * with the kernel.
++ * It uses the mq200 routines from the ucLinux driver from Lineo
++ *
++ * 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/autoconf.h>
++#include <linux/platform_device.h>
++#include <linux/module.h>
++#include <linux/fb.h>
++#include <linux/types.h>
++#include <linux/spinlock.h>
++
++#include "mq200_data.h"
++
++#if CONFIG_SA1100_SIMPAD
++/*
++ * Siemens SIMpad specefic data
++ */
++#include <asm/arch/simpad.h>
++#include <asm/arch/hardware.h>
++
++#define MQ200_REGIONS simpad_mq200_regions
++#define MQ200_MONITOR simpad_mq200_panel
++
++static struct mq200_io_regions simpad_mq200_regions = {
++ .fb_size = MQ200_FB_SIZE,
++ .phys_mmio_base = 0x4be00000,
++ .virt_mmio_base = 0xf2e00000,
++ .phys_fb_base = 0x4b800000,
++ .virt_fb_base = 0xf2800000,
++};
++
++static struct mq200_monitor_info simpad_mq200_panel = {
++ .horizontal_res = 800,
++ .vertical_res = 600,
++ .depth = 16,
++ .refresh = 60,
++ .line_length = 1600,
++ .flags = 0x00130004,
++};
++
++extern long get_cs3_shadow(void);
++extern void set_cs3_bit(int value);
++extern void clear_cs3_bit(int value);
++#endif
++
++
++
++struct mq200_info {
++ struct fb_info fb_info;
++ struct mq200_io_regions io_regions;
++ struct mq200_monitor_info monitor_info;
++
++ /* palette */
++ u32 pseudo_palette[17]; /* 16 colors + 1 in reserve not that well documented... */
++ spinlock_t lock;
++};
++
++
++
++static int mq200_blank( int blank_mode, struct fb_info *info )
++{
++#ifdef CONFIG_SA1100_SIMPAD
++ if(blank_mode ){
++ clear_cs3_bit(DISPLAY_ON);
++ }else {
++ set_cs3_bit(DISPLAY_ON);
++ }
++#endif
++ return 0;
++}
++
++
++static int mq200_check_var(struct fb_var_screeninfo *var,
++ struct fb_info *info )
++{ /* TODO do we need sanity checks here */
++ return 0;
++}
++
++
++static int mq200_set_par( struct fb_info *info )
++{
++ /* TODO set paraemeter */
++ return 0;
++}
++
++static int mq200_setcolreg(unsigned regno, unsigned red, unsigned green,
++ unsigned blue, unsigned transp,
++ struct fb_info *info )
++{
++ struct mq200_info *p;
++ unsigned long color;
++ u32* pal = info->pseudo_palette;
++
++ p = info->par;
++
++ if(regno > 255 )
++ return 1;
++
++ switch( info->var.bits_per_pixel ){
++ case 16:
++ pal[regno] =
++ ((red & 0xf800) >> 0) |
++ ((green & 0xf800) >> 5) | ((blue & 0xf800) >> 11);
++ break;
++ case 24:
++ pal[regno] =
++ ((red & 0xff00) << 8) |
++ ((green & 0xff00)) | ((blue & 0xff00) >> 8);
++ break;
++ case 32:
++ pal[regno] =
++ ((red & 0xff00) >> 8) |
++ ((green & 0xff00)) | ((blue & 0xff00) << 8);
++ break;
++ default:
++ break;
++ }
++
++ red &= 0xFF;
++ green &= 0xFF;
++ blue &= 0xFF;
++
++ color = red | (green << 8) | (blue << 16);
++ mq200_external_setpal(regno, color, p->io_regions.virt_mmio_base);
++
++ return 0;
++}
++
++
++static struct fb_ops mq200_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = mq200_check_var,
++ .fb_set_par = mq200_set_par,
++ .fb_setcolreg = mq200_setcolreg,
++#ifdef FB_SOFT_CURSOR
++ .fb_cursor = soft_cursor, /* FIXME use hardware cursor */
++#endif
++ .fb_fillrect = cfb_fillrect,
++ .fb_copyarea = cfb_copyarea,
++ .fb_imageblit = cfb_imageblit,
++ .fb_blank = mq200_blank,
++};
++
++
++/*********************************************************************
++ *
++ * Device driver and module init code
++ * this will register to the fb layer later
++ *
++ *********************************************************************/
++static void mq200_internal_init_color( struct fb_bitfield* red,
++ struct fb_bitfield* green,
++ struct fb_bitfield* blue,
++ int bpp )
++{
++ switch ( bpp )
++ {
++ case 16:
++ red->offset = 11;
++ green->offset = 5;
++ blue->offset = 0;
++
++ red->length = 5;
++ green->length = 6;
++ blue->length = 5;
++ break;
++ case 24:
++ red->offset = 16;
++ green->offset = 8;
++ blue->offset = 0;
++
++ red->length = 8;
++ green->length = 8;
++ blue->length = 8;
++ break;
++ case 32:
++ red->offset = 0;
++ green->offset = 8;
++ blue->offset = 16;
++
++ red->length = 8;
++ green->length = 8;
++ blue->length = 8;
++ case 8: /* fall through */
++ default:
++ red->offset = green->offset = blue->offset = 0;
++ red->length = green->length = blue->length = bpp;
++ break;
++ }
++
++}
++
++
++static struct mq200_info* __init mq200_internal_init_fbinfo(void)
++{
++ struct mq200_info *info = NULL;
++
++ info = (struct mq200_info*)kmalloc(sizeof(*info), GFP_KERNEL);
++ if(!info)
++ return NULL;
++
++ /*
++ * Initialize memory
++ */
++ memset(info, 0, sizeof(struct mq200_info) );
++ spin_lock_init(&info->lock);
++
++ /* set the base IO addresses */
++ info->io_regions = MQ200_REGIONS;
++ info->monitor_info = MQ200_MONITOR;
++
++ info->fb_info.screen_base = (char *)info->io_regions.virt_fb_base;
++
++ /* fb_fix_screeninfo filling */
++ strcpy(info->fb_info.fix.id, "MQ200_FB" );
++ info->fb_info.fix.smem_start = info->io_regions.phys_fb_base;
++ info->fb_info.fix.smem_len = info->io_regions.fb_size; /* - CURSOR_IMAGE */
++ info->fb_info.fix.mmio_start = info->io_regions.phys_mmio_base;
++ info->fb_info.fix.mmio_len = MQ200_REGS_SIZE;
++ info->fb_info.fix.type = FB_TYPE_PACKED_PIXELS;
++ info->fb_info.fix.accel = FB_ACCEL_NONE;
++ info->fb_info.fix.line_length = MQ200_MONITOR_LINE_LENGTH(info);
++
++ if(MQ200_MONITOR_DEPTH(info) <= 8 )
++ info->fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ else if( MQ200_MONITOR_DEPTH(info) >= 16 )
++ info->fb_info.fix.visual = FB_VISUAL_DIRECTCOLOR;
++ else
++ panic("Calling mq200 with wrong display data\n");
++
++ /* set the variable screen info */
++ info->fb_info.var.xres = MQ200_MONITOR_HORI_RES(info);
++ info->fb_info.var.yres = MQ200_MONITOR_VERT_RES(info);
++ info->fb_info.var.xres_virtual = MQ200_MONITOR_HORI_RES(info);
++ info->fb_info.var.yres_virtual = MQ200_MONITOR_VERT_RES(info);
++ info->fb_info.var.bits_per_pixel = MQ200_MONITOR_DEPTH(info);
++
++ mq200_internal_init_color(&info->fb_info.var.red,
++ &info->fb_info.var.green,
++ &info->fb_info.var.blue,
++ MQ200_MONITOR_DEPTH(info) );
++
++ info->fb_info.var.transp.length = info->fb_info.var.transp.offset = 0;
++ info->fb_info.var.height = info->fb_info.var.width = -1;
++
++ info->fb_info.var.vmode = FB_VMODE_NONINTERLACED;
++ info->fb_info.var.pixclock = 10000;
++ info->fb_info.var.left_margin = info->fb_info.var.right_margin = 16;
++ info->fb_info.var.upper_margin = info->fb_info.var.lower_margin = 16;
++ info->fb_info.var.hsync_len = info->fb_info.var.vsync_len = 8;
++
++ info->fb_info.var.nonstd = 0;
++ info->fb_info.var.activate = FB_ACTIVATE_NOW;
++ info->fb_info.var.accel_flags = 0;
++
++ return info;
++}
++
++
++extern void mq200_register_attributes(struct device* );
++/*
++ * gets called from the bus
++ * we will register our framebuffer from here
++ */
++static int __init mq200_probe(struct device *dev)
++{
++ struct mq200_info *info = NULL;
++ int retv= 0;
++
++ info = mq200_internal_init_fbinfo();
++ if(!mq200_external_probe(info->io_regions.virt_mmio_base))
++ goto error_out;
++
++ GPDR |= (1<<3);
++ GAFR &= ~(1<<3);
++ GPSR |= (1<<3);
++
++ mq200_external_setqmode(&info->monitor_info,
++ info->io_regions.virt_mmio_base,
++ &info->lock);
++
++ info->fb_info.fbops = &mq200_ops;
++ info->fb_info.flags = FBINFO_FLAG_DEFAULT;
++
++ mq200_check_var(&info->fb_info.var, &info->fb_info );
++
++ fb_alloc_cmap(&info->fb_info.cmap, 1 << MQ200_MONITOR_DEPTH(info), 0 );
++
++ info->fb_info.pseudo_palette = (void*)info->pseudo_palette;
++
++ /* save the pointer to the mq200 struct in var */
++ info->fb_info.par = info;
++
++ retv = register_framebuffer(&info->fb_info );
++ if(retv < 0)
++ goto error_out;
++
++
++ /* will get unset if retv != 0 */
++ dev_set_drvdata(dev, info );
++ return retv;
++
++/*
++ * Free the info and exit
++ */
++error_out:
++ kfree(info);
++ return -EINVAL;
++}
++
++#ifdef CONFIG_PM
++static struct mq200_info* get_mq200_info( struct device *dev)
++{
++ return dev_get_drvdata(dev);
++}
++
++static unsigned long get_mmio_base( struct device *dev )
++{
++ struct mq200_info *info = get_mq200_info(dev);
++ return info->io_regions.virt_mmio_base;
++}
++
++static struct mq200_monitor_info* get_monitor_info( struct device *dev)
++{
++ struct mq200_info *info = get_mq200_info(dev);
++ return &info->monitor_info;
++}
++
++static spinlock_t* get_spinlock( struct device *dev)
++{
++ return &get_mq200_info(dev)->lock;
++}
++
++/*
++ * FIXME: make sure we only call mq200_external_offdisplay only once
++ * a 2nd time will hang the kernel -zecke
++ *
++ * FIXME: save the content of the framebuffer inside dev->saved_state
++ * so on resume we can memcpy it back into the buffer and userspace
++ * does not need to redraw
++ *
++ * functions for suspending and resuming
++ */
++static int mq200_suspend(struct device *dev, pm_message_t state)
++{
++
++ mq200_external_offdisplay( get_mmio_base(dev) );
++ clear_cs3_bit(DISPLAY_ON);
++
++
++ return 0;
++}
++
++static int mq200_resume(struct device *dev)
++{
++ unsigned long mem = get_mmio_base(dev);
++ struct mq200_monitor_info *monitor = get_monitor_info(dev);
++ mq200_external_setqmode(monitor, mem, get_spinlock(dev) );
++
++
++ /*
++ * Set display on if it was on
++ */
++ set_cs3_bit(DISPLAY_ON);
++
++ return 0;
++}
++
++
++#endif
++
++
++static struct device_driver mq200fb_driver = {
++ .name = "simpad-mq200",
++ .bus = &platform_bus_type,
++ .probe = mq200_probe, /* will be called after we've registered the driver */
++ .suspend = mq200_suspend,
++ .resume = mq200_resume
++};
++
++int __devinit mq200_init(void)
++{
++ return driver_register(&mq200fb_driver);
++}
++
++module_init(mq200_init);
++MODULE_DESCRIPTION("MQ200 framebuffer driver");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Holger Hans Peter Freyther");
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-orinoco_cs-shared-irq.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-orinoco_cs-shared-irq.patch
new file mode 100644
index 0000000000..2568093020
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-orinoco_cs-shared-irq.patch
@@ -0,0 +1,21 @@
+diff -Nur linux-2.6.24.vanilla/drivers/net/wireless/orinoco_cs.c linux-2.6.24/drivers/net/wireless/orinoco_cs.c
+--- linux-2.6.24.vanilla/drivers/net/wireless/orinoco_cs.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/net/wireless/orinoco_cs.c 2008-02-22 21:23:51.000000000 +0100
+@@ -8,6 +8,8 @@
+ * cards such as the 3Com AirConnect and Ericsson WLAN.
+ *
+ * Copyright notice & release notes in file orinoco.c
++ *
++ * mrdata: added shared irq
+ */
+
+ #define DRIVER_NAME "orinoco_cs"
+@@ -120,7 +122,7 @@
+ link->priv = dev;
+
+ /* Interrupt setup */
+- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = orinoco_interrupt;
+ link->irq.Instance = dev;
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-pcmcia.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-pcmcia.patch
new file mode 100644
index 0000000000..5560d67494
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-pcmcia.patch
@@ -0,0 +1,209 @@
+diff -Nur linux-2.6.24.vanilla/drivers/pcmcia/cs.c linux-2.6.24/drivers/pcmcia/cs.c
+--- linux-2.6.24.vanilla/drivers/pcmcia/cs.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/pcmcia/cs.c 2008-02-22 20:53:06.000000000 +0100
+@@ -10,6 +10,8 @@
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ * (C) 1999 David A. Hinds
++ *
++ * mrdata: -added suspend fix
+ */
+
+ #include <linux/module.h>
+@@ -59,6 +61,10 @@
+ INT_MODULE_PARM(unreset_delay, 10); /* centiseconds */
+ INT_MODULE_PARM(unreset_check, 10); /* centiseconds */
+ INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */
++// INT_MODULE_PARM(unreset_delay, 20); /* centiseconds */
++// INT_MODULE_PARM(unreset_check, 100); /* centiseconds */
++// INT_MODULE_PARM(unreset_limit, 300); /* unreset_check's */
++
+
+ /* Access speed for attribute memory windows */
+ INT_MODULE_PARM(cis_speed, 300); /* ns */
+@@ -366,6 +372,7 @@
+ skt->ops->set_socket(skt, &skt->socket);
+
+ msleep(unreset_delay * 10);
++
+ for (i = 0; i < unreset_limit; i++) {
+ skt->ops->get_status(skt, &status);
+
+@@ -434,7 +441,7 @@
+
+ msleep(initial_delay * 10);
+
+- for (i = 0; i < 100; i++) {
++ for (i = 0; i < 100; i++) {
+ skt->ops->get_status(skt, &status);
+ if (!(status & SS_DETECT))
+ return CS_NO_CARD;
+diff -Nur linux-2.6.24.vanilla/drivers/pcmcia/sa1100_generic.c linux-2.6.24/drivers/pcmcia/sa1100_generic.c
+--- linux-2.6.24.vanilla/drivers/pcmcia/sa1100_generic.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/pcmcia/sa1100_generic.c 2008-02-22 20:53:06.000000000 +0100
+@@ -28,6 +28,9 @@
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
++ 2007 mrnice: added thesings changes from device_driver
++ to platform_driver - many thx to thesing
++
+ ======================================================================*/
+
+ #include <linux/module.h>
+@@ -81,13 +84,15 @@
+ return ret;
+ }
+
+-static struct device_driver sa11x0_pcmcia_driver = {
+- .probe = sa11x0_drv_pcmcia_probe,
+- .remove = soc_common_drv_pcmcia_remove,
+- .name = "sa11x0-pcmcia",
+- .bus = &platform_bus_type,
+- .suspend = pcmcia_socket_dev_suspend,
+- .resume = pcmcia_socket_dev_resume,
++static struct platform_driver sa11x0_pcmcia_driver = {
++ .driver = {
++ .name = "sa11x0-pcmcia",
++ .probe = sa11x0_drv_pcmcia_probe,
++ .remove = soc_common_drv_pcmcia_remove,
++ .suspend= pcmcia_socket_dev_suspend,
++ .resume = pcmcia_socket_dev_resume,
++ //.bus = &platform_bus_type,
++ },
+ };
+
+ /* sa11x0_pcmcia_init()
+@@ -100,7 +105,7 @@
+ */
+ static int __init sa11x0_pcmcia_init(void)
+ {
+- return driver_register(&sa11x0_pcmcia_driver);
++ return platform_driver_register(&sa11x0_pcmcia_driver);
+ }
+
+ /* sa11x0_pcmcia_exit()
+@@ -110,7 +115,7 @@
+ */
+ static void __exit sa11x0_pcmcia_exit(void)
+ {
+- driver_unregister(&sa11x0_pcmcia_driver);
++ platform_driver_unregister(&sa11x0_pcmcia_driver);
+ }
+
+ MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
+diff -Nur linux-2.6.24.vanilla/drivers/pcmcia/sa1100_simpad.c linux-2.6.24/drivers/pcmcia/sa1100_simpad.c
+--- linux-2.6.24.vanilla/drivers/pcmcia/sa1100_simpad.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/pcmcia/sa1100_simpad.c 2008-02-22 20:53:32.000000000 +0100
+@@ -8,13 +8,15 @@
+ #include <linux/kernel.h>
+ #include <linux/device.h>
+ #include <linux/init.h>
++#include <linux/delay.h>
+
+ #include <asm/hardware.h>
+ #include <asm/mach-types.h>
+ #include <asm/irq.h>
+ #include <asm/arch/simpad.h>
+ #include "sa1100_generic.h"
+-
++
++extern long get_cs3_ro(void);
+ extern long get_cs3_shadow(void);
+ extern void set_cs3_bit(int value);
+ extern void clear_cs3_bit(int value);
+@@ -25,8 +27,15 @@
+
+ static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+ {
+-
+- clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
++ clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1|PCMCIA_RESET);
++
++ set_cs3_bit(PCMCIA_BUFF_DIS);
++
++ msleep(10);
++
++ clear_cs3_bit(PCMCIA_BUFF_DIS);
++
++ msleep(5);
+
+ skt->irq = IRQ_GPIO_CF_IRQ;
+
+@@ -38,7 +47,7 @@
+ soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+ /* Disable CF bus: */
+- //set_cs3_bit(PCMCIA_BUFF_DIS);
++ set_cs3_bit(PCMCIA_BUFF_DIS);
+ clear_cs3_bit(PCMCIA_RESET);
+ }
+
+@@ -47,21 +56,17 @@
+ struct pcmcia_state *state)
+ {
+ unsigned long levels = GPLR;
+- long cs3reg = get_cs3_shadow();
+-
+- state->detect=((levels & GPIO_CF_CD)==0)?1:0;
+- state->ready=(levels & GPIO_CF_IRQ)?1:0;
+- state->bvd1=1; /* Not available on Simpad. */
+- state->bvd2=1; /* Not available on Simpad. */
+- state->wrprot=0; /* Not available on Simpad. */
+-
+- if((cs3reg & 0x0c) == 0x0c) {
+- state->vs_3v=0;
+- state->vs_Xv=0;
+- } else {
+- state->vs_3v=1;
+- state->vs_Xv=0;
+- }
++
++ state->detect = ((levels & GPIO_CF_CD) == 0) ? 1 : 0 ;
++ state->ready = (levels & GPIO_CF_IRQ) ? 1 : 0 ;
++
++ long cs3_ro_reg = get_cs3_ro();
++
++ state->bvd1 = (cs3_ro_reg & PCMCIA_BVD1) ? 1 : 0 ; /* old: =1 Not available on Simpad. */
++ state->bvd2 = (cs3_ro_reg & PCMCIA_BVD2) ? 1 : 0 ; /* old: =1 Not available on Simpad. */
++ state->wrprot = 0 ; /* Not available on Simpad. */
++ state->vs_3v = (cs3_ro_reg & PCMCIA_VS1) ? 0 : 1 ;
++ state->vs_Xv = (cs3_ro_reg & PCMCIA_VS2) ? 0 : 1 ;
+ }
+
+ static int
+@@ -95,7 +100,11 @@
+ local_irq_restore(flags);
+ return -1;
+ }
+-
++
++ if (state->flags & SS_RESET)
++ set_cs3_bit(PCMCIA_RESET);
++ else
++ clear_cs3_bit(PCMCIA_RESET);
+
+ local_irq_restore(flags);
+
+@@ -104,6 +113,7 @@
+
+ static void simpad_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+ {
++ clear_cs3_bit(PCMCIA_RESET);
+ soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ }
+
+diff -Nur linux-2.6.24.vanilla/drivers/pcmcia/soc_common.c linux-2.6.24/drivers/pcmcia/soc_common.c
+--- linux-2.6.24.vanilla/drivers/pcmcia/soc_common.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/pcmcia/soc_common.c 2008-02-22 20:53:06.000000000 +0100
+@@ -747,7 +747,9 @@
+
+ add_timer(&skt->poll_timer);
+
+- device_create_file(&skt->socket.dev, &dev_attr_status);
++ ret = device_create_file(&skt->socket.dev, &dev_attr_status);
++ if (ret)
++ goto out_err_7;
+ }
+
+ dev_set_drvdata(dev, sinfo);
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-rtc-sa1100.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-rtc-sa1100.patch
new file mode 100644
index 0000000000..407fd89a26
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-rtc-sa1100.patch
@@ -0,0 +1,68 @@
+diff -Nur linux-2.6.24.vanilla/drivers/rtc/rtc-sa1100.c linux-2.6.24_rtc/drivers/rtc/rtc-sa1100.c
+--- linux-2.6.24.vanilla/drivers/rtc/rtc-sa1100.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24_rtc/drivers/rtc/rtc-sa1100.c 2008-03-24 13:49:40.000000000 +0100
+@@ -79,7 +79,10 @@
+
+ rtsr = RTSR;
+ /* clear interrupt sources */
+- RTSR = 0;
++ RTSR &= ~RTSR_HZE; //RTSR = 0; is not possible and does not work
++ RTSR &= ~RTSR_HZ;
++ RTSR &= ~RTSR_ALE;
++ RTSR &= ~RTSR_AL;
+ RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2);
+
+ /* clear alarm interrupt if it has occurred */
+@@ -155,6 +158,20 @@
+ {
+ int ret;
+
++ /*
++ * On some devices RTSR is set to some value but it must be set to 0.
++ * We have to set RTSR to 0 and OIER/OSSR to default. This should not be
++ * necessary here but it is.
++ */
++ spin_lock_irq(&sa1100_rtc_lock);
++ RTSR &= ~RTSR_HZE;
++ RTSR &= ~RTSR_HZ;
++ RTSR &= ~RTSR_ALE;
++ RTSR &= ~RTSR_AL;
++ OIER &= ~OIER_E1;
++ OSSR = OSSR_M1;
++ spin_unlock_irq(&sa1100_rtc_lock);
++
+ ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED,
+ "rtc 1Hz", dev);
+ if (ret) {
+@@ -186,7 +203,10 @@
+ static void sa1100_rtc_release(struct device *dev)
+ {
+ spin_lock_irq(&sa1100_rtc_lock);
+- RTSR = 0;
++ RTSR &= ~RTSR_HZE; //RTSR = 0; is not possible and does not work
++ RTSR &= ~RTSR_HZ;
++ RTSR &= ~RTSR_ALE;
++ RTSR &= ~RTSR_AL;
+ OIER &= ~OIER_E1;
+ OSSR = OSSR_M1;
+ spin_unlock_irq(&sa1100_rtc_lock);
+@@ -339,6 +359,19 @@
+
+ platform_set_drvdata(pdev, rtc);
+
++ /*
++ * On some devices RTSR is set to some value but it must be set to 0.
++ * We have to set RTSR to 0 and OIER/OSSR to default.
++ */
++ spin_lock_irq(&sa1100_rtc_lock);
++ RTSR &= ~RTSR_HZE;
++ RTSR &= ~RTSR_HZ;
++ RTSR &= ~RTSR_ALE;
++ RTSR &= ~RTSR_AL;
++ OIER &= ~OIER_E1;
++ OSSR = OSSR_M1;
++ spin_unlock_irq(&sa1100_rtc_lock);
++
+ return 0;
+ }
+
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2 b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2
new file mode 100644
index 0000000000..e47d99288f
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2
@@ -0,0 +1,358 @@
+diff -Nur linux-2.6.24.vanilla/arch/arm/mach-sa1100/simpad.c linux-2.6.24/arch/arm/mach-sa1100/simpad.c
+--- linux-2.6.24.vanilla/arch/arm/mach-sa1100/simpad.c 2008-10-04 21:47:24.000000000 +0200
++++ linux-2.6.24/arch/arm/mach-sa1100/simpad.c 2008-10-04 22:01:20.000000000 +0200
+@@ -1,5 +1,15 @@
+ /*
+ * linux/arch/arm/mach-sa1100/simpad.c
++ *
++ * 2007/04/11 mrdata:
++ * - insert simpad_uart_set_mctrl()
++ * simpad_uart_get_mctrl()
++ * - internal RS232/DECT/Bluetooth
++ * works again (based on 2.4 simpad-serial.patch)
++ * - added cs3_ro
++ *
++ * 2007/04/12 Bernhard Guillon:
++ * -added gpio_keys (based on h3000.c from hh.org)
+ */
+
+ #include <linux/module.h>
+@@ -9,6 +19,9 @@
+ #include <linux/proc_fs.h>
+ #include <linux/string.h>
+ #include <linux/pm.h>
++
++#include <linux/apm-emulation.h>
++
+ #include <linux/platform_device.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+@@ -27,12 +40,21 @@
+
+ #include <linux/serial_core.h>
+ #include <linux/ioport.h>
++#include <linux/input.h>
++#include <linux/gpio_keys.h>
+ #include <asm/io.h>
+
+ #include "generic.h"
+
++long cs3_ro;
+ long cs3_shadow;
+
++long get_cs3_ro(void)
++{
++ cs3_ro = *(CS3BUSTYPE *)(CS3_BASE);
++ return cs3_ro;
++}
++
+ long get_cs3_shadow(void)
+ {
+ return cs3_shadow;
+@@ -55,9 +77,12 @@
+ *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow;
+ }
+
++EXPORT_SYMBOL(get_cs3_ro);
++EXPORT_SYMBOL(get_cs3_shadow);
+ EXPORT_SYMBOL(set_cs3_bit);
+ EXPORT_SYMBOL(clear_cs3_bit);
+
++
+ static struct map_desc simpad_io_desc[] __initdata = {
+ { /* MQ200 */
+ .virtual = 0xf2800000,
+@@ -73,23 +98,71 @@
+ };
+
+
++static void simpad_uart_set_mctrl(struct uart_port *port, u_int mctrl)
++{
++ if (port->mapbase == _Ser1UTCR0) {
++ /* internal serial port (ttySA1, DECT/Bluetooth) */
++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART1_RTS;
++ else GPSR = GPIO_UART1_RTS;
++
++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART1_DTR;
++ else GPSR = GPIO_UART1_DTR;
++ }
++
++ else if (port->mapbase == _Ser3UTCR0) {
++ /* external serial port (ttySA0, RS232) */
++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART3_RTS;
++ else GPSR = GPIO_UART3_RTS;
++
++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART3_DTR;
++ else GPSR = GPIO_UART3_DTR;
++ }
++}
++
++
++static u_int simpad_uart_get_mctrl(struct uart_port *port)
++{
++ u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
++
++ if (port->mapbase == _Ser1UTCR0) {
++ /* internal serial port (ttySA1, DECT/Bluetooth) */
++ int gplr = GPLR;
++ if (gplr & GPIO_UART1_DCD) ret &= ~TIOCM_CD;
++ if (gplr & GPIO_UART1_CTS) ret &= ~TIOCM_CTS;
++ if (gplr & GPIO_UART1_DSR) ret &= ~TIOCM_DSR;
++ }
++
++ else if (port->mapbase == _Ser3UTCR0) {
++ /* external serial port (ttySA0, RS232) */
++ int gplr = GPLR;
++ if (gplr & GPIO_UART3_DCD) ret &= ~TIOCM_CD;
++ if (gplr & GPIO_UART3_CTS) ret &= ~TIOCM_CTS;
++ if (gplr & GPIO_UART3_DSR) ret &= ~TIOCM_DSR;
++ }
++ return ret;
++}
++
++
+ static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+ {
+- if (port->mapbase == (u_int)&Ser1UTCR0) {
+- if (state)
+- {
+- clear_cs3_bit(RS232_ON);
+- clear_cs3_bit(DECT_POWER_ON);
+- }else
+- {
+- set_cs3_bit(RS232_ON);
+- set_cs3_bit(DECT_POWER_ON);
+- }
+- }
++ if (port->mapbase == (u_int)&Ser3UTCR0) {
++ if (state)
++ {
++ clear_cs3_bit(RS232_ON);
++ /* clear_cs3_bit(DECT_POWER_ON); */
++ }else
++ {
++ set_cs3_bit(RS232_ON);
++ /* set_cs3_bit(DECT_POWER_ON); */
++ }
++ }
+ }
+
++
+ static struct sa1100_port_fns simpad_port_fns __initdata = {
+- .pm = simpad_uart_pm,
++ .set_mctrl = simpad_uart_set_mctrl,
++ .get_mctrl = simpad_uart_get_mctrl,
++ .pm = simpad_uart_pm,
+ };
+
+
+@@ -135,7 +208,6 @@
+ };
+
+
+-
+ static void __init simpad_map_io(void)
+ {
+ sa1100_map_io();
+@@ -144,23 +216,45 @@
+
+ set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON |
+ ENABLE_5V | RESET_SIMCARD | DECT_POWER_ON);
+-
+-
++
+ sa1100_register_uart_fns(&simpad_port_fns);
+ sa1100_register_uart(0, 3); /* serial interface */
+ sa1100_register_uart(1, 1); /* DECT */
+
+- // Reassign UART 1 pins
++ /* Reassign UART 1 pins */
++ /* TEST SOME OLD KERNEL STUFF INSTEAD
+ GAFR |= GPIO_UART_TXD | GPIO_UART_RXD;
+ GPDR |= GPIO_UART_TXD | GPIO_LDD13 | GPIO_LDD15;
+ GPDR &= ~GPIO_UART_RXD;
+ PPAR |= PPAR_UPR;
++ */
++
++ // txd and rxd use their alternate function
++ GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD);
++
++ // the control lines are gpio
++ GAFR &= ~(GPIO_UART1_RTS | GPIO_UART1_CTS | GPIO_UART1_DCD);
++ GAFR &= ~(GPIO_UART1_DSR | GPIO_UART1_DTR);
++ GAFR &= ~(GPIO_UART3_RTS | GPIO_UART3_CTS | GPIO_UART3_DCD);
++ GAFR &= ~(GPIO_UART3_DSR | GPIO_UART3_DTR);
++
++ // txd, rts and dtr are outputs
++ GPDR |= GPIO_UART_TXD;
++ GPDR |= GPIO_UART1_RTS | GPIO_UART3_RTS;
++ GPDR |= GPIO_UART1_DTR | GPIO_UART3_DTR;
++
++ // cts, dcd, dsr and rxd are inputs
++ GPDR &= ~(GPIO_UART1_CTS | GPIO_UART3_CTS);
++ GPDR &= ~(GPIO_UART1_DCD | GPIO_UART3_DCD);
++ GPDR &= ~(GPIO_UART1_DSR | GPIO_UART3_DSR);
++ GPDR &= ~GPIO_UART_RXD;
++
++ PPAR |= PPAR_UPR;
+
+ /*
+ * Set up registers for sleep mode.
+ */
+
+-
+ PWER = PWER_GPIO0| PWER_RTC;
+ PGSR = 0x818;
+ PCFR = 0;
+@@ -171,9 +265,10 @@
+ sa11x0_set_mcp_data(&simpad_mcp_data);
+ }
+
++
+ static void simpad_power_off(void)
+ {
+- local_irq_disable(); // was cli
++ local_irq_disable(); /* was cli */
+ set_cs3(0x800); /* only SD_MEDIAQ */
+
+ /* disable internal oscillator, float CS lines */
+@@ -191,31 +286,52 @@
+ while(1);
+
+ local_irq_enable(); /* we won't ever call it */
++}
+
+
+-}
++/*
++ * gpio_keys
++*/
++
++static struct gpio_keys_button simpad_button_table[] = {
++ { KEY_POWER, IRQ_GPIO_POWER_BUTTON, 1, "power button" },
++};
++
++static struct gpio_keys_platform_data simpad_keys_data = {
++ .buttons = simpad_button_table,
++ .nbuttons = ARRAY_SIZE(simpad_button_table),
++};
++
++static struct platform_device simpad_keys = {
++ .name = "gpio-keys",
++ .dev = {
++ .platform_data = &simpad_keys_data,
++ },
++};
+
+
+ /*
+ * MediaQ Video Device
+ */
++
+ static struct platform_device simpad_mq200fb = {
+ .name = "simpad-mq200",
+ .id = 0,
+ };
+
++
+ static struct platform_device *devices[] __initdata = {
+- &simpad_mq200fb
++ &simpad_keys,
++ &simpad_mq200fb,
+ };
+
+
+-
+ static int __init simpad_init(void)
+ {
+ int ret;
+
+ pm_power_off = simpad_power_off;
+-
++
+ ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+ if(ret)
+ printk(KERN_WARNING "simpad: Unable to register mq200 framebuffer device");
+diff -Nur linux-2.6.24.vanilla/include/asm-arm/arch-sa1100/simpad.h linux-2.6.24/include/asm-arm/arch-sa1100/simpad.h
+--- linux-2.6.24.vanilla/include/asm-arm/arch-sa1100/simpad.h 2008-10-04 21:47:17.000000000 +0200
++++ linux-2.6.24/include/asm-arm/arch-sa1100/simpad.h 2008-10-04 22:00:57.000000000 +0200
+@@ -12,11 +12,12 @@
+ #define __ASM_ARCH_SIMPAD_H
+
+
+-#define GPIO_UART1_RTS GPIO_GPIO14
++#define GPIO_UART1_RTS GPIO_GPIO9
+ #define GPIO_UART1_DTR GPIO_GPIO7
+ #define GPIO_UART1_CTS GPIO_GPIO8
+ #define GPIO_UART1_DCD GPIO_GPIO23
+ #define GPIO_UART1_DSR GPIO_GPIO6
++#define GPIO_UART1_RI GPIO_GPIO19
+
+ #define GPIO_UART3_RTS GPIO_GPIO12
+ #define GPIO_UART3_DTR GPIO_GPIO16
+@@ -48,9 +49,9 @@
+ #define GPIO_SMART_CARD GPIO_GPIO10
+ #define IRQ_GPIO_SMARD_CARD IRQ_GPIO10
+
+-// CS3 Latch is write only, a shadow is necessary
++// CS3 Latch is write only 16-bit , a shadow is necessary
+
+-#define CS3BUSTYPE unsigned volatile long
++#define CS3BUSTYPE unsigned volatile long
+ #define CS3_BASE 0xf1000000
+
+ #define VCC_5V_EN 0x0001 // For 5V PCMCIA
+@@ -70,43 +71,17 @@
+ #define ENABLE_5V 0x4000 // Enable 5V circuit
+ #define RESET_SIMCARD 0x8000
+
+-#define RS232_ENABLE 0x0440
+-#define PCMCIAMASK 0x402f
++// CS3 Latch is readable only 8-bit interest
+
++#define PCMCIA_BVD1 0x0001
++#define PCMCIA_BVD2 0x0002
++#define PCMCIA_VS1 0x0004 // PCMCIA card voltage select
++#define PCMCIA_VS2 0x0008 // PCMCIA card voltage select, if both are in high state -> 5V PCMCIA card
++#define LOCK_IND 0x0010
++#define CHARGING_STATE 0x0020 // Ladestatus
++#define PCMCIA_SHORT 0x0040 // low active
+
+-struct simpad_battery {
+- unsigned char ac_status; /* line connected yes/no */
+- unsigned char status; /* battery loading yes/no */
+- unsigned char percentage; /* percentage loaded */
+- unsigned short life; /* life till empty */
+-};
+-
+-/* These should match the apm_bios.h definitions */
+-#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00
+-#define SIMPAD_AC_STATUS_AC_ONLINE 0x01
+-#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */
+-#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff
+-
+-/* These bitfields are rarely "or'd" together */
+-#define SIMPAD_BATT_STATUS_HIGH 0x01
+-#define SIMPAD_BATT_STATUS_LOW 0x02
+-#define SIMPAD_BATT_STATUS_CRITICAL 0x04
+-#define SIMPAD_BATT_STATUS_CHARGING 0x08
+-#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10
+-#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */
+-#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */
+-#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */
+-#define SIMPAD_BATT_STATUS_NOBATT 0x80
+-#define SIMPAD_BATT_STATUS_UNKNOWN 0xff
+-
+-extern int simpad_get_battery(struct simpad_battery* );
++#define RS232_ENABLE 0x0440
++#define PCMCIAMASK 0x402f
+
+ #endif // __ASM_ARCH_SIMPAD_H
+-
+-
+-
+-
+-
+-
+-
+-
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch
new file mode 100644
index 0000000000..4f471b5fb4
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch
@@ -0,0 +1,1622 @@
+Index: linux-2.6.24/sound/arm/sa11xx-ucb1x00.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.24/sound/arm/sa11xx-ucb1x00.c 2008-12-25 23:23:38.000000000 +0100
+@@ -0,0 +1,829 @@
++/*
++ * Driver for Philips UCB1300 on Siemens Simpad
++ * Copyright (C) 2008 Thomas Schätzlein <thomas@pnxs.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License.
++ *
++ */
++
++#include <sound/driver.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/err.h>
++#include <linux/platform_device.h>
++#include <linux/errno.h>
++#include <linux/ioctl.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++
++#ifdef CONFIG_PM
++#include <linux/pm.h>
++#endif
++
++#include <asm/dma.h>
++
++#include <sound/core.h>
++#include <sound/control.h>
++#include <sound/pcm.h>
++#include <sound/initval.h>
++#include <sound/info.h>
++
++#include <linux/mfd/ucb1x00.h>
++
++#undef DEBUG_MODE
++#undef DEBUG_FUNCTION_NAMES
++
++#define AUDIO_RATE_DEFAULT 44100
++
++MODULE_AUTHOR("Thomas Schätzlein <thomas@pnxs.de>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("SA1100/SA1111 + UCB1300 driver for ALSA");
++MODULE_SUPPORTED_DEVICE("{{UCB1x00,Simpad}}");
++
++static char *id; /* ID for this card */
++
++module_param(id, charp, 0444);
++MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UCB1x00 soundcard.");
++
++struct audio_stream {
++ char *id; /* identification string */
++ int stream_id; /* numeric identification */
++ dma_device_t dma_dev; /* device identifier for DMA */
++ dma_regs_t *dma_regs; /* points to our DMA registers */
++ unsigned int active:1; /* we are using this stream for transfer now */
++ int period; /* current transfer period */
++ int periods; /* current count of periods registerd in the DMA engine */
++ unsigned int old_offset;
++ spinlock_t dma_lock; /* for locking in DMA operations (see dma-sa1100.c in the kernel) */
++ struct snd_pcm_substream *stream;
++};
++
++struct sa11xx_ucb1x00 {
++ struct snd_card* card;
++ struct snd_pcm* pcm;
++ struct ucb1x00_dev* ucb1x00;
++ struct device* pdev;
++ long samplerate;
++ struct audio_stream s[2]; /* playback & capture */
++};
++
++#if 0
++static unsigned int rates[] = {
++ 8000, 10666, 10985, 14647,
++ 16000, 21970, 22050, 24000,
++ 29400, 32000, 44100, 48000,
++};
++
++static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
++ .count = ARRAY_SIZE(rates),
++ .list = rates,
++ .mask = 0,
++};
++#endif
++
++#define UCB1x00_SINGLE(xname, where, reg, shift, mask, invert) \
++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ucb1x00_info_single, \
++ .get = snd_ucb1x00_get_single, .put = snd_ucb1x00_put_single, \
++ .private_value = where | (reg << 5) | (shift << 9) | (mask << 13) | (invert << 19) \
++}
++
++#define UCB1x00_SINGLE_WHERE(value) (value & 31)
++#define UCB1x00_SINGLE_REG(value) ((value >> 5) & 15)
++#define UCB1x00_SINGLE_SHIFT(value) ((value >> 9) & 15)
++#define UCB1x00_SINGLE_MASK(value) ((value >> 13) & 63)
++#define UCB1x00_SINGLE_INV(value) ((value >> 19) & 1)
++
++static int snd_ucb1x00_info_single(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_info *uinfo)
++{
++ int mask = UCB1x00_SINGLE_MASK(kcontrol->private_value);
++
++// printk(KERN_INFO "snd_ucb1x00_info_single called\n");
++
++ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
++ uinfo->count = 1;
++ uinfo->value.integer.min = 0;
++ uinfo->value.integer.max = mask;
++ return 0;
++}
++
++static int snd_ucb1x00_get_single(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct sa11xx_ucb1x00* chip = snd_kcontrol_chip(kcontrol);
++ struct ucb1x00 *ucb = chip->ucb1x00->ucb;
++ unsigned int retval;
++
++ int reg = UCB1x00_SINGLE_REG(kcontrol->private_value);
++ int shift = UCB1x00_SINGLE_SHIFT(kcontrol->private_value);
++ int mask = UCB1x00_SINGLE_MASK(kcontrol->private_value);
++ int invert= UCB1x00_SINGLE_INV(kcontrol->private_value);
++
++// printk(KERN_INFO "snd_ucb1x00_get_single called\n");
++
++ retval = (ucb1x00_reg_read(ucb, reg) >> shift) & mask;
++
++// printk(KERN_INFO "ucb1x00_reg_read reg=%02X value=%08X\n", reg, retval);
++
++ ucontrol->value.integer.value[0] = retval;
++ if (invert) {
++ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
++ }
++
++ return 0;
++}
++
++static int snd_ucb1x00_put_single(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct sa11xx_ucb1x00* chip = snd_kcontrol_chip(kcontrol);
++ struct ucb1x00 *ucb = chip->ucb1x00->ucb;
++ int reg = UCB1x00_SINGLE_REG(kcontrol->private_value);
++ int shift = UCB1x00_SINGLE_SHIFT(kcontrol->private_value);
++ int mask = UCB1x00_SINGLE_MASK(kcontrol->private_value);
++ int invert= UCB1x00_SINGLE_INV(kcontrol->private_value);
++ unsigned short val;
++ unsigned int regval;
++
++ val = (ucontrol->value.integer.value[0] & mask);
++ if (invert)
++ val = mask - val;
++
++// printk(KERN_INFO "snd_ucb1x00_put_single called val=%d\n", val);
++
++ regval = ucb1x00_reg_read(ucb, reg);
++ regval &= ~(mask << shift);
++ regval |= val << shift;
++// printk(KERN_INFO "snd_ucb1x00_put_single write regval=%08X to reg %d\n", regval, reg);
++ ucb1x00_reg_write(ucb, reg, regval);
++ return 0;
++}
++
++enum ucb1x00_config {
++ CMD_OMUTE = 0,
++ CMD_VOLUME,
++ CMD_IGAIN,
++ CMD_IMUTE,
++ CMD_LOOPBACK,
++ CMD_CLIP,
++ CMD_OENA,
++};
++
++static struct snd_kcontrol_new snd_ucb1x00_controls[] = {
++ UCB1x00_SINGLE("Master Playback Switch", CMD_OMUTE, UCB_AC_B, 13, 0x01, 1),
++ UCB1x00_SINGLE("Master Playback Volume", CMD_VOLUME, UCB_AC_B, 0, 0x1F, 1),
++
++ UCB1x00_SINGLE("Input Gain", CMD_IGAIN, UCB_AC_A, 7, 0x1F, 1),
++// UCB1x00_SINGLE("Input Mute", CMD_IMUTE, UCB_AC_B, 14, 1, 1),
++// UCB1x00_SINGLE("Output Enabled", CMD_OENA, UCB_AC_B, 15, 1, 1),
++
++ UCB1x00_SINGLE("Audio Loopback", CMD_LOOPBACK, UCB_AC_B, 8, 1, 0),
++};
++
++static void sa11xx_ucb1x00_set_samplerate(struct sa11xx_ucb1x00 *sa11xx_ucb1x00, long rate)
++{
++ struct ucb1x00 *ucb = sa11xx_ucb1x00->ucb1x00->ucb;
++ unsigned int div_rate = ucb1x00_clkrate(ucb) / 32;
++ unsigned int div;
++ unsigned int reg;
++
++// printk(KERN_INFO "sa11xx_ucb1x00_set_samplerate called rate=%d div_rate=%d\n", rate, div_rate);
++
++ div = (div_rate + (rate / 2)) / rate;
++ if (div < 6) {
++ div = 6;
++ }
++ if (div > 127) {
++ div = 127;
++ }
++/*
++ printk(KERN_INFO "ucb=%08X\n", ucb);
++ printk(KERN_INFO "ucb1x00_set_audio_divisor(%d)\n", div*32);
++ printk(KERN_INFO "ucb1x00_reg_write(reg=%d, div=%d)\n", UCB_AC_A, div);
++*/
++ reg = ucb1x00_reg_read(ucb, UCB_AC_B);
++ reg &= ~(UCB_AC_B_IN_ENA | UCB_AC_B_OUT_ENA);
++ ucb1x00_reg_write(ucb, UCB_AC_B, reg);
++
++ // ucb1x00_reg_write(ucb, UCB_AC_B, 0);
++ ucb1x00_set_audio_divisor(ucb, div*32); /* set divisor in MCP */
++
++ reg = ucb1x00_reg_read(ucb, UCB_AC_A);
++ reg &= ~(0x1F);
++ reg |= div;
++ ucb1x00_reg_write(ucb, UCB_AC_A, reg); /* set divisor in UCB1x00 */
++
++ reg = ucb1x00_reg_read(ucb, UCB_AC_B);
++ reg |= UCB_AC_B_IN_ENA | UCB_AC_B_OUT_ENA;
++ ucb1x00_reg_write(ucb, UCB_AC_B, reg);
++
++ sa11xx_ucb1x00->samplerate = div_rate / div;
++
++// printk(KERN_INFO "sa11xx_ucb1x00_set_samplerate done\n");
++}
++
++/* HW init and shutdown */
++static void sa11xx_ucb1x00_audio_init(struct sa11xx_ucb1x00 *sa11xx_ucb1x00)
++{
++ //unsigned long flags;
++
++ /* Setup DMA stuff */
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK].id = "UCB1x00 out";
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK;
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = sa11xx_ucb1x00->ucb1x00->ucb->mcp->dma_audio_wr;
++
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE].id = "UCB1x00 in";
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE;
++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = sa11xx_ucb1x00->ucb1x00->ucb->mcp->dma_audio_rd;
++}
++
++#if 0
++static void sa11xx_ucb1x00_audio_shutdown(struct sa11xx_ucb1x00 *sa11xx_ucb1x00)
++{
++ /* mute on */
++// set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
++
++ /* disable the audio power and all signals leading to the audio chip */
++// l3_close(sa11xx_uda1341->uda1341);
++// Ser4SSCR0 = 0;
++// clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
++
++ /* power off and mute off */
++ /* FIXME - is muting off necesary??? */
++
++// clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
++// clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
++}
++#endif
++
++/* DMA stuff */
++
++/*
++ * these are the address and sizes used to fill the xmit buffer
++ * so we can get a clock in record only mode
++ */
++//#define FORCE_CLOCK_ADDR (dma_addr_t)FLUSH_BASE_PHYS
++//#define FORCE_CLOCK_SIZE 4096 // was 2048
++
++// FIXME Why this value exactly - wrote comment
++#define DMA_BUF_SIZE 8176 /* <= MAX_DMA_SIZE from asm/arch-sa1100/dma.h */
++
++static int audio_dma_request(struct audio_stream *s, void (*callback)(void *))
++{
++ int ret;
++
++// printk(KERN_INFO "audio_dma_request stream=%08X\n", s);
++
++ ret = sa1100_request_dma(s->dma_dev, s->id, callback, s, &s->dma_regs);
++ if (ret < 0)
++ printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
++ return ret;
++}
++
++static void audio_dma_free(struct audio_stream *s)
++{
++// printk(KERN_INFO "audio_dma_free stream=%08X\n", s);
++ sa1100_free_dma(s->dma_regs);
++ s->dma_regs = 0;
++}
++
++static u_int audio_get_dma_pos(struct audio_stream *s)
++{
++ struct snd_pcm_substream *substream = s->stream;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ unsigned int offset;
++ unsigned long flags;
++ dma_addr_t addr;
++
++ // this must be called w/ interrupts locked out see dma-sa1100.c in the kernel
++ spin_lock_irqsave(&s->dma_lock, flags);
++ addr = sa1100_get_dma_pos((s)->dma_regs);
++ offset = addr - runtime->dma_addr;
++ spin_unlock_irqrestore(&s->dma_lock, flags);
++
++// printk(KERN_INFO "audio_get_dma_pos real_offset=%d\n", offset);
++
++ offset = bytes_to_frames(runtime,offset);
++ if (offset >= runtime->buffer_size)
++ offset = 0;
++
++// printk(KERN_INFO "audio_get_dma_pos stream=%08X pos=%d\n", s, offset);
++
++ return offset;
++}
++
++/*
++ * this stops the dma and clears the dma ptrs
++ */
++static void audio_stop_dma(struct audio_stream *s)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&s->dma_lock, flags);
++ s->active = 0;
++ s->period = 0;
++ /* this stops the dma channel and clears the buffer ptrs */
++ sa1100_clear_dma(s->dma_regs);
++ spin_unlock_irqrestore(&s->dma_lock, flags);
++}
++
++static void audio_process_dma(struct audio_stream *s)
++{
++ struct snd_pcm_substream *substream = s->stream;
++ struct snd_pcm_runtime *runtime;
++ unsigned int dma_size;
++ unsigned int offset;
++ int ret;
++
++// printk(KERN_ERR "audio_process_dma called periods=%d period=%d\n", s->periods, s->period);
++
++ /* must be set here - only valid for running streams, not for forced_clock dma fills */
++ runtime = substream->runtime;
++ while (s->active && s->periods < runtime->periods) {
++ dma_size = frames_to_bytes(runtime, runtime->period_size);
++ if (s->old_offset) {
++ /* a little trick, we need resume from old position */
++ offset = frames_to_bytes(runtime, s->old_offset - 1);
++ s->old_offset = 0;
++ s->periods = 0;
++ s->period = offset / dma_size;
++ offset %= dma_size;
++ dma_size = dma_size - offset;
++ if (!dma_size)
++ continue; /* special case */
++ } else {
++ offset = dma_size * s->period;
++ snd_assert(dma_size <= DMA_BUF_SIZE, );
++ }
++// printk(KERN_INFO "start addr=%08X size=%d\n", runtime->dma_addr + offset, dma_size);
++ ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size);
++ if (ret) {
++// printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i)\n", ret);
++ return;
++ }
++
++ s->period++;
++ s->period %= runtime->periods;
++ s->periods++;
++ }
++}
++
++static void audio_dma_callback(void *data)
++{
++ struct audio_stream *s = data;
++
++// printk(KERN_INFO "audio_dma_callback called stream=%08X\n", s);
++
++ /*
++ * If we are getting a callback for an active stream then we inform
++ * the PCM middle layer we've finished a period
++ */
++ if (s->active) {
++// printk(KERN_INFO "audio_dma_callback period elapsed\n");
++ snd_pcm_period_elapsed(s->stream);
++ }
++
++ spin_lock(&s->dma_lock);
++ if (s->periods > 0) {
++ s->periods--;
++ }
++ audio_process_dma(s);
++ spin_unlock(&s->dma_lock);
++}
++
++static void sa11xx_ucb1x00_enable_audio(struct ucb1x00* ucb)
++{
++ uint32_t reg;
++ reg = ucb1x00_reg_read(ucb, UCB_AC_B);
++ reg |= UCB_AC_B_OUT_ENA;
++ ucb1x00_reg_write(ucb, UCB_AC_B, reg);
++}
++
++static void sa11xx_ucb1x00_disable_audio(struct ucb1x00* ucb)
++{
++ uint32_t reg;
++ reg = ucb1x00_reg_read(ucb, UCB_AC_B);
++ reg &= ~(UCB_AC_B_OUT_ENA);
++ ucb1x00_reg_write(ucb, UCB_AC_B, reg);
++}
++
++/* PCM setting */
++
++/* trigger & timer */
++static int snd_sa11xx_ucb1x00_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++ int stream_id = substream->pstr->stream;
++ struct audio_stream *s = &chip->s[stream_id];
++ int err = 0;
++
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_trigger called\n");
++
++ /* note local interrupts are already disabled in the midlevel code */
++ spin_lock(&s->dma_lock);
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ /* requested stream startup */
++ s->active = 1;
++ sa11xx_ucb1x00_enable_audio(chip->ucb1x00->ucb);
++ audio_process_dma(s);
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ /* requested stream shutdown */
++ sa11xx_ucb1x00_disable_audio(chip->ucb1x00->ucb);
++ audio_stop_dma(s);
++ break;
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ s->active = 0;
++ sa1100_stop_dma(s->dma_regs);
++ s->old_offset = audio_get_dma_pos(s) + 1;
++#ifdef HH_VERSION
++ sa1100_dma_flush_all(s->dma_regs);
++#else
++ //FIXME - DMA API
++#endif
++ s->periods = 0;
++ break;
++ case SNDRV_PCM_TRIGGER_RESUME:
++ s->active = 1;
++ audio_process_dma(s);
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ sa1100_stop_dma(s->dma_regs);
++ s->active = 0;
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ s->active = 1;
++ if (s->old_offset) {
++ audio_process_dma(s);
++ break;
++ }
++ sa1100_resume_dma(s->dma_regs);
++ break;
++ default:
++ err = -EINVAL;
++ break;
++ }
++ spin_unlock(&s->dma_lock);
++ return err;
++}
++
++static int snd_sa11xx_ucb1x00_prepare(struct snd_pcm_substream *substream)
++{
++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct audio_stream *s = &chip->s[substream->pstr->stream];
++
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_prepare called\n");
++
++ /* set requested samplerate */
++ sa11xx_ucb1x00_set_samplerate(chip, runtime->rate);
++
++ s->period = 0;
++ s->periods = 0;
++
++ return 0;
++}
++
++static snd_pcm_uframes_t snd_sa11xx_ucb1x00_pointer(struct snd_pcm_substream *substream)
++{
++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_pointer called\n");
++ return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
++}
++
++static struct snd_pcm_hardware snd_sa11xx_ucb1x00_hw =
++{
++ .info = (SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
++/* .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
++ SNDRV_PCM_RATE_KNOT),*/
++ .rate_min = 5000,
++ .rate_max = 48000,
++ .channels_min = 1,
++ .channels_max = 1,
++ .buffer_bytes_max = 64*1024,
++ .period_bytes_min = 4096,
++ .period_bytes_max = DMA_BUF_SIZE,
++ .periods_min = 2,
++ .periods_max = 255,
++ .fifo_size = 0,
++};
++
++#if 0
++static struct snd_pcm_hardware snd_sa11xx_ucb1x00_playback =
++{
++ .info = (SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
++ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
++ SNDRV_PCM_RATE_KNOT),
++ .rate_min = 8000,
++ .rate_max = 48000,
++ .channels_min = 2,
++ .channels_max = 2,
++ .buffer_bytes_max = 64*1024,
++ .period_bytes_min = 128,
++ .period_bytes_max = DMA_BUF_SIZE,
++ .periods_min = 2,
++ .periods_max = 8,
++ .fifo_size = 0,
++};
++#endif
++
++static int snd_card_sa11xx_ucb1x00_open(struct snd_pcm_substream *substream)
++{
++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ int stream_id = substream->pstr->stream;
++ //int err;
++
++ chip->s[stream_id].stream = substream;
++
++// printk(KERN_INFO "snd_card_sa11xx_ucb1x00_open called substream=%08X\n", substream);
++
++ runtime->hw = snd_sa11xx_ucb1x00_hw;
++ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
++ }
++ else {
++ }
++/*
++ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
++ return err;
++ }
++*/
++/* if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0) {
++ return err;
++ }
++*/
++ return 0;
++}
++
++static int snd_card_sa11xx_ucb1x00_close(struct snd_pcm_substream *substream)
++{
++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++
++ chip->s[substream->pstr->stream].stream = NULL;
++// printk(KERN_INFO "snd_card_sa11xx_ucb1x00_close called substream=%08X\n", substream);
++ return 0;
++}
++
++/* HW params & free */
++
++static int snd_sa11xx_ucb1x00_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *hw_params)
++{
++// struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++// struct ucb1x00* ucb = chip->ucb1x00->ucb;
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_hw_params called substream=%08X\n", substream);
++
++
++ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
++}
++
++static int snd_sa11xx_ucb1x00_hw_free(struct snd_pcm_substream *substream)
++{
++// struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream);
++// struct ucb1x00* ucb = chip->ucb1x00->ucb;
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_hw_free called substream=%08X\n", substream);
++
++ return snd_pcm_lib_free_pages(substream);
++}
++
++static struct snd_pcm_ops snd_card_sa11xx_ucb1x00_playback_ops = {
++ .open = snd_card_sa11xx_ucb1x00_open,
++ .close = snd_card_sa11xx_ucb1x00_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = snd_sa11xx_ucb1x00_hw_params,
++ .hw_free = snd_sa11xx_ucb1x00_hw_free,
++ .prepare = snd_sa11xx_ucb1x00_prepare,
++ .trigger = snd_sa11xx_ucb1x00_trigger,
++ .pointer = snd_sa11xx_ucb1x00_pointer,
++};
++
++static struct snd_pcm_ops snd_card_sa11xx_ucb1x00_capture_ops = {
++ .open = snd_card_sa11xx_ucb1x00_open,
++ .close = snd_card_sa11xx_ucb1x00_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = snd_sa11xx_ucb1x00_hw_params,
++ .hw_free = snd_sa11xx_ucb1x00_hw_free,
++ .prepare = snd_sa11xx_ucb1x00_prepare,
++ .trigger = snd_sa11xx_ucb1x00_trigger,
++ .pointer = snd_sa11xx_ucb1x00_pointer,
++};
++
++static int __init snd_card_sa11xx_ucb1x00_pcm(struct sa11xx_ucb1x00 *sa11xx_ucb1x00, int device)
++{
++ struct snd_pcm *pcm;
++ int err;
++
++ if ((err = snd_pcm_new(sa11xx_ucb1x00->card, "UCB1x00 PCM", device, 1, 1, &pcm)) < 0) {
++ return err;
++ }
++
++ sa11xx_ucb1x00_audio_init(sa11xx_ucb1x00);
++
++
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_ucb1x00_playback_ops);
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_ucb1x00_capture_ops);
++ pcm->private_data = sa11xx_ucb1x00;
++ pcm->info_flags = 0;
++ strcpy(pcm->name, "UCB1x00 PCM");
++
++ err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
++ NULL, 64*1024, 64*1024);
++// printk(KERN_INFO "snd_pcm_lib_preallocate_pages_for_all returns %d\n", err);
++
++
++ /* setup DMA controller */
++ audio_dma_request(&sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK], audio_dma_callback);
++ audio_dma_request(&sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE], audio_dma_callback);
++
++ sa11xx_ucb1x00->pcm = pcm;
++
++ return 0;
++}
++
++#if 0
++#ifdef CONFIG_PM
++
++static int snd_sa11xx_ucb1x00_suspend(struct platform_device *devptr,
++ pm_message_t state)
++{
++ struct snd_card *card = platform_get_drvdata(devptr);
++ struct sa11xx_ucb1x00 *chip = card->private_data;
++
++ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
++ snd_pcm_suspend_all(chip->pcm);
++#ifdef HH_VERSION
++ sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
++ sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
++#else
++ //FIXME
++#endif
++ l3_command(chip->ucb1x00, CMD_SUSPEND, NULL);
++ sa11xx_uda1341_audio_shutdown(chip);
++
++ return 0;
++}
++
++static int snd_sa11xx_ucb1x00_resume(struct platform_device *devptr)
++{
++ struct snd_card *card = platform_get_drvdata(devptr);
++ struct sa11xx_ucb1x00 *chip = card->private_data;
++
++ sa11xx_ucb1x00_audio_init(chip);
++ l3_command(chip->ucb1x00, CMD_RESUME, NULL);
++#ifdef HH_VERSION
++ sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
++ sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
++#else
++ //FIXME
++#endif
++ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
++ return 0;
++}
++#endif /* COMFIG_PM */
++#endif
++
++void snd_sa11xx_ucb1x00_free(struct snd_card *card)
++{
++ struct sa11xx_ucb1x00 *chip = card->private_data;
++
++// printk(KERN_INFO "snd_sa11xx_ucb1x00_free called\n");
++
++ if (&chip->s[SNDRV_PCM_STREAM_PLAYBACK]) {
++ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
++ }
++ if (&chip->s[SNDRV_PCM_STREAM_CAPTURE]) {
++ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
++ }
++}
++
++static int ucb1x00_dev_free(struct snd_device *device)
++{
++ return 0;
++}
++
++static int snd_chip_ucb1x00_mixer_new(struct snd_card *card)
++{
++ static struct snd_device_ops ops = {
++ .dev_free = ucb1x00_dev_free,
++ };
++
++ struct sa11xx_ucb1x00 *chip = card->private_data;
++ int idx;
++ int err;
++
++ snd_assert(card != NULL, return -EINVAL);
++
++ for (idx = 0; idx < ARRAY_SIZE(snd_ucb1x00_controls); idx++) {
++ if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ucb1x00_controls[idx], chip))) < 0) {
++ return err;
++ }
++ }
++
++ if ((err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops)) < 0) {
++ return err;
++ }
++
++ strcpy(card->mixername, "UCB1x00 Mixer");
++ // ((struct ucb1x00_dev *)clnt->driver_data)->card = card;
++
++ return 0;
++}
++
++static int __init sa11xx_ucb1x00_audio_add(struct ucb1x00_dev* dev)
++{
++ int err;
++ struct snd_card *card;
++ struct sa11xx_ucb1x00 *chip;
++
++ /* register the soundcard */
++ card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct sa11xx_ucb1x00));
++ if (card == NULL) {
++ return -ENOMEM;
++ }
++
++ ucb1x00_enable(dev->ucb);
++
++ chip = card->private_data;
++ spin_lock_init(&chip->s[0].dma_lock);
++ spin_lock_init(&chip->s[1].dma_lock);
++
++ card->private_free = snd_sa11xx_ucb1x00_free;
++ chip->card = card;
++ chip->samplerate = AUDIO_RATE_DEFAULT;
++ chip->ucb1x00 = dev;
++ chip->pdev = &dev->ucb->mcp->attached_device;
++ dev->priv = chip;
++
++ // mixer
++ if ((err = snd_chip_ucb1x00_mixer_new(card)))
++ goto nodev;
++
++ // PCM
++ if ((err = snd_card_sa11xx_ucb1x00_pcm(chip, 0)) < 0)
++ goto nodev;
++
++ strcpy(card->driver, "UCB1x00");
++ strcpy(card->shortname, "Simpad UCB1x00");
++ sprintf(card->longname, "Siemens Simpad with Philips UCB1x00");
++
++ snd_card_set_dev(card, chip->pdev);
++
++ if ((err = snd_card_register(card)) == 0) {
++ printk( KERN_INFO "Simpad audio support initialized 2\n" );
++ return 0;
++ }
++
++ nodev:
++ snd_card_free(card);
++ return err;
++}
++
++static void sa11xx_ucb1x00_audio_remove(struct ucb1x00_dev* dev)
++{
++ struct sa11xx_ucb1x00* chip = dev->priv;
++ printk(KERN_INFO "sa11xx_ucb1x00_audio_remove called\n");
++ ucb1x00_disable(dev->ucb);
++ snd_card_free(chip->card);
++}
++
++static int sa11xx_ucb1x00_audio_resume(struct ucb1x00_dev* dev)
++{
++ printk(KERN_INFO "sa11xx_ucb1x00_audio_resume called\n");
++ return 0;
++}
++
++/* ---- GOOD ----------------------------------------------------- */
++
++static struct ucb1x00_driver sa11xx_ucb1x00_audio_driver = {
++ .add = sa11xx_ucb1x00_audio_add,
++ .remove = sa11xx_ucb1x00_audio_remove,
++ .resume = sa11xx_ucb1x00_audio_resume,
++};
++
++static int __init sa11xx_ucb1x00_init(void)
++{
++ return ucb1x00_register_driver(&sa11xx_ucb1x00_audio_driver);
++}
++
++static void __exit sa11xx_ucb1x00_exit(void)
++{
++ ucb1x00_unregister_driver(&sa11xx_ucb1x00_audio_driver);
++}
++
++module_init(sa11xx_ucb1x00_init);
++module_exit(sa11xx_ucb1x00_exit);
++
+Index: linux-2.6.24/drivers/mfd/ucb1x00.h
+===================================================================
+--- linux-2.6.24.orig/drivers/mfd/ucb1x00.h 2008-12-23 22:00:03.000000000 +0100
++++ /dev/null 1970-01-01 00:00:00.000000000 +0000
+@@ -1,255 +0,0 @@
+-/*
+- * linux/drivers/mfd/ucb1x00.h
+- *
+- * Copyright (C) 2001 Russell King, All Rights Reserved.
+- *
+- * 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.
+- */
+-#ifndef UCB1200_H
+-#define UCB1200_H
+-
+-#define UCB_IO_DATA 0x00
+-#define UCB_IO_DIR 0x01
+-
+-#define UCB_IO_0 (1 << 0)
+-#define UCB_IO_1 (1 << 1)
+-#define UCB_IO_2 (1 << 2)
+-#define UCB_IO_3 (1 << 3)
+-#define UCB_IO_4 (1 << 4)
+-#define UCB_IO_5 (1 << 5)
+-#define UCB_IO_6 (1 << 6)
+-#define UCB_IO_7 (1 << 7)
+-#define UCB_IO_8 (1 << 8)
+-#define UCB_IO_9 (1 << 9)
+-
+-#define UCB_IE_RIS 0x02
+-#define UCB_IE_FAL 0x03
+-#define UCB_IE_STATUS 0x04
+-#define UCB_IE_CLEAR 0x04
+-#define UCB_IE_ADC (1 << 11)
+-#define UCB_IE_TSPX (1 << 12)
+-#define UCB_IE_TSMX (1 << 13)
+-#define UCB_IE_TCLIP (1 << 14)
+-#define UCB_IE_ACLIP (1 << 15)
+-
+-#define UCB_IRQ_TSPX 12
+-
+-#define UCB_TC_A 0x05
+-#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */
+-#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */
+-
+-#define UCB_TC_B 0x06
+-#define UCB_TC_B_VOICE_ENA (1 << 3)
+-#define UCB_TC_B_CLIP (1 << 4)
+-#define UCB_TC_B_ATT (1 << 6)
+-#define UCB_TC_B_SIDE_ENA (1 << 11)
+-#define UCB_TC_B_MUTE (1 << 13)
+-#define UCB_TC_B_IN_ENA (1 << 14)
+-#define UCB_TC_B_OUT_ENA (1 << 15)
+-
+-#define UCB_AC_A 0x07
+-#define UCB_AC_B 0x08
+-#define UCB_AC_B_LOOP (1 << 8)
+-#define UCB_AC_B_MUTE (1 << 13)
+-#define UCB_AC_B_IN_ENA (1 << 14)
+-#define UCB_AC_B_OUT_ENA (1 << 15)
+-
+-#define UCB_TS_CR 0x09
+-#define UCB_TS_CR_TSMX_POW (1 << 0)
+-#define UCB_TS_CR_TSPX_POW (1 << 1)
+-#define UCB_TS_CR_TSMY_POW (1 << 2)
+-#define UCB_TS_CR_TSPY_POW (1 << 3)
+-#define UCB_TS_CR_TSMX_GND (1 << 4)
+-#define UCB_TS_CR_TSPX_GND (1 << 5)
+-#define UCB_TS_CR_TSMY_GND (1 << 6)
+-#define UCB_TS_CR_TSPY_GND (1 << 7)
+-#define UCB_TS_CR_MODE_INT (0 << 8)
+-#define UCB_TS_CR_MODE_PRES (1 << 8)
+-#define UCB_TS_CR_MODE_POS (2 << 8)
+-#define UCB_TS_CR_BIAS_ENA (1 << 11)
+-#define UCB_TS_CR_TSPX_LOW (1 << 12)
+-#define UCB_TS_CR_TSMX_LOW (1 << 13)
+-
+-#define UCB_ADC_CR 0x0a
+-#define UCB_ADC_SYNC_ENA (1 << 0)
+-#define UCB_ADC_VREFBYP_CON (1 << 1)
+-#define UCB_ADC_INP_TSPX (0 << 2)
+-#define UCB_ADC_INP_TSMX (1 << 2)
+-#define UCB_ADC_INP_TSPY (2 << 2)
+-#define UCB_ADC_INP_TSMY (3 << 2)
+-#define UCB_ADC_INP_AD0 (4 << 2)
+-#define UCB_ADC_INP_AD1 (5 << 2)
+-#define UCB_ADC_INP_AD2 (6 << 2)
+-#define UCB_ADC_INP_AD3 (7 << 2)
+-#define UCB_ADC_EXT_REF (1 << 5)
+-#define UCB_ADC_START (1 << 7)
+-#define UCB_ADC_ENA (1 << 15)
+-
+-#define UCB_ADC_DATA 0x0b
+-#define UCB_ADC_DAT_VAL (1 << 15)
+-#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5)
+-
+-#define UCB_ID 0x0c
+-#define UCB_ID_1200 0x1004
+-#define UCB_ID_1300 0x1005
+-#define UCB_ID_TC35143 0x9712
+-
+-#define UCB_MODE 0x0d
+-#define UCB_MODE_DYN_VFLAG_ENA (1 << 12)
+-#define UCB_MODE_AUD_OFF_CAN (1 << 13)
+-
+-#include "mcp.h"
+-
+-struct ucb1x00_irq {
+- void *devid;
+- void (*fn)(int, void *);
+-};
+-
+-struct ucb1x00 {
+- spinlock_t lock;
+- struct mcp *mcp;
+- unsigned int irq;
+- struct semaphore adc_sem;
+- spinlock_t io_lock;
+- u16 id;
+- u16 io_dir;
+- u16 io_out;
+- u16 adc_cr;
+- u16 irq_fal_enbl;
+- u16 irq_ris_enbl;
+- struct ucb1x00_irq irq_handler[16];
+- struct class_device cdev;
+- struct list_head node;
+- struct list_head devs;
+-};
+-
+-struct ucb1x00_driver;
+-
+-struct ucb1x00_dev {
+- struct list_head dev_node;
+- struct list_head drv_node;
+- struct ucb1x00 *ucb;
+- struct ucb1x00_driver *drv;
+- void *priv;
+-};
+-
+-struct ucb1x00_driver {
+- struct list_head node;
+- struct list_head devs;
+- int (*add)(struct ucb1x00_dev *dev);
+- void (*remove)(struct ucb1x00_dev *dev);
+- int (*suspend)(struct ucb1x00_dev *dev, pm_message_t state);
+- int (*resume)(struct ucb1x00_dev *dev);
+-};
+-
+-#define classdev_to_ucb1x00(cd) container_of(cd, struct ucb1x00, cdev)
+-
+-int ucb1x00_register_driver(struct ucb1x00_driver *);
+-void ucb1x00_unregister_driver(struct ucb1x00_driver *);
+-
+-/**
+- * ucb1x00_clkrate - return the UCB1x00 SIB clock rate
+- * @ucb: UCB1x00 structure describing chip
+- *
+- * Return the SIB clock rate in Hz.
+- */
+-static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb)
+-{
+- return mcp_get_sclk_rate(ucb->mcp);
+-}
+-
+-/**
+- * ucb1x00_enable - enable the UCB1x00 SIB clock
+- * @ucb: UCB1x00 structure describing chip
+- *
+- * Enable the SIB clock. This can be called multiple times.
+- */
+-static inline void ucb1x00_enable(struct ucb1x00 *ucb)
+-{
+- mcp_enable(ucb->mcp);
+-}
+-
+-/**
+- * ucb1x00_disable - disable the UCB1x00 SIB clock
+- * @ucb: UCB1x00 structure describing chip
+- *
+- * Disable the SIB clock. The SIB clock will only be disabled
+- * when the number of ucb1x00_enable calls match the number of
+- * ucb1x00_disable calls.
+- */
+-static inline void ucb1x00_disable(struct ucb1x00 *ucb)
+-{
+- mcp_disable(ucb->mcp);
+-}
+-
+-/**
+- * ucb1x00_reg_write - write a UCB1x00 register
+- * @ucb: UCB1x00 structure describing chip
+- * @reg: UCB1x00 4-bit register index to write
+- * @val: UCB1x00 16-bit value to write
+- *
+- * Write the UCB1x00 register @reg with value @val. The SIB
+- * clock must be running for this function to return.
+- */
+-static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val)
+-{
+- mcp_reg_write(ucb->mcp, reg, val);
+-}
+-
+-/**
+- * ucb1x00_reg_read - read a UCB1x00 register
+- * @ucb: UCB1x00 structure describing chip
+- * @reg: UCB1x00 4-bit register index to write
+- *
+- * Read the UCB1x00 register @reg and return its value. The SIB
+- * clock must be running for this function to return.
+- */
+-static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg)
+-{
+- return mcp_reg_read(ucb->mcp, reg);
+-}
+-/**
+- * ucb1x00_set_audio_divisor -
+- * @ucb: UCB1x00 structure describing chip
+- * @div: SIB clock divisor
+- */
+-static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div)
+-{
+- mcp_set_audio_divisor(ucb->mcp, div);
+-}
+-
+-/**
+- * ucb1x00_set_telecom_divisor -
+- * @ucb: UCB1x00 structure describing chip
+- * @div: SIB clock divisor
+- */
+-static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div)
+-{
+- mcp_set_telecom_divisor(ucb->mcp, div);
+-}
+-
+-void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int);
+-void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int);
+-unsigned int ucb1x00_io_read(struct ucb1x00 *ucb);
+-
+-#define UCB_NOSYNC (0)
+-#define UCB_SYNC (1)
+-
+-unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync);
+-void ucb1x00_adc_enable(struct ucb1x00 *ucb);
+-void ucb1x00_adc_disable(struct ucb1x00 *ucb);
+-
+-/*
+- * Which edges of the IRQ do you want to control today?
+- */
+-#define UCB_RISING (1 << 0)
+-#define UCB_FALLING (1 << 1)
+-
+-int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid);
+-void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges);
+-void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges);
+-int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid);
+-
+-#endif
+Index: linux-2.6.24/sound/arm/Kconfig
+===================================================================
+--- linux-2.6.24.orig/sound/arm/Kconfig 2008-12-23 21:48:54.000000000 +0100
++++ linux-2.6.24/sound/arm/Kconfig 2008-12-23 21:50:13.000000000 +0100
+@@ -14,6 +14,16 @@
+ To compile this driver as a module, choose M here: the module
+ will be called snd-sa11xx-uda1341.
+
++config SND_SA11XX_UCB1X00
++ tristate "SA11xx UCB1x00 driver (Simpad)"
++ depends on ARCH_SA1100 && SND
++ select SND_PCM
++ help
++ Say Y here if you have a Simpad handheld computer
++
++ To compile this driver as a module, choose M here: the module
++ will be called snd-sa11xx-ucb1x00.
++
+ config SND_ARMAACI
+ tristate "ARM PrimeCell PL041 AC Link support"
+ depends on SND && ARM_AMBA
+Index: linux-2.6.24/sound/arm/Makefile
+===================================================================
+--- linux-2.6.24.orig/sound/arm/Makefile 2008-12-23 21:48:56.000000000 +0100
++++ linux-2.6.24/sound/arm/Makefile 2008-12-23 21:57:32.000000000 +0100
+@@ -5,6 +5,9 @@
+ obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o
+ snd-sa11xx-uda1341-objs := sa11xx-uda1341.o
+
++obj-$(CONFIG_SND_SA11XX_UCB1X00) += snd-sa11xx-ucb1x00.o
++snd-sa11xx-ucb1x00-objs := sa11xx-ucb1x00.o
++
+ obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o
+ snd-aaci-objs := aaci.o devdma.o
+
+Index: linux-2.6.24/drivers/mfd/ucb1x00-assabet.c
+===================================================================
+--- linux-2.6.24.orig/drivers/mfd/ucb1x00-assabet.c 2008-12-23 22:03:30.000000000 +0100
++++ linux-2.6.24/drivers/mfd/ucb1x00-assabet.c 2008-12-23 22:03:50.000000000 +0100
+@@ -17,7 +17,7 @@
+
+ #include <asm/dma.h>
+
+-#include "ucb1x00.h"
++#include <linux/ucb1x00.h>
+
+ #define UCB1X00_ATTR(name,input)\
+ static ssize_t name##_show(struct class_device *dev, char *buf) \
+Index: linux-2.6.24/drivers/mfd/ucb1x00-core.c
+===================================================================
+--- linux-2.6.24.orig/drivers/mfd/ucb1x00-core.c 2008-12-23 22:03:02.000000000 +0100
++++ linux-2.6.24/drivers/mfd/ucb1x00-core.c 2008-12-23 22:09:00.000000000 +0100
+@@ -28,7 +28,7 @@
+ #include <asm/dma.h>
+ #include <asm/hardware.h>
+
+-#include "ucb1x00.h"
++#include <linux/mfd/ucb1x00.h>
+
+ static DEFINE_MUTEX(ucb1x00_mutex);
+ static LIST_HEAD(ucb1x00_drivers);
+Index: linux-2.6.24/drivers/mfd/ucb1x00-switches.c
+===================================================================
+--- linux-2.6.24.orig/drivers/mfd/ucb1x00-switches.c 2008-12-23 22:03:36.000000000 +0100
++++ linux-2.6.24/drivers/mfd/ucb1x00-switches.c 2008-12-23 22:09:19.000000000 +0100
+@@ -35,7 +35,7 @@
+
+ #include <asm/dma.h>
+
+-#include "ucb1x00.h"
++#include <linux/mfd/ucb1x00.h>
+
+ #define KEY_PRESS 1
+ #define KEY_RELEASE 0
+Index: linux-2.6.24/drivers/mfd/ucb1x00-ts.c
+===================================================================
+--- linux-2.6.24.orig/drivers/mfd/ucb1x00-ts.c 2008-12-23 22:03:40.000000000 +0100
++++ linux-2.6.24/drivers/mfd/ucb1x00-ts.c 2008-12-23 22:09:11.000000000 +0100
+@@ -40,7 +40,7 @@
+ #include <asm/arch/collie.h>
+ #include <asm/mach-types.h>
+
+-#include "ucb1x00.h"
++#include <linux/mfd/ucb1x00.h>
+
+
+ struct ucb1x00_ts {
+Index: linux-2.6.24/include/linux/mfd/mcp.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.24/include/linux/mfd/mcp.h 2008-12-23 22:06:04.000000000 +0100
+@@ -0,0 +1,66 @@
++/*
++ * linux/drivers/mfd/mcp.h
++ *
++ * Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * 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.
++ */
++#ifndef MCP_H
++#define MCP_H
++
++struct mcp_ops;
++
++struct mcp {
++ struct module *owner;
++ struct mcp_ops *ops;
++ spinlock_t lock;
++ int use_count;
++ unsigned int sclk_rate;
++ unsigned int rw_timeout;
++ dma_device_t dma_audio_rd;
++ dma_device_t dma_audio_wr;
++ dma_device_t dma_telco_rd;
++ dma_device_t dma_telco_wr;
++ struct device attached_device;
++};
++
++struct mcp_ops {
++ void (*set_telecom_divisor)(struct mcp *, unsigned int);
++ void (*set_audio_divisor)(struct mcp *, unsigned int);
++ void (*reg_write)(struct mcp *, unsigned int, unsigned int);
++ unsigned int (*reg_read)(struct mcp *, unsigned int);
++ void (*enable)(struct mcp *);
++ void (*disable)(struct mcp *);
++};
++
++void mcp_set_telecom_divisor(struct mcp *, unsigned int);
++void mcp_set_audio_divisor(struct mcp *, unsigned int);
++void mcp_reg_write(struct mcp *, unsigned int, unsigned int);
++unsigned int mcp_reg_read(struct mcp *, unsigned int);
++void mcp_enable(struct mcp *);
++void mcp_disable(struct mcp *);
++#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate)
++
++struct mcp *mcp_host_alloc(struct device *, size_t);
++int mcp_host_register(struct mcp *);
++void mcp_host_unregister(struct mcp *);
++
++struct mcp_driver {
++ struct device_driver drv;
++ int (*probe)(struct mcp *);
++ void (*remove)(struct mcp *);
++ int (*suspend)(struct mcp *, pm_message_t);
++ int (*resume)(struct mcp *);
++};
++
++int mcp_driver_register(struct mcp_driver *);
++void mcp_driver_unregister(struct mcp_driver *);
++
++#define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device)
++#define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d)
++
++#define mcp_priv(mcp) ((void *)((mcp)+1))
++
++#endif
+Index: linux-2.6.24/drivers/mfd/mcp.h
+===================================================================
+--- linux-2.6.24.orig/drivers/mfd/mcp.h 2008-12-23 22:06:30.000000000 +0100
++++ /dev/null 1970-01-01 00:00:00.000000000 +0000
+@@ -1,66 +0,0 @@
+-/*
+- * linux/drivers/mfd/mcp.h
+- *
+- * Copyright (C) 2001 Russell King, All Rights Reserved.
+- *
+- * 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.
+- */
+-#ifndef MCP_H
+-#define MCP_H
+-
+-struct mcp_ops;
+-
+-struct mcp {
+- struct module *owner;
+- struct mcp_ops *ops;
+- spinlock_t lock;
+- int use_count;
+- unsigned int sclk_rate;
+- unsigned int rw_timeout;
+- dma_device_t dma_audio_rd;
+- dma_device_t dma_audio_wr;
+- dma_device_t dma_telco_rd;
+- dma_device_t dma_telco_wr;
+- struct device attached_device;
+-};
+-
+-struct mcp_ops {
+- void (*set_telecom_divisor)(struct mcp *, unsigned int);
+- void (*set_audio_divisor)(struct mcp *, unsigned int);
+- void (*reg_write)(struct mcp *, unsigned int, unsigned int);
+- unsigned int (*reg_read)(struct mcp *, unsigned int);
+- void (*enable)(struct mcp *);
+- void (*disable)(struct mcp *);
+-};
+-
+-void mcp_set_telecom_divisor(struct mcp *, unsigned int);
+-void mcp_set_audio_divisor(struct mcp *, unsigned int);
+-void mcp_reg_write(struct mcp *, unsigned int, unsigned int);
+-unsigned int mcp_reg_read(struct mcp *, unsigned int);
+-void mcp_enable(struct mcp *);
+-void mcp_disable(struct mcp *);
+-#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate)
+-
+-struct mcp *mcp_host_alloc(struct device *, size_t);
+-int mcp_host_register(struct mcp *);
+-void mcp_host_unregister(struct mcp *);
+-
+-struct mcp_driver {
+- struct device_driver drv;
+- int (*probe)(struct mcp *);
+- void (*remove)(struct mcp *);
+- int (*suspend)(struct mcp *, pm_message_t);
+- int (*resume)(struct mcp *);
+-};
+-
+-int mcp_driver_register(struct mcp_driver *);
+-void mcp_driver_unregister(struct mcp_driver *);
+-
+-#define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device)
+-#define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d)
+-
+-#define mcp_priv(mcp) ((void *)((mcp)+1))
+-
+-#endif
+Index: linux-2.6.24/drivers/mfd/mcp-core.c
+===================================================================
+--- linux-2.6.24.orig/drivers/mfd/mcp-core.c 2008-12-23 22:07:01.000000000 +0100
++++ linux-2.6.24/drivers/mfd/mcp-core.c 2008-12-23 22:07:23.000000000 +0100
+@@ -21,7 +21,7 @@
+ #include <asm/dma.h>
+ #include <asm/system.h>
+
+-#include "mcp.h"
++#include <linux/mfd/mcp.h>
+
+ #define to_mcp(d) container_of(d, struct mcp, attached_device)
+ #define to_mcp_driver(d) container_of(d, struct mcp_driver, drv)
+Index: linux-2.6.24/drivers/mfd/mcp-sa11x0.c
+===================================================================
+--- linux-2.6.24.orig/drivers/mfd/mcp-sa11x0.c 2008-12-23 22:07:06.000000000 +0100
++++ linux-2.6.24/drivers/mfd/mcp-sa11x0.c 2008-12-23 22:07:33.000000000 +0100
+@@ -28,7 +28,7 @@
+
+ #include <asm/arch/assabet.h>
+
+-#include "mcp.h"
++#include <linux/mfd/mcp.h>
+
+ struct mcp_sa11x0 {
+ u32 mccr0;
+Index: linux-2.6.24/include/linux/mfd/ucb1x00.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.24/include/linux/mfd/ucb1x00.h 2008-12-23 22:08:46.000000000 +0100
+@@ -0,0 +1,255 @@
++/*
++ * linux/drivers/mfd/ucb1x00.h
++ *
++ * Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * 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.
++ */
++#ifndef UCB1200_H
++#define UCB1200_H
++
++#define UCB_IO_DATA 0x00
++#define UCB_IO_DIR 0x01
++
++#define UCB_IO_0 (1 << 0)
++#define UCB_IO_1 (1 << 1)
++#define UCB_IO_2 (1 << 2)
++#define UCB_IO_3 (1 << 3)
++#define UCB_IO_4 (1 << 4)
++#define UCB_IO_5 (1 << 5)
++#define UCB_IO_6 (1 << 6)
++#define UCB_IO_7 (1 << 7)
++#define UCB_IO_8 (1 << 8)
++#define UCB_IO_9 (1 << 9)
++
++#define UCB_IE_RIS 0x02
++#define UCB_IE_FAL 0x03
++#define UCB_IE_STATUS 0x04
++#define UCB_IE_CLEAR 0x04
++#define UCB_IE_ADC (1 << 11)
++#define UCB_IE_TSPX (1 << 12)
++#define UCB_IE_TSMX (1 << 13)
++#define UCB_IE_TCLIP (1 << 14)
++#define UCB_IE_ACLIP (1 << 15)
++
++#define UCB_IRQ_TSPX 12
++
++#define UCB_TC_A 0x05
++#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */
++#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */
++
++#define UCB_TC_B 0x06
++#define UCB_TC_B_VOICE_ENA (1 << 3)
++#define UCB_TC_B_CLIP (1 << 4)
++#define UCB_TC_B_ATT (1 << 6)
++#define UCB_TC_B_SIDE_ENA (1 << 11)
++#define UCB_TC_B_MUTE (1 << 13)
++#define UCB_TC_B_IN_ENA (1 << 14)
++#define UCB_TC_B_OUT_ENA (1 << 15)
++
++#define UCB_AC_A 0x07
++#define UCB_AC_B 0x08
++#define UCB_AC_B_LOOP (1 << 8)
++#define UCB_AC_B_MUTE (1 << 13)
++#define UCB_AC_B_IN_ENA (1 << 14)
++#define UCB_AC_B_OUT_ENA (1 << 15)
++
++#define UCB_TS_CR 0x09
++#define UCB_TS_CR_TSMX_POW (1 << 0)
++#define UCB_TS_CR_TSPX_POW (1 << 1)
++#define UCB_TS_CR_TSMY_POW (1 << 2)
++#define UCB_TS_CR_TSPY_POW (1 << 3)
++#define UCB_TS_CR_TSMX_GND (1 << 4)
++#define UCB_TS_CR_TSPX_GND (1 << 5)
++#define UCB_TS_CR_TSMY_GND (1 << 6)
++#define UCB_TS_CR_TSPY_GND (1 << 7)
++#define UCB_TS_CR_MODE_INT (0 << 8)
++#define UCB_TS_CR_MODE_PRES (1 << 8)
++#define UCB_TS_CR_MODE_POS (2 << 8)
++#define UCB_TS_CR_BIAS_ENA (1 << 11)
++#define UCB_TS_CR_TSPX_LOW (1 << 12)
++#define UCB_TS_CR_TSMX_LOW (1 << 13)
++
++#define UCB_ADC_CR 0x0a
++#define UCB_ADC_SYNC_ENA (1 << 0)
++#define UCB_ADC_VREFBYP_CON (1 << 1)
++#define UCB_ADC_INP_TSPX (0 << 2)
++#define UCB_ADC_INP_TSMX (1 << 2)
++#define UCB_ADC_INP_TSPY (2 << 2)
++#define UCB_ADC_INP_TSMY (3 << 2)
++#define UCB_ADC_INP_AD0 (4 << 2)
++#define UCB_ADC_INP_AD1 (5 << 2)
++#define UCB_ADC_INP_AD2 (6 << 2)
++#define UCB_ADC_INP_AD3 (7 << 2)
++#define UCB_ADC_EXT_REF (1 << 5)
++#define UCB_ADC_START (1 << 7)
++#define UCB_ADC_ENA (1 << 15)
++
++#define UCB_ADC_DATA 0x0b
++#define UCB_ADC_DAT_VAL (1 << 15)
++#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5)
++
++#define UCB_ID 0x0c
++#define UCB_ID_1200 0x1004
++#define UCB_ID_1300 0x1005
++#define UCB_ID_TC35143 0x9712
++
++#define UCB_MODE 0x0d
++#define UCB_MODE_DYN_VFLAG_ENA (1 << 12)
++#define UCB_MODE_AUD_OFF_CAN (1 << 13)
++
++#include "mcp.h"
++
++struct ucb1x00_irq {
++ void *devid;
++ void (*fn)(int, void *);
++};
++
++struct ucb1x00 {
++ spinlock_t lock;
++ struct mcp *mcp;
++ unsigned int irq;
++ struct semaphore adc_sem;
++ spinlock_t io_lock;
++ u16 id;
++ u16 io_dir;
++ u16 io_out;
++ u16 adc_cr;
++ u16 irq_fal_enbl;
++ u16 irq_ris_enbl;
++ struct ucb1x00_irq irq_handler[16];
++ struct class_device cdev;
++ struct list_head node;
++ struct list_head devs;
++};
++
++struct ucb1x00_driver;
++
++struct ucb1x00_dev {
++ struct list_head dev_node;
++ struct list_head drv_node;
++ struct ucb1x00 *ucb;
++ struct ucb1x00_driver *drv;
++ void *priv;
++};
++
++struct ucb1x00_driver {
++ struct list_head node;
++ struct list_head devs;
++ int (*add)(struct ucb1x00_dev *dev);
++ void (*remove)(struct ucb1x00_dev *dev);
++ int (*suspend)(struct ucb1x00_dev *dev, pm_message_t state);
++ int (*resume)(struct ucb1x00_dev *dev);
++};
++
++#define classdev_to_ucb1x00(cd) container_of(cd, struct ucb1x00, cdev)
++
++int ucb1x00_register_driver(struct ucb1x00_driver *);
++void ucb1x00_unregister_driver(struct ucb1x00_driver *);
++
++/**
++ * ucb1x00_clkrate - return the UCB1x00 SIB clock rate
++ * @ucb: UCB1x00 structure describing chip
++ *
++ * Return the SIB clock rate in Hz.
++ */
++static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb)
++{
++ return mcp_get_sclk_rate(ucb->mcp);
++}
++
++/**
++ * ucb1x00_enable - enable the UCB1x00 SIB clock
++ * @ucb: UCB1x00 structure describing chip
++ *
++ * Enable the SIB clock. This can be called multiple times.
++ */
++static inline void ucb1x00_enable(struct ucb1x00 *ucb)
++{
++ mcp_enable(ucb->mcp);
++}
++
++/**
++ * ucb1x00_disable - disable the UCB1x00 SIB clock
++ * @ucb: UCB1x00 structure describing chip
++ *
++ * Disable the SIB clock. The SIB clock will only be disabled
++ * when the number of ucb1x00_enable calls match the number of
++ * ucb1x00_disable calls.
++ */
++static inline void ucb1x00_disable(struct ucb1x00 *ucb)
++{
++ mcp_disable(ucb->mcp);
++}
++
++/**
++ * ucb1x00_reg_write - write a UCB1x00 register
++ * @ucb: UCB1x00 structure describing chip
++ * @reg: UCB1x00 4-bit register index to write
++ * @val: UCB1x00 16-bit value to write
++ *
++ * Write the UCB1x00 register @reg with value @val. The SIB
++ * clock must be running for this function to return.
++ */
++static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val)
++{
++ mcp_reg_write(ucb->mcp, reg, val);
++}
++
++/**
++ * ucb1x00_reg_read - read a UCB1x00 register
++ * @ucb: UCB1x00 structure describing chip
++ * @reg: UCB1x00 4-bit register index to write
++ *
++ * Read the UCB1x00 register @reg and return its value. The SIB
++ * clock must be running for this function to return.
++ */
++static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg)
++{
++ return mcp_reg_read(ucb->mcp, reg);
++}
++/**
++ * ucb1x00_set_audio_divisor -
++ * @ucb: UCB1x00 structure describing chip
++ * @div: SIB clock divisor
++ */
++static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div)
++{
++ mcp_set_audio_divisor(ucb->mcp, div);
++}
++
++/**
++ * ucb1x00_set_telecom_divisor -
++ * @ucb: UCB1x00 structure describing chip
++ * @div: SIB clock divisor
++ */
++static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div)
++{
++ mcp_set_telecom_divisor(ucb->mcp, div);
++}
++
++void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int);
++void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int);
++unsigned int ucb1x00_io_read(struct ucb1x00 *ucb);
++
++#define UCB_NOSYNC (0)
++#define UCB_SYNC (1)
++
++unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync);
++void ucb1x00_adc_enable(struct ucb1x00 *ucb);
++void ucb1x00_adc_disable(struct ucb1x00 *ucb);
++
++/*
++ * Which edges of the IRQ do you want to control today?
++ */
++#define UCB_RISING (1 << 0)
++#define UCB_FALLING (1 << 1)
++
++int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid);
++void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges);
++void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges);
++int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid);
++
++#endif
+Index: linux-2.6.24/drivers/mfd/ucb1x00-simpad.c
+===================================================================
+--- linux-2.6.24.orig/drivers/mfd/ucb1x00-simpad.c 2008-12-23 22:09:59.000000000 +0100
++++ linux-2.6.24/drivers/mfd/ucb1x00-simpad.c 2008-12-23 22:10:12.000000000 +0100
+@@ -26,7 +26,7 @@
+ #include <asm/arch/simpad.h>
+ #include <asm/arch-sa1100/simpad_pm.h>
+
+-#include "ucb1x00.h"
++#include <linux/mfd/ucb1x00.h>
+ #include "ucb1x00-simpad.h"
+
+ #define UCB1X00_ATTR(name,input,designation) \ \ No newline at end of file
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch
new file mode 100644
index 0000000000..20cb56d11b
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch
@@ -0,0 +1,359 @@
+diff -Nur linux-2.6.24.vanilla/drivers/mfd/Kconfig linux-2.6.24/drivers/mfd/Kconfig
+--- linux-2.6.24.vanilla/drivers/mfd/Kconfig 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/mfd/Kconfig 2008-02-20 21:17:07.000000000 +0100
+@@ -38,4 +38,7 @@
+ tristate "Touchscreen interface support"
+ depends on MCP_UCB1200 && INPUT
+
++config MCP_UCB1200_SWITCHES
++ tristate "SIMpad Switches support"
++ depends on MCP_UCB1200 && INPUT
+ endmenu
+diff -Nur linux-2.6.24.vanilla/drivers/mfd/Makefile linux-2.6.24/drivers/mfd/Makefile
+--- linux-2.6.24.vanilla/drivers/mfd/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/mfd/Makefile 2008-02-20 21:17:07.000000000 +0100
+@@ -8,7 +8,7 @@
+ obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
+ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
+-
++obj-$(CONFIG_MCP_UCB1200_SWITCHES) += ucb1x00-switches.o
+ ifeq ($(CONFIG_SA1100_ASSABET),y)
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
+ endif
+diff -Nur linux-2.6.24.vanilla/drivers/mfd/ucb1x00-switches.c linux-2.6.24/drivers/mfd/ucb1x00-switches.c
+--- linux-2.6.24.vanilla/drivers/mfd/ucb1x00-switches.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/mfd/ucb1x00-switches.c 2008-02-20 21:17:07.000000000 +0100
+@@ -0,0 +1,332 @@
++/*
++ * linux/drivers/mfd/ucb1x00-switches.c
++ *
++ * Copyright (C) 2007 Bernhard Guillon.
++ *
++ * 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.
++ *
++ * This driver is for the Switches of Siemens SIMpad (CL4,SL4,SLC), T-Sinus-Pad and
++ * Swisscom WP50 devices.
++ *
++ * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8.
++ *
++ * This driver is based on the 2.4 ucb1x00-switches, the 2.6 ucb1x00-assabet
++ * and the ucb1x00-ts driver.
++ *
++ * 2007/06/21 mrdata:
++ * - create new thread kswd() to handle irq_events for ucb1300-gpio's
++ * - found out, that not every key-press or key-release
++ * generate a irq_event
++ * -> establish key_state handling
++ * key_state, key_state_last <-> KEY_PRESS, KEY_RELEASE
++ * -> after irq_event polling the ucb1300-gpio's till all keys
++ * in key_state = KEY_RELEASE
++ *
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/device.h>
++#include <linux/sched.h>
++#include <linux/freezer.h>
++#include <linux/kthread.h>
++
++#include <asm/dma.h>
++
++#include "ucb1x00.h"
++
++#define KEY_PRESS 1
++#define KEY_RELEASE 0
++
++static int key [6] = { KEY_PROG1,KEY_PROG2,KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT };
++
++static unsigned short int key_state [6] = { 0, 0, 0, 0, 0, 0};
++static unsigned short int key_state_last [6] = { 1, 1, 1, 1, 1, 1};
++
++struct ucb1x00_switches {
++ struct input_dev *idev;
++ struct ucb1x00 *ucb;
++
++ wait_queue_head_t irq_wait;
++ struct task_struct *rtask;
++
++ int idx;
++
++ unsigned int valid:1;
++};
++
++static int ucb1x00_thread(void *_switches_id)
++{
++ unsigned short int this;
++ int idx_tmp;
++ int i;
++ struct ucb1x00_switches *switches = _switches_id;
++ struct input_dev *idev = switches->idev;
++ struct task_struct *tsk = current;
++ DECLARE_WAITQUEUE(wait, tsk);
++
++ add_wait_queue(&switches->irq_wait, &wait);
++
++ while (!kthread_should_stop())
++ {
++ signed long timeout;
++
++ if ((switches->idx >= 0) && (switches->idx <= 5) && (switches->valid == 1))
++ {
++ switches->valid = 0;
++
++ idx_tmp = switches->idx;
++
++ ucb1x00_enable(switches->ucb);
++
++ this = ~ucb1x00_io_read(switches->ucb);
++
++ ucb1x00_disable(switches->ucb);
++
++ if (key_state[idx_tmp] == KEY_RELEASE)
++ {
++ key_state_last[idx_tmp] = KEY_RELEASE;
++ key_state[idx_tmp] = KEY_PRESS;
++
++ input_report_key(idev, key[idx_tmp], KEY_PRESS);
++ input_sync(idev);
++ }
++
++ for (i = 0; i < 6; i++)
++ {
++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS))
++ {
++ key_state_last[i] = KEY_RELEASE;
++ key_state[i] = KEY_PRESS;
++
++ input_report_key(idev, key[i], KEY_PRESS);
++ input_sync(idev);
++ }
++ }
++
++ for(;;)
++ {
++ ucb1x00_enable(switches->ucb);
++ this = ~ucb1x00_io_read(switches->ucb);
++ ucb1x00_disable(switches->ucb);
++
++ for (i = 0; i < 6; i++)
++ {
++ if ((key_state[i] == KEY_PRESS) && (((this & (1 << i)) ? 1 : 0) == KEY_RELEASE))
++ {
++ key_state_last[i] = KEY_PRESS;
++ key_state[i] = KEY_RELEASE;
++
++ input_report_key(idev, key[i], KEY_RELEASE);
++ input_sync(idev);
++ }
++
++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS))
++ {
++ key_state_last[i] = KEY_RELEASE;
++ key_state[i] = KEY_PRESS;
++
++ input_report_key(idev, key[i], KEY_PRESS);
++ input_sync(idev);
++ }
++
++ }
++
++ // left loop, if no key press detect
++ if ((this | 0xff80) == 0xff80)
++ {
++ break;
++ }
++
++ set_task_state(tsk, TASK_INTERRUPTIBLE);
++
++ try_to_freeze();
++
++ timeout = HZ / 100;
++
++ schedule_timeout(timeout);
++ }
++ }
++
++ set_task_state(tsk, TASK_INTERRUPTIBLE);
++
++ try_to_freeze();
++
++ timeout = MAX_SCHEDULE_TIMEOUT;
++
++ schedule_timeout(timeout);
++ }
++
++ remove_wait_queue(&switches->irq_wait, &wait);
++
++ switches->rtask = NULL;
++
++ return 0;
++}
++
++
++static void ucb1x00_dev_irq(int idx, void *id)
++{
++ struct ucb1x00_switches *switches = id;
++
++ switches->idx = idx;
++ switches->valid = 1;
++
++ wake_up(&switches->irq_wait);
++}
++
++static int ucb1x00_switches_add(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_switches *switches;
++ struct input_dev *idev;
++ int err,i;
++
++ switches = kzalloc(sizeof(struct ucb1x00_switches), GFP_KERNEL);
++ idev = input_allocate_device();
++
++ if (!switches || !idev) {
++ err = -ENOMEM;
++ goto fail;
++ }
++
++ switches->ucb = dev->ucb;
++
++ idev->private = switches;
++ idev->name = "SIMpad Switches";
++ idev->id.product = switches->ucb->id;
++
++ __set_bit(EV_KEY, idev->evbit);
++ __set_bit(EV_REP, idev->evbit);
++ __set_bit(KEY_PROG1, idev->keybit);
++ __set_bit(KEY_PROG2, idev->keybit);
++ __set_bit(KEY_UP, idev->keybit);
++ __set_bit(KEY_DOWN, idev->keybit);
++ __set_bit(KEY_LEFT, idev->keybit);
++ __set_bit(KEY_RIGHT, idev->keybit);
++
++ err = input_register_device(idev);
++ if (err)
++ goto fail;
++ switches->idev = idev;
++ dev->priv = switches;
++
++ BUG_ON(switches->rtask);
++
++ init_waitqueue_head(&switches->irq_wait);
++
++ ucb1x00_enable(switches->ucb);
++
++ ucb1x00_io_set_dir(switches->ucb,
++ UCB_IO_0 | UCB_IO_1 | UCB_IO_2 |
++ UCB_IO_3 | UCB_IO_4 | UCB_IO_5,
++ UCB_IO_8 | UCB_IO_9);
++
++ ucb1x00_disable(switches->ucb);
++
++ for (i = 0; i < 6; ++i) {
++ ucb1x00_enable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ if (ucb1x00_hook_irq(switches->ucb, i, ucb1x00_dev_irq, switches) < 0) {
++ printk(KERN_ERR "unable to hook IRQ for "
++ "UCB1300 SWITCH_%d\n", i);
++ return -EBUSY;
++ }
++ }
++
++ switches->rtask = kthread_run(ucb1x00_thread, switches, "kswd");
++ if (!IS_ERR(switches->rtask))
++ {
++ return 0;
++ }
++ else
++ {
++ input_unregister_device(switches->idev);
++
++ for (i = 5; i >= 0; --i) {
++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ /* Only error conditions are ENOENT and EINVAL; silently
++ * ignore:
++ */
++ ucb1x00_free_irq(switches->ucb, i, NULL);
++ }
++ switches->rtask = NULL;
++ ucb1x00_disable(switches->ucb);
++ kfree(switches);
++
++ return -EFAULT;
++ }
++
++fail:
++ input_free_device(idev);
++ kfree(switches);
++ return err;
++
++}
++
++static void ucb1x00_switches_remove(struct ucb1x00_dev *dev)
++{
++ int i;
++ struct ucb1x00_switches *switches = dev->priv;
++
++ if (switches->rtask)
++ kthread_stop(switches->rtask);
++
++ switches->rtask = NULL;
++
++ input_unregister_device(switches->idev);
++
++ for (i = 5; i >= 0; --i) {
++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ /* Only error conditions are ENOENT and EINVAL; silently
++ * ignore:
++ */
++ ucb1x00_free_irq(switches->ucb, i, NULL);
++ }
++ ucb1x00_disable(switches->ucb);
++ kfree(switches);
++}
++
++#ifdef CONFIG_PM
++static int ucb1x00_switches_resume(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_switches *switches = dev->priv;
++
++ if (switches->rtask != NULL)
++ {
++ switches->valid = 0;
++ wake_up(&switches->irq_wait);
++
++ printk(KERN_DEBUG "ucb1x00-switches.c -> _switches_resume() kswd - restart *DONE*\n");
++ }
++ return 0;
++}
++#else
++#define ucb1x00_switches_resume NULL
++#endif
++
++static struct ucb1x00_driver ucb1x00_switches_driver = {
++ .add = ucb1x00_switches_add,
++ .remove = ucb1x00_switches_remove,
++ .resume = ucb1x00_switches_resume,
++};
++
++static int __init ucb1x00_switches_init(void)
++{
++ return ucb1x00_register_driver(&ucb1x00_switches_driver);
++}
++
++static void __exit ucb1x00_switches_exit(void)
++{
++ ucb1x00_unregister_driver(&ucb1x00_switches_driver);
++}
++
++module_init(ucb1x00_switches_init);
++module_exit(ucb1x00_switches_exit);
++
++MODULE_AUTHOR("Bernhard Guillon <Bernhard.Guillon@opensimpad.org>");
++MODULE_DESCRIPTION("UCB1x00 Switches driver for Siemens SIMpad");
++MODULE_LICENSE("GPL");
diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-ts-supend-and-accuracy.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-ts-supend-and-accuracy.patch
new file mode 100644
index 0000000000..702bbfbfba
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-ts-supend-and-accuracy.patch
@@ -0,0 +1,121 @@
+diff -Nur linux-2.6.24.vanilla/drivers/mfd/ucb1x00-ts.c linux-2.6.24/drivers/mfd/ucb1x00-ts.c
+--- linux-2.6.24.vanilla/drivers/mfd/ucb1x00-ts.c 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/mfd/ucb1x00-ts.c 2008-02-20 21:21:51.000000000 +0100
+@@ -16,6 +16,10 @@
+ * It is important to note that the signal connected to the ADCSYNC
+ * pin should provide pulses even when the LCD is blanked, otherwise
+ * a pen touch needed to unblank the LCD will never be read.
++ *
++ * mrdata: -added some accuracy improvement based on thesings collie patch
++ * -added suspend fix
++ *
+ */
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+@@ -104,6 +108,8 @@
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+
++ udelay(55);
++
+ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+ }
+ }
+@@ -130,7 +136,7 @@
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+- udelay(55);
++ udelay(165);
+
+ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+ }
+@@ -158,7 +164,7 @@
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+- udelay(55);
++ udelay(165);
+
+ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
+ }
+@@ -216,13 +222,17 @@
+ signed long timeout;
+
+ ts->restart = 0;
+-
++
+ ucb1x00_adc_enable(ts->ucb);
+
+ x = ucb1x00_ts_read_xpos(ts);
++ ucb1x00_adc_disable(ts->ucb);
++ ucb1x00_adc_enable(ts->ucb);
+ y = ucb1x00_ts_read_ypos(ts);
++ ucb1x00_adc_disable(ts->ucb);
++ ucb1x00_adc_enable(ts->ucb);
+ p = ucb1x00_ts_read_pressure(ts);
+-
++
+ /*
+ * Switch back to interrupt mode.
+ */
+@@ -231,15 +241,19 @@
+
+ msleep(10);
+
++ if ((x < 60) || (y < 60)) {
++ p = 0;
++ }
++
+ ucb1x00_enable(ts->ucb);
+
+-
+ if (ucb1x00_ts_pen_down(ts)) {
++
+ set_task_state(tsk, TASK_INTERRUPTIBLE);
+
+ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING);
+ ucb1x00_disable(ts->ucb);
+-
++
+ /*
+ * If we spat out a valid sample set last time,
+ * spit out a "pen off" sample here.
+@@ -250,7 +264,9 @@
+ }
+
+ timeout = MAX_SCHEDULE_TIMEOUT;
++
+ } else {
++
+ ucb1x00_disable(ts->ucb);
+
+ /*
+@@ -269,6 +285,14 @@
+
+ try_to_freeze();
+
++ /*
++ * While suspend the ktsd-thread goes sleep -> try_to_freeze()
++ * While resume the ktsd-thread do wakup and must rune one time
++ * again to do a clean re-setup -> enable_irq: UCB_IRQ_TSPX
++ */
++ if(ts->restart)
++ timeout = HZ / 100;
++
+ schedule_timeout(timeout);
+ }
+
+@@ -351,8 +375,12 @@
+ * TS interrupt mode is set up again
+ * after sleep.
+ */
++
+ ts->restart = 1;
+ wake_up(&ts->irq_wait);
++
++ printk(KERN_INFO "ucb1x00-ts.c -> ucb1x00_ts_resume() ktsd - restart *DONE*\n");
++
+ }
+ return 0;
+ }
diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-GPIO-MMC-mod.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-GPIO-MMC-mod.patch
new file mode 100644
index 0000000000..deacad37b0
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-GPIO-MMC-mod.patch
@@ -0,0 +1,1701 @@
+Index: linux-2.6.27/drivers/mmc/card/Makefile
+===================================================================
+--- linux-2.6.27.orig/drivers/mmc/card/Makefile 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27/drivers/mmc/card/Makefile 2008-12-04 00:49:46.888896871 +0100
+@@ -6,6 +6,9 @@
+ EXTRA_CFLAGS += -DDEBUG
+ endif
+
++ifeq ($(CONFIG_SA1100_SIMPAD),y)
++# nothing to do
++else
+ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
+ mmc_block-objs := block.o queue.o
+ obj-$(CONFIG_MMC_TEST) += mmc_test.o
+Index: linux-2.6.27/drivers/mmc/core/Makefile
+===================================================================
+--- linux-2.6.27.orig/drivers/mmc/core/Makefile 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27/drivers/mmc/core/Makefile 2008-12-04 00:50:42.755561658 +0100
+@@ -6,6 +6,9 @@
+ EXTRA_CFLAGS += -DDEBUG
+ endif
+
++ifeq ($(CONFIG_SA1100_SIMPAD),y)
++# nothing to do
++else
+ obj-$(CONFIG_MMC) += mmc_core.o
+ mmc_core-y := core.o bus.o host.o \
+ mmc.o mmc_ops.o sd.o sd_ops.o \
+Index: linux-2.6.27/drivers/mmc/host/Kconfig
+===================================================================
+--- linux-2.6.27.orig/drivers/mmc/host/Kconfig 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27/drivers/mmc/host/Kconfig 2008-12-04 00:53:18.968875446 +0100
+@@ -4,6 +4,7 @@
+
+ comment "MMC/SD Host Controller Drivers"
+
++
+ config MMC_ARMMMCI
+ tristate "ARM AMBA Multimedia Card Interface support"
+ depends on ARM_AMBA
+@@ -153,6 +154,13 @@
+
+ If unsure, or if your system has no SPI master driver, say N.
+
++config MMC_SPI_BLOCK
++ tristate "MMC/SD over GPIO (Software SPI) for SIMpad (EXPERIMENTAL)"
++ depends on SA1100_SIMPAD && EXPERIMENTAL
++ help
++ Say Y here to enable MMC block device over GPIO
++ if you have done the MMC-Mod. For Module say M.
++
+ config MMC_S3C
+ tristate "Samsung S3C SD/MMC Card Interface support"
+ depends on ARCH_S3C2410 && MMC
+Index: linux-2.6.27/drivers/mmc/host/Makefile
+===================================================================
+--- linux-2.6.27.orig/drivers/mmc/host/Makefile 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27/drivers/mmc/host/Makefile 2008-12-04 00:54:44.972204850 +0100
+@@ -6,6 +6,9 @@
+ EXTRA_CFLAGS += -DDEBUG
+ endif
+
++ifeq ($(CONFIG_SA1100_SIMPAD),y)
++obj-$(CONFIG_MMC_SPI_BLOCK) += mmc_spi_block.o
++else
+ obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
+ obj-$(CONFIG_MMC_PXA) += pxamci.o
+ obj-$(CONFIG_MMC_IMX) += imxmmc.o
+@@ -22,4 +25,5 @@
+ obj-$(CONFIG_MMC_S3C) += s3cmci.o
+ obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
+ obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
++endif
+
+Index: linux-2.6.27/drivers/mmc/host/mmc_spi_block.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/drivers/mmc/host/mmc_spi_block.c 2008-12-04 00:55:17.229964087 +0100
+@@ -0,0 +1,1622 @@
++/*
++ * Copyright (c) Cl�ent Ballabriga, 2005 - GPL
++ * Copyright (c) Guylhem Aznar, 2005 - GPL
++ *
++ * Please check http://externe.net/zaurus/simpad-bluetooth reference design first.
++ *
++ * Based on Madsuk/Rohde work on a MMC driver for the WRT54G.
++ *
++ * This is an ugly hack of a driver. I am surprised if it ever works!
++ * So please use a real driver or contribute one to the 2.4/2.6 mmc framework
++ *
++ * mrdata: ported to 2.6
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/errno.h>
++#include <linux/hdreg.h>
++#include <linux/kdev_t.h>
++#include <linux/blkdev.h>
++#include <linux/spinlock.h>
++#include <linux/time.h>
++#include <linux/delay.h>
++#include <linux/timer.h>
++
++#include <linux/platform_device.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/simpad.h>
++#include <asm/arch/gpio.h>
++
++static int major = 121;
++
++#define DEVICE_NAME "mmc_spi"
++
++static int hd_sizes[1<<6];
++static int hd_blocksizes[1<<6];
++static int hd_hardsectsizes[1<<6];
++static int hd_maxsect[1<<6];
++static struct hd_struct hd[1<<6];
++
++static struct gendisk *mmc_disk;
++
++static struct platform_device *mmc_dev; /* the one and only instance */
++
++static spinlock_t mmc_spi_lock;
++
++/*
++ * *******************************************************************
++ *
++ * This is the only configurable part.
++ *
++ * *******************************************************************
++ *
++ */
++
++// #define DEBUG 1
++// #define DEBUG_HD 1
++// #define CHECK_MEDIA_CHANGE // for developement ONLY, not working yet
++
++/* Let that include where it is or compilation fails on INIT_REQUEST/CURRENT */
++
++
++/*
++ * If you are using different GPIOs in your hardware hack, you must
++ * first make sure they are unused for other functions and then
++ * configure them here.
++ *
++ * On the simpad I use spare pins from the UART1 (internal serial port -> DECT 20-polig):
++ *
++ * Funktion PIN ## Original direction GPIO ## SPI function New direction SD/MMC
++ * - DCD PIN 08 (in) GPIO 23 DO - new name: DI -> MISO (in) PIN 7 Data Out
++ * - DTR PIN 11 (out) GPIO 07 CS (out) PIN 1 Chip Select
++ * - RI PIN 14 (in) GPIO 19 CLK (out) PIN 5 Clock
++ * - DSR PIN 16 (in) GPIO 06 DI - new name: DO -> MOSI (out) PIN 2 Data In
++ *
++ *
++ * SPI: MISO = Master In / Slave OUT MOSI = Master Out / Slave In
++ *
++ * Don't worry about in/out original function - the GPIOs will be
++ * reprogrammed.
++ */
++
++#define GPIO_SD_DI 23
++#define GPIO_SD_CS 7
++#define GPIO_SD_CLK 19
++#define GPIO_SD_DO 6
++
++// #define FAST_GPIO_SD_DI GPIO_GPIO23
++// #define FAST_GPIO_SD_CS GPIO_GPIO7
++// #define FAST_GPIO_SD_CLK GPIO_GPIO19
++// #define FAST_GPIO_SD_DO GPIO_GPIO6
++
++#define FAST_GPIO_SD_DI GPIO_UART1_DCD
++#define FAST_GPIO_SD_CS GPIO_UART1_DTR
++#define FAST_GPIO_SD_CLK GPIO_UART1_RI
++#define FAST_GPIO_SD_DO GPIO_UART1_DSR
++
++/*
++ * *******************************************************************
++ *
++ * Do not change anything below !
++ *
++ * *******************************************************************
++ *
++ */
++
++/* GPIO states */
++#define LOW 0
++#define HIGH 1
++
++#define INPUT 0
++#define OUTPUT 1
++
++#define PRESENT 1
++#define ABSENT 0
++
++typedef unsigned int uint32;
++typedef unsigned long u32_t;
++typedef unsigned short u16_t;
++typedef unsigned char u8_t;
++
++// static struct timer_list mmc_timer;
++
++// static struct timeval s_zeit, e_zeit;
++
++/* start with no card */
++static int mmc_media_detect = 0;
++// static int mmc_media_changed = 1;
++
++
++/////////////////////
++// prototypes
++static int mmc_open(struct inode *inode, struct file *filp);
++static int mmc_release(struct inode *inode, struct file *filp);
++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
++static void mmc_spi_request(struct request_queue *q);
++
++
++/*
++ * *******************************************************************
++ *
++ * Begin GPIO hardware access functions.
++ *
++ * *******************************************************************
++ *
++ */
++
++#define gpio_read(a) ((GPLR & a) ? 1 : 0)
++#define gpio_write_high(a) GPSR = a
++#define gpio_write_low(a) GPCR = a
++
++/* set MMC_Chip_Select to HIGH (MMC/SD-Card inactiv) */
++#define MMC_Disable() gpio_write_high( FAST_GPIO_SD_CS)
++
++/* set MMC_Chip_Select to LOW (MMC/SD-Card activ) */
++#define MMC_Enable() gpio_write_low( FAST_GPIO_SD_CS)
++
++/*
++ * *******************************************************************
++ *
++ * Begin SPI hardware access functions.
++ *
++ * *******************************************************************
++ *
++ */
++static int mmc_spi_media_detect(void)
++{
++// FIXME: add card detection/test by SPI
++
++ return 1;
++}
++
++static int mmc_spi_hardware_init(void)
++{
++ printk("\nmmc: GPIO init\n");
++
++ /* cut existing functions */
++ gpio_set_alternative_function(GPIO_SD_CLK, 0);
++ gpio_set_alternative_function(GPIO_SD_DI, 0);
++ gpio_set_alternative_function(GPIO_SD_DO, 0);
++ gpio_set_alternative_function(GPIO_SD_CS, 0);
++
++ /* remap directions and set state of spi pins */
++ gpio_direction_output(GPIO_SD_CLK, 0);
++ gpio_direction_input(GPIO_SD_DI);
++ gpio_direction_output(GPIO_SD_DO, 0);
++ gpio_direction_output(GPIO_SD_CS, 0);
++
++ printk("mmc: initialising MMC\n");
++
++ /* Start */
++ MMC_Disable();
++ gpio_write_low( FAST_GPIO_SD_CLK);
++ gpio_write_high( FAST_GPIO_SD_DO);
++ return 0;
++}
++
++/* return what has been read, write the parameter */
++/* Clockrate round about 1,2 MHz */
++
++static unsigned char mmc_spi_readwrite(unsigned char data_out)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++ if (data_out & i)
++ {
++ gpio_write_high( FAST_GPIO_SD_DO);
++ }
++ else
++ {
++ gpio_write_low( FAST_GPIO_SD_DO);
++ }
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ if (gpio_read( FAST_GPIO_SD_DI) == 1)
++ {
++ result |= i;
++ }
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ }
++
++ gpio_write_high( FAST_GPIO_SD_DO);
++
++ return (result);
++}
++
++/* return what has been read, write the parameter */
++/* Clockrate round 200 kHz */
++
++static unsigned char mmc_spi_readwrite_slow(unsigned char data_out)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++ if (data_out & i)
++ {
++ gpio_write_high( FAST_GPIO_SD_DO);
++ }
++ else
++ {
++ gpio_write_low( FAST_GPIO_SD_DO);
++ }
++
++ udelay(10);
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ udelay(10);
++
++ if (gpio_read( FAST_GPIO_SD_DI) == 1)
++ {
++ result |= i;
++ }
++
++ udelay(10);
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ udelay(10);
++
++ }
++
++ gpio_write_high( FAST_GPIO_SD_DO);
++
++ udelay(10);
++
++ // printk("Send Byte = 0x%2X Receive Byte = 0x%2X \n", data_out, result);
++
++ return (result);
++}
++
++/* return what has been read */
++
++static unsigned char mmc_spi_read_only(void)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ if (gpio_read( FAST_GPIO_SD_DI) == 1)
++ {
++ result |= i;
++ }
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ }
++
++ return (result);
++}
++
++/* write the parameter */
++/* Clockrate round about 3,6 MHz */
++
++static unsigned char mmc_spi_write_only(unsigned char data_out)
++{
++ unsigned char i;
++ unsigned char result = 0;
++
++ for(i = 0x80 ; i != 0 ; i >>= 1)
++ {
++
++ if (data_out & i)
++ {
++ gpio_write_high( FAST_GPIO_SD_DO);
++ }
++ else
++ {
++ gpio_write_low( FAST_GPIO_SD_DO);
++ }
++
++ gpio_write_high( FAST_GPIO_SD_CLK);
++
++ gpio_write_low( FAST_GPIO_SD_CLK);
++
++ }
++
++ gpio_write_high( FAST_GPIO_SD_DO);
++
++ return (result);
++}
++
++
++/**
++ * this function was contributed by: rcichielo from openwrt forums
++ *
++ * Comments added by Marc DENTY on 2007-03-20
++ *
++ * Sequence to read a card's "CID" bytes (name, serial number etc)
++ *
++ * Send: 4ah,00h,00h,00h,00h,00h - CMD10, no args, null CRC
++ * Read: xx - NCR Time
++ * Read: xx - Command Response (Should be 00h)
++ * Read: until FEh is received - Wait for Data token
++ * Read: yy * 16 - Get 16 bytes from CID
++ * Read: zz - Read CRC lo byte
++ * Read: zz - Read CRC hi byte
++ *
++ * Useful locations in the returned data packet:
++ *
++ * 03h-08h Manufacturers's name in ascii
++ * 0ah-0dh Card's 32 bit serial number
++ */
++/**
++ * Comments added by Cyril CATTIAUX on 2007-03-21
++ *
++ * CID format specification (from Sandisk SD Product Manual v1.9)
++ *
++ * cid[00 ] Manufacturer ID (unsigned byte)
++ * cid[01-02] OEM/Application ID (ASCII)
++ * cid[03-07] Product Name (ASCII)
++ * cid[08 ] Product Revistion (BCD coded number)
++ * cid[09-12] Serial Number (32-bit unsigned int)
++ * cid[13-14] Reserved(bit 12->15) - Manufacture Date(bit 0->11)
++ * cid[15 ] CRC7(bit 1->7) - Not used, allways 1 (bit 0)
++*/
++static int mmc_read_cid(unsigned char *cid)
++{
++ unsigned char result = 0;
++ int i;
++
++ MMC_Enable();
++
++ /* wait */
++ for (i = 0; i < 4; i++)
++ {
++ result=mmc_spi_readwrite(0xff);
++ }
++
++ /* issue CID (card identification data) read request */
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0x40 | 10);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x95);
++
++ for (i = 0; i < 8; i++)
++ {
++ result=mmc_spi_readwrite(0xff);
++
++ if(result == 0x00)
++ break;
++ }
++
++ if (result != 0x00) {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return(1);
++ }
++
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xfe) break;
++ }
++
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return(2);
++ }
++
++ for (i = 0; i < 16; i++) {
++ result = mmc_spi_readwrite(0xff);
++ cid[i] = result;
++ }
++
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0xff);
++
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++
++ return 0;
++}
++
++
++/**
++ * Comments added by Cyril CATTIAUX on 2007-03-21
++ *
++ * CID format specification (from Sandisk SD Product Manual v1.9)
++ *
++ * cid[00 ] Manufacturer ID (unsigned byte)
++ * cid[01-02] OEM/Application ID (ASCII)
++ * cid[03-07] Product Name (ASCII)
++ * cid[08 ] Product Revision (BCD coded 2 digit number)
++ * cid[09-12] Serial Number (32-bit unsigned int)
++ * cid[13-14] Manufacture Date(bit 0->11) (BCD coded 3 digit number YYM offset from 2000) - Reserved(bit 12->15)
++ * cid[15 ] Not used, allways 1 (bit 0) - CRC7(bit 1->7)
++*/
++static void mmc_show_cid_info(void)
++{
++ int i, result;
++ unsigned short tmps;
++ unsigned char cid[16];
++
++ char manufacturer_id;
++ char oem_id[3];
++ char product_name[6];
++ unsigned char product_revision_h, product_revision_l;
++ unsigned int product_sn;
++ unsigned short product_date_y;
++ unsigned char product_date_m;
++
++ result = mmc_read_cid(cid);
++
++ if (result == 0)
++ {
++ printk("mmc_init: MMC/SD Card ID: ");
++ for (i=0; i<16; i++) {
++ printk("%02X ", cid[i]);
++ }
++ manufacturer_id=cid[0];
++ strncpy(oem_id, &cid[1], 2);
++ oem_id[2]='\0';
++ strncpy(product_name, &cid[3], 5);
++ product_name[5]='\0';
++ product_revision_h=(cid[8] >> 4) & 0xf;
++ product_revision_l=cid[8] & 0xf;
++ product_sn=(cid[9]<<24) + (cid[10]<<16) + (cid[11]<<8) + cid[12];
++ tmps=((cid[13]<<8) + cid[14]) & 0x0fff;
++ product_date_y=2000 + (((tmps >> 8) & 0xf) * 10) + ((tmps >> 4) & 0xf);
++ product_date_m=tmps & 0xf;
++
++ printk("\nManufacturer ID : %02X\n", manufacturer_id);
++ printk("OEM/Application ID: %s\n", oem_id);
++ printk("Product name : %s\n", product_name);
++ printk("Product revision : %d.%d\n", product_revision_h, product_revision_l);
++ printk("Product SN : %08X\n", product_sn);
++ printk("Product Date : %d-%d\n", product_date_y, product_date_m);
++
++ } else {
++ printk("mmc_init: impossible to get card indentification info for reason code: %02x", result);
++ }
++}
++
++
++/*
++static int mmc_spi_hw_test(void)
++{
++ unsigned char result, k;
++
++ unsigned int i, j, t;
++
++ printk("mmc_spi_hw_test -> \n\n");
++ k = 0x55;
++ for ( i = 0 ; i < 5; i++) {
++
++ printk("\n0x%2X - ", k);
++ for ( j = 0 ; j < 8; j++ ) {
++ do_gettimeofday( &s_zeit );
++ result = mmc_spi_readwrite_slow(k);
++ do_gettimeofday( &e_zeit );
++
++ if ( result != k ) {
++ printk("!>ERROR<! Transfer = 0x%2X Receive = 0x%2X Trail = %d \n", k, result, j);
++ // i = 255; j = 1000;
++ }
++
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("Durchlauf: %i Versuch: %i von 8 -> Laufzeit: 0x%X s\n", i , j, t);
++ udelay(200);
++ }
++ printk("ready ");
++
++ // k++;
++ }
++ printk("ready ");
++ printk("\n\n");
++ return (0);
++}
++*/
++
++/*
++static int mmc_spi_speed_test(void)
++{
++ unsigned int i, j, k, l, t;
++
++ MMC_Disable();
++
++ for (k = 1; k < 6; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 5; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_readwrite(0xff);
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_readwrite: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ for (k = 1; k < 1; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 1; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_readwrite_slow(0xff);
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_readwrite_slow: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ for (k = 1; k < 6; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 5; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_read_only();
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_read_only: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ for (k = 1; k < 6; k++)
++ {
++ l = 10000 * k;
++ for (j = 0; j < 5; j++)
++ {
++ do_gettimeofday( &s_zeit );
++ for (i = 0; i < l; i++)
++ mmc_spi_write_only(0xff);
++ do_gettimeofday( &e_zeit );
++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec);
++ printk("mmc_spi_write_only: Laufzeit %u x : 0x%X \n", l, t);
++ }
++ }
++
++ return (1);
++
++}
++*/
++
++
++static int mmc_spi_card_init(void)
++{
++ unsigned char result = 0;
++ short i, j;
++
++// unsigned long flags;
++
++ // save_flags(flags);
++ // cli();
++
++/*
++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CS), gpio_getalt(&gp, GPIO_SD_CS));
++ printk("GPIO_SD_DI dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DI), gpio_getalt(&gp, GPIO_SD_DI));
++ printk("GPIO_SD_DO dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DO), gpio_getalt(&gp, GPIO_SD_DO));
++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CLK), gpio_getalt(&gp, GPIO_SD_CLK));
++*/
++
++ // printk("\nmmc: mmc_spi_hw_test() *START*\n");
++
++ // mmc_spi_hw_test();
++
++ printk("\nmmc: card init 1/2 (CMD0)\n");
++
++ for (j = 0; j < 10; j++)
++ {
++ MMC_Disable();
++
++ for (i = 0; i < 10; i++)
++ mmc_spi_readwrite_slow(0xff);
++
++ MMC_Enable();
++
++ mmc_spi_readwrite_slow(0xff);
++
++ mmc_spi_readwrite_slow(0x40);
++
++ for (i = 0; i < 4; i++) {
++
++ mmc_spi_readwrite_slow(0x00);
++
++ }
++
++ mmc_spi_readwrite_slow(0x95);
++
++ for (i = 0; i < 8; i++) {
++
++ result = mmc_spi_readwrite_slow(0xff);
++
++#ifdef DEBUG_HD
++ if (result != 0xff) {
++ if (result > 0x1F && result < 0x80)
++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result);
++ else
++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X\n", j, i, result);
++ }
++#endif
++ if (result == 0x01)
++ break;
++ }
++
++ if (result == 0x01)
++ break;
++
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++
++ mdelay(60);
++ }
++
++ if (result != 0x01) {
++
++ printk("mmc: card init 1/2 error: %d (CMD0) failed\n", result);
++ printk(" -> Hint: MMC/SD-Card realy (fully) inserted ?\n");
++
++ return (1);
++ }
++
++ printk("mmc: card init 1/2 (CMD0) success\n\n");
++
++ mdelay(1);
++
++ printk("mmc: card init 2/2 (CMD1)\n");
++ for (j = 0; j < 10; j++) {
++
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0x41);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // if (result >= 32 && result <= 127)
++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result);
++ // else
++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result);
++#endif
++ if (result == 0x00)
++ break;
++ }
++
++ mmc_spi_readwrite_slow(0xff);
++
++ if (result == 0x00) {
++ printk("mmc: card init 2/2 (CMD1) success\n\n");
++
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0x4d);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++ for (i = 0; i < 6; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // if (result > 31 && result < 128)
++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result);
++ // else
++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result);
++#endif
++ // if (result == 0x00)
++ // break;
++ }
++ // mdelay(60);
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++ // mdelay(10);
++
++ // restore_flags(flags);
++
++ return (0);
++ }
++ mdelay(60);
++ }
++ return (2);
++}
++
++
++static int mmc_spi_card_config(void)
++{
++ unsigned char result = 0;
++ short i, j;
++ unsigned char csd[32];
++ unsigned int c_size;
++ unsigned int c_size_mult;
++ unsigned int mult;
++ unsigned int read_bl_len;
++ unsigned int blocknr = 0;
++ unsigned int block_len = 0;
++ unsigned int size = 0;
++ unsigned char rd_buffer[528];
++// unsigned long flags;
++
++ MMC_Enable();
++
++ mmc_spi_readwrite_slow(0xff);
++ result = mmc_spi_readwrite_slow(0x51);
++ // mmc_spi_readwrite_slow(0x4A);
++ // mmc_spi_readwrite_slow(0x40+0x0D);
++ // mmc_spi_readwrite_slow(0x42);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++
++ // printk("mmc: (CMD17) response von 0x51 result = 0x%X\n", result);
++
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // printk("mmc: (CMD17) response (start token) result = 0x%X\n", result);
++#endif
++ if (result == 0x00)
++ break;
++ }
++ if (result != 0x00) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // restore_flags(flags);
++ // mmc_spi_readwrite_slow(0xff);
++ return (1);
++ }
++ // restore_flags(flags);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ rd_buffer[i] = result;
++#ifdef DEBUG_HD
++ /*
++ if (result >= 32 && result <= 127)
++ printk("mmc: CMD17 response (start token) result = 0x%X Zeichen = %c\n", result, result);
++ else
++ printk("mmc: CMD17 response (start token) result = 0x%X\n", result);
++ */
++#endif
++ // if (result == 0xfe)
++ // break;
++ }
++ /*
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0xff);
++ return(1);
++ }
++ */
++
++ for (i = 8; i < 520; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ rd_buffer[i] = result;
++ }
++ for (i = 0; i < 2; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ }
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++
++ printk("Buffer - Start\n");
++
++ for ( i = 0 ; i < 33 ; i++) {
++ printk("\r\n%4X - ", i*16);
++ for ( j = 0 ; j < 16 ; j++) {
++ if ( rd_buffer[i*16+j] < 16)
++ printk("0%X ", rd_buffer[i*16+j]);
++ else
++ printk("%2X ", rd_buffer[i*16+j]);
++ }
++ for ( j = 0 ; j < 16 ; j++) {
++ if ( rd_buffer[i*16+j] < ' ')
++ printk(".");
++ else
++ printk("%c", rd_buffer[i*16+j]);
++ }
++ }
++
++ printk("\nBuffer - Ende\n");
++
++ mmc_show_cid_info();
++
++ for(j = 0 ; j < 1; j++) {
++ MMC_Enable();
++
++ // mdelay(1);
++
++ // save_flags(flags);
++ // cli();
++ mmc_spi_readwrite_slow(0xff);
++ mmc_spi_readwrite_slow(0x49);
++ // mmc_spi_readwrite_slow(0x4A);
++ // mmc_spi_readwrite_slow(0x40+0x0D);
++ // mmc_spi_readwrite_slow(0x42);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite_slow(0x00);
++ mmc_spi_readwrite_slow(0x95);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ // printk("mmc: (CMD9) response (start token) result = 0x%X\n", result);
++#endif
++ if (result == 0x00)
++ break;
++ }
++ // restore_flags(flags);
++ if (result != 0x00) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++ return (1);
++ }
++ for (i = 0; i < 22; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++#ifdef DEBUG_HD
++ if (result >= 32 && result <= 127)
++ printk("mmc: response (start token) result = 0x%X Zeichen = %c\n", result, result);
++ else
++ printk("mmc: response (start token) result = 0x%X\n", result);
++#endif
++ if (result == 0xfe)
++ break;
++ }
++ if (result == 0xfe)
++ break;
++
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++ }
++ mdelay(60);
++ }
++
++ if (result != 0xfe) {
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ printk("mmc: mmc card config (CMD9) failed result = 0x%X\n\n", result);
++ return (2);
++ }
++ for (i = 0; i < 16; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ csd[i] = result;
++ }
++ for (i = 0; i < 2; i++) {
++ result = mmc_spi_readwrite_slow(0xff);
++ }
++ MMC_Disable();
++ mmc_spi_readwrite_slow(0xff);
++ // mmc_spi_readwrite_slow(0xff);
++
++ if (result == 0x00)
++ return (3);
++
++ c_size = (csd[8] & 0xC0) + (csd[7] << 8) + ((csd[6] & 0x03) << 16);
++ c_size >>= 6;
++ c_size_mult = (csd[10] & 0x80) + ((csd[9] & 0x03) << 8);
++ c_size_mult >>= 7;
++ read_bl_len = csd[5] & 0x0f;
++ mult = 1;
++ mult <<= c_size_mult + 2;
++ blocknr = (c_size + 1) * mult;
++ block_len = 1;
++ block_len <<= read_bl_len;
++ size = block_len * blocknr;
++ size >>= 10;
++
++ for (i = 0; i < (1 << 6); i++) {
++ hd_blocksizes[i] = 1024;
++ hd_hardsectsizes[i] = block_len;
++ hd_maxsect[i] = 256;
++ }
++ hd_sizes[0] = size;
++ hd[0].nr_sects = blocknr;
++
++ printk("Size = %d, hardsectsize = %d, sectors = %d\n",
++ size, block_len, blocknr);
++
++ return 0;
++}
++
++
++/*
++ * *******************************************************************
++ *
++ * End of SPI hardware access functions.
++ *
++ * *******************************************************************
++ */
++
++
++static int mmc_spi_write_block(unsigned int dest_addr, unsigned char *data)
++{
++ unsigned int address;
++ unsigned char result = 0;
++ unsigned char ab0, ab1, ab2, ab3;
++ int i;
++
++ address = dest_addr;
++
++ ab3 = 0xff & (address >> 24);
++ ab2 = 0xff & (address >> 16);
++ ab1 = 0xff & (address >> 8);
++ ab0 = 0xff & address;
++
++ MMC_Enable();
++
++ mmc_spi_readwrite(0xff);
++
++ mmc_spi_readwrite(0x58);
++ mmc_spi_readwrite(ab3); /* msb */
++ mmc_spi_readwrite(ab2);
++ mmc_spi_readwrite(ab1);
++ mmc_spi_readwrite(ab0); /* lsb */
++ mmc_spi_readwrite(0xff);
++
++ for (i = 0; i < 8; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ {
++ break;
++ }
++ }
++
++ if (result != 0x00)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (1);
++ }
++
++ mmc_spi_readwrite(0xfe);
++
++ for (i = 0; i < 512; i += 32)
++ {
++ mmc_spi_write_only(data[i]);
++ mmc_spi_write_only(data[i+1]);
++ mmc_spi_write_only(data[i+2]);
++ mmc_spi_write_only(data[i+3]);
++ mmc_spi_write_only(data[i+4]);
++ mmc_spi_write_only(data[i+5]);
++ mmc_spi_write_only(data[i+6]);
++ mmc_spi_write_only(data[i+7]);
++ mmc_spi_write_only(data[i+8]);
++ mmc_spi_write_only(data[i+9]);
++ mmc_spi_write_only(data[i+10]);
++ mmc_spi_write_only(data[i+11]);
++ mmc_spi_write_only(data[i+12]);
++ mmc_spi_write_only(data[i+13]);
++ mmc_spi_write_only(data[i+14]);
++ mmc_spi_write_only(data[i+15]);
++ mmc_spi_write_only(data[i+16]);
++ mmc_spi_write_only(data[i+17]);
++ mmc_spi_write_only(data[i+18]);
++ mmc_spi_write_only(data[i+19]);
++ mmc_spi_write_only(data[i+20]);
++ mmc_spi_write_only(data[i+21]);
++ mmc_spi_write_only(data[i+22]);
++ mmc_spi_write_only(data[i+23]);
++ mmc_spi_write_only(data[i+24]);
++ mmc_spi_write_only(data[i+25]);
++ mmc_spi_write_only(data[i+26]);
++ mmc_spi_write_only(data[i+27]);
++ mmc_spi_write_only(data[i+28]);
++ mmc_spi_write_only(data[i+29]);
++ mmc_spi_write_only(data[i+30]);
++ mmc_spi_write_only(data[i+31]);
++ }
++
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0xff);
++
++ for (i = 0; i < 1000000; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xff)
++ {
++ break;
++ }
++ }
++
++ if (result != 0xff)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (3);
++ }
++
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (0);
++}
++
++static int mmc_spi_read_block(unsigned char *data, unsigned int src_addr)
++{
++ unsigned int address;
++ unsigned char result = 0;
++ unsigned char ab0, ab1, ab2, ab3;
++ // unsigned long flags;
++ // int i, j;
++ int i;
++
++ address = src_addr;
++
++ ab3 = 0xff & (address >> 24);
++ ab2 = 0xff & (address >> 16);
++ ab1 = 0xff & (address >> 8);
++ ab0 = 0xff & address;
++
++ MMC_Enable();
++
++ mmc_spi_readwrite(0xff);
++
++ mmc_spi_readwrite(0x51);
++ mmc_spi_readwrite(ab3); /* msb */
++ mmc_spi_readwrite(ab2);
++ mmc_spi_readwrite(ab1);
++ mmc_spi_readwrite(ab0); /* lsb */
++ mmc_spi_readwrite(0xff);
++
++ for (i = 0; i < 8; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ {
++ break;
++ }
++ }
++
++ if (result != 0x00)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (1);
++ }
++
++ for (i = 0; i < 100000; i++)
++ {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xfe)
++ {
++ break;
++ }
++ }
++
++ if (result != 0xfe)
++ {
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++ return (2);
++ }
++
++ for (i = 0; i < 512; i += 32 )
++ {
++ data[i] = mmc_spi_read_only();
++ data[i+1] = mmc_spi_read_only();
++ data[i+2] = mmc_spi_read_only();
++ data[i+3] = mmc_spi_read_only();
++ data[i+4] = mmc_spi_read_only();
++ data[i+5] = mmc_spi_read_only();
++ data[i+6] = mmc_spi_read_only();
++ data[i+7] = mmc_spi_read_only();
++ data[i+8] = mmc_spi_read_only();
++ data[i+9] = mmc_spi_read_only();
++ data[i+10] = mmc_spi_read_only();
++ data[i+11] = mmc_spi_read_only();
++ data[i+12] = mmc_spi_read_only();
++ data[i+13] = mmc_spi_read_only();
++ data[i+14] = mmc_spi_read_only();
++ data[i+15] = mmc_spi_read_only();
++ data[i+16] = mmc_spi_read_only();
++ data[i+17] = mmc_spi_read_only();
++ data[i+18] = mmc_spi_read_only();
++ data[i+19] = mmc_spi_read_only();
++ data[i+20] = mmc_spi_read_only();
++ data[i+21] = mmc_spi_read_only();
++ data[i+22] = mmc_spi_read_only();
++ data[i+23] = mmc_spi_read_only();
++ data[i+24] = mmc_spi_read_only();
++ data[i+25] = mmc_spi_read_only();
++ data[i+26] = mmc_spi_read_only();
++ data[i+27] = mmc_spi_read_only();
++ data[i+28] = mmc_spi_read_only();
++ data[i+29] = mmc_spi_read_only();
++ data[i+30] = mmc_spi_read_only();
++ data[i+31] = mmc_spi_read_only();
++ }
++
++ result = mmc_spi_readwrite(0xff);
++ result = mmc_spi_readwrite(0xff);
++
++ MMC_Disable();
++ mmc_spi_readwrite(0xff);
++
++ return (0);
++}
++
++/*
++static int mmc_revalidate(kdev_t dev)
++{
++ int target, max_p, start, i;
++
++ mmc_media_detect = mmc_spi_media_detect();
++
++ if (mmc_media_detect == 0)
++ {
++ return -ENODEV;
++ }
++
++ target = DEVICE_NR(dev);
++
++ max_p = hd_gendisk.max_p;
++ start = target << 6;
++ for (i = max_p - 1; i >= 0; i--)
++ {
++ int minor = start + i;
++ invalidate_device(MKDEV(MAJOR_NR, minor), 1);
++ hd_gendisk.part[minor].start_sect = 0;
++ hd_gendisk.part[minor].nr_sects = 0;
++ }
++
++ grok_partitions(&hd_gendisk, target, 1 << 6, hd_sizes[0] * 2);
++
++ return 0;
++}
++*/
++
++
++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
++ unsigned long arg)
++{
++ if (!inode || !inode->i_rdev)
++ return -EINVAL;
++
++ switch (cmd) {
++#if 0
++ case BLKGETSIZE:
++ return put_user(hd[MINOR(inode->i_rdev)].nr_sects,
++ (unsigned long *)arg);
++ case BLKGETSIZE64:
++ return put_user((u64) hd[MINOR(inode->i_rdev)].
++ nr_sects, (u64 *) arg);
++ case BLKRRPART:
++ if (!capable(CAP_SYS_ADMIN))
++ return -EACCES;
++
++ return mmc_revalidate(inode->i_rdev);
++#endif
++ case HDIO_GETGEO:
++ {
++ struct block_device *bdev = inode->i_bdev;
++ struct hd_geometry *loc, g;
++ loc = (struct hd_geometry *)arg;
++ if (!loc)
++ return -EINVAL;
++ memset(loc, 0, sizeof(struct hd_geometry));
++ g.heads = 4;
++ g.sectors = 16;
++ g.cylinders = get_capacity(bdev->bd_disk) / (4*16);
++ g.start = get_start_sect(bdev);
++ return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0;
++ }
++ default:
++ return -ENOTTY;
++ }
++}
++
++
++/*
++static int mmc_check_media_change(kdev_t dev)
++{
++ (void) dev;
++ if (mmc_media_changed == 1)
++ {
++ mmc_media_changed = 0;
++ return 1;
++ }
++ else
++ {
++ return 0;
++ }
++}
++*/
++
++
++/*
++static int mmc_init(void)
++{
++ int result;
++
++ result = mmc_spi_hardware_init();
++
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_spi_hardware_init\n", result);
++ return -1;
++ }
++
++
++ result = mmc_spi_hw_test();
++
++ if (result != 0)
++ {
++ printk("\n mmc: mmc_spi_hw_test i.O. \n\n");
++ return -1;
++ }
++
++
++ result = mmc_spi_speed_test();
++
++ if (result != 0)
++ {
++ printk("\n mmc: mmc_spi_speed_test i.O. \n\n");
++ return -1;
++ }
++
++
++ result = mmc_spi_card_init();
++ mdelay(50);
++ if (result != 0)
++ {
++ // Give it an extra shot
++ // result = mmc_spi_card_init();
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_card_init\n", result);
++ return -1;
++ }
++ }
++
++ result = mmc_spi_card_config();
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_card_config\n", result);
++ return -1;
++ }
++
++
++ return 0;
++}
++*/
++
++/*
++static void mmc_exit(void)
++{
++}
++*/
++
++
++/*
++static void mmc_check_media(void)
++{
++ int old_state, new_state;
++ int result;
++
++ old_state = mmc_media_detect;
++ new_state = mmc_spi_media_detect();
++
++ if (old_state != new_state)
++ {
++ mmc_media_changed = 1;
++ if (new_state == PRESENT)
++ {
++ result = mmc_init();
++ if (result != 0)
++ {
++ printk("mmc: error %d in mmc_init\n", result);
++ }
++ else
++ {
++ mmc_exit();
++ }
++ }
++ }
++
++#ifdef CHECK_MEDIA_CHANGE
++ del_timer(&mmc_timer);
++ mmc_timer.expires = jiffies + 10*HZ;
++ add_timer(&mmc_timer);
++#endif
++
++}
++*/
++
++
++/* NB: There might be several requests in the queue, simply dequeuing only one
++ and not checking for more will cause a stall because the block subsystem
++ will not call this function again unless the queue is "plugged" which can
++ only happen if it runs empty... */
++static void mmc_spi_request(struct request_queue *q)
++{
++ struct request *req;
++ int ret;
++
++ unsigned int mmc_address;
++ unsigned char *buffer_address;
++ int nr_sectors;
++ int i;
++ int result, success;
++
++ if (blk_queue_plugged(q)) {
++ return;
++ }
++
++ spin_lock(&mmc_spi_lock);
++ for(;;) {
++ req = elv_next_request(q);
++ if (!req)
++ break;
++
++ if (!blk_fs_request(req)) {
++ printk("not a blk_fs_request\n");
++ spin_unlock(&mmc_spi_lock);
++ continue;
++ }
++
++ mmc_address = req->sector * hd_hardsectsizes[0];
++ buffer_address = req->buffer;
++ nr_sectors = req->current_nr_sectors;
++ success = 1;
++ if (rq_data_dir(req) == READ) {
++ spin_unlock_irq(q->queue_lock);
++ for (i = 0; i < nr_sectors; i++) {
++ result = mmc_spi_read_block(buffer_address, mmc_address);
++ if (unlikely(result < 0)) {
++ printk(KERN_ERR "mmi_spi_block: error reading block (%d)\n", result);
++ success = 0;
++ break;
++ }
++ mmc_address += hd_hardsectsizes[0];
++ buffer_address += hd_hardsectsizes[0];
++ }
++ spin_lock_irq(q->queue_lock);
++ } else {
++ spin_unlock_irq(q->queue_lock);
++ for (i = 0; i < nr_sectors; i++) {
++ result = mmc_spi_write_block(mmc_address, buffer_address);
++ if (unlikely(result < 0)) {
++ printk(KERN_ERR "mmi_spi_block: error writing block (%d)\n", result);
++ success = 0;
++ break;
++ }
++ mmc_address += hd_hardsectsizes[0];
++ buffer_address += hd_hardsectsizes[0];
++ }
++ spin_lock_irq(q->queue_lock);
++ }
++ ret = end_that_request_chunk(req, success, nr_sectors * hd_hardsectsizes[0]);
++ if (!ret) {
++ blkdev_dequeue_request(req);
++ end_that_request_last(req, 0);
++ }
++ }
++ spin_unlock(&mmc_spi_lock);
++}
++
++
++static int mmc_open(struct inode *inode, struct file *filp)
++{
++ // int device;
++ (void) filp;
++ mmc_media_detect = mmc_spi_media_detect();
++
++ if (mmc_media_detect == 0)
++ return -ENODEV;
++
++ return 0;
++}
++
++static int mmc_release(struct inode *inode, struct file *filp)
++{
++ return 0;
++}
++
++
++static struct block_device_operations mmc_spi_bdops = {
++ .open = mmc_open,
++ .release = mmc_release,
++ .ioctl = mmc_ioctl,
++ .owner = THIS_MODULE,
++#if 0
++ .check_media_change = mmc_check_media_change,
++ .revalidate = mmc_revalidate,
++#endif
++};
++
++static int detect_card(void)
++{
++ int result;
++
++ result = mmc_spi_card_init();
++ if (result != 0) {
++ // Give it an extra shot
++ result = mmc_spi_card_init();
++ if (result != 0) {
++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_init (%d)\n", result);
++ return -ENODEV;
++ }
++ }
++
++ result = mmc_spi_card_config();
++ // result = mmc_spi_speed_test();
++ if (result != 0) {
++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_config (%d)\n", result);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++/* Fills in the gendisk structure from the received card
++ data. */
++static int gendisk_init(struct device *dev, struct gendisk *gd)
++{
++ if (!gd)
++ return -EINVAL;
++
++ gd->major = major;
++ gd->first_minor = 0; /* only one device supported */
++ gd->fops = &mmc_spi_bdops;
++ gd->driverfs_dev = dev;
++
++ gd->queue = blk_init_queue(mmc_spi_request,NULL);
++
++ if (!gd->queue)
++ return -ENOMEM;
++
++ sprintf(gd->disk_name, "mmcblk");
++
++ blk_queue_hardsect_size(gd->queue, hd_hardsectsizes[0]);
++
++ set_capacity(gd, hd_sizes[0]<<1);
++
++ return 0;
++}
++
++static int gendisk_fini(struct gendisk *gd)
++{
++ BUG_ON(!gd);
++
++ if (gd->queue)
++ blk_cleanup_queue(gd->queue);
++
++ del_gendisk(gd);
++
++ return 0;
++}
++
++/* platform driver device instance routines */
++static int mmc_spi_probe(struct platform_device *pdev)
++{
++ int result;
++ printk("$Id: mmc_spi_block.c,v 1.05 2008/01/04 01:02:10 mrdata Exp $\n");
++
++ result = mmc_spi_hardware_init();
++ if (result != 0) {
++ printk(KERN_ERR "mmc_spi_block: error in mmc_spi_hardware_init (%d)\n", result);
++ result = -ENODEV;
++ return result;
++ }
++
++ result = detect_card();
++ if (result < 0)
++ return result;
++
++ mmc_media_detect = 1;
++
++ result = register_blkdev(major, DEVICE_NAME);
++ if (result < 0)
++ return result;
++
++ if (!major)
++ major = result;
++
++ /* allow 8 partitions per device */
++ BUG_ON(mmc_disk!=NULL);
++ mmc_disk = alloc_disk(1 << 3);
++ if (!mmc_disk) {
++ result = -ENOMEM;
++ goto out;
++ }
++
++ result = gendisk_init(&pdev->dev, mmc_disk);
++ if (result < 0)
++ goto out;
++
++ add_disk(mmc_disk);
++
++ /*init_timer(&mmc_timer);
++ mmc_timer.expires = jiffies + HZ;
++ mmc_timer.function = (void *)mmc_check_media;
++ add_timer(&mmc_timer); */
++ return 0;
++
++out:
++ if (mmc_disk)
++ put_disk(mmc_disk);
++
++ unregister_blkdev(major, DEVICE_NAME);
++ return result;
++}
++
++static int mmc_spi_remove(struct platform_device *dev)
++{
++ // int ret;
++
++ if (mmc_disk) {
++ gendisk_fini(mmc_disk);
++ put_disk(mmc_disk);
++ }
++
++ // ret = unregister_blkdev(major, DEVICE_NAME);
++ unregister_blkdev(major, DEVICE_NAME);
++ return 0;
++}
++
++struct platform_driver mmc_spi_driver = {
++ .probe = mmc_spi_probe,
++ .remove = mmc_spi_remove,
++ .driver = {
++ .name = "mmc_spi_block",
++ .owner = THIS_MODULE,
++ },
++};
++
++
++/* module init/exit */
++static int __init mmc_spi_block_init(void)
++{
++ int ret;
++ spin_lock_init(&mmc_spi_lock);
++
++ ret = platform_driver_register(&mmc_spi_driver);
++ if (ret < 0)
++ return ret;
++
++ /* we just support one device */
++ mmc_dev = platform_device_register_simple("mmc_spi_block", -1, NULL, 0);
++ if (IS_ERR(mmc_dev))
++ return PTR_ERR(mmc_dev);
++
++ return 0;
++}
++
++
++static void __exit mmc_spi_block_exit(void)
++{
++ platform_driver_unregister(&mmc_spi_driver);
++ if (mmc_dev)
++ platform_device_unregister(mmc_dev);
++}
++
++
++module_init(mmc_spi_block_init);
++module_exit(mmc_spi_block_exit);
++
++MODULE_AUTHOR("Madsuk,Rohde,TaGana,Carsten Juttner <carjay@gmx.net>,Guylhem Aznar <mmc-driver@externe.net>,mrdata");
++MODULE_DESCRIPTION("Driver for MMC/SD-Cards in SPI mode over GPIO (Software SPI)");
++MODULE_SUPPORTED_DEVICE("SIMpad");
++MODULE_LICENSE("GPL");
++
++module_param(major, int, 0444);
++MODULE_PARM_DESC(major, "specify the major device number for the MMC/SD SPI driver");
diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-battery-old-way-but-also-with-sysfs.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-battery-old-way-but-also-with-sysfs.patch
new file mode 100644
index 0000000000..ce23e42806
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-battery-old-way-but-also-with-sysfs.patch
@@ -0,0 +1,577 @@
+Index: linux-2.6.27/drivers/mfd/Makefile
+===================================================================
+--- linux-2.6.27.orig/drivers/mfd/Makefile 2008-12-07 01:02:17.739605256 +0100
++++ linux-2.6.27/drivers/mfd/Makefile 2008-12-07 01:18:53.732838088 +0100
+@@ -22,3 +22,7 @@
+ ifeq ($(CONFIG_SA1100_ASSABET),y)
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
+ endif
++
++ifeq ($(CONFIG_SA1100_SIMPAD),y)
++obj-$(CONFIG_MCP_UCB1200) += ucb1x00-simpad.o
++endif
+Index: linux-2.6.27/drivers/mfd/ucb1x00-simpad.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/drivers/mfd/ucb1x00-simpad.c 2008-12-07 01:17:54.760583677 +0100
+@@ -0,0 +1,228 @@
++/*
++ * linux/drivers/mfd/ucb1x00-simpad.c
++ *
++ * Copyright (C) 2001-2003 Russell King, All Rights Reserved.
++ * 2007/03/18 mrdata:
++ * - adapted ucb1x00-assabet.c
++ * - transfer ucb1x00-simpad.c from kernel24
++ * to new structur of kernel26
++ *
++ * 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.
++ *
++ * We handle the machine-specific bits of the UCB1x00 driver here.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/proc_fs.h>
++#include <linux/device.h>
++
++#include <linux/apm-emulation.h>
++
++#include <asm/dma.h>
++
++#include <mach/simpad.h>
++#include <mach/simpad_pm.h>
++
++#include "ucb1x00.h"
++#include "ucb1x00-simpad.h"
++
++#define UCB1X00_ATTR(name,input,designation) \
++static ssize_t name##_show(struct device *dev, \
++ struct device_attribute *attr, \
++ char *buf) \
++{ \
++ struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); \
++ int val; \
++ ucb1x00_adc_enable(ucb); \
++ val = ucb1x00_adc_read(ucb, input, UCB_NOSYNC); \
++ ucb1x00_adc_disable(ucb); \
++ return sprintf(buf, "%d\n", CALIBRATE_##designation(val)); \
++} \
++static DEVICE_ATTR(name,0444,name##_show,NULL)
++
++UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1, BATTERY);
++UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD2, SUPPLY);
++UCB1X00_ATTR(icharger, UCB_ADC_INP_AD3, CHARGING);
++
++static struct ucb1x00 *ucb_alt;
++
++#define UCB1X00_WERT(name,input,designation) \
++static int ucb1x00_simpad_read_##name(struct ucb1x00 *ucb_alt) \
++{ \
++ int val; \
++ ucb1x00_adc_enable(ucb_alt); \
++ val = ucb1x00_adc_read(ucb_alt, input, UCB_NOSYNC); \
++ ucb1x00_adc_disable(ucb_alt); \
++ return CALIBRATE_##designation(val); \
++}
++
++UCB1X00_WERT(vbatt, UCB_ADC_INP_AD1, BATTERY);
++UCB1X00_WERT(vcharger, UCB_ADC_INP_AD2, SUPPLY);
++UCB1X00_WERT(icharger, UCB_ADC_INP_AD3, CHARGING);
++
++static int ucb1x00_simpad_add(struct ucb1x00_dev *dev)
++{
++ device_create_file(&dev->ucb->dev, &dev_attr_vbatt);
++ device_create_file(&dev->ucb->dev, &dev_attr_vcharger);
++ device_create_file(&dev->ucb->dev, &dev_attr_icharger);
++ ucb_alt = dev->ucb;
++ return 0;
++}
++
++static void ucb1x00_simpad_remove(struct ucb1x00_dev *dev)
++{
++ device_remove_file(&dev->ucb->dev, &dev_attr_icharger);
++ device_remove_file(&dev->ucb->dev, &dev_attr_vcharger);
++ device_remove_file(&dev->ucb->dev, &dev_attr_vbatt);
++}
++
++static struct ucb1x00_driver ucb1x00_simpad_driver = {
++ .add = ucb1x00_simpad_add,
++ .remove = ucb1x00_simpad_remove,
++};
++
++static int __init ucb1x00_simpad_init(void)
++{
++ apm_get_power_status = simpad_apm_get_power_status;
++ return ucb1x00_register_driver(&ucb1x00_simpad_driver);
++}
++
++static void __exit ucb1x00_simpad_exit(void)
++{
++ apm_get_power_status = NULL;
++ ucb1x00_unregister_driver(&ucb1x00_simpad_driver);
++}
++
++/****************************************************************************/
++/* Functions exported for use by the kernel and kernel modules */
++/****************************************************************************/
++
++int simpad_get_battery(struct simpad_battery_apm *bstat)
++{
++ int icharger, vcharger, vbatt;
++
++ if ( ucb_alt ) {
++ icharger = ucb1x00_simpad_read_icharger( ucb_alt );
++ vcharger = ucb1x00_simpad_read_vcharger( ucb_alt );
++ vbatt = ucb1x00_simpad_read_vbatt( ucb_alt );
++ } else {
++ bstat->ac_status = SIMPAD_AC_STATUS_AC_UNKNOWN;
++ bstat->status = SIMPAD_BATT_STATUS_UNKNOWN;
++ bstat->percentage = 0x64; // lets say 100%
++ bstat->life = 330; // lets say a long time
++ return 0;
++ }
++
++ /* AC status */
++ bstat->ac_status = SIMPAD_AC_STATUS_AC_OFFLINE;
++ if ( vcharger>MIN_SUPPLY ) {
++ bstat->ac_status = SIMPAD_AC_STATUS_AC_ONLINE;
++ }
++
++ /* charging */
++ bstat->status = 0x0;
++ if ( icharger >= CHARGING_LED_LEVEL )
++ bstat->status = SIMPAD_BATT_STATUS_CHARGING;
++
++ if ( vbatt > BATT_LOW )
++ bstat->status |= SIMPAD_BATT_STATUS_HIGH;
++ else if ( vbatt < BATT_CRITICAL )
++ bstat->status |= SIMPAD_BATT_STATUS_CRITICAL;
++ else
++ bstat->status |= SIMPAD_BATT_STATUS_LOW;
++
++ if (bstat->status & SIMPAD_BATT_STATUS_CHARGING) {
++ if (icharger > CHARGING_MAX_LEVEL)
++ icharger = CHARGING_MAX_LEVEL;
++ if (icharger < CHARGING_LED_LEVEL)
++ icharger = CHARGING_LED_LEVEL;
++
++ bstat->percentage = 100 - 100 * (icharger - CHARGING_LED_LEVEL) /
++ (CHARGING_MAX_LEVEL - CHARGING_LED_LEVEL);
++ } else {
++ if (vbatt > BATT_FULL)
++ vbatt = BATT_FULL;
++ if (vbatt < BATT_EMPTY)
++ vbatt = BATT_EMPTY;
++
++ bstat->percentage = 100 * (vbatt - BATT_EMPTY) / (BATT_FULL - BATT_EMPTY);
++ }
++
++ /* let's assume: full load is 7h */
++ /* bstat->life = 420*bstat->percentage/100; */
++ /* mrdata: think, 4h is more realistic */
++ bstat->life = 240*(bstat->percentage)/100;
++
++#if 0
++ printk("get_battery: ac: %02x / ch: %02x / perc: %02x / life: %d \n",
++ bstat->ac_status, bstat->status,
++ bstat->percentage, bstat->life );
++#endif
++
++ return 0;
++}
++
++void simpad_apm_get_power_status(struct apm_power_info *info)
++{
++ struct simpad_battery_apm bstat;
++ unsigned char ac = APM_AC_UNKNOWN;
++ unsigned char level = APM_BATTERY_STATUS_UNKNOWN;
++ int status, result;
++
++ result = simpad_get_battery(&bstat);
++ if (result) {
++ printk("%s: unable to access battery information: result=%d\n", __FUNCTION__, result);
++ return;
++ }
++
++ switch (bstat.ac_status) {
++ case SIMPAD_AC_STATUS_AC_OFFLINE:
++ ac = APM_AC_OFFLINE;
++ break;
++ case SIMPAD_AC_STATUS_AC_ONLINE:
++ ac = APM_AC_ONLINE;
++ break;
++ case SIMPAD_AC_STATUS_AC_BACKUP:
++ ac = APM_AC_BACKUP;
++ break;
++ }
++
++ info->ac_line_status = ac;
++
++ status = bstat.status;
++ if (status & (SIMPAD_BATT_STATUS_CHARGING | SIMPAD_BATT_STATUS_CHARGE_MAIN))
++ level = APM_BATTERY_STATUS_CHARGING;
++ else if (status & (SIMPAD_BATT_STATUS_HIGH | SIMPAD_BATT_STATUS_FULL))
++ level = APM_BATTERY_STATUS_HIGH;
++ else if (status & SIMPAD_BATT_STATUS_LOW)
++ level = APM_BATTERY_STATUS_LOW;
++ else if (status & SIMPAD_BATT_STATUS_CRITICAL)
++ level = APM_BATTERY_STATUS_CRITICAL;
++
++ info->battery_status = level;
++ info->battery_flag = info->battery_status;
++
++ info->battery_life = bstat.percentage;
++
++ /* we have a dumb battery - so we know nothing */
++ info->time = bstat.life;
++ info->units = APM_UNITS_MINS;
++
++#if 0
++ printk("apm_get_power: ac: %02x / bs: %02x / bf: %02x / perc: %02x / life: %d\n",
++ info->ac_line_status, info->battery_status, info->battery_flag,
++ info->battery_life, info->time );
++#endif
++ return;
++}
++
++module_init(ucb1x00_simpad_init);
++module_exit(ucb1x00_simpad_exit);
++
++
++MODULE_AUTHOR("Juergen Messerer <juergen.messerer@freesurf.ch>");
++MODULE_DESCRIPTION("SIMpad noddy testing only example ADC driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.27/drivers/mfd/ucb1x00-simpad.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/drivers/mfd/ucb1x00-simpad.h 2008-12-07 01:02:19.936271486 +0100
+@@ -0,0 +1,86 @@
++/*
++ * linux/drivers/mfd/ucb1x00-simpad.h
++ *
++ * Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * 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.
++ */
++#ifndef UCB1300_SIMPAD_H
++#define UCB1300_SIMPAD_H
++
++/*
++ * Conversion from AD -> mV
++ * 7.5V = 1023 7.33137mV/Digit
++ *
++ * 400 Units == 9.7V
++ * a = ADC value
++ * 2007/03/24 mrdata:
++ * according UCB1300 datasheet ADC error max. 3 LSB
++ * 5-95% of full scale -> 3*7.33137mV = 21.99mV
++ * // old
++ * 21 = ADC error
++ * 12600 = Divident to get 2*7.3242
++ * 860 = Divider to get 2*7.3242
++ * 170 = Voltagedrop over
++ *
++ * // new
++ * 3 = ADC error
++ * 12610 = Divident to get 2*7.33139
++ * 860 = Divider to get 2*7.33139
++ * 170 = Voltagedrop over
++ */
++// #define CALIBRATE_BATTERY(a) ((((a + 21)*12600)/860) + 170)
++#define CALIBRATE_BATTERY(a) ((((a + 3)*12610)/860) + 170)
++
++/*
++ * We have two types of batteries a small and a large one
++ * To get the right value we to distinguish between those two
++ * 450 Units == 15 V
++ */
++#ifdef SMALL_BATTERY
++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 51)
++#define MIN_SUPPLY 8500 /* Less then 8.5V means no powersupply */
++#else
++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 45)
++//#define MIN_SUPPLY 14000 /* Less then 14V means no powersupply */
++#define MIN_SUPPLY 12000 /* Less then 12V means no powersupply */
++#endif
++
++/*
++ * Charging Current
++ * if value is >= 50 then charging is on
++ */
++// #define CALIBRATE_CHARGING(a) (((a) * 1000) / (152/4))
++#define CALIBRATE_CHARGING(a) (a)
++//#define CHARGING_LED_LEVEL 50
++
++#ifdef CONFIG_SA1100_SIMPAD_SINUSPAD
++
++// type small battery
++#define CHARGING_LED_LEVEL 12
++#define CHARGING_MAX_LEVEL 120
++#define BATT_FULL 8100
++#define BATT_LOW 7300
++#define BATT_CRITICAL 6700
++#define BATT_EMPTY 6400
++
++#else // CONFIG_SA1100_SIMPAD_SINUSPAD
++
++// type large battery
++// because of ADC error CHARGING_LED_LEVEL can changed
++// from 27 to 28
++#define CHARGING_LED_LEVEL 27
++#define CHARGING_MAX_LEVEL 265
++// BATT_FULL with SIMPAD_AC_STATUS_AC_OFFLINE
++#define BATT_FULL 8100
++#define BATT_LOW 7400
++#define BATT_CRITICAL 6800
++#define BATT_EMPTY 6500
++
++#endif // CONFIG_SA1100_SIMPAD_SINUSPAD
++
++// int simpad_get_battery(struct simpad_battery_apm *bstat);
++
++#endif
+Index: linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad_pm.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad_pm.h 2008-12-07 01:02:19.936271486 +0100
+@@ -0,0 +1,236 @@
++/*
++* Abstraction interface for microcontroller connection to rest of system
++*
++* Copyright 2003 Peter Pregler
++* Copyright 2000,1 Compaq Computer Corporation.
++*
++* Use consistent with the GNU GPL is permitted,
++* provided that this copyright notice is
++* preserved in its entirety in all copies and derived works.
++*
++* COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
++* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
++* FITNESS FOR ANY PARTICULAR PURPOSE.
++*
++* Author: Peter Pregler (based on work for ipaq by Andrew Christian)
++*
++*/
++
++#ifndef __SIMPAD_HAL_H
++#define __SIMPAD_HAL_H
++
++#include <linux/apm-emulation.h>
++
++struct simpad_battery_apm {
++ unsigned char ac_status; /* line connected yes/no */
++ unsigned char status; /* battery loading yes/no */
++ unsigned char percentage; /* percentage loaded */
++ unsigned short life; /* life till empty */
++};
++
++extern void simpad_apm_get_power_status(struct apm_power_info *);
++
++// extern int simpad_get_battery(struct simpad_battery_apm *bstat);
++
++/* These should match the apm_bios.h definitions */
++#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00
++#define SIMPAD_AC_STATUS_AC_ONLINE 0x01
++#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */
++#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff
++
++
++/* These bitfields are rarely "or'd" together */
++#define SIMPAD_BATT_STATUS_HIGH 0x01
++#define SIMPAD_BATT_STATUS_LOW 0x02
++#define SIMPAD_BATT_STATUS_CRITICAL 0x04
++#define SIMPAD_BATT_STATUS_CHARGING 0x08
++#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10
++#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */
++#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */
++#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */
++#define SIMPAD_BATT_STATUS_NOBATT 0x80
++#define SIMPAD_BATT_STATUS_UNKNOWN 0xff
++
++#if 0 // FIXME
++#include <linux/simpad_ts.h>
++
++enum simpad_asset_type {
++ ASSET_TCHAR = 0,
++ ASSET_SHORT,
++ ASSET_LONG
++};
++
++#define TTYPE(_type) (((unsigned int)_type) << 8)
++#define TCHAR(_len) (TTYPE(ASSET_TCHAR) | (_len))
++#define TSHORT TTYPE(ASSET_SHORT)
++#define TLONG TTYPE(ASSET_LONG)
++#define ASSET(_type,_num) ((((unsigned int)_type)<<16) | (_num))
++
++#define ASSET_HM_VERSION ASSET( TCHAR(10), 0 ) /* 1.1, 1.2 */
++#define ASSET_SERIAL_NUMBER ASSET( TCHAR(40), 1 ) /* Unique iPAQ serial number */
++#define ASSET_MODULE_ID ASSET( TCHAR(20), 2 ) /* E.g., "iPAQ 3700" */
++#define ASSET_PRODUCT_REVISION ASSET( TCHAR(10), 3 ) /* 1.0, 2.0 */
++#define ASSET_PRODUCT_ID ASSET( TSHORT, 4 ) /* 2 = Palm-sized computer */
++#define ASSET_FRAME_RATE ASSET( TSHORT, 5 )
++#define ASSET_PAGE_MODE ASSET( TSHORT, 6 ) /* 0 = Flash memory */
++#define ASSET_COUNTRY_ID ASSET( TSHORT, 7 ) /* 0 = USA */
++#define ASSET_IS_COLOR_DISPLAY ASSET( TSHORT, 8 ) /* Boolean, 1 = yes */
++#define ASSET_ROM_SIZE ASSET( TSHORT, 9 ) /* 16, 32 */
++#define ASSET_RAM_SIZE ASSET( TSHORT, 10 ) /* 32768 */
++#define ASSET_HORIZONTAL_PIXELS ASSET( TSHORT, 11 ) /* 240 */
++#define ASSET_VERTICAL_PIXELS ASSET( TSHORT, 12 ) /* 320 */
++
++#define ASSET_TYPE(_asset) (((_asset)&0xff000000)>>24)
++#define ASSET_TCHAR_LEN(_asset) (((_asset)&0x00ff0000)>>16)
++#define ASSET_NUMBER(_asset) ((_asset)&0x0000ffff)
++
++#define MAX_TCHAR_LEN 40
++
++struct simpad_asset {
++ unsigned int type;
++ union {
++ unsigned char tchar[ MAX_TCHAR_LEN ];
++ unsigned short vshort;
++ unsigned long vlong;
++ } a;
++};
++
++/********************************************************************
++ * Interface to the hardware-type specific functions
++ *
++ * get_version Read the version number of the microcontroller on the option pack SPI bus
++ * spi_read Reads from the serial EEPROM memory on the option pack SPI bus
++ * spi_write Write to the serial EEPROM memory on the option pack SPI bus
++ * get_option_detect Returns whether or not an option pack is present
++ *
++ * get_thermal_sensor Return measured temperature of the unit, in units of 0.125 deg C
++ * set_notify_led Turns on, off, or blinks the Green LED
++ * read_light_sensor Returns the value of the front light sensor
++ * get_battery Returns the current voltage and charging state of all batteries
++ * audio_clock Sets the audio CODEC to run at a particular rate
++ * audio_power Turns on/off audio CODEC (internally calls audio_clock)
++ * audio_mute Mutes the audio CODEC
++ * asset_read Extracts PocketPC-style asset information from persistent memory
++ * backlight_control Adjusts the backlight level (only on/off for 3100)
++ *
++ *
++ * iPAQ 3100 only
++ * ==============
++ * codec_control Reset/mute/control level of 3100 audio codec
++ * contrast_control Adjusts the contrast level (only for 3100)
++ *
++ * iPAQ 3600, 3700 only
++ * ====================
++ * eeprom_read Reads from the asset information on the eeprom of a 3600 (deprecated)
++ * eeprom_write Writes to the asset information on the eeprom (deprecated)
++ *
++ * The interfaces to the EEPROM functions are maintained only because the simpad_ts driver has
++ * a deprecated ioctl call for them. They are used internally by the "asset_read" function.
++ *
++ * iPAQ 3800, 3900 only
++ * ====================
++ * set_ebat Tells enhanced PCMCIA sleeves that this iPAQ can handle
++ * a wider voltage range (applies to 3800, 3900)
++ *
++ *********************************************************************/
++
++struct simpad_hal_ops {
++ /* Functions provided by the underlying hardware */
++ int (*get_version)( struct simpad_ts_version * );
++ int (*eeprom_read)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*eeprom_write)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*get_thermal_sensor)( unsigned short * );
++ int (*set_notify_led)( unsigned char mode, unsigned char duration,
++ unsigned char ontime, unsigned char offtime );
++ int (*read_light_sensor)( unsigned char *result );
++ int (*get_battery)( struct simpad_battery * );
++ int (*spi_read)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*spi_write)( unsigned short address, unsigned char *data, unsigned short len );
++ int (*codec_control)( unsigned char, unsigned char );
++ int (*get_option_detect)( int *result );
++ int (*audio_clock)( long samplerate );
++ int (*audio_power)( long samplerate );
++ int (*audio_mute)( int mute );
++ int (*asset_read)( struct simpad_asset *asset );
++ int (*set_ebat)( void );
++
++ /* Functions indirectly provided by the underlying hardware */
++ int (*backlight_control)( enum flite_pwr power, unsigned char level );
++ int (*contrast_control)( unsigned char level );
++
++ /* for module use counting */
++ struct module *owner;
++};
++
++/* Used by the device-specific hardware module to register itself */
++extern int simpad_hal_register_interface( struct simpad_hal_ops *ops );
++extern void simpad_hal_unregister_interface( struct simpad_hal_ops *ops );
++
++/*
++ * Calls into HAL from the device-specific hardware module
++ * These run at interrupt time
++ */
++extern void simpad_hal_keypress( unsigned char key );
++extern void simpad_hal_touchpanel( unsigned short x, unsigned short y, int down );
++extern void simpad_hal_option_detect( int present );
++
++/* Callbacks registered by device drivers */
++struct simpad_driver_ops {
++ void (*keypress)( unsigned char key );
++ void (*touchpanel)( unsigned short x, unsigned short y, int down );
++ void (*option_detect)( int present );
++};
++
++extern int simpad_hal_register_driver( struct simpad_driver_ops * );
++extern void simpad_hal_unregister_driver( struct simpad_driver_ops * );
++
++
++/* Calls into HAL from device drivers and other kernel modules */
++extern void simpad_get_flite( struct simpad_ts_backlight *bl );
++extern void simpad_get_contrast( unsigned char *contrast );
++extern int simpad_set_flite( enum flite_pwr pwr, unsigned char brightness );
++extern int simpad_set_contrast( unsigned char contrast );
++extern int simpad_toggle_frontlight( void );
++
++extern int simpad_apm_get_power_status(unsigned char *ac_line_status, unsigned char *battery_status,
++ unsigned char *battery_flag, unsigned char *battery_percentage,
++ unsigned short *battery_life);
++
++extern struct simpad_hal_ops *simpad_hal_ops;
++
++/* Do not use this macro in driver files - instead, use the inline functions defined below */
++#define CALL_HAL( f, args... ) \
++ { int __result = -EIO; \
++ if ( simpad_hal_ops && simpad_hal_ops->f ) { \
++ __MOD_INC_USE_COUNT(simpad_hal_ops->owner); \
++ __result = simpad_hal_ops->f(args); \
++ __MOD_DEC_USE_COUNT(simpad_hal_ops->owner); \
++ } \
++ return __result; }
++
++#define HFUNC static __inline__ int
++
++/* The eeprom_read/write address + len has a maximum value of 512. Both must be even numbers */
++HFUNC simpad_eeprom_read( u16 addr, u8 *data, u16 len ) CALL_HAL(eeprom_read,addr,data,len)
++HFUNC simpad_eeprom_write( u16 addr, u8 *data, u16 len) CALL_HAL(eeprom_write,addr,data,len)
++HFUNC simpad_spi_read( u8 addr, u8 *data, u16 len) CALL_HAL(spi_read,addr,data,len)
++HFUNC simpad_spi_write( u8 addr, u8 *data, u16 len) CALL_HAL(spi_write,addr,data,len)
++HFUNC simpad_get_version( struct simpad_ts_version *v ) CALL_HAL(get_version,v)
++HFUNC simpad_get_thermal_sensor( u16 *thermal ) CALL_HAL(get_thermal_sensor,thermal)
++HFUNC simpad_set_led( u8 mode, u8 dur, u8 ont, u8 offt ) CALL_HAL(set_notify_led, mode, dur, ont, offt)
++HFUNC simpad_get_light_sensor( u8 *result ) CALL_HAL(read_light_sensor,result)
++HFUNC simpad_get_battery( struct simpad_battery *bat ) CALL_HAL(get_battery,bat)
++HFUNC simpad_get_option_detect( int *result) CALL_HAL(get_option_detect,result)
++HFUNC simpad_audio_clock( long samplerate ) CALL_HAL(audio_clock,samplerate)
++HFUNC simpad_audio_power( long samplerate ) CALL_HAL(audio_power,samplerate)
++HFUNC simpad_audio_mute( int mute ) CALL_HAL(audio_mute,mute)
++HFUNC simpad_asset_read( struct simpad_asset *asset ) CALL_HAL(asset_read,asset)
++HFUNC simpad_set_ebat( void ) CALL_HAL(set_ebat)
++
++/* Don't use these functions directly - rather, call {get,set}_{flite,contrast} */
++ /* Functions indirectly provided by the underlying hardware */
++HFUNC simpad_backlight_control( enum flite_pwr p, u8 v ) CALL_HAL(backlight_control,p,v)
++HFUNC simpad_contrast_control( u8 level ) CALL_HAL(contrast_control,level)
++
++#endif
++#endif
diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-cs3-simpad.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-cs3-simpad.patch
new file mode 100644
index 0000000000..400e171208
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-cs3-simpad.patch
@@ -0,0 +1,192 @@
+Index: linux-2.6.27/arch/arm/mach-sa1100/Makefile
+===================================================================
+--- linux-2.6.27.orig/arch/arm/mach-sa1100/Makefile 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27/arch/arm/mach-sa1100/Makefile 2008-12-04 03:12:57.098042257 +0100
+@@ -41,6 +41,7 @@
+ obj-$(CONFIG_SA1100_SHANNON) += shannon.o
+
+ obj-$(CONFIG_SA1100_SIMPAD) += simpad.o
++obj-$(CONFIG_SA1100_SIMPAD) += cs3-simpad.o
+ led-$(CONFIG_SA1100_SIMPAD) += leds-simpad.o
+
+ # LEDs support
+Index: linux-2.6.27/arch/arm/mach-sa1100/cs3-simpad.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/arch/arm/mach-sa1100/cs3-simpad.c 2008-12-07 00:36:17.353090743 +0100
+@@ -0,0 +1,175 @@
++/*
++ * simpad-cs3.c
++ *
++ * This driver shows the GPIO states of the cs3 latch. You can also
++ * switch some GPIOS.
++ *
++ * (c) 2007 Bernhard Guillon <Bernhard.Guillon@OpenSIMpad.org>
++ *
++ * You may use this code as per GPL version 2
++ *
++ * Some parts are based on battery.c
++ *
++ * mrdata: -added cs3_ro support
++ * -added preprocessor stuff
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/device.h>
++
++#include <mach/simpad.h>
++
++extern long get_cs3_ro(void);
++extern long get_cs3_shadow(void);
++extern void set_cs3_bit(int value);
++extern void clear_cs3_bit(int value);
++
++struct cs3 {
++ struct device dev;
++ const char *name;
++ char *id;
++ int type;
++};
++
++struct cs3 cs3 ={
++ .name = "latch_cs3",
++};
++
++
++#define CS3_STORE_ATTR(namek,nameg) \
++static ssize_t namek##_store (struct device *dev, \
++ struct device_attribute *attr, \
++ const char *buf, size_t count) \
++{ \
++ char val; \
++ if (sscanf(buf, "%c",&val) != 1) \
++ return -EINVAL; \
++ if (val == '1') \
++ set_cs3_bit(nameg); \
++ else if (val == '0') \
++ clear_cs3_bit(nameg); \
++ return strlen(buf); \
++}
++
++CS3_STORE_ATTR(display_on, DISPLAY_ON);
++CS3_STORE_ATTR(dect_power_on, DECT_POWER_ON);
++CS3_STORE_ATTR(irda_sd, IRDA_SD);
++CS3_STORE_ATTR(sd_mediaq, SD_MEDIAQ);
++CS3_STORE_ATTR(led2_on, LED2_ON);
++CS3_STORE_ATTR(irda_mode, IRDA_MODE);
++CS3_STORE_ATTR(reset_simcard, RESET_SIMCARD);
++
++
++#define CS3_ATTR(shadro,namek,nameg,mode,store) \
++static ssize_t namek##_show(struct device *dev, \
++ struct device_attribute *attr, \
++ char *buf) \
++{ \
++ if (get_cs3_##shadro() & nameg ) { \
++ return sprintf(buf, "1\n"); \
++ } \
++ else { \
++ return sprintf(buf, "0\n"); \
++ } \
++} \
++static DEVICE_ATTR(namek, mode, namek##_show, store)
++
++CS3_ATTR(shadow, vcc_5v_en, VCC_5V_EN, 0444, NULL);
++CS3_ATTR(shadow, vcc_3v_en, VCC_3V_EN, 0444, NULL);
++CS3_ATTR(shadow, en1, EN1, 0444, NULL);
++CS3_ATTR(shadow, en0, EN0, 0444, NULL);
++CS3_ATTR(shadow, display_on, DISPLAY_ON, 0664, display_on_store);
++CS3_ATTR(shadow, pcmcia_buff_dis, PCMCIA_BUFF_DIS, 0444, NULL);
++CS3_ATTR(shadow, mq_reset, MQ_RESET, 0444, NULL);
++CS3_ATTR(shadow, pcmcia_reset, PCMCIA_RESET, 0444, NULL);
++CS3_ATTR(shadow, dect_power_on, DECT_POWER_ON, 0664, dect_power_on_store);
++CS3_ATTR(shadow, irda_sd, IRDA_SD, 0664, irda_sd_store);
++CS3_ATTR(shadow, rs232_on, RS232_ON, 0444, NULL);
++CS3_ATTR(shadow, sd_mediaq, SD_MEDIAQ, 0664, sd_mediaq_store);
++CS3_ATTR(shadow, led2_on, LED2_ON, 0664, led2_on_store);
++CS3_ATTR(shadow, irda_mode, IRDA_MODE, 0664, irda_mode_store);
++CS3_ATTR(shadow, enable_5v, ENABLE_5V, 0444, NULL);
++CS3_ATTR(shadow, reset_simcard, RESET_SIMCARD, 0664, reset_simcard_store);
++CS3_ATTR(ro, pcmcia_bvd1, PCMCIA_BVD1, 0444, NULL);
++CS3_ATTR(ro, pcmcia_bvd2, PCMCIA_BVD2, 0444, NULL);
++CS3_ATTR(ro, pcmcia_vs1, PCMCIA_VS1, 0444, NULL);
++CS3_ATTR(ro, pcmcia_vs2, PCMCIA_VS2, 0444, NULL);
++CS3_ATTR(ro, lock_ind, LOCK_IND, 0444, NULL);
++CS3_ATTR(ro, charging_state, CHARGING_STATE, 0444, NULL);
++CS3_ATTR(ro, pcmcia_short, PCMCIA_SHORT, 0444, NULL);
++
++static struct device simpad_gpios_device = {
++ .init_name = "simpad",
++};
++
++#define create_entry_conditional(namek) \
++ rc = device_create_file(&cs3->dev, &dev_attr_##namek); \
++ if (rc) goto out; \
++
++static int register_cs3_latch(struct cs3 *cs3)
++{
++ int rc = 0;
++ cs3->dev.parent = &simpad_gpios_device;
++ strcpy(cs3->dev.bus_id, cs3->name);
++ rc = device_register(&cs3->dev);
++ if(rc)
++ goto out;
++
++ create_entry_conditional(vcc_5v_en);
++ create_entry_conditional(vcc_3v_en);
++ create_entry_conditional(en1);
++ create_entry_conditional(en0);
++ create_entry_conditional(display_on);
++ create_entry_conditional(pcmcia_buff_dis);
++ create_entry_conditional(mq_reset);
++ create_entry_conditional(pcmcia_reset);
++ create_entry_conditional(dect_power_on);
++ create_entry_conditional(irda_sd);
++ create_entry_conditional(rs232_on);
++ create_entry_conditional(sd_mediaq);
++ create_entry_conditional(led2_on);
++ create_entry_conditional(irda_mode);
++ create_entry_conditional(enable_5v);
++ create_entry_conditional(reset_simcard);
++ create_entry_conditional(pcmcia_bvd1);
++ create_entry_conditional(pcmcia_bvd2);
++ create_entry_conditional(pcmcia_vs1);
++ create_entry_conditional(pcmcia_vs2);
++ create_entry_conditional(lock_ind);
++ create_entry_conditional(charging_state);
++ create_entry_conditional(pcmcia_short);
++
++out:
++ return rc;
++}
++
++static int __init simpad_gpios_device_init(void)
++{
++ int rc = 0;
++
++ rc = device_register(&simpad_gpios_device);
++
++ if(rc != 0)
++ {
++ printk(KERN_ERR "cs3 latch device failed to register properly\n");
++ return rc;
++ }
++
++ rc = register_cs3_latch(&cs3);
++ return rc;
++}
++
++static void __exit simpad_gpios_device_exit(void)
++{
++ device_unregister(&simpad_gpios_device);
++}
++
++module_init(simpad_gpios_device_init);
++module_exit(simpad_gpios_device_exit);
++
++MODULE_AUTHOR("Bernhard Guillon");
++MODULE_DESCRIPTION("CS3_latch driver");
++MODULE_LICENSE("GPL");
diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-mq200.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-mq200.patch
new file mode 100644
index 0000000000..b64715116c
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-mq200.patch
@@ -0,0 +1,2640 @@
+Index: linux-2.6.27/drivers/video/Kconfig
+===================================================================
+--- linux-2.6.27.orig/drivers/video/Kconfig 2008-12-07 01:18:57.236171217 +0100
++++ linux-2.6.27/drivers/video/Kconfig 2008-12-07 01:19:16.649503720 +0100
+@@ -16,7 +16,7 @@
+ config VIDEO_OUTPUT_CONTROL
+ tristate "Lowlevel video output switch controls"
+ help
+- This framework adds support for low-level control of the video
++ This framework adds support for low-level control of the video
+ output switch.
+
+ menuconfig FB
+@@ -231,7 +231,7 @@
+ This is particularly important to one driver, matroxfb. If
+ unsure, say N.
+
+-comment "Frame buffer hardware drivers"
++comment "Frambuffer hardware drivers"
+ depends on FB
+
+ config FB_CIRRUS
+@@ -611,7 +611,7 @@
+ BIOS routines contained in a ROM chip in HP PA-RISC based machines.
+ Enabling this option will implement the linux framebuffer device
+ using calls to the STI BIOS routines for initialisation.
+-
++
+ If you enable this option, you will get a planar framebuffer device
+ /dev/fb which will work on the most common HP graphic cards of the
+ NGLE family, including the artist chips (in the 7xx and Bxxx series),
+@@ -1061,36 +1061,36 @@
+ select FB_CFB_IMAGEBLIT
+ select VGASTATE
+ help
+- This driver supports the on-board graphics built in to the Intel 810
++ This driver supports the on-board graphics built in to the Intel 810
+ and 815 chipsets. Say Y if you have and plan to use such a board.
+
+ To compile this driver as a module, choose M here: the
+ module will be called i810fb.
+
+- For more information, please read
++ For more information, please read
+ <file:Documentation/fb/intel810.txt>
+
+ config FB_I810_GTF
+ bool "use VESA Generalized Timing Formula"
+ depends on FB_I810
+ help
+- If you say Y, then the VESA standard, Generalized Timing Formula
++ If you say Y, then the VESA standard, Generalized Timing Formula
+ or GTF, will be used to calculate the required video timing values
+- per video mode. Since the GTF allows nondiscrete timings
++ per video mode. Since the GTF allows nondiscrete timings
+ (nondiscrete being a range of values as opposed to discrete being a
+- set of values), you'll be able to use any combination of horizontal
++ set of values), you'll be able to use any combination of horizontal
+ and vertical resolutions, and vertical refresh rates without having
+ to specify your own timing parameters. This is especially useful
+- to maximize the performance of an aging display, or if you just
+- have a display with nonstandard dimensions. A VESA compliant
++ to maximize the performance of an aging display, or if you just
++ have a display with nonstandard dimensions. A VESA compliant
+ monitor is recommended, but can still work with non-compliant ones.
+- If you need or want this, then select this option. The timings may
+- not be compliant with Intel's recommended values. Use at your own
++ If you need or want this, then select this option. The timings may
++ not be compliant with Intel's recommended values. Use at your own
+ risk.
+
+- If you say N, the driver will revert to discrete video timings
++ If you say N, the driver will revert to discrete video timings
+ using a set recommended by Intel in their documentation.
+-
++
+ If unsure, say N.
+
+ config FB_I810_I2C
+@@ -1214,10 +1214,10 @@
+ framebuffer section. G450/G550 secondary head and digital output
+ are supported without additional modules.
+
+- The driver starts in monitor mode. You must use the matroxset tool
+- (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to
+- swap primary and secondary head outputs, or to change output mode.
+- Secondary head driver always start in 640x480 resolution and you
++ The driver starts in monitor mode. You must use the matroxset tool
++ (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to
++ swap primary and secondary head outputs, or to change output mode.
++ Secondary head driver always start in 640x480 resolution and you
+ must use fbset to change it.
+
+ Do not forget that second head supports only 16 and 32 bpp
+@@ -1315,7 +1315,7 @@
+ "I2C support" and "I2C bit-banging support" in the character devices
+ section.
+
+- If you say M here then "I2C support" and "I2C bit-banging support"
++ If you say M here then "I2C support" and "I2C bit-banging support"
+ can be build either as modules or built-in.
+
+ There is a product page at
+@@ -1327,7 +1327,7 @@
+ select FB_DDC
+ default y
+ help
+- Say Y here if you want DDC/I2C support for your Radeon board.
++ Say Y here if you want DDC/I2C support for your Radeon board.
+
+ config FB_RADEON_BACKLIGHT
+ bool "Support for backlight control"
+@@ -1431,6 +1431,15 @@
+ ---help---
+ Driver for graphics boards with S3 Trio / S3 Virge chip.
+
++config FB_MQ200
++ bool "MQ200 Driver"
++ depends on (FB = y) && ARM && ARCH_SA1100
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ select FB_CFB_IMAGEBLIT
++ help
++ This is a MQ200 driver tested only on Siemens SIMpads.
++
+ config FB_SAVAGE
+ tristate "S3 Savage support"
+ depends on FB && PCI && EXPERIMENTAL
+@@ -1508,7 +1517,7 @@
+ select VGASTATE
+ help
+ This driver supports notebooks with NeoMagic PCI chips.
+- Say Y if you have such a graphics card.
++ Say Y if you have such a graphics card.
+
+ To compile this driver as a module, choose M here: the
+ module will be called neofb.
+@@ -1554,7 +1563,7 @@
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+- Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or
++ Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or
+ Voodoo2 (cvg) based graphics card.
+
+ To compile this driver as a module, choose M here: the
+Index: linux-2.6.27/drivers/video/Makefile
+===================================================================
+--- linux-2.6.27.orig/drivers/video/Makefile 2008-12-07 01:18:57.252839170 +0100
++++ linux-2.6.27/drivers/video/Makefile 2008-12-07 01:19:16.649503720 +0100
+@@ -36,6 +36,7 @@
+ obj-$(CONFIG_FB_PM2) += pm2fb.o
+ obj-$(CONFIG_FB_PM3) += pm3fb.o
+
++obj-$(CONFIG_FB_MQ200) += mq200/
+ obj-$(CONFIG_FB_MATROX) += matrox/
+ obj-$(CONFIG_FB_RIVA) += riva/
+ obj-$(CONFIG_FB_NVIDIA) += nvidia/
+Index: linux-2.6.27/drivers/video/backlight/Kconfig
+===================================================================
+--- linux-2.6.27.orig/drivers/video/backlight/Kconfig 2008-12-07 01:18:57.262838759 +0100
++++ linux-2.6.27/drivers/video/backlight/Kconfig 2008-12-07 01:19:16.649503720 +0100
+@@ -164,3 +164,19 @@
+ If you have an Apple Macbook Pro with Nvidia graphics hardware say Y
+ to enable a driver for its backlight
+
++config BACKLIGHT_SIMPAD
++ tristate "SIMpad MQ200 Backlight driver"
++ depends on SA1100_SIMPAD && BACKLIGHT_CLASS_DEVICE
++ default y
++ help
++ If you have a Siemens SIMpad say Y to enable the
++ backlight driver.
++
++config LCD_SIMPAD
++ tristate "SIMpad MQ200 LCD driver"
++ depends on SA1100_SIMPAD && LCD_CLASS_DEVICE
++ default y
++ help
++ If you have a Siemens SIMpad say Y to enable the
++ LCD driver.
++
+Index: linux-2.6.27/drivers/video/backlight/Makefile
+===================================================================
+--- linux-2.6.27.orig/drivers/video/backlight/Makefile 2008-12-07 01:18:57.279504370 +0100
++++ linux-2.6.27/drivers/video/backlight/Makefile 2008-12-07 01:19:16.652836923 +0100
+@@ -16,4 +16,5 @@
+ obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
+ obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o
+ obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o
+-
++obj-$(CONFIG_BACKLIGHT_SIMPAD) += simpad_bl.o
++obj-$(CONFIG_LCD_SIMPAD) += simpad_lcd.o
+Index: linux-2.6.27/drivers/video/backlight/simpad_bl.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/drivers/video/backlight/simpad_bl.c 2008-12-07 01:32:34.467302764 +0100
+@@ -0,0 +1,208 @@
++/*
++ * GPLv2 <zecke@handhelds.org
++ *
++ * Implementation of the backlight_driver for
++ * the mq200 framebuffer
++ *
++ * 2007/03/17 mrdata:
++ * - small changes simpad_bl_get_brightness()
++ * simpad_bl_set_brightness()
++ * - new function simpad_bl_update_status()
++ * - changed struct backlight_properties simpad_bl_props()
++ * to new one
++ * - changed __init simpad_bl_init() -> backlight_device_register
++ *
++ * 2007/03/24 mrdata
++ * - added .brightness=127 in
++ * struct backlight_properties simpad_bl_props()
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++#include <linux/fb.h>
++#include <linux/backlight.h>
++
++#include <asm/types.h>
++#include <mach/hardware.h>
++#include <asm/io.h>
++
++#include "../mq200/mq200_data.h"
++
++#define SIMPAD_BACKLIGHT_MASK 0x00a10044
++#define SIMPAD_DEFAULT_INTENSITY 127
++#define SIMPAD_MAX_INTENSITY 254
++#define REGISTER_BASE 0xf2e00000
++
++static int simpad_bl_suspended;
++static int current_intensity = 0;
++
++static void simpad_bl_send_intensity(struct backlight_device *bd)
++{
++ int intensity = bd->props.brightness;
++
++ union fp0fr fp0fr;
++ unsigned long dutyCycle, pwmcontrol;
++
++ if (intensity > SIMPAD_MAX_INTENSITY)
++ intensity = SIMPAD_MAX_INTENSITY;
++
++ if (bd->props.power != FB_BLANK_UNBLANK)
++ intensity = 0;
++
++ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
++ intensity = 0;
++
++ if (simpad_bl_suspended)
++ intensity = 0;
++
++ if (intensity != current_intensity)
++ {
++ /*
++ * Determine dutyCycle.
++ * Note: the lower the value, the brighter the display!
++ */
++
++ dutyCycle = SIMPAD_MAX_INTENSITY - intensity;
++
++ /*
++ * Configure PWM0 (source clock = oscillator clock, pwm always enabled,
++ * zero, clock pre-divider = 4) pwm frequency = 12.0kHz
++ */
++
++ fp0fr.whole = readl(FP0FR(REGISTER_BASE));
++ pwmcontrol = fp0fr.whole & 0xffff00ff;
++ fp0fr.whole &= 0xffffff00;
++ fp0fr.whole |= 0x00000044;
++ writel(fp0fr.whole, FP0FR(REGISTER_BASE));
++
++ /* Write to pwm duty cycle register. */
++ fp0fr.whole = dutyCycle << 8;
++ fp0fr.whole &= 0x0000ff00;
++ fp0fr.whole |= pwmcontrol;
++ writel(fp0fr.whole, FP0FR(REGISTER_BASE));
++
++ current_intensity = intensity;
++ }
++}
++
++
++#ifdef CONFIG_PM
++static int simpad_bl_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct backlight_device *bd = platform_get_drvdata(pdev);
++
++ simpad_bl_suspended = 1;
++ simpad_bl_send_intensity(bd);
++ return 0;
++}
++
++static int simpad_bl_resume(struct platform_device *pdev)
++{
++ struct backlight_device *bd = platform_get_drvdata(pdev);
++
++ simpad_bl_suspended = 0;
++ simpad_bl_send_intensity(bd);
++ return 0;
++}
++#else
++#define simpad_bl_suspend NULL
++#define simpad_bl_resume NULL
++#endif
++
++
++static int simpad_bl_set_intensity(struct backlight_device *bd)
++{
++ simpad_bl_send_intensity(bd);
++ return 0;
++}
++
++
++static int simpad_bl_get_intensity(struct backlight_device *bd)
++{
++ return current_intensity;
++}
++
++
++static struct backlight_ops simpad_bl_ops = {
++ .get_brightness = simpad_bl_get_intensity,
++ .update_status = simpad_bl_set_intensity,
++};
++
++
++static int __init simpad_bl_probe(struct platform_device *pdev)
++{
++ struct backlight_device *bd;
++
++ bd = backlight_device_register("simpad-mq200-bl", &pdev->dev, NULL, &simpad_bl_ops);
++
++ if (IS_ERR (bd))
++ return PTR_ERR (bd);
++
++ platform_set_drvdata(pdev, bd);
++
++ bd->props.max_brightness = SIMPAD_MAX_INTENSITY;
++ bd->props.brightness = SIMPAD_DEFAULT_INTENSITY;
++ simpad_bl_send_intensity(bd);
++
++ return 0;
++}
++
++
++static int simpad_bl_remove(struct platform_device *pdev)
++{
++ struct backlight_device *bd = platform_get_drvdata(pdev);
++
++ bd->props.brightness = 0;
++ bd->props.power = 0;
++ simpad_bl_send_intensity(bd);
++
++ backlight_device_unregister(bd);
++
++ return 0;
++}
++
++static struct platform_driver simpad_bl_driver = {
++ .probe = simpad_bl_probe,
++ .remove = simpad_bl_remove,
++ .suspend = simpad_bl_suspend,
++ .resume = simpad_bl_resume,
++ .driver = {
++ .name = "simpad-mq200-bl",
++ },
++};
++
++static struct platform_device *simpad_bl_device = NULL;
++
++static int __init simpad_bl_init(void)
++{
++ int ret;
++
++ ret = platform_driver_register(&simpad_bl_driver);
++ if (!ret) {
++ simpad_bl_device = platform_device_alloc("simpad-mq200-bl", -1);
++ if (!simpad_bl_device)
++ return -ENOMEM;
++
++ ret = platform_device_add(simpad_bl_device);
++
++ if (ret) {
++ platform_device_put(simpad_bl_device);
++ platform_driver_unregister(&simpad_bl_driver);
++ }
++ }
++ return ret;
++}
++
++static void __exit simpad_bl_exit(void)
++{
++ platform_device_unregister(simpad_bl_device);
++ platform_driver_unregister(&simpad_bl_driver);
++}
++
++
++module_init(simpad_bl_init);
++module_exit(simpad_bl_exit);
++MODULE_AUTHOR("Holger Hans Peter Freyther");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.27/drivers/video/backlight/simpad_lcd.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/drivers/video/backlight/simpad_lcd.c 2008-12-07 01:33:39.386863247 +0100
+@@ -0,0 +1,172 @@
++/*
++ * GPLv2 <zecke@handhelds.org
++ *
++ * Implementation of the lcd_driver for the mq200 framebuffer
++ *
++ * 2007/03/24 mrdata:
++ * - added simpad_lcd_get_contrast()
++ * - added simpad_lcd_set_contrast()
++ * - modify struct lcd_properties simpad_lcd_props
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/fb.h>
++#include <linux/lcd.h>
++
++#include <mach/simpad.h>
++#include <mach/hardware.h>
++
++extern long get_cs3_shadow(void);
++extern void set_cs3_bit(int);
++extern void clear_cs3_bit(int);
++
++#define UNUSED(x) x=x
++
++static int simpad_lcd_get_power(struct lcd_device* dev)
++{
++ UNUSED(dev);
++
++ return (get_cs3_shadow() & DISPLAY_ON) ? 0 : 4;
++}
++
++static int simpad_lcd_set_power(struct lcd_device* dev, int power)
++{
++ UNUSED(dev);
++
++ if( power == 4 )
++ clear_cs3_bit(DISPLAY_ON);
++ else
++ set_cs3_bit(DISPLAY_ON);
++
++ return 0;
++}
++
++static int simpad_lcd_get_contrast(struct lcd_device* dev)
++{
++ UNUSED(dev);
++
++ return 0;
++}
++
++static int simpad_lcd_set_contrast(struct lcd_device* dev, int contrast)
++{
++ UNUSED(dev);
++
++ UNUSED(contrast);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int simpad_lcd_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ static int ret;
++ struct lcd_device* ld;
++
++ UNUSED(state);
++
++ ld = platform_get_drvdata(pdev);
++
++ ret = simpad_lcd_set_power(ld, 4);
++
++ return ret;
++}
++
++static int simpad_lcd_resume(struct platform_device *pdev)
++{
++ struct lcd_device *ld;
++ static int ret;
++
++ ld = platform_get_drvdata(pdev);
++
++ ret = simpad_lcd_set_power(ld, 0);
++
++ return ret;
++}
++#else
++#define simpad_lcd_suspend NULL
++#define simpad_lcd_resume NULL
++#endif
++
++
++/*FIXME
++static struct lcd_properties simpad_lcd_props = {
++ .max_contrast = 0,
++};
++*/
++
++static struct lcd_ops simpad_lcd_ops = {
++ .get_power = simpad_lcd_get_power,
++ .set_power = simpad_lcd_set_power,
++ .get_contrast = simpad_lcd_get_contrast,
++ .set_contrast = simpad_lcd_set_contrast,
++};
++
++static int __init simpad_lcd_probe(struct platform_device *pdev)
++{
++ struct lcd_device *ld;
++
++ ld = lcd_device_register ("simpad-mq200-lcd", &pdev->dev, NULL, &simpad_lcd_ops);
++
++ if (IS_ERR(ld))
++ return PTR_ERR(ld);
++
++ platform_set_drvdata(pdev, ld);
++
++ ld->props.max_contrast = 0;
++
++ return 0;
++}
++
++static int simpad_lcd_remove(struct platform_device *pdev)
++{
++ struct lcd_device *ld = platform_get_drvdata(pdev);
++
++ lcd_device_unregister(ld);
++
++ return 0;
++}
++
++static struct platform_driver simpad_lcd_driver = {
++ .probe = simpad_lcd_probe,
++ .remove = simpad_lcd_remove,
++ .suspend = simpad_lcd_suspend,
++ .resume = simpad_lcd_resume,
++ .driver = {
++ .name = "simpad-mq200-lcd",
++ },
++};
++
++static struct platform_device *simpad_lcd_device = NULL;
++
++static int __init simpad_lcd_init(void)
++{
++ int ret;
++
++ ret = platform_driver_register(&simpad_lcd_driver);
++ if (!ret) {
++ simpad_lcd_device = platform_device_alloc("simpad-mq200-lcd", -1);
++ if (!simpad_lcd_device)
++ return -ENOMEM;
++
++ ret = platform_device_add(simpad_lcd_device);
++
++ if (ret) {
++ platform_device_put(simpad_lcd_device);
++ platform_driver_unregister(&simpad_lcd_driver);
++ }
++ }
++ return ret;
++}
++
++static void __exit simpad_lcd_exit(void) {
++ platform_driver_unregister(&simpad_lcd_driver);
++ platform_device_unregister(simpad_lcd_device);
++}
++
++module_init(simpad_lcd_init);
++module_exit(simpad_lcd_exit);
++MODULE_AUTHOR("Holger Hans Peter Freyther");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.27/drivers/video/mq200/Makefile
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/drivers/video/mq200/Makefile 2008-12-07 01:19:16.652836923 +0100
+@@ -0,0 +1,6 @@
++# Makefile for mq200 video driver
++# 4 Aug 2003, Holger Hans Peter Freyther
++#
++
++obj-$(CONFIG_FB_MQ200) += mq_skeleton.o mq_external.o
++
+Index: linux-2.6.27/drivers/video/mq200/mq200_data.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/drivers/video/mq200/mq200_data.h 2008-12-07 01:19:16.656170018 +0100
+@@ -0,0 +1,1120 @@
++/*
++ * From ucLinux mq200fb.c and mq200fb.h
++ *
++ * 2007/03/11 mrdata:
++ * insert registers for graphics controller 2 module
++ */
++
++#ifndef __MQ200_FB_H__
++#define __MQ200_FB_H__
++
++struct mq200_io_regions {
++ u32 fb_size; /* framebuffer size */
++ unsigned long phys_mmio_base; /* physical register memory base */
++ unsigned long virt_mmio_base; /* virtual start of registers */
++ unsigned long phys_fb_base; /* physical address of frame buffer */
++ unsigned long virt_fb_base; /* virtual start of the framebuffer */
++};
++
++#define MQ200_MONITOR_HORI_RES(info) info->monitor_info.horizontal_res
++#define MQ200_MONITOR_VERT_RES(info) info->monitor_info.vertical_res
++#define MQ200_MONITOR_DEPTH(info) info->monitor_info.depth
++#define MQ200_MONITOR_LINE_LENGTH(info) info->monitor_info.line_length
++
++struct mq200_monitor_info {
++ unsigned int horizontal_res;
++ unsigned int vertical_res;
++ unsigned int depth;
++ unsigned int refresh;
++ unsigned int line_length;
++ unsigned long flags;
++};
++
++
++/**
++ * Addresses of Module
++ */
++#define MQ200_FB_BASE (x) (x + 0x1800000) /* framebuffer */
++#define MQ200_FB_SIZE 0x200000 /* framebuffer size in bytes */
++#define MQ200_REGS_BASE(x) (x + 0x1e00000) /* start of registers area */
++#define MQ200_REGS_SIZE 0x200000 /* registers area size */
++
++#define PMU_OFFSET 0x00000 /* power management */
++#define CPU_OFFSET 0x02000 /* CPU interface */
++#define MIU_OFFSET 0x04000 /* memory controller */
++#define IN_OFFSET 0x08000 /* interrupt controller */
++#define GC_OFFSET 0x0a000 /* graphics controller 1&2 */
++#define GE_OFFSET 0x0c000 /* graphics engine */
++#define FPI_OFFSET 0x0e000 /* flat panel controller */
++#define CP1_OFFSET 0x10000 /* color palette 1 */
++#define DC_OFFSET 0x14000 /* device configuration */
++#define PCI_OFFSET 0x16000 /* PCI configuration */
++#define PSF_OFFSET 0x18000 /* ??? */
++
++
++/****
++ * Registers
++ */
++
++/* power management unit */
++#define PMR(addr) (addr + PCI_OFFSET + 0x40)/* power management
++ register */
++#define PMR_VALUE 0x06210001 /* expected read value of PMR register */
++#define PM00R(addr) (addr + PMU_OFFSET + 0x00) /* power management unit
++ configuration
++ register */
++#define PM01R(addr) (addr + PMU_OFFSET + 0x04) /* D1 state control */
++#define PM02R(addr) (addr + PMU_OFFSET + 0x08) /* d2 state control */
++#define PM06R(addr) (addr + PMU_OFFSET + 0x18) /* PLL 2 programming */
++#define PM07R(addr) (addr + PMU_OFFSET + 0x1c) /* PLL 3 programming */
++
++#define PMCSR(addr) (addr + PCI_OFFSET + 0x44) /* power management
++ control/status
++ register */
++
++/* memory interface unit */
++#define MM00R(addr) (addr + MIU_OFFSET + 0x00)/* MIU interface control
++ 0 */
++#define MM01R(addr) (addr + MIU_OFFSET + 0x04) /* MIU interface control
++ 1 */
++#define MM02R(addr) (addr + MIU_OFFSET + 0x08) /* memory interface
++ control 2 */
++#define MM03R(addr) (addr + MIU_OFFSET + 0x0c) /* memory interface
++ control 3 */
++#define MM04R(addr) (addr + MIU_OFFSET + 0x10) /* memory interface
++ control 4 */
++/* graphics controller 1 module */
++#define GC00R(addr) (addr + GC_OFFSET + 0x00) /* graphics controller 1
++ control */
++#define GC01R(addr) (addr + GC_OFFSET + 0x04) /* graphics controller
++ CRT control */
++#define GC02R(addr) (addr + GC_OFFSET + 0x08) /* horizontal display 1
++ control */
++#define GC03R(addr) (addr + GC_OFFSET + 0x0c) /* vertical display 1
++ control */
++#define GC04R(addr) (addr + GC_OFFSET + 0x10) /* horizontal sync 1
++ control */
++#define GC05R(addr) (addr + GC_OFFSET + 0x14) /* vertical sync 1
++ control */
++#define GC07R(addr) (addr + GC_OFFSET + 0x1c) /* vertical display 1
++ count */
++#define GC08R(addr) (addr + GC_OFFSET + 0x20) /* horizontal window 1
++ control */
++#define GC09R(addr) (addr + GC_OFFSET + 0x24) /* vertical window 1
++ control */
++#define GC0AR(addr) (addr + GC_OFFSET + 0x28) /* alternate horizontal
++ window 1 control */
++#define GC0BR(addr) (addr + GC_OFFSET + 0x2c) /* alternate vertical
++ window 1 control */
++#define GC0CR(addr) (addr + GC_OFFSET + 0x30) /* window 1
++ start address */
++#define GC0DR(addr) (addr + GC_OFFSET + 0x34) /* alternate window 1
++ start address */
++#define GC0ER(addr) (addr + GC_OFFSET + 0x38) /* alternate window 1
++ stride */
++#define GC0FR(addr) (addr + GC_OFFSET + 0x3c) /* alternate window 1
++ line size */
++#define GC10R(addr) (addr + GC_OFFSET + 0x40) /* hardware cursor 1
++ position */
++#define GC11R(addr) (addr + GC_OFFSET + 0x44) /* hardware cursor 1
++ start address and
++ offset */
++#define GC12R(addr) (addr + GC_OFFSET + 0x48) /* hardware cursor 1
++ foreground color */
++#define GC13R(addr) (addr + GC_OFFSET + 0x4c) /* hardware cursor 1
++ background color */
++
++/* graphics controller 2 module */
++#define GC20R(addr) (addr + GC_OFFSET + 0x80) /* graphics controller 2
++ control */
++#define GC21R(addr) (addr + GC_OFFSET + 0x84) /* graphics controller
++ CRC control */
++#define GC22R(addr) (addr + GC_OFFSET + 0x88) /* horizontal display 2
++ control */
++#define GC23R(addr) (addr + GC_OFFSET + 0x8c) /* vertical display 2
++ control */
++#define GC24R(addr) (addr + GC_OFFSET + 0x90) /* horizontal sync 2
++ control */
++#define GC25R(addr) (addr + GC_OFFSET + 0x94) /* vertical sync 2
++ control */
++#define GC27R(addr) (addr + GC_OFFSET + 0x9c) /* vertical display 2
++ count */
++#define GC28R(addr) (addr + GC_OFFSET + 0xa0) /* horizontal window 2
++ control */
++#define GC29R(addr) (addr + GC_OFFSET + 0xa4) /* vertical window 2
++ control */
++#define GC2AR(addr) (addr + GC_OFFSET + 0xa8) /* alternate horizontal
++ window 2 control */
++#define GC2BR(addr) (addr + GC_OFFSET + 0xac) /* alternate vertical
++ window 2 control */
++#define GC2CR(addr) (addr + GC_OFFSET + 0xb0) /* window 2
++ start address */
++#define GC2DR(addr) (addr + GC_OFFSET + 0xb4) /* alternate window 2
++ start address */
++#define GC2ER(addr) (addr + GC_OFFSET + 0xb8) /* alternate window 2
++ stride */
++#define GC2FR(addr) (addr + GC_OFFSET + 0xbc) /* alternate window 2
++ line size */
++#define GC30R(addr) (addr + GC_OFFSET + 0xc0) /* hardware cursor 2
++ position */
++#define GC31R(addr) (addr + GC_OFFSET + 0xc4) /* hardware cursor 2
++ start address and
++ offset */
++#define GC32R(addr) (addr + GC_OFFSET + 0xc8) /* hardware cursor 2
++ foreground color */
++#define GC33R(addr) (addr + GC_OFFSET + 0xcc) /* hardware cursor 2
++ background color */
++
++/* graphics engine */
++#define ROP_SRCCOPY 0xCC /* dest = source */
++#define ROP_SRCPAINT 0xEE /* dest = source OR dest */
++#define ROP_SRCAND 0x88 /* dest = source AND dest */
++#define ROP_SRCINVERT 0x66 /* dest = source XOR dest */
++#define ROP_SRCERASE 0x44 /* dest = source AND (NOT dest) */
++#define ROP_NOTSRCCOPY 0x33 /* dest = NOT source */
++#define ROP_NOTSRCERASE 0x11 /* dest = (NOT source) AND (NOT dest) */
++#define ROP_MERGECOPY 0xC0 /* dest = source AND pattern */
++#define ROP_MERGEPAINT 0xBB /* dest = (NOT source) OR dest */
++#define ROP_PATCOPY 0xF0 /* dest = pattern */
++#define ROP_PATPAINT 0xFB /* dest = DPSnoo */
++#define ROP_PATINVERT 0x5A /* dest = pattern XOR dest */
++#define ROP_DSTINVERT 0x55 /* dest = NOT dest */
++#define ROP_BLACKNESS 0x00 /* dest = BLACK */
++#define ROP_WHITENESS 0xFF /* dest = WHITE */
++
++#define GE00R(addr) (addr + GE_OFFSET + 0x00) /* primary drawing command
++ register */
++#define GE01R(addr) (addr + GE_OFFSET + 0x04) /* primary width and
++ height register */
++#define GE02R(addr) (addr + GE_OFFSET + 0x08) /* primary destination
++ address register */
++#define GE03R(addr) (addr + GE_OFFSET + 0x0c) /* primary source XY
++ register */
++#define GE04R(addr) (addr + GE_OFFSET + 0x10) /* primary color compare
++ register */
++#define GE05R(addr) (addr + GE_OFFSET + 0x14) /* primary clip left/top
++ register */
++#define GE06R(addr) (addr + GE_OFFSET + 0x18) /* primary clip
++ right/bottom register
++ */
++#define GE07R(addr) (addr + GE_OFFSET + 0x1c) /* primary source and
++ pattern offset
++ register */
++#define GE08R(addr) (addr + GE_OFFSET + 0x20) /* primary foreground
++ color
++ register/rectangle
++ fill register */
++#define GE09R(addr) (addr + GE_OFFSET + 0x24) /* source stride/offset
++ register */
++#define GE0AR(addr) (addr + GE_OFFSET + 0x28) /* destination stride
++ register and color
++ depth */
++#define GE0BR(addr) (addr + GE_OFFSET + 0x2c) /* image base address
++ register */
++#define GE40R(addr) (addr + GE_OFFSET + 0x100) /* mono pattern register
++ 0 */
++#define GE41R(addr) (addr + GE_OFFSET + 0x104) /* mono pattern register
++ 1 */
++#define GE42R(addr) (addr + GE_OFFSET + 0x108) /* foreground color
++ register */
++#define GE43R(addr) (addr + GE_OFFSET + 0x10c) /* background color
++ register */
++/* color palette */
++#define C1xxR(addr, regno) \
++ (addr + CP1_OFFSET + (regno) * 4) /* graphics controller color
++ palette 1 */
++/* device configuration */
++#define DC00R(addr) (addr + DC_OFFSET + 0x00) /* device configuration
++ register 0 */
++#define DC_RESET 0x4000
++/* PCI configuration space */
++#define PC00R(addr) (addr + PCI_OFFSET + 0x00)/* device ID/vendor ID
++ register */
++/* Flatpanel Control */
++#define FP00R(addr) (addr + FPI_OFFSET + 0x00) /* Flat Panel Control 0 */
++#define FP01R(addr) (addr + FPI_OFFSET + 0x04) /* Flat Panel Output Pin */
++#define FP02R(addr) (addr + FPI_OFFSET + 0x08) /* Flat Panel Gener Purpose
++ Outout Control Register */
++#define FP03R(addr) (addr + FPI_OFFSET + 0x0c) /* General Purpose I/O Port
++ Control Register */
++#define FP04R(addr) (addr + FPI_OFFSET + 0x10) /* STN Panel Control Register */
++#define FP05R(addr) (addr + FPI_OFFSET + 0x14) /* D-STN Half Frame Buffer
++ Control Register -By Guess */
++#define FP0FR(addr) (addr + FPI_OFFSET + 0x3c) /* Pulse Width Modulation
++ Control Register */
++#define FRCTL_PATTERN_COUNT 32
++#define FP10R(addr) (addr + FPI_OFFSET + 0x40) /* Frame-Rate Control Pattern
++ Register */
++#define FP11R(addr) (addr + FPI_OFFSET + 0x44)
++#define FP2FR(addr) (addr + FPI_OFFSET + 0xc0) /* Frame-Rate Control Weight
++ Registers */
++
++
++
++
++/* power management miscellaneous control */
++union pm00r {
++ struct {
++ u32 pll1_n_b5 :1; /* PLL 1 N parameter bit 5 is 0 */
++ u32 reserved_1 :1;
++ u32 pll2_enbl :1; /* PLL 2 enable */
++ u32 pll3_enbl :1; /* PLL 3 enable */
++ u32 reserved_2 :1;
++ u32 pwr_st_ctrl :1; /* power state status control */
++ u32 reserved_3 :2;
++
++ u32 ge_enbl :1; /* graphics engine enable */
++ u32 ge_bsy_gl :1; /* graphics engine force busy (global) */
++ u32 ge_bsy_lcl :1; /* graphics engine force busy (local) */
++ u32 ge_clock :2; /* graphics engine clock select */
++ u32 ge_cmd_fifo :1; /* graphics engine command FIFO reset */
++ u32 ge_src_fifo :1; /* graphics engine CPU source FIFO reset */
++ u32 miu_pwr_seq :1; /* memory interface unit power sequencing
++ enable */
++
++ u32 d3_mem_rfsh :1; /* D3 memory refresh */
++ u32 d4_mem_rfsh :1; /* D4 memory refresh */
++ u32 gpwr_intrvl :2; /* general power sequencing interval */
++ u32 fppwr_intrvl:2; /* flat panel power sequencing interval */
++ u32 gpwr_seq_ctr:1; /* general power sequencing interval control */
++ u32 pmu_tm :1; /* PMU test mode */
++
++ u32 pwr_state :2; /* power state (read only) */
++ u32 pwr_seq_st :1; /* power sequencing active status (read
++ only) */
++ u32 reserved_4 :5;
++ } part;
++ u32 whole;
++};
++
++/* D1 state control */
++union pm01r {
++ struct {
++ u32 osc_enbl :1; /* D1 oscillator enable */
++ u32 pll1_enbl :1; /* D1 PLL 1 enable */
++ u32 pll2_enbl :1; /* D1 PLL 2 enable */
++ u32 pll3_enbl :1; /* D1 PLL 3 enable */
++ u32 miu_enbl :1; /* D1 Memory Interface Unit (MIU) enable */
++ u32 mem_rfsh :1; /* D1 memory refresh enable */
++ u32 ge_enbl :1; /* D1 Graphics Engine (GE) enable */
++ u32 reserved_1 :1;
++
++ u32 crt_enbl :1; /* D1 CRT enable */
++ u32 fpd_enbl :1; /* D1 Flat Panel enable */
++ u32 reserved_2 :6;
++
++ u32 ctl1_enbl :1; /* D1 controller 1 enable */
++ u32 win1_enbl :1; /* D1 window 1 enable */
++ u32 awin1_enbl :1; /* D1 alternate window 1 enable */
++ u32 cur1_enbl :1; /* D1 cursor 1 enable */
++ u32 reserved_3 :4;
++
++ u32 ctl2_enbl :1; /* D1 controller 2 enable */
++ u32 win2_enbl :1; /* D1 window 2 enable */
++ u32 awin2_enbl :1; /* D1 alternate window 2 enable */
++ u32 cur2_enbl :1; /* D1 cursor 2 enable */
++ u32 reserved_4 :4;
++ } part;
++ u32 whole;
++};
++
++/* D2 state control */
++union pm02r {
++ struct {
++ u32 osc_enbl :1; /* D2 oscillator enable */
++ u32 pll1_enbl :1; /* D2 PLL 1 enable */
++ u32 pll2_enbl :1; /* D2 PLL 2 enable */
++ u32 pll3_enbl :1; /* D2 PLL 3 enable */
++ u32 miu_enbl :1; /* D2 Memory Interface Unit (MIU) enable */
++ u32 mem_rfsh :1; /* D2 memory refresh enable */
++ u32 ge_enbl :1; /* D2 Graphics Engine (GE) enable */
++ u32 reserved_1 :1;
++
++ u32 crt_enbl :1; /* D2 CRT enable */
++ u32 fpd_enbl :1; /* D2 Flat Panel enable */
++ u32 reserved_2 :6;
++
++ u32 ctl1_enbl :1; /* D2 controller 1 enable */
++ u32 win1_enbl :1; /* D2 window 1 enable */
++ u32 awin1_enbl :1; /* D2 alternate window 1 enable */
++ u32 cur1_enbl :1; /* D2 cursor 1 enable */
++ u32 reserved_3 :4;
++
++ u32 ctl2_enbl :1; /* D2 controller 2 enable */
++ u32 win2_enbl :1; /* D2 window 2 enable */
++ u32 awin2_enbl :1; /* D2 alternate window 2 enable */
++ u32 cur2_enbl :1; /* D2 cursor 2 enable */
++ u32 reserved_4 :4;
++ } part;
++ u32 whole;
++};
++
++/* PLL 2 programming */
++union pm06r {
++ struct {
++ u32 clk_src :1; /* PLL 2 reference clock source */
++ u32 bypass :1; /* PLL 2 bypass */
++ u32 reserved_1 :2;
++ u32 p_par :3; /* PLL 2 P parameter */
++ u32 reserved_2 :1;
++
++ u32 n_par :5; /* PLL 2 N parameter */
++ u32 reserved_3 :3;
++
++ u32 m_par :8; /* PLL 2 M parameter */
++
++ u32 reserved_4 :4;
++ u32 trim :4; /* PLL 2 trim value */
++ } part;
++ u32 whole;
++};
++
++/* PLL 3 programming */
++union pm07r {
++ struct {
++ u32 clk_src :1; /* PLL 3 reference clock source */
++ u32 bypass :1; /* PLL 3 bypass */
++ u32 reserved_1 :2;
++ u32 p_par :3; /* PLL 3 P parameter */
++ u32 reserved_2 :1;
++
++ u32 n_par :5; /* PLL 3 N parameter */
++ u32 reserved_3 :3;
++
++ u32 m_par :8; /* PLL 3 M parameter */
++
++ u32 reserved_4 :4;
++ u32 trim :4; /* PLL 3 trim value */
++ } part;
++ u32 whole;
++};
++
++
++
++/* MIU interface control 1 */
++union mm00r {
++ struct {
++ u32 miu_enbl :1; /* MIU enable bit */
++ u32 mr_dsbl :1; /* MIU reset disable bit */
++ u32 edr_dsbl :1; /* embedded DRAM reset disable bit */
++ u32 reserved_1 :29;
++ } part;
++ u32 whole;
++};
++
++/* MIU interface control 2 */
++union mm01r {
++ struct {
++ u32 mc_src :1; /* memory clock source */
++ u32 msr_enbl :1; /* memory slow refresh enable bit */
++ u32 pb_cpu :1; /* page break enable for CPU */
++ u32 pb_gc1 :1; /* page break enable for GC1 */
++ u32 pb_gc2 :1; /* page break enable for GC2 */
++ u32 pb_stn_r :1; /* page break enable for STN read */
++ u32 pb_stn_w :1; /* page break enable for STN write */
++ u32 pb_ge :1; /* page break enable for GE */
++ u32 reserved_1 :4;
++ u32 mr_interval :14; /* normal memory refresh time interval */
++ u32 reserved_2 :4;
++ u32 edarm_enbl :1; /* embedded DRAM auto-refresh mode enable */
++ u32 eds_enbl :1; /* EDRAM standby enable for EDRAM normal
++ mode operation */
++ } part;
++ u32 whole;
++};
++
++/* memory interface control 3 */
++union mm02r {
++ struct {
++ u32 bs_ :2;
++ u32 bs_stnr :2; /* burst count for STN read memory cycles */
++ u32 bs_stnw :2; /* burst count for STN write memroy cycles */
++ u32 bs_ge :2; /* burst count for graphics engine
++ read/write memroy cycles */
++ u32 bs_cpuw :2; /* burst count for CPU write memory cycles */
++ u32 fifo_gc1 :4; /* GC1 display refresh FIFO threshold */
++ u32 fifo_gc2 :4; /* GC2 display refresh FIFO threshold */
++ u32 fifo_stnr :4; /* STN read FIFO threshold */
++ u32 fifo_stnw :4; /* STN write FIFO threshold */
++ u32 fifo_ge_src :3; /* GE source read FIFO threshold */
++ u32 fifo_ge_dst :3; /* GE destination read FIFO threshold */
++ } part;
++ u32 whole;
++};
++
++/* memory interface control 4 */
++union mm03r {
++ struct {
++ u32 rd_late_req :1; /* read latency request */
++ u32 reserved_1 :31;
++ } part;
++ u32 whole;
++};
++
++/* memory interface control 5 */
++union mm04r {
++ struct {
++ u32 latency :3; /* EDRAM latency */
++ u32 dmm_cyc :1; /* enable for the dummy cycle insertion
++ between read and write cycles */
++ u32 pre_dmm_cyc :1; /* enable for the dummy cycle insertion
++ between read/write and precharge cycles
++ for the same bank */
++ u32 reserved_1 :3;
++ u32 bnk_act_cls :2; /* bank activate command to bank close
++ command timing interval control */
++ u32 bnk_act_rw :1; /* bank activate command to read/wirte
++ command timing interval control */
++ u32 bnk_cls_act :1; /* bank close command to bank activate
++ command timing interval control */
++ u32 trc :1; /* row cycle time */
++ u32 reserved_2 :3;
++ u32 delay_r :2; /* programmable delay for read clock */
++ u32 delay_m :2; /* programmable delay for internal memory
++ clock */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller 1 register */
++union gc00r {
++ struct {
++ u32 ctl_enbl :1; /* Controller 1 Enable */
++ u32 hc_reset :1; /* Horizontal Counter 1 Reset */
++ u32 vc_reset :1; /* Vertical Counter 1 Reset */
++ u32 iwin_enbl :1; /* Image Window 1 Enable */
++ u32 gcd :4; /* Graphics Color Depth (GCD) */
++
++ u32 hc_enbl :1; /* Hardware Cursor 1 Enable */
++ u32 reserved_1 :2;
++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */
++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */
++
++ u32 g1rclk_src :2; /* G1RCLK Source */
++ u32 tm0 :1; /* Test Mode 0 */
++ u32 tm1 :1; /* Test Mode 1 */
++ u32 fd :3; /* G1MCLK First Clock Divisor (FD1) */
++ u32 reserved_2 :1;
++
++ u32 sd :8; /* G1MCLK Second Clock Divisor (SD1) */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller CRT control */
++union gc01r {
++ struct {
++ u32 dac_enbl :2; /* CRT DAC enable */
++ u32 hsync_out :1; /* CRT HSYNC output during power down mode */
++ u32 vsync_out :1; /* CRT VSYNC output during power down mode */
++ u32 hsync_ctl :2; /* CRT HSYNC control */
++ u32 vsync_ctl :2; /* CRT VSYNC control */
++ /**/
++ u32 hsync_pol :1; /* CRT HSYNC polarity */
++ u32 vsync_pol :1; /* CRT VSYNC polarity */
++ u32 sync_p_enbl :1; /* sync pedestal enable */
++ u32 blnk_p_enbl :1; /* blank pedestal enable */
++ u32 c_sync_enbl :1; /* composite sync enable */
++ u32 vref_sel :1; /* VREF select */
++ u32 mn_sns_enbl :1; /* monitor sense enable */
++ u32 ct_out_enbl :1; /* constant output enable */
++ /**/
++ u32 dac_out_lvl :8; /* monitor sense DAC output level */
++ /**/
++ u32 blue_dac_r :1; /* blue DAC sense result */
++ u32 green_dac_r :1; /* green DAC sense result */
++ u32 red_dac_r :1; /* red DAC sense result */
++ u32 reserved_1 :1;
++ u32 mon_col_sel :1; /* mono/color monitor select */
++ u32 reserved_2 :3;
++ } part;
++ u32 whole;
++};
++
++/* horizontal display 1 control */
++union gc02r {
++ struct {
++ u32 hd1t :12; /* horizontal display 1 total */
++ u32 reserved_1 :4;
++
++ u32 hd1e :12; /* horizontal display 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 1 control */
++union gc03r {
++ struct {
++ u32 vd1t :12; /* vertical display 1 total */
++ u32 reserved_1 :4;
++
++ u32 vd1e :12; /* vertical display 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* horizontal sync 1 control */
++union gc04r {
++ struct {
++ u32 hs1s :12; /* horizontal sync 1 start */
++ u32 reserved_1 :4;
++
++ u32 hs1e :12; /* horizontal sync 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical sync 1 control */
++union gc05r {
++ struct {
++ u32 vs1s :12; /* vertical sync 1 start */
++ u32 reserved_1 :4;
++
++ u32 vs1e :12; /* vertical sync 1 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 1 count */
++union gc07r {
++ struct {
++ u32 vd_cnt :12; /* vertical display 1 count */
++ u32 reverved_1 :20;
++ } part;
++ u32 whole;
++};
++
++/* horizontal window 1 control */
++union gc08r {
++ struct {
++ u32 hw1s :12; /* horizontal window 1 start (HW1S) */
++ u32 reserved_1 :4;
++
++ u32 hw1w :12; /* horizontal window 1 width (HW1W) */
++ u32 w1ald :4; /* window 1 additional line data */
++ } part;
++ u32 whole;
++};
++
++/* vertical window 1 control */
++union gc09r {
++ struct {
++ u32 vw1s :12; /* vertical window 1 start */
++ u32 reserved_1 :4;
++ u32 vw1h :12; /* vertical window 1 height */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* window 1 start address */
++union gc0cr {
++ struct {
++ u32 w1sa :21; /* window 1 start address */
++ u32 reserved_1 :11;
++ } part;
++ u32 whole;
++};
++
++/* window 1 stride */
++union gc0er {
++ struct {
++ s16 w1st; /* window 1 stride */
++ s16 aw1st; /* alternate window 1 stride */
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 position */
++union gc10r {
++ struct {
++ u32 hc1s :12; /* horizontal cursor 1 start */
++ u32 reserved_1 :4;
++ u32 vc1s :12; /* vertical cursor 1 start */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 start address and offset */
++union gc11r {
++ struct {
++ u32 hc1sa :11; /* hardware cursor 1 start address */
++ u32 reserved_1 :5;
++ u32 hc1o :6; /* horizontal cursor 1 offset */
++ u32 reserved_2 :2;
++ u32 vc1o :6; /* vertical cursor 1 offset */
++ u32 reserved_3 :2;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 foreground color */
++union gc12r {
++ struct {
++ u32 hc1fc :24; /* hardware cursor 1 foreground color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 1 background color */
++union gc13r {
++ struct {
++ u32 hc1bc :24; /* hardware cursor 1 background color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++
++/* graphics controller 2 register */
++union gc20r {
++ struct {
++ u32 ctl_enbl :1; /* Controller 2 Enable */
++ u32 hc_reset :1; /* Horizontal Counter 2 Reset */
++ u32 vc_reset :1; /* Vertical Counter 2 Reset */
++ u32 iwin_enbl :1; /* Image Window 2 Enable */
++ u32 gcd :4; /* Graphics Color Depth (GCD) */
++
++ u32 hc_enbl :1; /* Hardware Cursor 2 Enable */
++ u32 reserved_1 :2;
++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */
++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */
++
++ u32 g2rclk_src :2; /* G2RCLK Source */
++ u32 tm0 :1; /* Test Mode 0 */
++ u32 tm1 :1; /* Test Mode 1 */
++ u32 fd :3; /* G2MCLK First Clock Divisor (FD1) */
++ u32 reserved_2 :1;
++
++ u32 sd :8; /* G2MCLK Second Clock Divisor (SD1) */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller CRC control */
++union gc21r {
++ struct {
++ u32 crc_enbl :1; /* CRC enable */
++ u32 vsync_wait :1; /* CRC input data control waitime of VSYNC */
++ u32 crc_o_sel :2; /* CRC output select */
++ u32 reserved_1 :4;
++ u32 crc_result :22; /* CRC result (read only) */
++ u32 reserved_2 :2;
++ } part;
++ u32 whole;
++};
++
++/* horizontal display 2 control */
++union gc22r {
++ struct {
++ u32 hd2t :12; /* horizontal display 2 total */
++ u32 reserved_1 :4;
++
++ u32 hd2e :12; /* horizontal display 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 2 control */
++union gc23r {
++ struct {
++ u32 vd2t :12; /* vertical display 2 total */
++ u32 reserved_1 :4;
++
++ u32 vd2e :12; /* vertical display 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* horizontal sync 2 control */
++union gc24r {
++ struct {
++ u32 hs2s :12; /* horizontal sync 2 start */
++ u32 reserved_1 :4;
++
++ u32 hs2e :12; /* horizontal sync 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical sync 2 control */
++union gc25r {
++ struct {
++ u32 vs2s :12; /* vertical sync 2 start */
++ u32 reserved_1 :4;
++
++ u32 vs2e :12; /* vertical sync 2 end */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* vertical display 2 count */
++union gc27r {
++ struct {
++ u32 vd_cnt :12; /* vertical display 2 count */
++ u32 reverved_1 :20;
++ } part;
++ u32 whole;
++};
++
++/* horizontal window 2 control */
++union gc28r {
++ struct {
++ u32 hw2s :12; /* horizontal window 2 start (HW2S) */
++ u32 reserved_1 :4;
++
++ u32 hw2w :12; /* horizontal window 2 width (HW2W) */
++ u32 w2ald :4; /* window 2 additional line data */
++ } part;
++ u32 whole;
++};
++
++/* vertical window 2 control */
++union gc29r {
++ struct {
++ u32 vw2s :12; /* vertical window 2 start */
++ u32 reserved_1 :4;
++ u32 vw2h :12; /* vertical window 2 height */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* window 2 start address */
++union gc2cr {
++ struct {
++ u32 w2sa :21; /* window 2 start address */
++ u32 reserved_1 :11;
++ } part;
++ u32 whole;
++};
++
++/* window 2 stride */
++union gc2er {
++ struct {
++ s16 w2st; /* window 2 stride */
++ s16 aw2st; /* alternate window 2 stride */
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 position */
++union gc30r {
++ struct {
++ u32 hc2s :12; /* horizontal cursor 2 start */
++ u32 reserved_1 :4;
++ u32 vc2s :12; /* vertical cursor 2 start */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 start address and offset */
++union gc31r {
++ struct {
++ u32 hc2sa :11; /* hardware cursor 2 start address */
++ u32 reserved_1 :5;
++ u32 hc2o :6; /* horizontal cursor 2 offset */
++ u32 reserved_2 :2;
++ u32 vc2o :6; /* vertical cursor 2 offset */
++ u32 reserved_3 :2;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 foreground color */
++union gc32r {
++ struct {
++ u32 hc2fc :24; /* hardware cursor 2 foreground color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++/* hardware cursor 2 background color */
++union gc33r {
++ struct {
++ u32 hc2bc :24; /* hardware cursor 2 background color */
++ u32 reserved_1 :8;
++ } part;
++ u32 whole;
++};
++
++
++/* primary drawing command register */
++union ge00r {
++ struct {
++ u32 rop :8; /* raster operation */
++ /**/
++ u32 cmd_typ :3; /* command type */
++ u32 x_dir :1; /* x direction */
++ u32 y_dir :1; /* y direction */
++ u32 src_mem :1; /* source memory */
++ u32 mon_src :1; /* mono source */
++ u32 mon_ptn :1; /* mono pattern */
++ /**/
++ u32 dst_trns_e :1; /* destination transparency enable */
++ u32 dst_trns_p :1; /* destination transparency polarity */
++ u32 mon_trns_e :1; /* mono source or mono pattern transparency
++ enable */
++ u32 mon_trns_p :1; /* mono transparency polarity */
++ u32 mod_sel :1; /* memory to screen or off screen to screen
++ mode select */
++ u32 alpha_sel :2; /* Alpha byte mask selection */
++ u32 sol_col :1; /* solid color */
++ /**/
++ u32 stride_eq :1; /* source stride is equal to destination
++ stride */
++ u32 rop2_sel :1; /* ROP2 code selection */
++ u32 clipping :1; /* enable clipping */
++ u32 auto_exec :1; /* auto execute */
++ u32 reserved_1 :4;
++ } part;
++ u32 whole;
++};
++
++/* primary width and height register */
++union ge01r {
++ struct {
++ u32 width :12; /* source/destination window width */
++ u32 reserved_1 :4;
++
++ u32 height :12; /* source/destination window height */
++ u32 reserved_2 :1;
++ u32 reserved_3 :3;
++ } bitblt;
++ struct {
++ u32 dm :17;
++ u32 axis_major :12;
++ u32 x_y :1; /* x-major or y-major */
++ u32 last_pix :1; /* decision to draw or not to draw the last
++ pixel of the line */
++ u32 reserved_1 :1;
++ } bresenham;
++ u32 whole;
++};
++
++/* primary destination address register */
++union ge02r {
++ struct {
++ u32 dst_x :12; /* destination x position */
++ u32 reserved_1 :1;
++ u32 h_offset :3; /* mono/color pattern horizontal offset */
++
++ u32 dst_y :12; /* destination y position */
++ u32 reserved_2 :1;
++ u32 v_offset :3; /* mono/color pattern vertical offset */
++ } window;
++ struct {
++ u32 x :12; /* starting x coordinate */
++ u32 dm :17; /* 17 bits major-axis delta */
++ u32 reserved_1 :3;
++ } line;
++ u32 whole;
++};
++
++/* source XY register/line draw starting Y coordinate and mintor axis delta */
++union ge03r {
++ struct {
++ u32 src_x :12; /* source X position */
++ u32 reserved_1 :4;
++
++ u32 src_y :12; /* source Y position */
++ u32 reserved_2 :4;
++ } window;
++ struct {
++ u32 start_y :12; /* starting Y coordinate */
++ u32 dn :17; /* 17 bits minor-axis delta */
++ u32 reserved_1 :3;
++ } line;
++ u32 whole;
++};
++
++/* clip left/top register */
++union ge05r {
++ struct {
++ u32 left :12; /* left edge of clipping rectangle */
++ u32 reserved_1 :4;
++
++ u32 top :12; /* top edge of clipping rectangle */
++ u32 reserved_2 :4;
++ } part;
++ u32 whole;
++};
++
++/* source stride/offset register */
++union ge09r {
++ struct {
++ u32 src_strid :12; /* source line stride */
++ u32 reserved_1 :13;
++ u32 strt_bit :3; /* initial mono source bit offset */
++ u32 strt_byte :3; /* initial mono/color source byte offset */
++ u32 reserved_2 :1;
++ } line;
++ struct {
++ u32 strt_bit :5; /* initial mono source bit offset */
++ u32 reserved_1 :1;
++ u32 amount :10; /* number of 16 bytes amount that MIU need
++ to fetch from frame buffer */
++
++ u32 reserved_2 :9;
++ u32 bit_spc :7; /* bit space between lines */
++ } pack_mono;
++ struct {
++ u32 strt_bit :3; /* initial mono source bit offset */
++ u32 strt_byte :3; /* initial mono/color source byte offset */
++ u32 amount :10; /* number of 16 bytes amount that MIU need
++ to fetch from frame buffer */
++
++ u32 reserved_1 :9;
++ u32 bit_spc :3; /* bit space between lines */
++ u32 byt_spc :4; /* byte space between lines */
++ } pack_color;
++ u32 whole;
++};
++
++/* destination stride register and color depth */
++union ge0ar {
++ struct {
++ u32 dst_strid :12; /* destination line stride and color depth */
++ u32 reserved_1 :18;
++ u32 col_dpth :2; /* color depth */
++ } part;
++ u32 whole;
++};
++
++/* graphics controller color pallete */
++union c1xxr {
++ struct {
++ u8 red; /* red color pallete */
++ u8 green; /* green/gray color pallete */
++ u8 blue; /* blue color palette */
++ u8 reserved_1;
++ } part;
++ u32 whole;
++};
++
++/* devicee configuration register 0 */
++union dc00r {
++ struct {
++ u32 osc_bypass :1; /* oscillator bypass */
++ u32 osc_enbl :1; /* oscillator enable */
++ u32 pll1_bypass :1; /* PLL1 bypass */
++ u32 pll1_enbl :1; /* PLL1 enable */
++ u32 pll1_p_par :3; /* PLL1 P parameter */
++ u32 cpu_div :1; /* CPU interface clock divisor */
++ u32 pll1_n_par :5; /* PLL1 N parameter */
++ u32 saisc :1; /* StrongARM interface synchronizer control */
++ u32 s_chp_reset :1; /* software chip reset */
++ u32 mem_enbl :1; /* memory standby enable */
++ u32 pll1_m_par :8; /* PLL 1 M parameter */
++ u32 osc_shaper :1; /* oscillator shaper disable */
++ u32 fast_pwr :1; /* fast power sequencing */
++ u32 osc_frq :2; /* oscillator frequency select */
++ u32 pll1_trim :4; /* PLL 1 trim value */
++ } part;
++ u32 whole;
++};
++
++/* device ID/vendor ID register */
++union pc00r {
++ struct {
++ u16 device; /* device ID */
++ u16 vendor; /* vendor ID */
++ } part;
++ u32 whole;
++};
++
++/* Flat Panel Control Register */
++union fp00r {
++ struct {
++ u32 flatp_enbl : 2; /* Flat Panel Interface Enable */
++ u32 flatp_type : 2; /* Flat Panel Type */
++ u32 mono : 1; /* Mono/Color Panel Select */
++ u32 flatp_intf : 3; /* Flat Panel Interface */
++ u32 dither_pat : 2; /* Dither Pattern */
++ u32 reserved : 2; /* Reserved Must Be 0*/
++ u32 dither_col : 3; /* Dither Base Color */
++ u32 alt_win_ctl: 1; /* Alternate Window Control */
++ u32 frc_ctl : 2; /* FRC Control */
++ u32 dither_adj1: 6; /* Dither Pattern Adjust 1 */
++ u32 dither_adj2: 3; /* Dither Pattern Adjust 2 */
++ u32 dither_adj3: 1; /* Dither Pattern Adjust 3 */
++ u32 test_mode0 : 1; /* Test Mode 0 */
++ u32 test_mode1 : 1; /* Test Mode 1 */
++ u32 test_mode2 : 1; /* Test Mode 2 */
++ u32 test_mode3 : 1; /* Test Mode 3 */
++ } part;
++ u32 whole;
++};
++
++union fp01r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp02r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp03r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp04r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp05r {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++union fp0fr {
++ struct {
++ u32 dummy;
++ } part;
++ u32 whole;
++};
++
++
++
++
++/****
++ * Others
++ */
++
++#define CHIPNAME "MQ-200"
++
++extern void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr);
++extern void mq200_external_setqmode(struct mq200_monitor_info*, unsigned long, spinlock_t *);
++extern void mq200_external_offdisplay(unsigned long);
++extern void mq200_external_ondisplay (unsigned long);
++extern int mq200_external_probe(unsigned long);
++
++
++
++#endif
+Index: linux-2.6.27/drivers/video/mq200/mq_external.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/drivers/video/mq200/mq_external.c 2008-12-07 01:35:01.269407967 +0100
+@@ -0,0 +1,513 @@
++/*
++ * Copyright (C) 2005 Holger Hans Peter Freyther
++ *
++ * Based ON:
++ *
++ * linux/drivers/video/mq200fb.c -- MQ-200 for a frame buffer device
++ * based on linux/driver/video/pm2fb.c
++ *
++ * 2007/03/11 mrdata:
++ * bug found in gc1_reset(), renaming to gc1_gc2_reset()
++ * extend mq200_external_ondisplay() -> LCD for GC2 and CRT for GC1
++ *
++ * Copyright (C) 2000 Lineo, Japan
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive
++ * for more details.
++ */
++
++#include <asm/types.h>
++#include <asm/io.h>
++#include <linux/delay.h>
++#include <linux/spinlock.h>
++
++#include <mach/hardware.h>
++
++#include "mq200_data.h"
++
++
++#if 1
++#define PRINTK(args...) printk(args)
++#else
++#define PRINTK(args...)
++#endif
++
++
++/****
++ * power state transition to "state".
++ */
++static void
++power_state_transition(unsigned long register_base, int state)
++{
++ int i;
++ writel(state, PMCSR(register_base));
++ mdelay(300);
++ for (i = 1; ; i++) {
++ udelay(100);
++ if ((readl(PMCSR(register_base)) & 0x3) == state) {
++ break;
++ }
++ }
++}
++
++
++/****
++ * device configuration initialization.
++ */
++static void
++dc_reset(unsigned long register_base)
++{
++ union dc00r dc00r;
++
++ /* Reset First */
++ dc00r.whole = DC_RESET;
++ writel(dc00r.whole, DC00R(register_base));
++ mdelay(100);
++
++ dc00r.whole = 0xEF2082A;
++ writel(dc00r.whole, DC00R(register_base));
++ mdelay(300);
++ PRINTK(CHIPNAME ": DC00R = 0x%08X\n", readl(DC00R(register_base)));
++}
++
++
++/****
++ * initialize memory interface unit.
++ */
++static void
++miu_reset(unsigned long register_base)
++{
++ union mm00r mm00r;
++ union mm01r mm01r;
++ union mm02r mm02r;
++ union mm03r mm03r;
++ union mm04r mm04r;
++
++ /* MIU interface control 1 */
++ mm00r.whole = 0x4;
++ writel(mm00r.whole, MM00R(register_base));
++ mdelay(50);
++ writel(0, MM00R(register_base));
++ mdelay(50);
++
++ /* MIU interface control 2
++ * o PLL 1 output is used as memory clock source.
++ */
++ mm01r.whole = 0x4143e086;
++ writel(mm01r.whole, MM01R(register_base));
++
++ /* memory interface control 3 */
++ mm02r.whole = 0x6d6aabff;
++ writel(mm02r.whole, MM02R(register_base));
++
++ /* memory interface control 5 */
++ mm04r.whole = 0x10d;
++ writel(mm04r.whole, MM04R(register_base));
++
++ /* memory interface control 4 */
++ mm03r.whole = 0x1;
++ writel(mm03r.whole, MM03R(register_base));
++ mdelay(50);
++
++ /* MIU interface control 1 */
++ mm00r.whole = 0x3;
++ writel(mm00r.whole, MM00R(register_base));
++ mdelay(50);
++}
++
++/****
++ *
++ */
++static
++void fpctrl_reset(unsigned long addr)
++{
++ /*
++ * We're in D0 State, let us set the FPCTRL
++ */
++ union fp00r fp00r;
++ union fp01r fp01r;
++ union fp02r fp02r;
++ union fp03r fp03r;
++ union fp04r fp04r;
++ union fp0fr fp0fr;
++
++ fp00r.whole = 0x6320;
++ writel(fp00r.whole, FP00R(addr));
++
++ fp01r.whole = 0x20;
++ writel(fp01r.whole, FP01R(addr));
++
++ fp04r.whole = 0xBD0001;
++ writel(fp04r.whole, FP04R(addr));
++
++ /* Set Flat Panel General Purpose register first */
++ fp02r.whole = 0x0;
++ writel(fp02r.whole, FP02R(addr));
++
++ fp03r.whole = 0x0;
++ writel(fp03r.whole, FP03R(addr));
++
++ fp0fr.whole = 0xA16c44;
++ writel(fp0fr.whole, FP0FR(addr));
++
++ /* Set them again */
++ fp02r.whole = 0x0;
++ writel(fp02r.whole, FP02R(addr));
++
++ fp03r.whole = 0x0;
++ writel(fp03r.whole, FP03R(addr));
++}
++
++
++/****
++ * initialize power management unit.
++ */
++static void
++pmu_reset(unsigned long register_base)
++{
++ union pm00r pm00r;
++ union pm01r pm01r;
++ union pm02r pm02r;
++
++ /* power management miscellaneous control
++ * o GE is driven by PLL 1 clock.
++ */
++ pm00r.whole = 0xc0900;
++ writel(pm00r.whole, PM00R(register_base));
++
++ /* D1 state control */
++ pm01r.whole = 0x5000271;
++ writel(pm01r.whole, PM01R(register_base));
++
++ /* D2 state control */
++ pm02r.whole = 0x271;
++ writel(pm02r.whole, PM02R(register_base));
++}
++
++/****
++ * initialize graphics controller 1
++ * and graphics controller 2
++ */
++static void
++gc1_gc2_reset(unsigned long register_base, spinlock_t *lock )
++{
++ unsigned long flags;
++ union gc00r gc00r;
++ union gc01r gc01r;
++ union gc02r gc02r;
++ union gc03r gc03r;
++ union gc04r gc04r;
++ union gc05r gc05r;
++ union gc08r gc08r;
++ union gc09r gc09r;
++ union gc0cr gc0cr;
++ union gc0er gc0er;
++ union gc20r gc20r;
++ union gc22r gc22r;
++ union gc23r gc23r;
++ union gc24r gc24r;
++ union gc25r gc25r;
++ union gc28r gc28r;
++ union gc29r gc29r;
++ union gc2cr gc2cr;
++ union gc2er gc2er;
++
++ union pm00r pm00r;
++ union pm06r pm06r;
++ union pm06r pm07r;
++
++ spin_lock_irqsave(lock, flags);
++
++ /* alternate window 1 stride */
++ gc0er.whole = 0x640;
++ writel(gc0er.whole, GC0ER(register_base));
++
++ /* image window 1 start address */
++ gc0cr.whole = 0x0;
++ writel(gc0cr.whole, GC0CR(register_base));
++
++ /* alternate window 2 stride */
++ gc2er.whole = 0x640;
++ writel(gc0er.whole, GC2ER(register_base));
++
++ /* image window 2 start address */
++ gc2cr.whole = 0x0;
++ writel(gc2cr.whole, GC2CR(register_base));
++
++ /* read PM Register */
++ pm00r.whole = readl(PM00R(register_base));
++
++ /* horizontal window 1 control */
++ gc08r.whole = 0x131f0000;
++ writel(gc08r.whole, GC08R(register_base));
++
++ /* vertical window 1 control */
++ gc09r.whole = 0x12570000;
++ writel(gc09r.whole, GC09R(register_base));
++
++ /* horizontal display 1 control */
++ gc02r.whole = 0x320041e;
++ writel(gc02r.whole, GC02R(register_base));
++
++ /* vertical display 1 control */
++ gc03r.whole = 0x2570273;
++ writel(gc03r.whole, GC03R(register_base));
++
++ /* horizontal sync 1 control */
++ gc04r.whole = 0x3c70347;
++ writel(gc04r.whole, GC04R(register_base));
++
++ /* vertical sync 1 control */
++ gc05r.whole = 0x25d0259;
++ writel(gc05r.whole, GC05R(register_base));
++
++ /* graphics controller CRT control */
++ gc01r.whole = 0x800;
++ writel(gc01r.whole, GC01R(register_base));
++
++ /* PLL 2 programming */
++ pm06r.whole = 0xE90830;
++ writel(pm06r.whole, PM06R(register_base));
++
++ /* graphics controller 1 register
++ * o GC1 clock source is PLL 2.
++ * o hardware cursor is disabled.
++ */
++ gc00r.whole = 0x10000C8 | 0x20000;
++ writel(gc00r.whole, GC00R(register_base));
++
++#if 0
++ /* alternate horizontal window 1 control */
++ writel(0, GC0AR(register_base));
++
++ /* alternate vertical window 1 control */
++ writel(0, GC0BR(register_base));
++
++ /* window 1 start address */
++ writel(0x2004100, GC0CR(register_base));
++
++ /* alternate window 1 start address */
++ writel(0, GC0DR(register_base));
++
++ /* window 1 stride */
++ gc0er.whole = 0x5100048;
++ writel(gc0er.whole, GC0ER(register_base));
++
++ /* reserved register - ??? - */
++ writel(0x31f, GC0FR(register_base));
++#endif
++
++#if 0
++ /* hardware cursor 1 position */
++ writel(0, GC10R(register_base));
++
++ /* hardware cursor 1 start address and offset */
++ gc11r.whole = 0x5100048;
++ writel(gc11r.whole, GC11R(register_base));
++
++ /* hardware cursor 1 foreground color */
++ writel(0x00ffffff, GC12R(register_base));
++
++ /* hardware cursor 1 background color */
++ writel(0x00000000, GC13R(register_base));
++#endif
++
++ /* horizontal window 2 control */
++ gc28r.whole = 0x31f0000;
++ writel(gc28r.whole, GC28R(register_base));
++
++ /* vertical window 2 control */
++ gc29r.whole = 0x2570000;
++ writel(gc29r.whole, GC29R(register_base));
++
++ /* horizontal display 2 control */
++ gc22r.whole = 0x320041e;
++ writel(gc22r.whole, GC22R(register_base));
++
++ /* vertical display 2 control */
++ gc23r.whole = 0x2570273;
++ writel(gc23r.whole, GC23R(register_base));
++
++ /* horizontal sync 2 control */
++ gc24r.whole = 0x3c70347;
++ writel(gc24r.whole, GC24R(register_base));
++
++ /* vertical sync 2 control */
++ gc25r.whole = 0x25d0259;
++ writel(gc25r.whole, GC25R(register_base));
++
++ /* graphics controller CRT control */
++ gc01r.whole = 0x800;
++ writel(gc01r.whole, GC01R(register_base));
++
++ /* PLL 3 programming */
++ pm07r.whole = 0xE90830;
++ writel(pm07r.whole, PM07R(register_base));
++
++ /* graphics controller 2 register
++ * o GC2 clock source is PLL 3.
++ * o hardware cursor is disabled.
++ */
++ gc20r.whole = 0x10000C8 | 0x30000;
++ writel(gc20r.whole, GC20R(register_base));
++
++ /*
++ * Enable PLL2 and PLL3 in the PM Register
++ */
++ pm00r.part.pll2_enbl = 0x1;
++ pm00r.part.pll3_enbl = 0x1;
++ writel(pm00r.whole, PM00R(register_base));
++
++ spin_unlock_irqrestore(lock, flags);
++}
++
++
++/****
++ * initialize graphics engine.
++ */
++static void
++ge_reset(unsigned long register_base)
++{
++ /* drawing command register */
++ writel(0, GE00R(register_base));
++
++ /* promary width and height register */
++ writel(0, GE01R(register_base));
++
++ /* primary destination address register */
++ writel(0, GE02R(register_base));
++
++ /* primary source XY register */
++ writel(0, GE03R(register_base));
++
++ /* primary color compare register */
++ writel(0, GE04R(register_base));
++
++ /* primary clip left/top register */
++ writel(0, GE05R(register_base));
++
++ /* primary clip right/bottom register */
++ writel(0, GE06R(register_base));
++
++ /* primary source and pattern offset register */
++ writel(0, GE07R(register_base));
++
++ /* primary foreground color register/rectangle fill color depth */
++ writel(0, GE08R(register_base));
++
++ /* source stride/offset register */
++ writel(0, GE09R(register_base));
++
++ /* destination stride register and color depth */
++ writel(0, GE0AR(register_base));
++
++ /* image base address register */
++ writel(0, GE0BR(register_base));
++}
++
++/****
++ * initialize Color Palette 1.
++ */
++static void
++cp1_reset(unsigned long addr_info)
++{
++ int i;
++
++ for (i = 0; i < 256; i++)
++ writel(0, C1xxR(addr_info, i));
++}
++
++
++/*
++ * Below functions are called from the skeleton
++ */
++void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr)
++{
++ writel(color,C1xxR(addr,regno));
++}
++
++void mq200_external_setqmode(struct mq200_monitor_info* info,
++ unsigned long addr, spinlock_t *lock)
++{
++ dc_reset(addr); /* device configuration */
++
++ power_state_transition(addr, 0); /* transition to D0 state */
++
++ pmu_reset(addr); /* power management unit */
++
++ miu_reset(addr); /* memory interface unit */
++
++ ge_reset(addr); /* graphics engine */
++
++ fpctrl_reset(addr); /* reset the panel settings */
++
++ gc1_gc2_reset(addr, lock); /* graphics controller 1 and 2 */
++
++ cp1_reset(addr); /* color palette 1 */
++
++ mq200_external_ondisplay(addr); /* LCD and CRT */
++}
++
++void mq200_external_offdisplay(unsigned long addr)
++{
++ /*
++ * Move the MQ200 to D3 mode
++ */
++ power_state_transition(addr, 3);
++}
++
++/**
++ * to be called after mq200_external_setqmode
++ */
++void mq200_external_ondisplay (unsigned long addr)
++{
++ /*
++ * Set the framebuffer details
++ */
++ union gc00r gc00r;
++ union gc01r gc01r;
++ union gc20r gc20r;
++ union fp00r fp00r;
++
++ /* enable LCD for GC2 */
++ fp00r.whole = readl(FP00R(addr));
++ fp00r.whole &= 0xfffffffc;
++
++ gc20r.whole = readl(GC20R(addr));
++
++ if(!(gc20r.whole & 0x1)) {
++ gc20r.whole |= 0x1;
++ writel(gc20r.whole, GC20R(addr));
++ }
++
++ fp00r.whole |= 0x3;
++ writel(fp00r.whole, FP00R(addr));
++
++ /* enable CRT for GC1 */
++ gc00r.whole = readl(GC00R(addr));
++
++ if(!(gc00r.whole & 0x1)) {
++ gc00r.whole |= 0x1;
++ writel(gc00r.whole, GC00R(addr));
++ }
++
++ gc01r.whole = readl(GC01R(addr));
++ gc01r.whole &= 0xfffffffc;
++
++ gc01r.whole |= 0x1;
++ writel(gc01r.whole, GC01R(addr));
++
++}
++
++int mq200_external_probe(unsigned long addr)
++{
++ union pc00r pc00r;
++ if(readl(PMR(addr)) != PMR_VALUE)
++ return 0;
++
++ pc00r.whole = readl(PC00R(addr));
++ printk(KERN_INFO "mq200 video driver found Vendor: 0x%X Device: 0x%X\n",
++ pc00r.part.device, pc00r.part.vendor);
++ return 1;
++}
+Index: linux-2.6.27/drivers/video/mq200/mq_skeleton.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/drivers/video/mq200/mq_skeleton.c 2008-12-07 01:34:29.666075531 +0100
+@@ -0,0 +1,398 @@
++/*
++ * Author: Holger Hans Peter Freyther
++ *
++ *
++ * This implements the frame buffer driver interface to communicate
++ * with the kernel.
++ * It uses the mq200 routines from the ucLinux driver from Lineo
++ *
++ * 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/autoconf.h>
++#include <linux/platform_device.h>
++#include <linux/module.h>
++#include <linux/fb.h>
++#include <linux/types.h>
++#include <linux/spinlock.h>
++
++#include "mq200_data.h"
++
++#if CONFIG_SA1100_SIMPAD
++/*
++ * Siemens SIMpad specefic data
++ */
++#include <mach/simpad.h>
++#include <mach/hardware.h>
++
++#define MQ200_REGIONS simpad_mq200_regions
++#define MQ200_MONITOR simpad_mq200_panel
++
++static struct mq200_io_regions simpad_mq200_regions = {
++ .fb_size = MQ200_FB_SIZE,
++ .phys_mmio_base = 0x4be00000,
++ .virt_mmio_base = 0xf2e00000,
++ .phys_fb_base = 0x4b800000,
++ .virt_fb_base = 0xf2800000,
++};
++
++static struct mq200_monitor_info simpad_mq200_panel = {
++ .horizontal_res = 800,
++ .vertical_res = 600,
++ .depth = 16,
++ .refresh = 60,
++ .line_length = 1600,
++ .flags = 0x00130004,
++};
++
++extern long get_cs3_shadow(void);
++extern void set_cs3_bit(int value);
++extern void clear_cs3_bit(int value);
++#endif
++
++
++
++struct mq200_info {
++ struct fb_info fb_info;
++ struct mq200_io_regions io_regions;
++ struct mq200_monitor_info monitor_info;
++
++ /* palette */
++ u32 pseudo_palette[17]; /* 16 colors + 1 in reserve not that well documented... */
++ spinlock_t lock;
++};
++
++
++
++static int mq200_blank( int blank_mode, struct fb_info *info )
++{
++#ifdef CONFIG_SA1100_SIMPAD
++ if(blank_mode ){
++ clear_cs3_bit(DISPLAY_ON);
++ }else {
++ set_cs3_bit(DISPLAY_ON);
++ }
++#endif
++ return 0;
++}
++
++
++static int mq200_check_var(struct fb_var_screeninfo *var,
++ struct fb_info *info )
++{ /* TODO do we need sanity checks here */
++ return 0;
++}
++
++
++static int mq200_set_par( struct fb_info *info )
++{
++ /* TODO set paraemeter */
++ return 0;
++}
++
++static int mq200_setcolreg(unsigned regno, unsigned red, unsigned green,
++ unsigned blue, unsigned transp,
++ struct fb_info *info )
++{
++ struct mq200_info *p;
++ unsigned long color;
++ u32* pal = info->pseudo_palette;
++
++ p = info->par;
++
++ if(regno > 255 )
++ return 1;
++
++ switch( info->var.bits_per_pixel ){
++ case 16:
++ pal[regno] =
++ ((red & 0xf800) >> 0) |
++ ((green & 0xf800) >> 5) | ((blue & 0xf800) >> 11);
++ break;
++ case 24:
++ pal[regno] =
++ ((red & 0xff00) << 8) |
++ ((green & 0xff00)) | ((blue & 0xff00) >> 8);
++ break;
++ case 32:
++ pal[regno] =
++ ((red & 0xff00) >> 8) |
++ ((green & 0xff00)) | ((blue & 0xff00) << 8);
++ break;
++ default:
++ break;
++ }
++
++ red &= 0xFF;
++ green &= 0xFF;
++ blue &= 0xFF;
++
++ color = red | (green << 8) | (blue << 16);
++ mq200_external_setpal(regno, color, p->io_regions.virt_mmio_base);
++
++ return 0;
++}
++
++
++static struct fb_ops mq200_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = mq200_check_var,
++ .fb_set_par = mq200_set_par,
++ .fb_setcolreg = mq200_setcolreg,
++#ifdef FB_SOFT_CURSOR
++ .fb_cursor = soft_cursor, /* FIXME use hardware cursor */
++#endif
++ .fb_fillrect = cfb_fillrect,
++ .fb_copyarea = cfb_copyarea,
++ .fb_imageblit = cfb_imageblit,
++ .fb_blank = mq200_blank,
++};
++
++
++/*********************************************************************
++ *
++ * Device driver and module init code
++ * this will register to the fb layer later
++ *
++ *********************************************************************/
++static void mq200_internal_init_color( struct fb_bitfield* red,
++ struct fb_bitfield* green,
++ struct fb_bitfield* blue,
++ int bpp )
++{
++ switch ( bpp )
++ {
++ case 16:
++ red->offset = 11;
++ green->offset = 5;
++ blue->offset = 0;
++
++ red->length = 5;
++ green->length = 6;
++ blue->length = 5;
++ break;
++ case 24:
++ red->offset = 16;
++ green->offset = 8;
++ blue->offset = 0;
++
++ red->length = 8;
++ green->length = 8;
++ blue->length = 8;
++ break;
++ case 32:
++ red->offset = 0;
++ green->offset = 8;
++ blue->offset = 16;
++
++ red->length = 8;
++ green->length = 8;
++ blue->length = 8;
++ case 8: /* fall through */
++ default:
++ red->offset = green->offset = blue->offset = 0;
++ red->length = green->length = blue->length = bpp;
++ break;
++ }
++
++}
++
++
++static struct mq200_info* __init mq200_internal_init_fbinfo(void)
++{
++ struct mq200_info *info = NULL;
++
++ info = (struct mq200_info*)kmalloc(sizeof(*info), GFP_KERNEL);
++ if(!info)
++ return NULL;
++
++ /*
++ * Initialize memory
++ */
++ memset(info, 0, sizeof(struct mq200_info) );
++ spin_lock_init(&info->lock);
++
++ /* set the base IO addresses */
++ info->io_regions = MQ200_REGIONS;
++ info->monitor_info = MQ200_MONITOR;
++
++ info->fb_info.screen_base = (char *)info->io_regions.virt_fb_base;
++
++ /* fb_fix_screeninfo filling */
++ strcpy(info->fb_info.fix.id, "MQ200_FB" );
++ info->fb_info.fix.smem_start = info->io_regions.phys_fb_base;
++ info->fb_info.fix.smem_len = info->io_regions.fb_size; /* - CURSOR_IMAGE */
++ info->fb_info.fix.mmio_start = info->io_regions.phys_mmio_base;
++ info->fb_info.fix.mmio_len = MQ200_REGS_SIZE;
++ info->fb_info.fix.type = FB_TYPE_PACKED_PIXELS;
++ info->fb_info.fix.accel = FB_ACCEL_NONE;
++ info->fb_info.fix.line_length = MQ200_MONITOR_LINE_LENGTH(info);
++
++ if(MQ200_MONITOR_DEPTH(info) <= 8 )
++ info->fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ else if( MQ200_MONITOR_DEPTH(info) >= 16 )
++ info->fb_info.fix.visual = FB_VISUAL_DIRECTCOLOR;
++ else
++ panic("Calling mq200 with wrong display data\n");
++
++ /* set the variable screen info */
++ info->fb_info.var.xres = MQ200_MONITOR_HORI_RES(info);
++ info->fb_info.var.yres = MQ200_MONITOR_VERT_RES(info);
++ info->fb_info.var.xres_virtual = MQ200_MONITOR_HORI_RES(info);
++ info->fb_info.var.yres_virtual = MQ200_MONITOR_VERT_RES(info);
++ info->fb_info.var.bits_per_pixel = MQ200_MONITOR_DEPTH(info);
++
++ mq200_internal_init_color(&info->fb_info.var.red,
++ &info->fb_info.var.green,
++ &info->fb_info.var.blue,
++ MQ200_MONITOR_DEPTH(info) );
++
++ info->fb_info.var.transp.length = info->fb_info.var.transp.offset = 0;
++ info->fb_info.var.height = info->fb_info.var.width = -1;
++
++ info->fb_info.var.vmode = FB_VMODE_NONINTERLACED;
++ info->fb_info.var.pixclock = 10000;
++ info->fb_info.var.left_margin = info->fb_info.var.right_margin = 16;
++ info->fb_info.var.upper_margin = info->fb_info.var.lower_margin = 16;
++ info->fb_info.var.hsync_len = info->fb_info.var.vsync_len = 8;
++
++ info->fb_info.var.nonstd = 0;
++ info->fb_info.var.activate = FB_ACTIVATE_NOW;
++ info->fb_info.var.accel_flags = 0;
++
++ return info;
++}
++
++
++extern void mq200_register_attributes(struct device* );
++/*
++ * gets called from the bus
++ * we will register our framebuffer from here
++ */
++static int __init mq200_probe(struct device *dev)
++{
++ struct mq200_info *info = NULL;
++ int retv= 0;
++
++ info = mq200_internal_init_fbinfo();
++ if(!mq200_external_probe(info->io_regions.virt_mmio_base))
++ goto error_out;
++
++ GPDR |= (1<<3);
++ GAFR &= ~(1<<3);
++ GPSR |= (1<<3);
++
++ mq200_external_setqmode(&info->monitor_info,
++ info->io_regions.virt_mmio_base,
++ &info->lock);
++
++ info->fb_info.fbops = &mq200_ops;
++ info->fb_info.flags = FBINFO_FLAG_DEFAULT;
++
++ mq200_check_var(&info->fb_info.var, &info->fb_info );
++
++ fb_alloc_cmap(&info->fb_info.cmap, 1 << MQ200_MONITOR_DEPTH(info), 0 );
++
++ info->fb_info.pseudo_palette = (void*)info->pseudo_palette;
++
++ /* save the pointer to the mq200 struct in var */
++ info->fb_info.par = info;
++
++ retv = register_framebuffer(&info->fb_info );
++ if(retv < 0)
++ goto error_out;
++
++
++ /* will get unset if retv != 0 */
++ dev_set_drvdata(dev, info );
++ return retv;
++
++/*
++ * Free the info and exit
++ */
++error_out:
++ kfree(info);
++ return -EINVAL;
++}
++
++#ifdef CONFIG_PM
++static struct mq200_info* get_mq200_info( struct device *dev)
++{
++ return dev_get_drvdata(dev);
++}
++
++static unsigned long get_mmio_base( struct device *dev )
++{
++ struct mq200_info *info = get_mq200_info(dev);
++ return info->io_regions.virt_mmio_base;
++}
++
++static struct mq200_monitor_info* get_monitor_info( struct device *dev)
++{
++ struct mq200_info *info = get_mq200_info(dev);
++ return &info->monitor_info;
++}
++
++static spinlock_t* get_spinlock( struct device *dev)
++{
++ return &get_mq200_info(dev)->lock;
++}
++
++/*
++ * FIXME: make sure we only call mq200_external_offdisplay only once
++ * a 2nd time will hang the kernel -zecke
++ *
++ * FIXME: save the content of the framebuffer inside dev->saved_state
++ * so on resume we can memcpy it back into the buffer and userspace
++ * does not need to redraw
++ *
++ * functions for suspending and resuming
++ */
++static int mq200_suspend(struct device *dev, pm_message_t state)
++{
++
++ mq200_external_offdisplay( get_mmio_base(dev) );
++ clear_cs3_bit(DISPLAY_ON);
++
++
++ return 0;
++}
++
++static int mq200_resume(struct device *dev)
++{
++ unsigned long mem = get_mmio_base(dev);
++ struct mq200_monitor_info *monitor = get_monitor_info(dev);
++ mq200_external_setqmode(monitor, mem, get_spinlock(dev) );
++
++
++ /*
++ * Set display on if it was on
++ */
++ set_cs3_bit(DISPLAY_ON);
++
++ return 0;
++}
++
++
++#endif
++
++
++static struct device_driver mq200fb_driver = {
++ .name = "simpad-mq200",
++ .bus = &platform_bus_type,
++ .probe = mq200_probe, /* will be called after we've registered the driver */
++ .suspend = mq200_suspend,
++ .resume = mq200_resume
++};
++
++int __devinit mq200_init(void)
++{
++ return driver_register(&mq200fb_driver);
++}
++
++module_init(mq200_init);
++MODULE_DESCRIPTION("MQ200 framebuffer driver");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Holger Hans Peter Freyther");
diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-pcmcia.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-pcmcia.patch
new file mode 100644
index 0000000000..13abef9ba2
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-pcmcia.patch
@@ -0,0 +1,226 @@
+Index: linux-2.6.27/drivers/pcmcia/cs.c
+===================================================================
+--- linux-2.6.27.orig/drivers/pcmcia/cs.c 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27/drivers/pcmcia/cs.c 2008-12-04 01:10:04.236701484 +0100
+@@ -10,6 +10,8 @@
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ * (C) 1999 David A. Hinds
++ *
++ * mrdata: -added suspend fix
+ */
+
+ #include <linux/module.h>
+@@ -57,6 +59,10 @@
+ INT_MODULE_PARM(unreset_delay, 10); /* centiseconds */
+ INT_MODULE_PARM(unreset_check, 10); /* centiseconds */
+ INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */
++// INT_MODULE_PARM(unreset_delay, 20); /* centiseconds */
++// INT_MODULE_PARM(unreset_check, 100); /* centiseconds */
++// INT_MODULE_PARM(unreset_limit, 300); /* unreset_check's */
++
+
+ /* Access speed for attribute memory windows */
+ INT_MODULE_PARM(cis_speed, 300); /* ns */
+@@ -362,6 +368,7 @@
+ skt->ops->set_socket(skt, &skt->socket);
+
+ msleep(unreset_delay * 10);
++
+ for (i = 0; i < unreset_limit; i++) {
+ skt->ops->get_status(skt, &status);
+
+@@ -826,7 +833,7 @@
+ int pcmcia_resume_card(struct pcmcia_socket *skt)
+ {
+ int ret;
+-
++
+ cs_dbg(skt, 1, "waking up socket\n");
+
+ mutex_lock(&skt->skt_mutex);
+@@ -854,7 +861,7 @@
+ int pcmcia_eject_card(struct pcmcia_socket *skt)
+ {
+ int ret;
+-
++
+ cs_dbg(skt, 1, "user eject request\n");
+
+ mutex_lock(&skt->skt_mutex);
+Index: linux-2.6.27/drivers/pcmcia/sa1100_generic.c
+===================================================================
+--- linux-2.6.27.orig/drivers/pcmcia/sa1100_generic.c 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27/drivers/pcmcia/sa1100_generic.c 2008-12-04 01:03:33.558818180 +0100
+@@ -28,6 +28,9 @@
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
++ 2007 mrnice: added thesings changes from device_driver
++ to platform_driver - many thx to thesing
++
+ ======================================================================*/
+
+ #include <linux/module.h>
+@@ -81,13 +84,15 @@
+ return ret;
+ }
+
+-static struct device_driver sa11x0_pcmcia_driver = {
+- .probe = sa11x0_drv_pcmcia_probe,
+- .remove = soc_common_drv_pcmcia_remove,
+- .name = "sa11x0-pcmcia",
+- .bus = &platform_bus_type,
+- .suspend = pcmcia_socket_dev_suspend,
+- .resume = pcmcia_socket_dev_resume,
++static struct platform_driver sa11x0_pcmcia_driver = {
++ .driver = {
++ .name = "sa11x0-pcmcia",
++ .probe = sa11x0_drv_pcmcia_probe,
++ .remove = soc_common_drv_pcmcia_remove,
++ .suspend= pcmcia_socket_dev_suspend,
++ .resume = pcmcia_socket_dev_resume,
++ //.bus = &platform_bus_type,
++ },
+ };
+
+ /* sa11x0_pcmcia_init()
+@@ -100,7 +105,7 @@
+ */
+ static int __init sa11x0_pcmcia_init(void)
+ {
+- return driver_register(&sa11x0_pcmcia_driver);
++ return platform_driver_register(&sa11x0_pcmcia_driver);
+ }
+
+ /* sa11x0_pcmcia_exit()
+@@ -110,7 +115,7 @@
+ */
+ static void __exit sa11x0_pcmcia_exit(void)
+ {
+- driver_unregister(&sa11x0_pcmcia_driver);
++ platform_driver_unregister(&sa11x0_pcmcia_driver);
+ }
+
+ MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
+Index: linux-2.6.27/drivers/pcmcia/sa1100_simpad.c
+===================================================================
+--- linux-2.6.27.orig/drivers/pcmcia/sa1100_simpad.c 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27/drivers/pcmcia/sa1100_simpad.c 2008-12-04 01:10:21.458773451 +0100
+@@ -8,15 +8,17 @@
+ #include <linux/kernel.h>
+ #include <linux/device.h>
+ #include <linux/init.h>
++#include <linux/delay.h>
+
+ #include <mach/hardware.h>
+ #include <asm/mach-types.h>
+ #include <asm/irq.h>
+ #include <mach/simpad.h>
+ #include "sa1100_generic.h"
+-
++
++extern long get_cs3_ro(void);
+ extern long get_cs3_shadow(void);
+-extern void set_cs3_bit(int value);
++extern void set_cs3_bit(int value);
+ extern void clear_cs3_bit(int value);
+
+ static struct pcmcia_irqs irqs[] = {
+@@ -25,8 +27,15 @@
+
+ static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+ {
++ clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1|PCMCIA_RESET);
++
++ set_cs3_bit(PCMCIA_BUFF_DIS);
++
++ msleep(10);
++
++ clear_cs3_bit(PCMCIA_BUFF_DIS);
+
+- clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
++ msleep(5);
+
+ skt->irq = IRQ_GPIO_CF_IRQ;
+
+@@ -38,8 +47,8 @@
+ soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+ /* Disable CF bus: */
+- //set_cs3_bit(PCMCIA_BUFF_DIS);
+- clear_cs3_bit(PCMCIA_RESET);
++ set_cs3_bit(PCMCIA_BUFF_DIS);
++ clear_cs3_bit(PCMCIA_RESET);
+ }
+
+ static void
+@@ -47,21 +56,17 @@
+ struct pcmcia_state *state)
+ {
+ unsigned long levels = GPLR;
+- long cs3reg = get_cs3_shadow();
+
+- state->detect=((levels & GPIO_CF_CD)==0)?1:0;
+- state->ready=(levels & GPIO_CF_IRQ)?1:0;
+- state->bvd1=1; /* Not available on Simpad. */
+- state->bvd2=1; /* Not available on Simpad. */
+- state->wrprot=0; /* Not available on Simpad. */
+-
+- if((cs3reg & 0x0c) == 0x0c) {
+- state->vs_3v=0;
+- state->vs_Xv=0;
+- } else {
+- state->vs_3v=1;
+- state->vs_Xv=0;
+- }
++ state->detect = ((levels & GPIO_CF_CD) == 0) ? 1 : 0 ;
++ state->ready = (levels & GPIO_CF_IRQ) ? 1 : 0 ;
++
++ long cs3_ro_reg = get_cs3_ro();
++
++ state->bvd1 = (cs3_ro_reg & PCMCIA_BVD1) ? 1 : 0 ; /* old: =1 Not available on Simpad. */
++ state->bvd2 = (cs3_ro_reg & PCMCIA_BVD2) ? 1 : 0 ; /* old: =1 Not available on Simpad. */
++ state->wrprot = 0 ; /* Not available on Simpad. */
++ state->vs_3v = (cs3_ro_reg & PCMCIA_VS1) ? 0 : 1 ;
++ state->vs_Xv = (cs3_ro_reg & PCMCIA_VS2) ? 0 : 1 ;
+ }
+
+ static int
+@@ -78,7 +83,7 @@
+ clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
+ break;
+
+- case 33:
++ case 33:
+ clear_cs3_bit(VCC_3V_EN|EN1);
+ set_cs3_bit(VCC_5V_EN|EN0);
+ break;
+@@ -96,6 +101,10 @@
+ return -1;
+ }
+
++ if (state->flags & SS_RESET)
++ set_cs3_bit(PCMCIA_RESET);
++ else
++ clear_cs3_bit(PCMCIA_RESET);
+
+ local_irq_restore(flags);
+
+@@ -104,6 +113,7 @@
+
+ static void simpad_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+ {
++ clear_cs3_bit(PCMCIA_RESET);
+ soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ }
+
+@@ -113,7 +123,7 @@
+ set_cs3_bit(PCMCIA_RESET);
+ }
+
+-static struct pcmcia_low_level simpad_pcmcia_ops = {
++static struct pcmcia_low_level simpad_pcmcia_ops = {
+ .owner = THIS_MODULE,
+ .hw_init = simpad_pcmcia_hw_init,
+ .hw_shutdown = simpad_pcmcia_hw_shutdown,
diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2 b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2
new file mode 100644
index 0000000000..b044b471c5
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2
@@ -0,0 +1,358 @@
+diff -Nur linux-2.6.24.vanilla/arch/arm/mach-sa1100/simpad.c linux-2.6.24/arch/arm/mach-sa1100/simpad.c
+--- linux-2.6.24.vanilla/arch/arm/mach-sa1100/simpad.c 2008-10-04 21:47:24.000000000 +0200
++++ linux-2.6.24/arch/arm/mach-sa1100/simpad.c 2008-10-04 22:01:20.000000000 +0200
+@@ -1,5 +1,15 @@
+ /*
+ * linux/arch/arm/mach-sa1100/simpad.c
++ *
++ * 2007/04/11 mrdata:
++ * - insert simpad_uart_set_mctrl()
++ * simpad_uart_get_mctrl()
++ * - internal RS232/DECT/Bluetooth
++ * works again (based on 2.4 simpad-serial.patch)
++ * - added cs3_ro
++ *
++ * 2007/04/12 Bernhard Guillon:
++ * -added gpio_keys (based on h3000.c from hh.org)
+ */
+
+ #include <linux/module.h>
+@@ -9,6 +19,9 @@
+ #include <linux/proc_fs.h>
+ #include <linux/string.h>
+ #include <linux/pm.h>
++
++#include <linux/apm-emulation.h>
++
+ #include <linux/platform_device.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+@@ -27,12 +40,21 @@
+
+ #include <linux/serial_core.h>
+ #include <linux/ioport.h>
++#include <linux/input.h>
++#include <linux/gpio_keys.h>
+ #include <asm/io.h>
+
+ #include "generic.h"
+
++long cs3_ro;
+ long cs3_shadow;
+
++long get_cs3_ro(void)
++{
++ cs3_ro = *(CS3BUSTYPE *)(CS3_BASE);
++ return cs3_ro;
++}
++
+ long get_cs3_shadow(void)
+ {
+ return cs3_shadow;
+@@ -55,9 +77,12 @@
+ *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow;
+ }
+
++EXPORT_SYMBOL(get_cs3_ro);
++EXPORT_SYMBOL(get_cs3_shadow);
+ EXPORT_SYMBOL(set_cs3_bit);
+ EXPORT_SYMBOL(clear_cs3_bit);
+
++
+ static struct map_desc simpad_io_desc[] __initdata = {
+ { /* MQ200 */
+ .virtual = 0xf2800000,
+@@ -73,23 +98,71 @@
+ };
+
+
++static void simpad_uart_set_mctrl(struct uart_port *port, u_int mctrl)
++{
++ if (port->mapbase == _Ser1UTCR0) {
++ /* internal serial port (ttySA1, DECT/Bluetooth) */
++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART1_RTS;
++ else GPSR = GPIO_UART1_RTS;
++
++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART1_DTR;
++ else GPSR = GPIO_UART1_DTR;
++ }
++
++ else if (port->mapbase == _Ser3UTCR0) {
++ /* external serial port (ttySA0, RS232) */
++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART3_RTS;
++ else GPSR = GPIO_UART3_RTS;
++
++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART3_DTR;
++ else GPSR = GPIO_UART3_DTR;
++ }
++}
++
++
++static u_int simpad_uart_get_mctrl(struct uart_port *port)
++{
++ u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
++
++ if (port->mapbase == _Ser1UTCR0) {
++ /* internal serial port (ttySA1, DECT/Bluetooth) */
++ int gplr = GPLR;
++ if (gplr & GPIO_UART1_DCD) ret &= ~TIOCM_CD;
++ if (gplr & GPIO_UART1_CTS) ret &= ~TIOCM_CTS;
++ if (gplr & GPIO_UART1_DSR) ret &= ~TIOCM_DSR;
++ }
++
++ else if (port->mapbase == _Ser3UTCR0) {
++ /* external serial port (ttySA0, RS232) */
++ int gplr = GPLR;
++ if (gplr & GPIO_UART3_DCD) ret &= ~TIOCM_CD;
++ if (gplr & GPIO_UART3_CTS) ret &= ~TIOCM_CTS;
++ if (gplr & GPIO_UART3_DSR) ret &= ~TIOCM_DSR;
++ }
++ return ret;
++}
++
++
+ static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+ {
+- if (port->mapbase == (u_int)&Ser1UTCR0) {
+- if (state)
+- {
+- clear_cs3_bit(RS232_ON);
+- clear_cs3_bit(DECT_POWER_ON);
+- }else
+- {
+- set_cs3_bit(RS232_ON);
+- set_cs3_bit(DECT_POWER_ON);
+- }
+- }
++ if (port->mapbase == (u_int)&Ser3UTCR0) {
++ if (state)
++ {
++ clear_cs3_bit(RS232_ON);
++ /* clear_cs3_bit(DECT_POWER_ON); */
++ }else
++ {
++ set_cs3_bit(RS232_ON);
++ /* set_cs3_bit(DECT_POWER_ON); */
++ }
++ }
+ }
+
++
+ static struct sa1100_port_fns simpad_port_fns __initdata = {
+- .pm = simpad_uart_pm,
++ .set_mctrl = simpad_uart_set_mctrl,
++ .get_mctrl = simpad_uart_get_mctrl,
++ .pm = simpad_uart_pm,
+ };
+
+
+@@ -135,7 +208,6 @@
+ };
+
+
+-
+ static void __init simpad_map_io(void)
+ {
+ sa1100_map_io();
+@@ -144,23 +216,45 @@
+
+ set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON |
+ ENABLE_5V | RESET_SIMCARD | DECT_POWER_ON);
+-
+-
++
+ sa1100_register_uart_fns(&simpad_port_fns);
+ sa1100_register_uart(0, 3); /* serial interface */
+ sa1100_register_uart(1, 1); /* DECT */
+
+- // Reassign UART 1 pins
++ /* Reassign UART 1 pins */
++ /* TEST SOME OLD KERNEL STUFF INSTEAD
+ GAFR |= GPIO_UART_TXD | GPIO_UART_RXD;
+ GPDR |= GPIO_UART_TXD | GPIO_LDD13 | GPIO_LDD15;
+ GPDR &= ~GPIO_UART_RXD;
+ PPAR |= PPAR_UPR;
++ */
++
++ // txd and rxd use their alternate function
++ GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD);
++
++ // the control lines are gpio
++ GAFR &= ~(GPIO_UART1_RTS | GPIO_UART1_CTS | GPIO_UART1_DCD);
++ GAFR &= ~(GPIO_UART1_DSR | GPIO_UART1_DTR);
++ GAFR &= ~(GPIO_UART3_RTS | GPIO_UART3_CTS | GPIO_UART3_DCD);
++ GAFR &= ~(GPIO_UART3_DSR | GPIO_UART3_DTR);
++
++ // txd, rts and dtr are outputs
++ GPDR |= GPIO_UART_TXD;
++ GPDR |= GPIO_UART1_RTS | GPIO_UART3_RTS;
++ GPDR |= GPIO_UART1_DTR | GPIO_UART3_DTR;
++
++ // cts, dcd, dsr and rxd are inputs
++ GPDR &= ~(GPIO_UART1_CTS | GPIO_UART3_CTS);
++ GPDR &= ~(GPIO_UART1_DCD | GPIO_UART3_DCD);
++ GPDR &= ~(GPIO_UART1_DSR | GPIO_UART3_DSR);
++ GPDR &= ~GPIO_UART_RXD;
++
++ PPAR |= PPAR_UPR;
+
+ /*
+ * Set up registers for sleep mode.
+ */
+
+-
+ PWER = PWER_GPIO0| PWER_RTC;
+ PGSR = 0x818;
+ PCFR = 0;
+@@ -171,9 +265,10 @@
+ sa11x0_set_mcp_data(&simpad_mcp_data);
+ }
+
++
+ static void simpad_power_off(void)
+ {
+- local_irq_disable(); // was cli
++ local_irq_disable(); /* was cli */
+ set_cs3(0x800); /* only SD_MEDIAQ */
+
+ /* disable internal oscillator, float CS lines */
+@@ -191,31 +286,52 @@
+ while(1);
+
+ local_irq_enable(); /* we won't ever call it */
++}
+
+
+-}
++/*
++ * gpio_keys
++*/
++
++static struct gpio_keys_button simpad_button_table[] = {
++ { KEY_POWER, IRQ_GPIO_POWER_BUTTON, 1, "power button" },
++};
++
++static struct gpio_keys_platform_data simpad_keys_data = {
++ .buttons = simpad_button_table,
++ .nbuttons = ARRAY_SIZE(simpad_button_table),
++};
++
++static struct platform_device simpad_keys = {
++ .name = "gpio-keys",
++ .dev = {
++ .platform_data = &simpad_keys_data,
++ },
++};
+
+
+ /*
+ * MediaQ Video Device
+ */
++
+ static struct platform_device simpad_mq200fb = {
+ .name = "simpad-mq200",
+ .id = 0,
+ };
+
++
+ static struct platform_device *devices[] __initdata = {
+- &simpad_mq200fb
++ &simpad_keys,
++ &simpad_mq200fb,
+ };
+
+
+-
+ static int __init simpad_init(void)
+ {
+ int ret;
+
+ pm_power_off = simpad_power_off;
+-
++
+ ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+ if(ret)
+ printk(KERN_WARNING "simpad: Unable to register mq200 framebuffer device");
+diff -Nur linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad.h linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad.h
+--- linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad.h 2008-10-04 21:47:17.000000000 +0200
++++ linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad.h 2008-10-04 22:00:57.000000000 +0200
+@@ -12,11 +12,12 @@
+ #define __ASM_ARCH_SIMPAD_H
+
+
+-#define GPIO_UART1_RTS GPIO_GPIO14
++#define GPIO_UART1_RTS GPIO_GPIO9
+ #define GPIO_UART1_DTR GPIO_GPIO7
+ #define GPIO_UART1_CTS GPIO_GPIO8
+ #define GPIO_UART1_DCD GPIO_GPIO23
+ #define GPIO_UART1_DSR GPIO_GPIO6
++#define GPIO_UART1_RI GPIO_GPIO19
+
+ #define GPIO_UART3_RTS GPIO_GPIO12
+ #define GPIO_UART3_DTR GPIO_GPIO16
+@@ -48,9 +49,9 @@
+ #define GPIO_SMART_CARD GPIO_GPIO10
+ #define IRQ_GPIO_SMARD_CARD IRQ_GPIO10
+
+-// CS3 Latch is write only, a shadow is necessary
++// CS3 Latch is write only 16-bit , a shadow is necessary
+
+-#define CS3BUSTYPE unsigned volatile long
++#define CS3BUSTYPE unsigned volatile long
+ #define CS3_BASE 0xf1000000
+
+ #define VCC_5V_EN 0x0001 // For 5V PCMCIA
+@@ -70,43 +71,17 @@
+ #define ENABLE_5V 0x4000 // Enable 5V circuit
+ #define RESET_SIMCARD 0x8000
+
+-#define RS232_ENABLE 0x0440
+-#define PCMCIAMASK 0x402f
++// CS3 Latch is readable only 8-bit interest
+
++#define PCMCIA_BVD1 0x0001
++#define PCMCIA_BVD2 0x0002
++#define PCMCIA_VS1 0x0004 // PCMCIA card voltage select
++#define PCMCIA_VS2 0x0008 // PCMCIA card voltage select, if both are in high state -> 5V PCMCIA card
++#define LOCK_IND 0x0010
++#define CHARGING_STATE 0x0020 // Ladestatus
++#define PCMCIA_SHORT 0x0040 // low active
+
+-struct simpad_battery {
+- unsigned char ac_status; /* line connected yes/no */
+- unsigned char status; /* battery loading yes/no */
+- unsigned char percentage; /* percentage loaded */
+- unsigned short life; /* life till empty */
+-};
+-
+-/* These should match the apm_bios.h definitions */
+-#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00
+-#define SIMPAD_AC_STATUS_AC_ONLINE 0x01
+-#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */
+-#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff
+-
+-/* These bitfields are rarely "or'd" together */
+-#define SIMPAD_BATT_STATUS_HIGH 0x01
+-#define SIMPAD_BATT_STATUS_LOW 0x02
+-#define SIMPAD_BATT_STATUS_CRITICAL 0x04
+-#define SIMPAD_BATT_STATUS_CHARGING 0x08
+-#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10
+-#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */
+-#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */
+-#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */
+-#define SIMPAD_BATT_STATUS_NOBATT 0x80
+-#define SIMPAD_BATT_STATUS_UNKNOWN 0xff
+-
+-extern int simpad_get_battery(struct simpad_battery* );
++#define RS232_ENABLE 0x0440
++#define PCMCIAMASK 0x402f
+
+ #endif // __ASM_ARCH_SIMPAD_H
+-
+-
+-
+-
+-
+-
+-
+-
diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-switches.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-switches.patch
new file mode 100644
index 0000000000..f67a9c29b6
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-switches.patch
@@ -0,0 +1,362 @@
+Index: linux-2.6.27/drivers/mfd/Kconfig
+===================================================================
+--- linux-2.6.27.orig/drivers/mfd/Kconfig 2008-12-07 01:18:53.719505919 +0100
++++ linux-2.6.27/drivers/mfd/Kconfig 2008-12-07 01:19:19.436170998 +0100
+@@ -101,4 +101,7 @@
+ tristate "Touchscreen interface support"
+ depends on MCP_UCB1200 && INPUT
+
++config MCP_UCB1200_SWITCHES
++ tristate "SIMpad Switches support"
++ depends on MCP_UCB1200 && INPUT
+ endmenu
+Index: linux-2.6.27/drivers/mfd/Makefile
+===================================================================
+--- linux-2.6.27.orig/drivers/mfd/Makefile 2008-12-07 01:18:53.732838088 +0100
++++ linux-2.6.27/drivers/mfd/Makefile 2008-12-07 01:19:19.436170998 +0100
+@@ -18,7 +18,7 @@
+ obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
+ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
+-
++obj-$(CONFIG_MCP_UCB1200_SWITCHES) += ucb1x00-switches.o
+ ifeq ($(CONFIG_SA1100_ASSABET),y)
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
+ endif
+Index: linux-2.6.27/drivers/mfd/ucb1x00-switches.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.27/drivers/mfd/ucb1x00-switches.c 2008-12-07 01:29:24.479452365 +0100
+@@ -0,0 +1,332 @@
++/*
++ * linux/drivers/mfd/ucb1x00-switches.c
++ *
++ * Copyright (C) 2007 Bernhard Guillon.
++ *
++ * 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.
++ *
++ * This driver is for the Switches of Siemens SIMpad (CL4,SL4,SLC), T-Sinus-Pad and
++ * Swisscom WP50 devices.
++ *
++ * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8.
++ *
++ * This driver is based on the 2.4 ucb1x00-switches, the 2.6 ucb1x00-assabet
++ * and the ucb1x00-ts driver.
++ *
++ * 2007/06/21 mrdata:
++ * - create new thread kswd() to handle irq_events for ucb1300-gpio's
++ * - found out, that not every key-press or key-release
++ * generate a irq_event
++ * -> establish key_state handling
++ * key_state, key_state_last <-> KEY_PRESS, KEY_RELEASE
++ * -> after irq_event polling the ucb1300-gpio's till all keys
++ * in key_state = KEY_RELEASE
++ *
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/device.h>
++#include <linux/sched.h>
++#include <linux/freezer.h>
++#include <linux/kthread.h>
++
++#include <asm/dma.h>
++
++#include "ucb1x00.h"
++
++#define KEY_PRESS 1
++#define KEY_RELEASE 0
++
++static int key [6] = { KEY_PROG1,KEY_PROG2,KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT };
++
++static unsigned short int key_state [6] = { 0, 0, 0, 0, 0, 0};
++static unsigned short int key_state_last [6] = { 1, 1, 1, 1, 1, 1};
++
++struct ucb1x00_switches {
++ struct input_dev *idev;
++ struct ucb1x00 *ucb;
++
++ wait_queue_head_t irq_wait;
++ struct task_struct *rtask;
++
++ int idx;
++
++ unsigned int valid:1;
++};
++
++static int ucb1x00_thread(void *_switches_id)
++{
++ unsigned short int this;
++ int idx_tmp;
++ int i;
++ struct ucb1x00_switches *switches = _switches_id;
++ struct input_dev *idev = switches->idev;
++ struct task_struct *tsk = current;
++ DECLARE_WAITQUEUE(wait, tsk);
++
++ add_wait_queue(&switches->irq_wait, &wait);
++
++ while (!kthread_should_stop())
++ {
++ signed long timeout;
++
++ if ((switches->idx >= 0) && (switches->idx <= 5) && (switches->valid == 1))
++ {
++ switches->valid = 0;
++
++ idx_tmp = switches->idx;
++
++ ucb1x00_enable(switches->ucb);
++
++ this = ~ucb1x00_io_read(switches->ucb);
++
++ ucb1x00_disable(switches->ucb);
++
++ if (key_state[idx_tmp] == KEY_RELEASE)
++ {
++ key_state_last[idx_tmp] = KEY_RELEASE;
++ key_state[idx_tmp] = KEY_PRESS;
++
++ input_report_key(idev, key[idx_tmp], KEY_PRESS);
++ input_sync(idev);
++ }
++
++ for (i = 0; i < 6; i++)
++ {
++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS))
++ {
++ key_state_last[i] = KEY_RELEASE;
++ key_state[i] = KEY_PRESS;
++
++ input_report_key(idev, key[i], KEY_PRESS);
++ input_sync(idev);
++ }
++ }
++
++ for(;;)
++ {
++ ucb1x00_enable(switches->ucb);
++ this = ~ucb1x00_io_read(switches->ucb);
++ ucb1x00_disable(switches->ucb);
++
++ for (i = 0; i < 6; i++)
++ {
++ if ((key_state[i] == KEY_PRESS) && (((this & (1 << i)) ? 1 : 0) == KEY_RELEASE))
++ {
++ key_state_last[i] = KEY_PRESS;
++ key_state[i] = KEY_RELEASE;
++
++ input_report_key(idev, key[i], KEY_RELEASE);
++ input_sync(idev);
++ }
++
++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS))
++ {
++ key_state_last[i] = KEY_RELEASE;
++ key_state[i] = KEY_PRESS;
++
++ input_report_key(idev, key[i], KEY_PRESS);
++ input_sync(idev);
++ }
++
++ }
++
++ // left loop, if no key press detect
++ if ((this | 0xff80) == 0xff80)
++ {
++ break;
++ }
++
++ set_task_state(tsk, TASK_INTERRUPTIBLE);
++
++ try_to_freeze();
++
++ timeout = HZ / 100;
++
++ schedule_timeout(timeout);
++ }
++ }
++
++ set_task_state(tsk, TASK_INTERRUPTIBLE);
++
++ try_to_freeze();
++
++ timeout = MAX_SCHEDULE_TIMEOUT;
++
++ schedule_timeout(timeout);
++ }
++
++ remove_wait_queue(&switches->irq_wait, &wait);
++
++ switches->rtask = NULL;
++
++ return 0;
++}
++
++
++static void ucb1x00_dev_irq(int idx, void *id)
++{
++ struct ucb1x00_switches *switches = id;
++
++ switches->idx = idx;
++ switches->valid = 1;
++
++ wake_up(&switches->irq_wait);
++}
++
++static int ucb1x00_switches_add(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_switches *switches;
++ struct input_dev *idev;
++ int err,i;
++
++ switches = kzalloc(sizeof(struct ucb1x00_switches), GFP_KERNEL);
++ idev = input_allocate_device();
++
++ if (!switches || !idev) {
++ err = -ENOMEM;
++ goto fail;
++ }
++
++ switches->ucb = dev->ucb;
++
++ input_set_drvdata(idev, switches);
++ idev->name = "SIMpad Switches";
++ idev->id.product = switches->ucb->id;
++
++ __set_bit(EV_KEY, idev->evbit);
++ __set_bit(EV_REP, idev->evbit);
++ __set_bit(KEY_PROG1, idev->keybit);
++ __set_bit(KEY_PROG2, idev->keybit);
++ __set_bit(KEY_UP, idev->keybit);
++ __set_bit(KEY_DOWN, idev->keybit);
++ __set_bit(KEY_LEFT, idev->keybit);
++ __set_bit(KEY_RIGHT, idev->keybit);
++
++ err = input_register_device(idev);
++ if (err)
++ goto fail;
++ switches->idev = idev;
++ dev->priv = switches;
++
++ BUG_ON(switches->rtask);
++
++ init_waitqueue_head(&switches->irq_wait);
++
++ ucb1x00_enable(switches->ucb);
++
++ ucb1x00_io_set_dir(switches->ucb,
++ UCB_IO_0 | UCB_IO_1 | UCB_IO_2 |
++ UCB_IO_3 | UCB_IO_4 | UCB_IO_5,
++ UCB_IO_8 | UCB_IO_9);
++
++ ucb1x00_disable(switches->ucb);
++
++ for (i = 0; i < 6; ++i) {
++ ucb1x00_enable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ if (ucb1x00_hook_irq(switches->ucb, i, ucb1x00_dev_irq, switches) < 0) {
++ printk(KERN_ERR "unable to hook IRQ for "
++ "UCB1300 SWITCH_%d\n", i);
++ return -EBUSY;
++ }
++ }
++
++ switches->rtask = kthread_run(ucb1x00_thread, switches, "kswd");
++ if (!IS_ERR(switches->rtask))
++ {
++ return 0;
++ }
++ else
++ {
++ input_unregister_device(switches->idev);
++
++ for (i = 5; i >= 0; --i) {
++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ /* Only error conditions are ENOENT and EINVAL; silently
++ * ignore:
++ */
++ ucb1x00_free_irq(switches->ucb, i, NULL);
++ }
++ switches->rtask = NULL;
++ ucb1x00_disable(switches->ucb);
++ kfree(switches);
++
++ return -EFAULT;
++ }
++
++fail:
++ input_free_device(idev);
++ kfree(switches);
++ return err;
++
++}
++
++static void ucb1x00_switches_remove(struct ucb1x00_dev *dev)
++{
++ int i;
++ struct ucb1x00_switches *switches = dev->priv;
++
++ if (switches->rtask)
++ kthread_stop(switches->rtask);
++
++ switches->rtask = NULL;
++
++ input_unregister_device(switches->idev);
++
++ for (i = 5; i >= 0; --i) {
++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ /* Only error conditions are ENOENT and EINVAL; silently
++ * ignore:
++ */
++ ucb1x00_free_irq(switches->ucb, i, NULL);
++ }
++ ucb1x00_disable(switches->ucb);
++ kfree(switches);
++}
++
++#ifdef CONFIG_PM
++static int ucb1x00_switches_resume(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_switches *switches = dev->priv;
++
++ if (switches->rtask != NULL)
++ {
++ switches->valid = 0;
++ wake_up(&switches->irq_wait);
++
++ printk(KERN_DEBUG "ucb1x00-switches.c -> _switches_resume() kswd - restart *DONE*\n");
++ }
++ return 0;
++}
++#else
++#define ucb1x00_switches_resume NULL
++#endif
++
++static struct ucb1x00_driver ucb1x00_switches_driver = {
++ .add = ucb1x00_switches_add,
++ .remove = ucb1x00_switches_remove,
++ .resume = ucb1x00_switches_resume,
++};
++
++static int __init ucb1x00_switches_init(void)
++{
++ return ucb1x00_register_driver(&ucb1x00_switches_driver);
++}
++
++static void __exit ucb1x00_switches_exit(void)
++{
++ ucb1x00_unregister_driver(&ucb1x00_switches_driver);
++}
++
++module_init(ucb1x00_switches_init);
++module_exit(ucb1x00_switches_exit);
++
++MODULE_AUTHOR("Bernhard Guillon <Bernhard.Guillon@opensimpad.org>");
++MODULE_DESCRIPTION("UCB1x00 Switches driver for Siemens SIMpad");
++MODULE_LICENSE("GPL");
diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-ts-supend-and-accuracy.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-ts-supend-and-accuracy.patch
new file mode 100644
index 0000000000..ef40cd05ef
--- /dev/null
+++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-ts-supend-and-accuracy.patch
@@ -0,0 +1,105 @@
+Index: linux-2.6.27/drivers/mfd/ucb1x00-ts.c
+===================================================================
+--- linux-2.6.27.orig/drivers/mfd/ucb1x00-ts.c 2008-10-10 00:13:53.000000000 +0200
++++ linux-2.6.27/drivers/mfd/ucb1x00-ts.c 2008-12-04 01:27:04.168672848 +0100
+@@ -16,6 +16,10 @@
+ * It is important to note that the signal connected to the ADCSYNC
+ * pin should provide pulses even when the LCD is blanked, otherwise
+ * a pen touch needed to unblank the LCD will never be read.
++ *
++ * mrdata: -added some accuracy improvement based on thesings collie patch
++ * -added suspend fix
++ *
+ */
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+@@ -103,6 +107,8 @@
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+
++ udelay(55);
++
+ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+ }
+ }
+@@ -129,7 +135,7 @@
+ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+- udelay(55);
++ udelay(165);
+
+ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+ }
+@@ -157,7 +163,7 @@
+ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+- udelay(55);
++ udelay(165);
+
+ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
+ }
+@@ -218,7 +224,11 @@
+ ucb1x00_adc_enable(ts->ucb);
+
+ x = ucb1x00_ts_read_xpos(ts);
++ ucb1x00_adc_disable(ts->ucb);
++ ucb1x00_adc_enable(ts->ucb);
+ y = ucb1x00_ts_read_ypos(ts);
++ ucb1x00_adc_disable(ts->ucb);
++ ucb1x00_adc_enable(ts->ucb);
+ p = ucb1x00_ts_read_pressure(ts);
+
+ /*
+@@ -229,8 +239,11 @@
+
+ msleep(10);
+
+- ucb1x00_enable(ts->ucb);
++ if ((x < 60) || (y < 60)) {
++ p = 0;
++ }
+
++ ucb1x00_enable(ts->ucb);
+
+ if (ucb1x00_ts_pen_down(ts)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+@@ -248,7 +261,9 @@
+ }
+
+ timeout = MAX_SCHEDULE_TIMEOUT;
++
+ } else {
++
+ ucb1x00_disable(ts->ucb);
+
+ /*
+@@ -267,6 +282,14 @@
+
+ try_to_freeze();
+
++ /*
++ * While suspend the ktsd-thread goes sleep -> try_to_freeze()
++ * While resume the ktsd-thread do wakup and must rune one time
++ * again to do a clean re-setup -> enable_irq: UCB_IRQ_TSPX
++ */
++ if(ts->restart)
++ timeout = HZ / 100;
++
+ schedule_timeout(timeout);
+ }
+
+@@ -349,8 +372,12 @@
+ * TS interrupt mode is set up again
+ * after sleep.
+ */
++
+ ts->restart = 1;
+ wake_up(&ts->irq_wait);
++
++ printk(KERN_INFO "ucb1x00-ts.c -> ucb1x00_ts_resume() ktsd - restart *DONE*\n");
++
+ }
+ return 0;
+ }