Index: linux-2.6.26/arch/arm/Kconfig
===================================================================
--- linux-2.6.26.orig/arch/arm/Kconfig	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/arch/arm/Kconfig	2008-10-17 18:15:31.391792839 +0200
@@ -967,7 +967,7 @@
 
 config CPU_FREQ_SA1110
 	bool
-	depends on CPU_FREQ && (SA1100_ASSABET || SA1100_CERF || SA1100_PT_SYSTEM3)
+	depends on CPU_FREQ && (SA1100_ASSABET || SA1100_CERF || SA1100_PT_SYSTEM3 || SA1100_COLLIE)
 	default y
 
 config CPU_FREQ_INTEGRATOR
Index: linux-2.6.26/arch/arm/mach-sa1100/collie.c
===================================================================
--- linux-2.6.26.orig/arch/arm/mach-sa1100/collie.c	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/arch/arm/mach-sa1100/collie.c	2008-10-17 18:15:31.391792839 +0200
@@ -206,7 +206,7 @@
 }
 
 static struct flash_platform_data collie_flash_data = {
-	.map_name	= "cfi_probe",
+	.map_name	= "sharp",
 	.set_vpp	= collie_set_vpp,
 	.parts		= collie_partitions,
 	.nr_parts	= ARRAY_SIZE(collie_partitions),
Index: linux-2.6.26/arch/arm/mach-sa1100/dma.c
===================================================================
--- linux-2.6.26.orig/arch/arm/mach-sa1100/dma.c	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/arch/arm/mach-sa1100/dma.c	2008-10-17 18:15:31.399789199 +0200
@@ -39,7 +39,7 @@
 
 static sa1100_dma_t dma_chan[SA1100_DMA_CHANNELS];
 
-static spinlock_t dma_list_lock;
+static  DEFINE_SPINLOCK(dma_list_lock);
 
 
 static irqreturn_t dma_irq_handler(int irq, void *dev_id)
Index: linux-2.6.26/drivers/input/keyboard/locomokbd.c
===================================================================
--- linux-2.6.26.orig/drivers/input/keyboard/locomokbd.c	2008-10-17 18:13:16.000000000 +0200
+++ linux-2.6.26/drivers/input/keyboard/locomokbd.c	2008-10-17 18:15:31.403791239 +0200
@@ -272,6 +272,7 @@
 	for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
 		set_bit(locomokbd->keycode[i], input_dev->keybit);
 	clear_bit(0, input_dev->keybit);
+	locomo_writel(0, locomokbd->base + LOCOMO_KSC);
 
 	/* attempt to get the interrupt */
 	err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
Index: linux-2.6.26/drivers/mfd/Kconfig
===================================================================
--- linux-2.6.26.orig/drivers/mfd/Kconfig	2008-10-17 18:13:21.000000000 +0200
+++ linux-2.6.26/drivers/mfd/Kconfig	2008-10-17 18:15:31.403791239 +0200
@@ -77,4 +77,10 @@
 	tristate "Touchscreen interface support"
 	depends on MCP_UCB1200 && INPUT
 
+config MCP_COLLIE_TS
+	tristate "Touchscreen collie support"
+	depends on MCP_UCB1200 && INPUT && !MCP_UCB1200_TS
+	 ---help---
+	  Driver for touchscreen on collie - sharp sl-5500.
+
 endmenu
Index: linux-2.6.26/drivers/mfd/Makefile
===================================================================
--- linux-2.6.26.orig/drivers/mfd/Makefile	2008-10-17 18:13:21.000000000 +0200
+++ linux-2.6.26/drivers/mfd/Makefile	2008-10-17 18:15:31.407791679 +0200
@@ -14,7 +14,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_COLLIE_TS)     += collie-ts.o
 ifeq ($(CONFIG_SA1100_ASSABET),y)
 obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-assabet.o
 endif
Index: linux-2.6.26/drivers/mfd/collie-ts.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.26/drivers/mfd/collie-ts.c	2008-10-17 18:15:31.415790559 +0200
@@ -0,0 +1,449 @@
+/*
+ *  Touchscreen driver for UCB1x00-based touchscreens
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *  Copyright (C) 2005 Pavel Machek
+ *
+ * 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.
+ *
+ * 21-Jan-2002 <jco@ict.es> :
+ *
+ * Added support for synchronous A/D mode. This mode is useful to
+ * avoid noise induced in the touchpanel by the LCD, provided that
+ * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin.
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/freezer.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/semaphore.h>
+
+#include <asm/dma.h>
+#include <asm/arch/collie.h>
+#include <asm/mach-types.h>
+
+#include "ucb1x00.h"
+
+struct ucb1x00_ts {
+	struct input_dev	*idev;
+	struct ucb1x00		*ucb;
+
+	wait_queue_head_t	irq_wait;
+	struct task_struct	*rtask;
+	u16			x_res;
+	u16			y_res;
+
+	unsigned int		adcsync:1;
+};
+
+static int adcsync;
+
+/**********************************
+
+     ................
+     .              . = 340
+     .              .
+     .             ^.
+     .             ^.
+     .             ^.
+     .             ^.
+     .              .
+     .             X. = 10
+     .  <<<<<<<<  Y .
+     ................
+     .   Sharp    =200
+     .              .
+     .  -   O    -  .
+     .              .
+     ................
+
+**********************************/
+
+
+static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
+{
+	struct input_dev *idev = ts->idev;
+
+	input_report_abs(idev, ABS_X, x);
+	input_report_abs(idev, ABS_Y, y);
+	input_report_abs(idev, ABS_PRESSURE, pressure);
+        input_report_key(idev, BTN_TOUCH, 1);
+	input_sync(idev);
+}
+
+static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
+{
+	struct input_dev *idev = ts->idev;
+
+	input_report_abs(idev, ABS_PRESSURE, 0);
+        input_report_key(idev, BTN_TOUCH, 0);
+	input_sync(idev);
+}
+
+/*
+ * Switch to interrupt mode. This set touchscreen to interrupt 
+ * mode, so that chip is able to send interrupt.
+ */
+static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+			UCB_TS_CR_MODE_INT);
+}
+
+/*
+ * Switch to pressure mode, and read pressure.  We don't need to wait
+ * here, since both plates are being driven.
+ *
+ * set_read_pressure() in sharp code
+ */
+static inline void ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
+{
+	ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0);
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			  UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW |
+			  UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+	ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | 
+			  UCB_ADC_INP_AD2 | 
+			  UCB_ADC_SYNC_ENA);
+	udelay(100);
+	ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | 
+			  UCB_ADC_INP_AD2 | 
+			  UCB_ADC_SYNC_ENA | UCB_ADC_START);
+}
+
+/*
+ * Switch to X position mode and measure Y plate.  We switch the plate
+ * configuration in pressure mode, then switch to position mode.  This
+ * gives a faster response time.  Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static inline void ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts)
+{
+	ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+
+	ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | 
+			  UCB_ADC_INP_TSPY | UCB_ADC_SYNC_ENA);
+	udelay(100);
+	ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | 
+			  UCB_ADC_INP_TSPY | UCB_ADC_SYNC_ENA | 
+		 	  UCB_ADC_START);
+}
+
+/*
+ * Switch to Y position mode and measure X plate.  We switch the plate
+ * configuration in pressure mode, then switch to position mode.  This
+ * gives a faster response time.  Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static inline void ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
+{
+	ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
+
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+
+	ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | 
+			  UCB_ADC_INP_TSPX | UCB_ADC_SYNC_ENA);
+	udelay(100);
+	ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr |
+			  UCB_ADC_INP_TSPX | UCB_ADC_SYNC_ENA | 
+			  UCB_ADC_START);
+}
+
+/*
+ * Switch to X plate resistance mode.  Set MX to ground, PX to
+ * supply.  Measure current.
+ */
+static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
+}
+
+/*
+ * Switch to Y plate resistance mode.  Set MY to ground, PY to
+ * supply.  Measure current.
+ */
+static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
+}
+
+/*
+ * This is a RT kernel thread that handles the ADC accesses
+ * (mainly so we can use semaphores in the UCB1200 core code
+ * to serialise accesses to the ADC).
+ */
+static int ucb1x00_thread(void *_ts)
+{
+	struct ucb1x00_ts *ts = _ts;
+	struct task_struct *tsk = current;
+	DECLARE_WAITQUEUE(wait, tsk);
+	int state;
+
+	/*
+	 * We could run as a real-time thread.  However, thus far
+	 * this doesn't seem to be necessary.
+	 */
+
+	add_wait_queue(&ts->irq_wait, &wait);	
+
+	while (!kthread_should_stop()) {
+		unsigned int data[3];
+			
+		for (state=0; state<3; state++) {
+			
+			ucb1x00_adc_enable(ts->ucb);
+			ucb1x00_enable_irq(ts->ucb, UCB_IRQ_ADC, UCB_FALLING);
+			switch (state) {
+			/* Order matters here; last measurement seems to be more noisy then the
+			   rest, and we care about pressure least */
+				case 2: ucb1x00_ts_read_pressure(ts);
+					break;
+				case 0: ucb1x00_ts_read_ypos(ts);
+					break;
+				case 1: ucb1x00_ts_read_xpos(ts);
+					break;
+			}
+			/* wait for adc */
+			try_to_freeze();
+			schedule_timeout(1000 * HZ);
+			ucb1x00_disable_irq(ts->ucb, UCB_IRQ_ADC, UCB_FALLING);
+			data[state] = UCB_ADC_DAT(ucb1x00_reg_read(ts->ucb, UCB_ADC_DATA));
+			ucb1x00_adc_disable(ts->ucb);
+		}	
+		
+		/* If not pressed any more, try to sleep! */
+		if (data[2] < 300) {
+			set_task_state(tsk, TASK_INTERRUPTIBLE);
+			ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING);
+			ucb1x00_ts_mode_int(ts);
+			ucb1x00_disable(ts->ucb);
+			ucb1x00_ts_event_release(ts);
+			try_to_freeze();
+			schedule_timeout(1000 * HZ);
+			ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING);
+			ucb1x00_enable(ts->ucb);
+		} else {	
+			ucb1x00_ts_evt_add(ts, data[2], data[1], data[0]);
+		}
+		ucb1x00_disable(ts->ucb);
+		msleep(20);
+		ucb1x00_enable(ts->ucb);
+	}
+
+	remove_wait_queue(&ts->irq_wait, &wait);
+
+	ts->rtask = NULL;
+	return 0;
+}
+
+/*
+ * We only detect touch screen _touches_ with this interrupt
+ * handler, and even then we just schedule our task.
+ */
+static void ucb1x00_ts_irq(int idx, void *id)
+{
+	struct ucb1x00_ts *ts = id;
+	wake_up(&ts->irq_wait);
+}
+
+static void ucb1x00_adc_irq(int idx, void *id)
+{
+	struct ucb1x00_ts *ts = id;
+	wake_up(&ts->irq_wait);
+}
+
+static int ucb1x00_ts_open(struct input_dev *idev)
+{
+	struct ucb1x00_ts *ts = input_get_drvdata(idev);
+	int ret = 0;
+
+	BUG_ON(ts->rtask);
+
+	init_waitqueue_head(&ts->irq_wait);
+	
+	ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
+	if (ret < 0)
+		return ret;
+	
+	ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_ADC, ucb1x00_adc_irq, ts);
+	if (ret < 0) {
+		ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+		return ret;
+	}
+
+	ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING);
+	
+	/*
+	 * If we do this at all, we should allow the user to
+	 * measure and read the X and Y resistance at any time.
+	 */
+	ucb1x00_adc_enable(ts->ucb);
+	ts->x_res = ucb1x00_ts_read_xres(ts);
+	ts->y_res = ucb1x00_ts_read_yres(ts);
+	ucb1x00_adc_disable(ts->ucb);
+
+	if (machine_is_collie()) {
+		ucb1x00_io_set_dir(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
+	}
+
+	ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd");
+	if (!IS_ERR(ts->rtask)) {
+		ret = 0;
+	} else {
+		ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+		ts->rtask = NULL;
+		ret = -EFAULT;
+	}
+	
+	return ret;
+}
+
+/*
+ * Release touchscreen resources.  Disable IRQs.
+ */
+static void ucb1x00_ts_close(struct input_dev *idev)
+{
+	struct ucb1x00_ts *ts = input_get_drvdata(idev);
+
+	if (ts->rtask)
+		kthread_stop(ts->rtask);
+
+	ucb1x00_enable(ts->ucb);
+	ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+	ucb1x00_free_irq(ts->ucb, UCB_IRQ_ADC, ts);
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
+	ucb1x00_disable(ts->ucb);
+}
+
+#ifdef CONFIG_PM
+static int ucb1x00_ts_resume(struct ucb1x00_dev *dev)
+{
+	struct ucb1x00_ts *ts = dev->priv;
+
+	if (ts->rtask != NULL) {
+		/*
+		 * Restart the TS thread to ensure the
+		 * TS interrupt mode is set up again
+		 * after sleep.
+		 */
+		wake_up(&ts->irq_wait);
+	}
+	return 0;
+}
+#else
+#define ucb1x00_ts_resume NULL
+#endif
+
+
+/*
+ * Initialisation.
+ */
+static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
+{
+	struct ucb1x00_ts *ts;
+	struct input_dev *idev;
+	int err;
+
+	ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
+	idev = input_allocate_device();
+	if (!ts || !idev) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	ts->ucb = dev->ucb;
+	ts->idev = idev;
+	ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
+
+	input_set_drvdata(idev, ts);
+	idev->name       = "Touchscreen panel";
+	idev->id.product = ts->ucb->id;
+	idev->open       = ucb1x00_ts_open;
+	idev->close      = ucb1x00_ts_close;
+
+	__set_bit(EV_ABS, idev->evbit);
+	__set_bit(ABS_X, idev->absbit);
+	__set_bit(ABS_Y, idev->absbit);
+	__set_bit(ABS_PRESSURE, idev->absbit);
+
+        input_set_abs_params(ts->idev, ABS_X, 0, 450, 0, 0);
+        input_set_abs_params(ts->idev, ABS_Y, 200, 800, 0, 0);
+        input_set_abs_params(ts->idev, ABS_PRESSURE, 400, 800, 0, 0);
+
+
+	err = input_register_device(idev);
+	if (err)
+		goto fail;
+
+	dev->priv = ts;
+
+	return 0;
+
+ fail:
+	input_free_device(idev);
+	kfree(ts);
+	return err;
+}
+
+static void ucb1x00_ts_remove(struct ucb1x00_dev *dev)
+{
+	struct ucb1x00_ts *ts = dev->priv;
+
+	input_unregister_device(ts->idev);
+	kfree(ts);
+}
+
+static struct ucb1x00_driver ucb1x00_ts_driver = {
+	.add		= ucb1x00_ts_add,
+	.remove		= ucb1x00_ts_remove,
+	.resume		= ucb1x00_ts_resume,
+};
+
+static int __init ucb1x00_ts_init(void)
+{
+	return ucb1x00_register_driver(&ucb1x00_ts_driver);
+}
+
+static void __exit ucb1x00_ts_exit(void)
+{
+	ucb1x00_unregister_driver(&ucb1x00_ts_driver);
+}
+
+module_param(adcsync, int, 0444);
+module_init(ucb1x00_ts_init);
+module_exit(ucb1x00_ts_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
+MODULE_LICENSE("GPL");
Index: linux-2.6.26/drivers/mfd/ucb1x00.h
===================================================================
--- linux-2.6.26.orig/drivers/mfd/ucb1x00.h	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/drivers/mfd/ucb1x00.h	2008-10-17 18:15:31.415790559 +0200
@@ -34,7 +34,10 @@
 #define UCB_IE_TCLIP		(1 << 14)
 #define UCB_IE_ACLIP		(1 << 15)
 
+/* UCB1200 irqs */
+#define UCB_IRQ_ADC		11
 #define UCB_IRQ_TSPX		12
+#define UCB_IRQ_TSMX		13
 
 #define UCB_TC_A	0x05
 #define UCB_TC_A_LOOP		(1 << 7)	/* UCB1200 */
Index: linux-2.6.26/drivers/mtd/chips/Kconfig
===================================================================
--- linux-2.6.26.orig/drivers/mtd/chips/Kconfig	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/drivers/mtd/chips/Kconfig	2008-10-17 18:15:31.419791479 +0200
@@ -239,5 +239,13 @@
 	  used for XIP purposes.  If you're not sure what this is all about
 	  then say N.
 
+config MTD_SHARP
+        tristate "pre-CFI Sharp chip support"
+        depends on MTD
+        help
+          This option enables support for flash chips using Sharp-compatible
+          commands, including some which are not CFI-compatible and hence
+          cannot be used with the CONFIG_MTD_CFI_INTELxxx options.
+
 endmenu
 
Index: linux-2.6.26/drivers/mtd/chips/Makefile
===================================================================
--- linux-2.6.26.orig/drivers/mtd/chips/Makefile	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/drivers/mtd/chips/Makefile	2008-10-17 18:15:31.419791479 +0200
@@ -12,4 +12,5 @@
 obj-$(CONFIG_MTD_JEDECPROBE)	+= jedec_probe.o
 obj-$(CONFIG_MTD_RAM)		+= map_ram.o
 obj-$(CONFIG_MTD_ROM)		+= map_rom.o
+obj-$(CONFIG_MTD_SHARP)         += sharp.o
 obj-$(CONFIG_MTD_ABSENT)	+= map_absent.o
Index: linux-2.6.26/drivers/mtd/chips/sharp.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.26/drivers/mtd/chips/sharp.c	2008-10-17 18:15:31.423790399 +0200
@@ -0,0 +1,645 @@
+/*
+ * MTD chip driver for pre-CFI Sharp flash chips
+ *
+ * Copyright 2000,2001 David A. Schleef <ds@schleef.org>
+ *           2000,2001 Lineo, Inc.
+ *
+ * $Id: sharp.c,v 1.17 2005/11/29 14:28:28 gleixner Exp $
+ *
+ * Devices supported:
+ *   LH28F016SCT Symmetrical block flash memory, 2Mx8
+ *   LH28F008SCT Symmetrical block flash memory, 1Mx8
+ *
+ * Documentation:
+ *   http://www.sharpmeg.com/datasheets/memic/flashcmp/
+ *   http://www.sharpmeg.com/datasheets/memic/flashcmp/01symf/16m/016sctl9.pdf
+ *   016sctl9.pdf
+ *
+ * Limitations:
+ *   This driver only supports 4x1 arrangement of chips.
+ *   Not tested on anything but PowerPC.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/cfi.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#define CMD_RESET		0xffffffff
+#define CMD_READ_ID		0x90909090
+#define CMD_READ_STATUS		0x70707070
+#define CMD_CLEAR_STATUS	0x50505050
+#define CMD_BLOCK_ERASE_1	0x20202020
+#define CMD_BLOCK_ERASE_2	0xd0d0d0d0
+#define CMD_BYTE_WRITE		0x40404040
+#define CMD_SUSPEND		0xb0b0b0b0
+#define CMD_RESUME		0xd0d0d0d0
+#define CMD_SET_BLOCK_LOCK_1	0x60606060
+#define CMD_SET_BLOCK_LOCK_2	0x01010101
+#define CMD_SET_MASTER_LOCK_1	0x60606060
+#define CMD_SET_MASTER_LOCK_2	0xf1f1f1f1
+#define CMD_CLEAR_BLOCK_LOCKS_1	0x60606060
+#define CMD_CLEAR_BLOCK_LOCKS_2	0xd0d0d0d0
+
+#define SR_READY		0x80808080 // 1 = ready
+#define SR_ERASE_SUSPEND	0x40404040 // 1 = block erase suspended
+#define SR_ERROR_ERASE		0x20202020 // 1 = error in block erase or clear lock bits
+#define SR_ERROR_WRITE		0x10101010 // 1 = error in byte write or set lock bit
+#define	SR_VPP			0x08080808 // 1 = Vpp is low
+#define SR_WRITE_SUSPEND	0x04040404 // 1 = byte write suspended
+#define SR_PROTECT		0x02020202 // 1 = lock bit set
+#define SR_RESERVED		0x01010101
+
+#define SR_ERRORS (SR_ERROR_ERASE|SR_ERROR_WRITE|SR_VPP|SR_PROTECT)
+
+#define BLOCK_MASK		0xfffe0000
+
+/* Configuration options */
+
+#define AUTOUNLOCK  /* automatically unlocks blocks before erasing */
+
+static struct mtd_info *sharp_probe(struct map_info *);
+
+static int sharp_probe_map(struct map_info *map, struct mtd_info *mtd);
+
+static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len,
+	size_t *retlen, u_char *buf);
+static int sharp_write(struct mtd_info *mtd, loff_t from, size_t len,
+	size_t *retlen, const u_char *buf);
+static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr);
+static void sharp_sync(struct mtd_info *mtd);
+static int sharp_suspend(struct mtd_info *mtd);
+static void sharp_resume(struct mtd_info *mtd);
+static void sharp_destroy(struct mtd_info *mtd);
+
+static int sharp_write_oneword(struct map_info *map, struct flchip *chip,
+	unsigned long adr, __u32 datum);
+static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip,
+	unsigned long adr);
+#ifdef AUTOUNLOCK
+static inline void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip,
+	unsigned long adr);
+#endif
+
+
+struct sharp_info{
+	struct flchip *chip;
+	int bogus;
+	int chipshift;
+	int numchips;
+	struct flchip chips[1];
+};
+
+static void sharp_destroy(struct mtd_info *mtd);
+
+static struct mtd_chip_driver sharp_chipdrv = {
+	.probe		= sharp_probe,
+	.destroy	= sharp_destroy,
+	.name		= "sharp",
+	.module		= THIS_MODULE
+};
+
+static void sharp_udelay(unsigned long i) {
+	if (in_interrupt()) {
+		udelay(i);
+	} else {
+		schedule();
+	}
+}
+
+static struct mtd_info *sharp_probe(struct map_info *map)
+{
+	struct mtd_info *mtd = NULL;
+	struct sharp_info *sharp = NULL;
+	int width;
+
+	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
+	if(!mtd)
+		return NULL;
+
+	sharp = kzalloc(sizeof(*sharp), GFP_KERNEL);
+	if(!sharp) {
+		kfree(mtd);
+		return NULL;
+	}
+
+	width = sharp_probe_map(map,mtd);
+	if(!width){
+		kfree(mtd);
+		kfree(sharp);
+		return NULL;
+	}
+
+	mtd->priv = map;
+	mtd->type = MTD_NORFLASH;
+	mtd->erase = sharp_erase;
+	mtd->read = sharp_read;
+	mtd->write = sharp_write;
+	mtd->sync = sharp_sync;
+	mtd->suspend = sharp_suspend;
+	mtd->resume = sharp_resume;
+	mtd->flags = MTD_CAP_NORFLASH;
+	mtd->writesize = 1;
+	mtd->name = map->name;
+
+	sharp->chipshift = 24;
+	sharp->numchips = 1;
+	sharp->chips[0].start = 0;
+	sharp->chips[0].state = FL_READY;
+	sharp->chips[0].mutex = &sharp->chips[0]._spinlock;
+	sharp->chips[0].word_write_time = 0;
+	init_waitqueue_head(&sharp->chips[0].wq);
+	spin_lock_init(&sharp->chips[0]._spinlock);
+
+	map->fldrv = &sharp_chipdrv;
+	map->fldrv_priv = sharp;
+
+	__module_get(THIS_MODULE);
+	return mtd;
+}
+
+static inline void sharp_send_cmd(struct map_info *map, unsigned long cmd, unsigned long adr)
+{
+	map_word map_cmd;
+	map_cmd.x[0] = cmd;
+	map_write(map, map_cmd, adr);
+}
+
+static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd)
+{
+	map_word tmp, read0, read4;
+	unsigned long base = 0;
+	int width = 4;
+
+	tmp = map_read(map, base+0);
+
+	sharp_send_cmd(map, CMD_READ_ID, base+0);
+
+	read0 = map_read(map, base+0);
+	read4 = map_read(map, base+4);
+	if (read0.x[0] == 0x00b000b0) {
+		printk("Sharp chip, %lx, %lx, width = %d\n", read0.x[0], read4.x[0], width);
+		/* Prints b000b0, b000b0, width = 4 on collie */
+		switch(read4.x[0]){
+		case 0xaaaaaaaa:
+		case 0xa0a0a0a0:
+			/* aa - LH28F016SCT-L95 2Mx8, 32 64k blocks*/
+			/* a0 - LH28F016SCT-Z4  2Mx8, 32 64k blocks*/
+			mtd->erasesize = 0x10000 * width;
+			mtd->size = 0x200000 * width;
+			return width;
+		case 0xa6a6a6a6:
+			/* a6 - LH28F008SCT-L12 1Mx8, 16 64k blocks*/
+			/* a6 - LH28F008SCR-L85 1Mx8, 16 64k blocks*/
+			mtd->erasesize = 0x10000 * width;
+			mtd->size = 0x100000 * width;
+			return width;
+		case 0x00b000b0:
+			/* a6 - LH28F640BFHE 8 64k * 2 chip blocks*/
+			mtd->erasesize = 0x10000 * width / 2;
+			mtd->size = 0x800000 * width / 2;
+			return width;
+		default:
+			printk("Sort-of looks like sharp flash, 0x%08lx 0x%08lx\n",
+				read0.x[0], read4.x[0]);
+		}
+	} else if ((map_read(map, base+0).x[0] == CMD_READ_ID)){
+		/* RAM, probably */
+		printk("Looks like RAM\n");
+		map_write(map, tmp, base+0);
+	}else{
+		printk("Doesn't look like sharp flash, 0x%08lx 0x%08lx\n",
+			read0.x[0], read4.x[0]);
+	}
+
+	return 0;
+}
+
+/* This function returns with the chip->mutex lock held. */
+static int sharp_wait(struct map_info *map, struct flchip *chip)
+{
+	map_word status;
+	unsigned long timeo = jiffies + HZ;
+	DECLARE_WAITQUEUE(wait, current);
+	int adr = 0;
+
+retry:
+	spin_lock_bh(chip->mutex);
+
+	switch (chip->state) {
+	case FL_READY:
+		sharp_send_cmd(map, CMD_READ_STATUS, adr);
+		chip->state = FL_STATUS;
+	case FL_STATUS:
+		status = map_read(map, adr);
+		if ((status.x[0] & SR_READY) == SR_READY)
+			break;
+		spin_unlock_bh(chip->mutex);
+		if (time_after(jiffies, timeo)) {
+			printk("Waiting for chip to be ready timed out in erase\n");
+			return -EIO;
+		}
+		sharp_udelay(1);
+		goto retry;
+	default:
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&chip->wq, &wait);
+
+		spin_unlock_bh(chip->mutex);
+
+		sharp_udelay(1);
+
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&chip->wq, &wait);
+
+		if(signal_pending(current))
+			return -EINTR;
+
+		timeo = jiffies + HZ;
+
+		goto retry;
+	}
+
+	sharp_send_cmd(map, CMD_RESET, adr);
+
+	chip->state = FL_READY;
+
+	return 0;
+}
+
+static void sharp_release(struct flchip *chip)
+{
+	wake_up(&chip->wq);
+	spin_unlock_bh(chip->mutex);
+}
+
+static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len,
+	size_t *retlen, u_char *buf)
+{
+	struct map_info *map = mtd->priv;
+	struct sharp_info *sharp = map->fldrv_priv;
+	int chipnum;
+	int ret = 0;
+	int ofs = 0;
+
+	chipnum = (from >> sharp->chipshift);
+	ofs = from & ((1 << sharp->chipshift)-1);
+
+	*retlen = 0;
+
+	while(len){
+		unsigned long thislen;
+
+		if(chipnum>=sharp->numchips)
+			break;
+
+		thislen = len;
+		if(ofs+thislen >= (1<<sharp->chipshift))
+			thislen = (1<<sharp->chipshift) - ofs;
+
+		ret = sharp_wait(map,&sharp->chips[chipnum]);
+		if(ret<0)
+			break;
+
+		map_copy_from(map,buf,ofs,thislen);
+
+		sharp_release(&sharp->chips[chipnum]);
+
+		*retlen += thislen;
+		len -= thislen;
+		buf += thislen;
+
+		ofs = 0;
+		chipnum++;
+	}
+	return ret;
+}
+
+static int sharp_write(struct mtd_info *mtd, loff_t to, size_t len,
+	size_t *retlen, const u_char *buf)
+{
+	struct map_info *map = mtd->priv;
+	struct sharp_info *sharp = map->fldrv_priv;
+	int ret = 0;
+	int i,j;
+	int chipnum;
+	unsigned long ofs;
+	union { u32 l; unsigned char uc[4]; } tbuf;
+
+	*retlen = 0;
+
+	while(len){
+		tbuf.l = 0xffffffff;
+		chipnum = to >> sharp->chipshift;
+		ofs = to & ((1<<sharp->chipshift)-1);
+
+		j=0;
+		for(i=ofs&3;i<4 && len;i++){
+			tbuf.uc[i] = *buf;
+			buf++;
+			to++;
+			len--;
+			j++;
+		}
+		sharp_write_oneword(map, &sharp->chips[chipnum], ofs&~3, tbuf.l);
+		if(ret<0)
+			return ret;
+		(*retlen)+=j;
+	}
+
+	return 0;
+}
+
+static int sharp_write_oneword(struct map_info *map, struct flchip *chip,
+	unsigned long adr, __u32 datum)
+{
+	int ret;
+	int try;
+	int i;
+	map_word data, status;
+
+	status.x[0] = 0;
+	ret = sharp_wait(map,chip);
+	if (ret < 0)
+		return ret;
+
+	for (try=0; try<10; try++) {
+		long timeo;
+
+		sharp_send_cmd(map, CMD_BYTE_WRITE, adr);
+		/* cpu_to_le32 -> hack to fix the writel be->le conversion */
+		data.x[0] = cpu_to_le32(datum);
+		map_write(map, data, adr);
+
+		chip->state = FL_WRITING;
+		timeo = jiffies + (HZ/2);
+
+		sharp_send_cmd(map, CMD_READ_STATUS, adr);
+		for(i=0;i<100;i++){
+			status = map_read(map, adr);
+			if((status.x[0] & SR_READY) == SR_READY)
+				break;
+		}
+#ifdef AUTOUNLOCK
+		if (status.x[0] & SR_PROTECT) { /* lock block */
+			sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
+			sharp_unlock_oneblock(map,chip,adr);
+			sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
+			sharp_send_cmd(map, CMD_RESET, adr);
+			continue;
+		}
+#endif
+		if(i==100){
+			printk("sharp: timed out writing\n");
+		}
+
+		if (!(status.x[0] & SR_ERRORS))
+			break;
+
+		printk("sharp: error writing byte at addr=%08lx status=%08lx\n", adr, status.x[0]);
+
+		sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
+	}
+	sharp_send_cmd(map, CMD_RESET, adr);
+	chip->state = FL_READY;
+
+	sharp_release(chip);
+
+	return 0;
+}
+
+static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	struct map_info *map = mtd->priv;
+	struct sharp_info *sharp = map->fldrv_priv;
+	unsigned long adr,len;
+	int chipnum, ret=0;
+
+	if(instr->addr & (mtd->erasesize - 1))
+		return -EINVAL;
+	if(instr->len & (mtd->erasesize - 1))
+		return -EINVAL;
+	if(instr->len + instr->addr > mtd->size)
+		return -EINVAL;
+
+	chipnum = instr->addr >> sharp->chipshift;
+	adr = instr->addr & ((1<<sharp->chipshift)-1);
+	len = instr->len;
+
+	while(len){
+		ret = sharp_erase_oneblock(map, &sharp->chips[chipnum], adr);
+		if(ret)return ret;
+
+		if (adr >= 0xfe0000) {
+			adr += mtd->erasesize / 8;
+			len -= mtd->erasesize / 8;
+		} else {
+			adr += mtd->erasesize;
+			len -= mtd->erasesize;
+		}
+		if(adr >> sharp->chipshift){
+			adr = 0;
+			chipnum++;
+			if(chipnum>=sharp->numchips)
+				break;
+		}
+	}
+
+	instr->state = MTD_ERASE_DONE;
+	mtd_erase_callback(instr);
+
+	return 0;
+}
+
+static inline int sharp_do_wait_for_ready(struct map_info *map, struct flchip *chip,
+	unsigned long adr)
+{
+	int ret;
+	unsigned long timeo;
+	map_word status;
+	DECLARE_WAITQUEUE(wait, current);
+
+	sharp_send_cmd(map, CMD_READ_STATUS, adr);
+	status = map_read(map, adr);
+
+	timeo = jiffies + HZ * 10;
+
+	while (time_before(jiffies, timeo)) {
+		sharp_send_cmd(map, CMD_READ_STATUS, adr);
+		status = map_read(map, adr);
+		if ((status.x[0] & SR_READY) == SR_READY) {
+			ret = 0;
+			goto out;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&chip->wq, &wait);
+
+		spin_unlock_bh(chip->mutex);
+
+		schedule_timeout(1);
+		schedule();
+
+		spin_lock_bh(chip->mutex);
+
+		remove_wait_queue(&chip->wq, &wait);
+		set_current_state(TASK_RUNNING);
+	}
+	ret = -ETIME;
+out:
+	return ret;
+}
+
+static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip,
+	unsigned long adr)
+{
+	int ret;
+	map_word status;
+
+	ret = sharp_wait(map,chip);
+	if (ret < 0)
+		return ret;
+
+#ifdef AUTOUNLOCK
+	/* This seems like a good place to do an unlock */
+	sharp_unlock_oneblock(map,chip,adr);
+#endif
+
+	sharp_send_cmd(map, CMD_BLOCK_ERASE_1, adr);
+	sharp_send_cmd(map, CMD_BLOCK_ERASE_2, adr);
+
+	chip->state = FL_ERASING;
+
+	ret = sharp_do_wait_for_ready(map,chip,adr);
+	if(ret<0) {
+		spin_unlock_bh(chip->mutex);
+		return ret;
+	}
+
+	sharp_send_cmd(map, CMD_READ_STATUS, adr);
+	status = map_read(map, adr);
+
+	if (!(status.x[0] & SR_ERRORS)) {
+		sharp_send_cmd(map, CMD_RESET, adr);
+		chip->state = FL_READY;
+		spin_unlock_bh(chip->mutex);
+		return 0;
+	}
+
+	printk("sharp: error erasing block at addr=%08lx status=%08lx\n", adr, status.x[0]);
+	sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
+
+	sharp_release(chip);
+
+	return -EIO;
+}
+
+#ifdef AUTOUNLOCK
+static inline void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip,
+	unsigned long adr)
+{
+	map_word status;
+
+	sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_1, adr & BLOCK_MASK);
+	sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_2, adr & BLOCK_MASK);
+
+	sharp_do_wait_for_ready(map,chip,adr);
+
+	status = map_read(map, adr);
+
+	if (!(status.x[0] & SR_ERRORS)) {
+		sharp_send_cmd(map, CMD_RESET, adr);
+		chip->state = FL_READY;
+		return;
+	}
+
+	printk("sharp: error unlocking block at addr=%08lx status=%08lx\n", adr, status.x[0]);
+	sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
+}
+#endif
+
+static void sharp_sync(struct mtd_info *mtd)
+{
+}
+
+static int sharp_suspend(struct mtd_info *mtd)
+{
+	struct map_info *map = mtd->priv;
+	struct sharp_info *sharp = map->fldrv_priv;
+	int i;
+	struct flchip *chip;
+	int ret = 0;
+
+	for (i = 0; !ret && i < sharp->numchips; i++) {
+		chip = &sharp->chips[i];
+		ret = sharp_wait(map,chip);
+
+		if (ret) {
+			ret = -EAGAIN;
+		} else {
+			chip->state = FL_PM_SUSPENDED;
+			spin_unlock_bh(chip->mutex);
+		}
+	}
+	return ret;
+}
+
+static void sharp_resume(struct mtd_info *mtd)
+{
+	struct map_info *map = mtd->priv;
+	struct sharp_info *sharp = map->fldrv_priv;
+	int i;
+	struct flchip *chip;
+
+	for (i = 0; i < sharp->numchips; i++) {
+		chip = &sharp->chips[i];
+
+		spin_lock_bh(chip->mutex);
+
+		if (chip->state == FL_PM_SUSPENDED) {
+			/* We need to force it back to a known state */
+			sharp_send_cmd(map, CMD_RESET, chip->start);
+			chip->state = FL_READY;
+			wake_up(&chip->wq);
+		}
+
+		spin_unlock_bh(chip->mutex);
+	}
+}
+
+static void sharp_destroy(struct mtd_info *mtd)
+{
+	struct map_info *map = mtd->priv;
+	struct sharp_info *sharp = map->fldrv_priv;
+
+	kfree(sharp);
+}
+
+static int __init sharp_probe_init(void)
+{
+	printk("MTD Sharp chip driver <ds@lineo.com>\n");
+
+	register_mtd_chip_driver(&sharp_chipdrv);
+
+	return 0;
+}
+
+static void __exit sharp_probe_exit(void)
+{
+	unregister_mtd_chip_driver(&sharp_chipdrv);
+}
+
+module_init(sharp_probe_init);
+module_exit(sharp_probe_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Schleef <ds@schleef.org>");
+MODULE_DESCRIPTION("Old MTD chip driver for pre-CFI Sharp flash chips");
Index: linux-2.6.26/drivers/mtd/maps/Kconfig
===================================================================
--- linux-2.6.26.orig/drivers/mtd/maps/Kconfig	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/drivers/mtd/maps/Kconfig	2008-10-17 18:15:31.431789839 +0200
@@ -392,7 +392,7 @@
 
 config MTD_SA1100
 	tristate "CFI Flash device mapped on StrongARM SA11x0"
-	depends on MTD_CFI && ARCH_SA1100 && MTD_PARTITIONS
+	depends on (MTD_CFI || MTD_SHARP) && ARCH_SA1100 && MTD_PARTITIONS
 	help
 	  This enables access to the flash chips on most platforms based on
 	  the SA1100 and SA1110, including the Assabet and the Compaq iPAQ.
Index: linux-2.6.26/drivers/mtd/maps/sa1100-flash.c
===================================================================
--- linux-2.6.26.orig/drivers/mtd/maps/sa1100-flash.c	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/drivers/mtd/maps/sa1100-flash.c	2008-10-17 18:15:31.431789839 +0200
@@ -210,6 +210,12 @@
 		goto err;
 	}
 	subdev->mtd->owner = THIS_MODULE;
+	
+#ifdef CONFIG_SA1100_COLLIE
+	/* collie flash starts locked */
+//	 if (subdev->mtd->unlock)
+//		 subdev->mtd->unlock(subdev->mtd, 0xc0000, subdev->mtd->size - 0xc0000);
+#endif
 
 	printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, "
 		"%d-bit\n", phys, subdev->mtd->size >> 20,
Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_cs.c
===================================================================
--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_cs.c	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/drivers/net/wireless/hostap/hostap_cs.c	2008-10-17 18:15:31.435790279 +0200
@@ -35,7 +35,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 +499,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;
 }
Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_hw.c
===================================================================
--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_hw.c	2008-10-17 18:13:21.000000000 +0200
+++ linux-2.6.26/drivers/net/wireless/hostap/hostap_hw.c	2008-10-17 18:15:31.443789719 +0200
@@ -54,6 +54,7 @@
 #include "hostap.h"
 #include "hostap_ap.h"
 
+extern int activar;
 
 /* #define final_version */
 
@@ -1534,6 +1535,8 @@
 	if (local->hw_downloading)
 		return 1;
 
+	activar=1;
+
 	if (prism2_hw_init(dev, initial)) {
 		return local->no_pri ? 0 : 1;
 	}
@@ -2665,8 +2668,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;
 
 	if(dev->base_addr == 0)
 	{
Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_pci.c
===================================================================
--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_pci.c	2008-10-17 18:13:18.000000000 +0200
+++ linux-2.6.26/drivers/net/wireless/hostap/hostap_pci.c	2008-10-17 18:15:31.447790279 +0200
@@ -19,6 +19,7 @@
 
 #include "hostap_wlan.h"
 
+int activar=1;
 
 static char *dev_info = "hostap_pci";
 
Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_plx.c
===================================================================
--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_plx.c	2008-10-17 18:13:18.000000000 +0200
+++ linux-2.6.26/drivers/net/wireless/hostap/hostap_plx.c	2008-10-17 18:15:31.451790719 +0200
@@ -21,7 +21,7 @@
 #include <asm/io.h>
 
 #include "hostap_wlan.h"
-
+int activar=1;
 
 static char *dev_info = "hostap_plx";
 
Index: linux-2.6.26/drivers/pcmcia/sa1100_generic.c
===================================================================
--- linux-2.6.26.orig/drivers/pcmcia/sa1100_generic.c	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/drivers/pcmcia/sa1100_generic.c	2008-10-17 18:15:31.459789719 +0200
@@ -81,13 +81,14 @@
 	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,
+	},
 };
 
 /* sa11x0_pcmcia_init()
@@ -100,7 +101,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 +111,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.26/drivers/spi/Kconfig
===================================================================
--- linux-2.6.26.orig/drivers/spi/Kconfig	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/drivers/spi/Kconfig	2008-10-17 18:15:31.463790519 +0200
@@ -123,6 +123,10 @@
 	  This enables using the Freescale MPC52xx Programmable Serial
 	  Controller in master SPI mode.
 
+config SPI_LOCOMO
+	tristate "Locomo SPI master"
+	depends on SPI_MASTER && SHARP_LOCOMO && EXPERIMENTAL
+
 config SPI_MPC83xx
 	tristate "Freescale MPC83xx/QUICC Engine SPI controller"
 	depends on SPI_MASTER && (PPC_83xx || QUICC_ENGINE) && EXPERIMENTAL
Index: linux-2.6.26/drivers/spi/Makefile
===================================================================
--- linux-2.6.26.orig/drivers/spi/Makefile	2008-07-13 23:51:29.000000000 +0200
+++ linux-2.6.26/drivers/spi/Makefile	2008-10-17 18:15:31.463790519 +0200
@@ -28,6 +28,7 @@
 obj-$(CONFIG_SPI_TXX9)			+= spi_txx9.o
 obj-$(CONFIG_SPI_XILINX)		+= xilinx_spi.o
 obj-$(CONFIG_SPI_SH_SCI)		+= spi_sh_sci.o
+obj-$(CONFIG_SPI_LOCOMO)                += locomo_spi.o
 # 	... add above this line ...
 
 # SPI protocol drivers (device/link on bus)
Index: linux-2.6.26/drivers/spi/locomo_spi.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.26/drivers/spi/locomo_spi.c	2008-10-17 18:15:31.471790439 +0200
@@ -0,0 +1,1097 @@
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <asm/hardware/locomo.h>
+#include <asm/errno.h>
+#include <linux/mmc/host.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include "locomo_spi.h"
+static struct locomospi_dev * spidev;
+static struct work_struct transfer_wq;
+int  delay;
+
+char* transtxbuf=(char*)NULL;
+char* transrxbuf=(char*)NULL;
+int transfercount=0, transfersize=0;
+static DECLARE_WAIT_QUEUE_HEAD(transferqueue);
+/* MMC_SPI functions *********************************************************/
+
+static int locomommcspi_init(struct device *dev, irqreturn_t (*isr)(int, void*), void *mmc)
+{
+	int result;
+	result=request_irq(IRQ_LOCOMO_CARDDETECT, isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "locomo-spi",  mmc);
+	return result;
+}
+
+static void locomommcspi_exit(struct device *dev, void* mmc)
+{
+	free_irq(IRQ_LOCOMO_CARDDETECT, mmc);
+}
+
+static int locomommcspi_getro(struct device *dev)
+{
+	return locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_WRITE_PROT) > 0 ? 1 : 0;
+}
+
+static void locomommcspi_setpower(struct device *dev, unsigned int mask)
+{
+	if(!mask && spidev->card_power)
+		locomospi_power(0);
+	else if( !spidev->card_power )
+		locomospi_power(1);
+
+}
+
+
+static struct mmc_spi_platform_data colliemmc ={
+	.init 		= locomommcspi_init,
+	.exit 		= locomommcspi_exit,
+	.detect_delay 	= 200,
+	.get_ro 	= locomommcspi_getro,
+	.ocr_mask 	= MMC_VDD_32_33 | MMC_VDD_33_34,
+	.setpower	= locomommcspi_setpower,
+	.powerup_msecs  = 200,
+};
+
+/* Utility function **********************************************************/
+
+static void locomospi_power(int on)
+{
+	locomo_gpio_write(spidev->ldev->dev.parent, LOCOMO_GPIO_CARD_POWER, on);
+	spidev->card_power=on;
+	printk(KERN_DEBUG "locomospi: power %d\n",on);
+}
+
+static void locomospi_setclock(unsigned int div, unsigned int clock)
+{
+	u16 r = ioread16(spidev->base+LOCOMO_SPIMD);
+	div &= 0x7;
+	clock &= 0x3;
+	if(clock != spidev->clock_base || div != spidev->clock_div){
+		r &= ~(LOCOMO_SPI_XSEL  | LOCOMO_SPI_CLKSEL | LOCOMO_SPI_XEN);
+		iowrite16(r,spidev->base+LOCOMO_SPIMD);
+		r |=  (div | (clock <<3) | LOCOMO_SPI_XEN);
+		iowrite16(r,spidev->base+LOCOMO_SPIMD);
+		spidev->clock_div = div;
+		spidev->clock_base = clock;
+		udelay(300);
+	}
+
+}
+// returns 1 if card ist present, 0 otherwise
+static int locomospi_carddetect()
+{
+	return (locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_CARD_DETECT)>0)?0:1;
+}
+
+static void locomospi_setcs(int high)
+{
+	u16 r;
+	printk(KERN_DEBUG "locomospi: cs %d\n",high);
+	r = ioread16(spidev->base + LOCOMO_SPICT);
+	if(high)
+		r |= LOCOMO_SPI_CS;
+	else
+		r &= ~LOCOMO_SPI_CS;
+	iowrite16(r, spidev->base + LOCOMO_SPICT);
+}
+
+static void locomospi_reg_open()
+{
+	u16 r;
+	spidev->clock_div = DIV_64;
+	spidev->clock_base = CLOCK_18MHZ;
+	locomospi_power(1);
+	msleep(100);
+//	iowrite16( 0xec00 | (CLOCK_18MHZ <<3)|DIV_64, spidev->base+LOCOMO_SPIMD);
+	iowrite16( LOCOMO_SPI_MSB1ST | LOCOMO_SPI_DOSTAT | LOCOMO_SPI_RCPOL | LOCOMO_SPI_TCPOL 
+			|(CLOCK_18MHZ <<3) | DIV_64, spidev->base+LOCOMO_SPIMD);
+//	if(locomospi_carddetect()){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16( r, spidev->base+LOCOMO_SPIMD);
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XEN;
+		iowrite16( r, spidev->base+LOCOMO_SPIMD);
+//	}
+	iowrite16( LOCOMO_SPI_CS, spidev->base+LOCOMO_SPICT);
+	r = ioread16(spidev->base+LOCOMO_SPICT);
+	r |= (LOCOMO_SPI_CEN | LOCOMO_SPI_RXUEN | LOCOMO_SPI_ALIGNEN);
+	iowrite16( r, spidev->base+LOCOMO_SPICT);
+	udelay(200);
+	r = ioread16(spidev->base+LOCOMO_SPICT);
+	iowrite16(r, spidev->base+LOCOMO_SPICT);
+	r = ioread16(spidev->base+LOCOMO_SPICT);
+	r &= ~LOCOMO_SPI_CS;
+	iowrite16(r, spidev->base+LOCOMO_SPICT);
+}
+
+static void locomospi_reg_release()
+{
+	u16 r;
+	r = ioread16(spidev->base+LOCOMO_SPICT);
+	r &= ~LOCOMO_SPI_CEN;
+	iowrite16(r, spidev->base+LOCOMO_SPICT);
+	r = ioread16(spidev->base+LOCOMO_SPIMD);
+	r &= ~LOCOMO_SPI_XEN;
+	iowrite16(r, spidev->base+LOCOMO_SPIMD);
+	r = ioread16(spidev->base+LOCOMO_SPIMD);
+	r &= ~LOCOMO_SPI_XON;
+	iowrite16(r, spidev->base+LOCOMO_SPIMD);
+	r = ioread16(spidev->base+LOCOMO_SPICT);
+	r |= LOCOMO_SPI_XEN;
+	iowrite16(r, spidev->base+LOCOMO_SPICT);
+	locomospi_power(0);
+}
+#if 0
+static int txrx(const char* txbuffer, char* rxbuffer, int size)
+{
+	u16 r = ioread16(spidev->base+LOCOMO_SPICT);
+	r |= LOCOMO_SPI_ALIGNEN;
+	iowrite16(r, spidev->base+LOCOMO_SPICT);
+	printk(KERN_DEBUG "locomospi: %d bytes to prozess\n",size);
+	/* initialize global vars for isr */
+	transfercount=0; transfersize=size; 
+	transtxbuf=txbuffer; transrxbuf=rxbuffer;
+
+	/* start transmit and go sleep  isr will wake us*/
+	enable_irq(IRQ_LOCOMO_SPI_TEND);
+	iowrite8(txbuffer[0], spidev->base+LOCOMO_SPITD);
+	wait_event(transferqueue, transfercount >= transfersize);
+	disable_irq(IRQ_LOCOMO_SPI_TEND);
+	transrxbuf=NULL; transtxbuf=NULL;
+	
+	r = ioread16(spidev->base+LOCOMO_SPICT);
+	r &= ~LOCOMO_SPI_ALIGNEN;
+	iowrite16(r, spidev->base+LOCOMO_SPICT);
+	int i;
+	for(i=0; i< size; i++)
+		printk(KERN_DEBUG "locomospi: sent: %x  received: %x \n",txbuffer[i], rxbuffer[i]);
+	
+	
+	return size;
+}
+
+
+static int tx(const char* txbuffer, int size)
+{
+	printk(KERN_DEBUG "locomospi: %d bytes to send\n",size);
+	/* initialize global vars for isr */
+	transfercount=0; transfersize=size; 
+	transtxbuf=txbuffer; 
+
+	/* start transmit and go sleep  isr will wake us*/
+	enable_irq(IRQ_LOCOMO_SPI_RFW);
+	iowrite8(txbuffer[0], spidev->base+LOCOMO_SPITD);
+	wait_event(transferqueue, transfercount >= transfersize);
+	disable_irq(IRQ_LOCOMO_SPI_RFW);
+	transtxbuf=NULL;
+	
+	int i;
+	for(i=0; i< size; i++)
+		printk(KERN_DEBUG "locomospi: sent: %x\n",txbuffer[i]);
+	
+	
+	return size;
+}
+
+static int rx(char* rxbuffer, int size)
+{
+	printk(KERN_DEBUG "locomospi: %d bytes to read\n",size);
+	/* initialize global vars for isr */
+	transfercount=0; transfersize=size; 
+	transrxbuf=rxbuffer;
+
+	/* start transmit and go sleep  isr will wake us*/
+	enable_irq(IRQ_LOCOMO_SPI_RFR);
+	rxbuffer[0]=ioread8(spidev->base+LOCOMO_SPIRD);
+	wait_event(transferqueue, transfercount >= transfersize);
+	disable_irq(IRQ_LOCOMO_SPI_RFR);
+	transrxbuf=NULL;
+	
+	int i;
+	for(i=0; i< size; i++)
+		printk(KERN_DEBUG "locomospi: received: %x \n", rxbuffer[i]);
+	
+	
+	return size;
+}
+
+#else
+static int txrx(const char* txbuffer, char* rxbuffer, int size)
+{
+	int i=0,j=0;
+	int wait;
+	u16 r;
+/*	char * txback = kmalloc(size * sizeof(char), GFP_KERNEL);
+	memcpy(txback, txbuffer, size);	
+*/	
+	if(spidev->clock_div == 4)
+		wait = 0x10000;
+	else
+		wait = 8;
+	
+//	printk(KERN_DEBUG "locomospi: txrx %d bytes to prozess\n",size);
+	
+//	r = ioread16(spidev->base+LOCOMO_SPICT);
+//	r |= LOCOMO_SPI_ALIGNEN;
+//	iowrite16(r, spidev->base+LOCOMO_SPICT);
+	//discard first bogus byte
+
+	ioread8(spidev->base+LOCOMO_SPIRD);
+	for(i=0; i<size; i++){
+		for(j=0; j <= wait; j++){
+			if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFW)
+				break;
+		}
+		iowrite8(txbuffer[i], spidev->base+LOCOMO_SPITD);
+		ndelay(delay);
+		
+		for(j=0; j <= wait; j++){
+			if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFR)
+				break;
+		}
+		rxbuffer[i] = ioread8(spidev->base+LOCOMO_SPIRD);
+		ndelay(delay);
+	}
+//	r = ioread16(spidev->base+LOCOMO_SPICT);
+//	r &= ~LOCOMO_SPI_ALIGNEN;
+//	iowrite16(r, spidev->base+LOCOMO_SPICT);
+	
+/*	for(j=0; j< size; j++)
+		printk(KERN_DEBUG "locomospi: sent: %x  received: %x \n",txback[j], rxbuffer[j]);
+	
+	kfree(txback);	
+*/	return i;
+}
+
+static int tx(const char* buffer, int size)
+{
+	int i=0,j=0;
+	int wait;
+	u16 r;
+	if(spidev->clock_div == 4)
+		wait = 0x10000;
+	else
+		wait = 8;
+	r = ioread16(spidev->base+LOCOMO_SPICT);
+	r &= ~LOCOMO_SPI_ALIGNEN;
+	iowrite16(r, spidev->base+LOCOMO_SPICT);
+
+//	printk(KERN_DEBUG "locomospi: tx %d bytes to transmit\n",size);
+	for(i=0; i<size; i++){
+		for(j=0; j <= wait; j++){
+			if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFW)
+				break;
+		}
+		iowrite8(buffer[i], spidev->base+LOCOMO_SPITD);
+		ndelay(delay);
+	}
+
+	for(j=0; j <= wait; j++){
+		if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_TEND)
+			break;
+	}
+	
+	r = ioread16(spidev->base+LOCOMO_SPICT);
+	r |= LOCOMO_SPI_ALIGNEN;
+	iowrite16(r, spidev->base+LOCOMO_SPICT);
+	
+//	for(j=0; j< size; j++)
+//		printk(KERN_DEBUG "locomospi: sent: %x \n", buffer[j]);
+//	printk(KERN_DEBUG "locomospi: tx %d bytes transmitted\n",i);
+	return i;
+}
+
+static int rx(char* buffer, int size)
+{
+	int i,j;
+	int wait;
+	u16 r; 	
+	printk(KERN_DEBUG "locomospi: rx %d bytes to receive\n",size);
+	if(spidev->clock_div == 4)
+		wait = 0x10000;
+	else
+		wait = 8;
+	r = ioread16(spidev->base+LOCOMO_SPICT);
+	r &= ~LOCOMO_SPI_ALIGNEN;
+	iowrite16(r, spidev->base+LOCOMO_SPICT);
+
+	for(i=0; i<size; i++){
+
+		for(j=0; j <= wait; j++){
+			if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFR)
+				break;
+		}
+		buffer[i]= ioread8(spidev->base+LOCOMO_SPIRD);
+		ndelay(delay);
+	}
+	
+	r = ioread16(spidev->base+LOCOMO_SPICT);
+	r |= LOCOMO_SPI_ALIGNEN;
+	iowrite16(r, spidev->base+LOCOMO_SPICT);
+	
+	for(j=0; j< size; j++)
+		printk(KERN_DEBUG "locomospi: received: %x \n", buffer[j]);
+	printk(KERN_DEBUG "locomospi: rx %d bytes received\n",i);
+	return i;
+}
+#endif
+/*
+static irqreturn_t locomospi_rwready(int irq, void *dev_id)
+{
+	struct locomospi_dev* dev=(struct locomospi_dev*) dev_id;
+//	dev_dbg(&spidev->sdev->dev, "IRQ: %d\n", irq);
+//	printk(KERN_DEBUG "locomospi: IRQ: %d\n", irq);
+	wake_up_interruptible(&dev->waitqueue);
+	return IRQ_HANDLED;
+}
+*/
+static irqreturn_t locomospi_testisr(int irq, void *dev_id)
+{
+	char *buf="";
+	switch(irq){
+		case IRQ_LOCOMO_SPI_RFR: buf="RFR";
+					 break;
+		case IRQ_LOCOMO_SPI_RFW: buf="RFW";
+					 break;
+		case IRQ_LOCOMO_SPI_REND:buf="REND";
+					 break;
+		case IRQ_LOCOMO_SPI_TEND:buf="TEND";
+					 break;
+		case IRQ_LOCOMO_CARDDETECT:
+					 buf="CARD_DETECT";
+					 break;
+		default:		return IRQ_NONE;
+	}
+	printk(KERN_DEBUG "locomospi: IRQ: %s\n",buf);
+//	dev_dbg(&spidev->sdev->dev, "IRQ: %s\n",buf);
+	return IRQ_HANDLED;
+}
+static irqreturn_t locomospi_txrxisr(int irq, void *dev_id)
+{
+	if(transfercount < transfersize){
+		transrxbuf[transfercount++] = ioread8(spidev->base+LOCOMO_SPIRD);
+		iowrite8(transtxbuf[transfercount], spidev->base+LOCOMO_SPITD); 		
+	}
+	else{
+		/* transfer complete. wake up txrx */
+		wake_up(&transferqueue);
+	}
+	return IRQ_HANDLED;	
+}	
+
+static irqreturn_t locomospi_txisr(int irq, void *dev_id)
+{
+	if(transfercount < transfersize){
+		iowrite8(transtxbuf[transfercount++], spidev->base+LOCOMO_SPITD); 		
+	}
+	else{
+		/* transfer complete. wake up txrx */
+		wake_up(&transferqueue);
+	}
+	return IRQ_HANDLED;	
+}	
+
+static irqreturn_t locomospi_rxisr(int irq, void *dev_id)
+{
+	if(transfercount < transfersize){
+		transrxbuf[transfercount++] = ioread8(spidev->base+LOCOMO_SPIRD);
+	}
+	else{
+		/* transfer complete. wake up txrx */
+		wake_up(&transferqueue);
+	}
+	return IRQ_HANDLED;	
+}	
+
+static void locomospi_clock(unsigned int Hz)
+{
+	u16 r;
+	printk(KERN_DEBUG "locomospi: changing clock to: %d\n", Hz);
+	if(Hz == 0){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r &= ~LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+	}
+	else if(Hz >= 24576000){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_1, CLOCK_25MHZ);
+		delay=41;
+	}
+	else if(Hz >= 22579200){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_1, CLOCK_22MHZ);
+		delay=45;
+	}
+	else if(Hz >= 18432000){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_1, CLOCK_18MHZ);
+		delay=55;
+	}
+	else if(Hz >= 12288000){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_2, CLOCK_25MHZ);
+		delay=82;
+	}
+	else if(Hz >= 11289600){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_2, CLOCK_22MHZ);
+		delay=89;
+	}
+	else if(Hz >= 9216000){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_2, CLOCK_18MHZ);
+		delay=110;
+	}
+	else if(Hz >= 6144000){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_4, CLOCK_25MHZ);
+		delay=164;
+	}
+	else if(Hz >= 5644800){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_4, CLOCK_22MHZ);
+		delay=178;
+	}
+	else if(Hz >= 4608000){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_4, CLOCK_18MHZ);
+		delay=218;
+	}
+	else if(Hz >= 3072000){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_8, CLOCK_25MHZ);
+		delay=327;
+	}
+	else if(Hz >= 2822400){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_8, CLOCK_22MHZ);
+		delay=355;
+	}
+	else if(Hz >= 2304000){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_8, CLOCK_18MHZ);
+		delay=435;
+	}
+	else if(Hz >= 384000){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_64, CLOCK_25MHZ);
+		delay=2605;
+	}
+	else if(Hz >= 352800){
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_64, CLOCK_22MHZ);
+		delay=2834;
+	}
+	else{ /* set to 288 KHz */
+		r = ioread16(spidev->base+LOCOMO_SPIMD);
+		r |= LOCOMO_SPI_XON;
+		iowrite16(r, spidev->base+LOCOMO_SPIMD);
+		locomospi_setclock(DIV_64, CLOCK_18MHZ);
+		delay=3473;
+	}
+	spidev->clock = Hz;
+}
+
+/* sysfs attributes used for debug *******************************************/
+
+/* SPI registers */
+ssize_t locomospi_showspimd(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIMD));
+}
+
+ssize_t locomospi_storespimd(struct device_driver *drv, const char *buf, size_t count)
+{
+	iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIMD);
+	return count;
+}
+static DRIVER_ATTR(spimd, S_IWUSR | S_IRUGO, locomospi_showspimd, locomospi_storespimd);
+
+ssize_t locomospi_showspict(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPICT));
+}
+
+ssize_t locomospi_storespict(struct device_driver *drv, const char *buf, size_t count)
+{
+	iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPICT);
+	return count;
+}
+static DRIVER_ATTR(spict, S_IWUSR | S_IRUGO, locomospi_showspict, locomospi_storespict);
+
+ssize_t locomospi_showspist(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIST));
+}
+
+ssize_t locomospi_storespist(struct device_driver *drv, const char *buf, size_t count)
+{
+	iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIST);
+	return count;
+}
+static DRIVER_ATTR(spist, S_IWUSR | S_IRUGO, locomospi_showspist, locomospi_storespist);
+
+ssize_t locomospi_showspitd(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPITD));
+}
+
+ssize_t locomospi_storespitd(struct device_driver *drv, const char *buf, size_t count)
+{
+	iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPITD);
+	return count;
+}
+static DRIVER_ATTR(spitd, S_IWUSR | S_IRUGO, locomospi_showspitd, locomospi_storespitd);
+
+ssize_t locomospi_showspird(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIRD));
+}
+
+ssize_t locomospi_storespird(struct device_driver *drv, const char *buf, size_t count)
+{
+	iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIRD);
+	return count;
+}
+static DRIVER_ATTR(spird, S_IWUSR | S_IRUGO, locomospi_showspird, locomospi_storespird);
+
+ssize_t locomospi_showspits(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPITS));
+}
+
+ssize_t locomospi_storespits(struct device_driver *drv, const char *buf, size_t count)
+{
+	iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPITS);
+	return count;
+}
+static DRIVER_ATTR(spits, S_IWUSR | S_IRUGO, locomospi_showspits, locomospi_storespits);
+
+ssize_t locomospi_showspirs(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIRS));
+}
+
+ssize_t locomospi_storespirs(struct device_driver *drv, const char *buf, size_t count)
+{
+	iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIRS);
+	return count;
+}
+static DRIVER_ATTR(spirs, S_IWUSR | S_IRUGO, locomospi_showspirs, locomospi_storespirs);
+
+/* MMC Card status */
+
+ssize_t locomospi_showpower(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%d\n", spidev->card_power);
+}
+
+ssize_t locomospi_storepower(struct device_driver *drv, const char *buf, size_t count)
+{
+	locomospi_power(simple_strtoul(buf, NULL, 10));
+	return count;
+}
+static DRIVER_ATTR(cardpower, S_IWUSR | S_IRUGO, locomospi_showpower, locomospi_storepower);
+
+ssize_t locomospi_detectcard(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%d\n",(locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_CARD_DETECT)>0)?0:1);
+}
+static DRIVER_ATTR(carddetect, S_IRUGO, locomospi_detectcard, NULL);
+
+ssize_t locomospi_writeprotect(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%d\n",(locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_WRITE_PROT)>0)?1:0);
+}
+static DRIVER_ATTR(cardwriteprotect, S_IRUGO, locomospi_writeprotect, NULL);
+
+
+ssize_t locomospi_showclock(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%d\n", spidev->clock);
+}
+
+ssize_t locomospi_storeclock(struct device_driver *drv, const char *buf, size_t count)
+{
+	locomospi_clock(simple_strtoul(buf, NULL, 10));
+	return count;
+}
+static DRIVER_ATTR(clock, S_IWUSR | S_IRUGO, locomospi_showclock, locomospi_storeclock);
+
+/* debug */
+ssize_t locomospi_showdelay(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "%d\n", delay);
+}
+
+ssize_t locomospi_storedelay(struct device_driver *drv, const char *buf, size_t count)
+{
+	delay=simple_strtoul(buf,NULL,10);
+	return count;
+}
+static DRIVER_ATTR(delay, S_IWUSR | S_IRUGO, locomospi_showdelay, locomospi_storedelay);
+
+ssize_t locomospi_reset(struct device_driver *drv, const char *buf, size_t count)
+{
+	int choice = simple_strtoul(buf, NULL, 10);
+	char   buff[100];
+	u16 r; 
+	switch(choice){
+		case 0: locomospi_reg_release();
+			schedule_timeout(2*HZ);
+			locomospi_reg_open();
+			break;
+		case 1: {
+				char b1[] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
+				char b2[] = "\xff\x40\x00\x00\x00\x00\x95\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
+				locomospi_setcs(1);
+				txrx(b1,b1,17);
+				locomospi_setcs(0);
+				txrx(b2,b2,18);
+
+			}
+			break;
+		case 2: locomospi_setcs(1);
+			txrx("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",buff,18);
+			locomospi_setcs(0);
+			txrx("\xff\x40\x00\x00\x00\x00\x95\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",buff,17);
+			break;
+		case 3: 
+			r = ioread16(spidev->base+LOCOMO_SPIMD);
+			r |= LOCOMO_SPI_LOOPBACK;
+			iowrite16(r, spidev->base+LOCOMO_SPIMD);
+			txrx("X",buff,1);
+			txrx("abcdefghijklmnopqrstuvwxyz1234567890",buff,36);
+			txrx("Y",buff,1);
+			udelay(100);
+			txrx("Z",buff,1);
+			schedule_timeout(HZ);	
+			txrx("abcdefghijklmnopqrstuvwxyz1234567890",buff,36);
+			
+			r = ioread16(spidev->base+LOCOMO_SPIMD);
+			r &= ~LOCOMO_SPI_LOOPBACK;
+			iowrite16(r, spidev->base+LOCOMO_SPIMD);
+			break;
+		default: /* do nothing */;
+	}
+	return count;
+}
+static DRIVER_ATTR(reset, S_IWUSR, NULL, locomospi_reset);
+
+typedef struct locomo_reg_entry {
+        u32 addr;
+        char* name;
+} locomo_reg_entry_t;
+#define LCM     (sizeof(locomo_regs)/sizeof(locomo_reg_entry_t))
+static locomo_reg_entry_t locomo_regs[] =
+{
+/*      { addr,    name,     description } */
+        { 0x00, "VER" },
+        { 0x04, "ST" },
+        { 0x08, "C32K" },
+        { 0x0C, "ICR" },
+        { 0x10, "MCSX0" },
+        { 0x14, "MCSX1" },
+        { 0x18, "MCSX2" },
+        { 0x1C, "MCSX3" },
+        { 0x20, "ASD" },
+        { 0x28, "HSD" },
+        { 0x2C, "HSC" },
+        { 0x30, "TADC" },
+        { 0x38, "TC" },
+        { 0x3C, "CPSD" },
+        { 0x40, "KIB" },
+        { 0x44, "KSC" },
+        { 0x48, "KCMD" },
+        { 0x4C, "KIC" },
+        { 0x54, "ACC" },
+        { 0x60, "SPIMD" },
+        { 0x64, "SPICT" },
+        { 0x68, "SPIST" },
+        { 0x70, "SPIIS" },
+        { 0x74, "SPIWE" },
+        { 0x78, "SPIIE" },
+        { 0x7C, "SPIIR" },
+        { 0x80, "SPITD" },
+        { 0x84, "SPIRD" },
+        { 0x88, "SPITS" },
+        { 0x8C, "SPIRS" },
+        { 0x90, "GPD" },
+        { 0x94, "GPE" },
+        { 0x98, "GPL" },
+        { 0x9C, "GPO" },
+        { 0xa0, "GRIE" },
+        { 0xa4, "GFIE" },
+        { 0xa8, "GIS" },
+        { 0xac, "GWE" },
+        { 0xb0, "GIE" },
+        { 0xb4, "GIR" },
+        { 0xc8, "ALC" },
+        { 0xcc, "ALR" },
+        { 0xd0, "PAIF" },
+        { 0xd8, "LTC" },
+        { 0xdc, "LTINT" },
+        { 0xe0, "DAC" },
+        { 0xe8, "LPT0" },
+        { 0xec, "LPT1" },
+        { 0xfc, "TCR" },
+};
+
+static ssize_t lcm_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int base = spidev->base - LOCOMO_SPI; 
+	char b[4000]="";
+	char c[30];
+	int i;
+	for(i=0; i<LCM; i++){
+		sprintf(c,"%s:\t\t 0x%x\n",locomo_regs[i].name, ioread16(base + locomo_regs[i].addr));
+		strcat(b,c);
+	}
+	return sprintf(buf,"%s",b);
+}
+
+static DRIVER_ATTR(regs, 0444, lcm_show, NULL);
+
+
+/* SPI functions *************************************************************/
+
+static void locomospi_do_transfer(struct work_struct *wrk)
+{
+	struct list_head *mptr, *tptr, *mptr2;
+	struct spi_transfer *entry;
+	struct spi_message *msg;
+
+	list_for_each_safe(mptr, mptr2, &spidev->message_list){
+		msg = list_entry(mptr, struct spi_message, queue);
+
+		msg->status = 0;
+		msg->actual_length = 0;
+		list_for_each(tptr, &msg->transfers){
+			entry = list_entry(tptr, struct spi_transfer, transfer_list);
+			if(entry->tx_buf && entry->rx_buf){ //duplex
+				txrx((char*) entry->tx_buf, (char*) entry->rx_buf, entry->len);				
+				msg->actual_length += entry->len;
+			} else if(entry->tx_buf && !entry->rx_buf){ //write
+				tx((char*) entry->tx_buf, entry->len);
+				msg->actual_length += entry->len;
+			} else if(!entry->tx_buf && entry->rx_buf){ //read
+				rx((char*) entry->rx_buf, entry->len);
+				msg->actual_length += entry->len;
+			} else if(!entry->tx_buf && !entry->rx_buf){ //error
+				dev_err(&spidev->sdev->dev, "do_transfer: no buffers allocated\n");
+				msg->status = -EFAULT;
+			}
+		}
+		spin_lock(&spidev->message_lock);
+		list_del(mptr);
+		spin_unlock(&spidev->message_lock);
+		msg->complete(msg->context);
+	}
+}
+
+static int locomospi_setup(struct spi_device *spi)
+{
+	if((spi->mode & SPI_CS_HIGH) != (spidev->spimode & SPI_CS_HIGH)) 
+		locomospi_setcs(spi->mode & SPI_CS_HIGH ? 1 : 0 );
+	if(spidev->clock != spi->max_speed_hz){
+		locomospi_clock(spi->max_speed_hz);
+	}
+	spidev->spimode = spi->mode;
+
+	return 0;
+}
+
+static int locomospi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+
+	spin_lock(&spidev->message_lock);
+	list_add_tail(&msg->queue, &spidev->message_list);
+	spin_unlock(&spidev->message_lock);
+	schedule_work(&transfer_wq);
+	return 0;
+}
+
+static struct locomo_driver locomo_spi_driver = {
+	.drv = {
+		.name = "locomo-spi",
+	},
+	.devid	= LOCOMO_DEVID_SPI,
+	.probe	= locomospi_probe,
+	.remove	= locomospi_remove,
+#ifdef CONFIG_PM
+	.suspend = locomospi_suspend,
+	.resume = locomospi_resume,
+#endif
+};
+
+static struct spi_board_info board = {
+	.modalias 	= "mmc_spi",
+	.platform_data 	= (void*) &colliemmc,
+	.controller_data= NULL,
+	.irq		= 0,
+	.max_speed_hz	= 25000000,
+	.bus_num	= 0,
+	.chip_select	= 0,
+	.mode		= 0,
+};
+
+#ifdef CONFIG_PM
+static int locomospi_suspend(struct locomo_dev *dev, pm_message_t state)
+{
+	disable_irq(IRQ_LOCOMO_CARDDETECT);
+	return 0;
+}
+
+static int locomospi_resume(struct locomo_dev *dev)
+{
+	enable_irq(IRQ_LOCOMO_CARDDETECT);
+	return 0;
+}
+#endif
+
+static int locomospi_probe(struct locomo_dev *dev)
+{
+	int result=0;
+	printk(KERN_DEBUG "Collie MMC over SPI Driver\n");
+	spidev=kmalloc(sizeof(struct locomospi_dev),GFP_KERNEL);
+	if(!spidev){
+		return -ENOMEM;
+	}
+	spidev->ldev = dev;
+	spidev->card_power = 1;
+	spidev->spimode = 0;
+
+	if(!request_mem_region((unsigned long) dev->mapbase, dev->length, LOCOMO_DRIVER_NAME(dev))) {
+		dev_err(&dev->dev, " Can't aquire access to io memory\n");
+		return -EBUSY;
+	}
+	spidev->base=(unsigned long) dev->mapbase;
+	locomospi_reg_open();
+
+	locomo_gpio_set_dir(dev->dev.parent, LOCOMO_GPIO_CARD_POWER, 0);
+	locomo_gpio_set_dir(dev->dev.parent, LOCOMO_GPIO_CARD_DETECT, 1);
+	locomo_gpio_set_dir(dev->dev.parent, LOCOMO_GPIO_WRITE_PROT, 1);
+
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_cardpower);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_carddetect);
+	if(result){
+		dev_err(&dev->dev,"error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_cardwriteprotect);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spimd);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spict);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spist);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spitd);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spird);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spits);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spirs);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_clock);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_delay);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_reset);
+	if(result){
+		dev_err(&dev->dev, "error creating driver attribute\n");
+		goto region;
+	}
+	result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_regs);
+        if(result){
+                dev_err(&dev->dev, "error creating driver attribute\n");
+                goto region;
+        }
+	INIT_WORK(&transfer_wq, locomospi_do_transfer);
+	INIT_LIST_HEAD(&spidev->message_list);
+	spin_lock_init(&spidev->message_lock);
+	init_waitqueue_head(&spidev->waitqueue);
+	spidev->master=spi_alloc_master(&dev->dev,0);
+	if(!spidev->master){
+		result=-ENOMEM;
+		goto region;
+	}
+	spidev->master->bus_num = 0;
+	spidev->master->num_chipselect = 1;
+	spidev->master->setup = locomospi_setup;
+	spidev->master->transfer = locomospi_transfer;
+	spidev->sdev = spi_new_device(spidev->master, &board);
+	if(!spidev->sdev){
+		dev_err(&dev->dev, "failed to register spi device\n");
+		result = -EINVAL;
+		goto master;
+	}
+/*	result=request_irq(IRQ_LOCOMO_SPI_RFR, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev);
+	if(result) {
+		dev_err(&dev->dev, "Could not get IRQ: RFR\n");
+		goto regdev;
+	}
+	//disable_irq(IRQ_LOCOMO_SPI_RFR);
+*//*	result=request_irq(IRQ_LOCOMO_SPI_RFW, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev);
+	if(result) {
+		dev_err(&dev->dev, "Could not get IRQ: RFW\n");
+		goto irq1;
+	}
+	//disable_irq(IRQ_LOCOMO_SPI_RFW);
+*//*	result=request_irq(IRQ_LOCOMO_SPI_REND, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev);
+	if(result) {
+		dev_err(&dev->dev, "Could not get IRQ: REND\n");
+		goto irq2;
+	}
+*//*	result=request_irq(IRQ_LOCOMO_SPI_TEND, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev);
+	if(result) {
+		dev_err(&dev->dev, "Could not get IRQ: TEND\n");
+		goto irq3;
+	}
+	//disable_irq(IRQ_LOCOMO_SPI_TEND);	
+*/	spidev->workqueue = create_singlethread_workqueue("locomo-spi");
+	if(!spidev->workqueue){
+		dev_err(&dev->dev, "failed to create workqueue\n");
+		goto irq4;
+	}
+	result=spi_register_master(spidev->master);
+	if(result){
+		dev_err(&dev->dev, "failed to register spimaster\n");
+		goto wq;
+	}
+	return 0;
+wq:
+	destroy_workqueue(spidev->workqueue);
+irq4:
+//	free_irq(IRQ_LOCOMO_SPI_TEND, (void*) spidev);
+irq3:
+//	free_irq(IRQ_LOCOMO_SPI_REND, (void*) spidev);
+irq2:
+//	free_irq(IRQ_LOCOMO_SPI_RFW, (void*) spidev);
+irq1:
+//	free_irq(IRQ_LOCOMO_SPI_RFR, (void*) spidev);
+regdev:
+	spi_unregister_device(spidev->sdev);
+master:
+	spi_master_put(spidev->master);
+region:
+	release_mem_region((unsigned long) dev->mapbase, dev->length);
+	kfree(spidev);
+	return result;
+
+}
+
+static int locomospi_remove(struct locomo_dev *dev)
+{
+	spi_unregister_device(spidev->sdev);
+	spi_unregister_master(spidev->master);
+	destroy_workqueue(spidev->workqueue);
+	locomospi_reg_release();
+//	free_irq(IRQ_LOCOMO_SPI_TEND, (void*) spidev);
+//	free_irq(IRQ_LOCOMO_SPI_REND, (void*) spidev);
+//	free_irq(IRQ_LOCOMO_SPI_RFW, (void*) spidev);
+//	free_irq(IRQ_LOCOMO_SPI_RFR, (void*) spidev);
+	spi_master_put(spidev->master);
+	release_mem_region((unsigned long) dev->mapbase, dev->length);
+	kfree(spidev);
+	return 0;
+}
+
+
+
+static int __init locomospi_init(void)
+{
+	int ret = locomo_driver_register(&locomo_spi_driver);
+	if (ret)
+		return ret;
+
+
+	return 0;
+}
+
+static void __exit locomospi_exit(void)
+{
+	locomo_driver_unregister(&locomo_spi_driver);
+}
+
+module_init(locomospi_init);
+module_exit(locomospi_exit);
+
+MODULE_AUTHOR("Thomas Kunze thommy@tabao.de");
+MODULE_DESCRIPTION("Collie mmc driver");
+MODULE_LICENSE("GPL");
Index: linux-2.6.26/drivers/spi/locomo_spi.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.26/drivers/spi/locomo_spi.h	2008-10-17 18:15:31.471790439 +0200
@@ -0,0 +1,75 @@
+#include <asm/hardware/locomo.h>
+#ifndef __LOCOMO_SPI_H__
+#define __LOCOMO_SPI_H__
+
+/* locomo-spi status register LOCOMO_SPIST */
+#define LOCOMO_SPI_TEND (1 << 3)        /* Transfer end bit */
+#define LOCOMO_SPI_REND (1 << 2)        /* Receive end bit */
+#define LOCOMO_SPI_RFW  (1 << 1)        /* write buffer bit */
+#define LOCOMO_SPI_RFR  (1)             /* read buffer bit */
+
+/* locomo-spi mode register LOCOMO_SPIMD */
+#define LOCOMO_SPI_LOOPBACK (1 << 15)	/* loopback tx to rx 	*/
+#define LOCOMO_SPI_MSB1ST   (1 << 14)	/* send MSB first 	*/
+#define LOCOMO_SPI_DOSTAT   (1 << 13)	/* transmit line is idle high */
+#define LOCOMO_SPI_TCPOL    (1 << 11) 	/* transmit CPOL (maybe affects CPHA too) */
+#define LOCOMO_SPI_RCPOL    (1 << 10)	/* receive CPOL (maybe affects CPHA too) */
+#define	LOCOMO_SPI_TDINV    (1 << 9)	/* invert transmit line */
+#define LOCOMO_SPI_RDINV    (1 << 8)	/* invert receive line */
+#define LOCOMO_SPI_XON	    (1 << 7)	/* enable spi controller clock */
+#define LOCOMO_SPI_XEN	    (1 << 6)	/* clock bit write enable xon must be off, wait 300 us before xon->1 */ 
+#define LOCOMO_SPI_XSEL	    0x0018 	/* clock select 		*/
+#define CLOCK_18MHZ	    0		/* 18,432 MHz clock 	*/
+#define CLOCK_22MHZ	    1		/* 22,5792 MHz clock	*/
+#define CLOCK_25MHZ	    2		/* 24,576 MHz clock	*/
+#define LOCOMO_SPI_CLKSEL   0x7	
+#define DIV_1		    0		/* don't divide clock   */
+#define DIV_2		    1		/* divide clock by two	*/
+#define DIV_4		    2		/* divide clock by four */
+#define DIV_8		    3		/* divide clock by eight*/
+#define DIV_64		    4		/* divide clock by 64 */
+
+/* locomo-spi control register LOCOMO_SPICT */
+#define LOCOMO_SPI_CRC16_7_B 	(1 << 15) 	/* 0: crc16 1: crc7 */
+#define LOCOMO_SPI_CRCRX_TX_B	(1 << 14)
+#define LOCOMO_SPI_CRCRESET_B	(1 << 13)
+#define LOCOMO_SPI_CEN		(1 << 7)	/* ?? enable */
+#define LOCOMO_SPI_CS		(1 << 6)	/* chip select */
+#define LOCOMO_SPI_UNIT16	(1 << 5) 	/* 0: 8 bit units, 1: 16 bit unit */
+#define LOCOMO_SPI_ALIGNEN	(1 << 2)	/* align transfer enable */
+#define LOCOMO_SPI_RXWEN	(1 << 1)	/* continous receive */
+#define LOCOMO_SPI_RXUEN	(1 << 0)	/* aligned receive */
+
+#define IRQ_LOCOMO_CARDDETECT	IRQ_LOCOMO_GPIO13
+
+
+struct locomospi_dev {
+	struct locomo_dev *ldev;
+	struct spi_master *master;
+	struct spi_device *sdev;
+	int card_power;
+	int clock_base;
+	int clock_div;
+	int clock;
+	unsigned long base;
+	u8 spimode;
+	wait_queue_head_t waitqueue;
+	struct workqueue_struct *workqueue;
+	struct list_head message_list;
+	spinlock_t message_lock;
+};
+
+
+static irqreturn_t 	locomospi_cardisr(int, void*);
+static int 	locomospi_probe(struct locomo_dev*);
+static int 	locomospi_remove(struct locomo_dev*);
+static int 	locomospi_carddetect(void);
+static void 	locomospi_reg_open(void);
+static void	locomospi_reg_release(void);
+static int 	tx(const char*, int);
+static int 	rx(char *, int);
+static void 	locomospi_power(int on);
+static int locomospi_suspend(struct locomo_dev *dev, pm_message_t state);
+static int locomospi_resume(struct locomo_dev *dev);
+static void locomospi_setcs(int high);
+#endif