--- linux-2.6.5/kernel/printk.c~heh	2004-04-03 22:38:24.000000000 -0500
+++ linux-2.6.5/kernel/printk.c	2004-04-30 20:57:36.000000000 -0400
@@ -832,3 +832,25 @@
 				printk_ratelimit_burst);
 }
 EXPORT_SYMBOL(printk_ratelimit);
+
+#include <linux/sysrq.h>
+
+static void
+show_msg_info(int key, struct pt_regs *regs, struct tty_struct *tty)
+{
+	call_console_drivers(log_end - logged_chars, log_end);
+}
+
+static struct sysrq_key_op msg_info_op = {
+	.handler	= show_msg_info,
+	.help_msg	= "Dumpmsgs",
+	.action_msg	= "Kernel Messages",
+};
+
+static int __init dbg_init(void)
+{
+	register_sysrq_key('d', &msg_info_op);
+	return 0;
+}
+
+__initcall(dbg_init);
--- linux-2.6.5/kernel/resource.c~heh	2004-04-03 22:37:36.000000000 -0500
+++ linux-2.6.5/kernel/resource.c	2004-04-30 20:57:36.000000000 -0400
@@ -179,6 +179,8 @@
 {
 	struct resource *tmp, **p;
 
+	BUG_ON(old->child);
+
 	p = &old->parent->child;
 	for (;;) {
 		tmp = *p;
@@ -409,6 +411,47 @@
 EXPORT_SYMBOL(adjust_resource);
 
 /*
+ * Given an existing resource, change its start and size to match the
+ * arguments.  Returns -EBUSY if it can't fit.  Existing children of
+ * the resource are assumed to be immutable.
+ */
+int reallocate_resource(struct resource *res, unsigned long start, unsigned long size)
+{
+	struct resource *tmp, *parent = res->parent;
+	unsigned long end = start + size - 1;
+	int result = -EBUSY;
+
+	write_lock(&resource_lock);
+
+	if ((start < parent->start) || (end > parent->end))
+		goto out;
+
+	for (tmp = res->child; tmp; tmp = tmp->sibling) {
+		if ((tmp->start < start) || (tmp->end > end))
+			goto out;
+	}
+
+	if (res->sibling && (res->sibling->start <= end))
+		goto out;
+
+	tmp = parent->child;
+	if (tmp != res) {
+		while (tmp->sibling != res)
+			tmp = tmp->sibling;
+		if (start <= tmp->end)
+			goto out;
+	}
+
+	res->start = start;
+	res->end = end;
+	result = 0;
+
+ out:
+	write_unlock(&resource_lock);
+	return result;
+}
+
+/*
  * This is compatibility stuff for IO resources.
  *
  * Note how this, unlike the above, knows about
--- linux-2.6.5/include/asm-arm/mach/irq.h~heh	2004-04-03 22:36:54.000000000 -0500
+++ linux-2.6.5/include/asm-arm/mach/irq.h	2004-04-30 20:57:36.000000000 -0400
@@ -14,6 +14,19 @@
 struct pt_regs;
 struct seq_file;
 
+/*
+ * Architectures are expected to define NR_IRQ_DEVICES and
+ * NR_IRQ_DEVICE_SHIFT if they wish to use dynamic IRQs.
+ */
+#ifndef NR_IRQ_DEVICES
+#define NR_IRQ_DEVICES		1
+#endif
+#define NR_IRQ_PER_DEVICE	(1 << (NR_IRQ_DEVICE_SHIFT - 1))
+
+#define IRQ_DEVICE(i)	((i) >> NR_IRQ_DEVICE_SHIFT)
+#define IRQ_INDEX(i)	((i) & (NR_IRQ_PER_GROUP - 1))
+#define TO_IRQ(g,i)	(((g) << NR_IRQ_DEVICE_SHIFT) + (i))
+
 typedef void (*irq_handler_t)(unsigned int, struct irqdesc *, struct pt_regs *);
 typedef void (*irq_control_t)(unsigned int);
 
--- linux-2.6.5/include/asm-arm/arch-pxa/uncompress.h~heh	2004-04-03 22:36:17.000000000 -0500
+++ linux-2.6.5/include/asm-arm/arch-pxa/uncompress.h	2004-04-30 20:57:36.000000000 -0400
@@ -12,6 +12,7 @@
 #define FFUART		((volatile unsigned long *)0x40100000)
 #define BTUART		((volatile unsigned long *)0x40200000)
 #define STUART		((volatile unsigned long *)0x40700000)
+#define HWUART		((volatile unsigned long *)0x41600000)
 
 #define UART		FFUART
 
--- linux-2.6.5/include/asm-arm/arch-pxa/dma.h~heh	2004-04-03 22:38:18.000000000 -0500
+++ linux-2.6.5/include/asm-arm/arch-pxa/dma.h	2004-04-30 20:57:36.000000000 -0400
@@ -22,11 +22,11 @@
  * Note: this structure must always be aligned to a 16-byte boundary.
  */
 
-typedef struct {
-	volatile u32 ddadr;	/* Points to the next descriptor + flags */
-	volatile u32 dsadr;	/* DSADR value for the current transfer */
-	volatile u32 dtadr;	/* DTADR value for the current transfer */
-	volatile u32 dcmd;	/* DCMD value for the current transfer */
+typedef struct pxa_dma_desc {
+	u32 ddadr;	/* Points to the next descriptor + flags */
+	u32 dsadr;	/* DSADR value for the current transfer */
+	u32 dtadr;	/* DTADR value for the current transfer */
+	u32 dcmd;	/* DCMD value for the current transfer */
 } pxa_dma_desc;
 
 /*
--- linux-2.6.5/include/asm-arm/arch-pxa/serial.h~heh	2004-04-03 22:37:06.000000000 -0500
+++ linux-2.6.5/include/asm-arm/arch-pxa/serial.h	2004-04-30 20:57:36.000000000 -0400
@@ -43,6 +43,15 @@
 		io_type:		SERIAL_IO_MEM,	\
 		irq:			IRQ_BTUART,	\
 		flags:			STD_COM_FLAGS,	\
+	}, {	\
+		type:			PORT_PXA,	\
+		xmit_fifo_size:		64,		\
+		baud_base:		BAUD_BASE,	\
+		iomem_base:		&HWUART,	\
+		iomem_reg_shift:	2,		\
+		io_type:		SERIAL_IO_MEM,	\
+		irq:			IRQ_HWUART,	\
+		flags:			STD_COM_FLAGS,	\
 	}
 
 #define EXTRA_SERIAL_PORT_DEFNS
--- linux-2.6.5/include/asm-arm/arch-pxa/pxa-regs.h~heh	2004-04-03 22:37:36.000000000 -0500
+++ linux-2.6.5/include/asm-arm/arch-pxa/pxa-regs.h	2004-04-30 20:57:36.000000000 -0400
@@ -124,26 +124,26 @@
 #define DRCMR12		__REG(0x40000130)  /* Request to Channel Map Register for AC97 audio transmit Request */
 #define DRCMR13		__REG(0x40000134)  /* Request to Channel Map Register for SSP receive Request */
 #define DRCMR14		__REG(0x40000138)  /* Request to Channel Map Register for SSP transmit Request */
-#define DRCMR15		__REG(0x4000013c)  /* Reserved */
-#define DRCMR16		__REG(0x40000140)  /* Reserved */
+#define DRCMR15		__REG(0x4000013c)  /* Request to Channel Map Register for NSSP receive Request */
+#define DRCMR16		__REG(0x40000140)  /* Request to Channel Map Register for NSSP transmit Request */
 #define DRCMR17		__REG(0x40000144)  /* Request to Channel Map Register for ICP receive Request */
 #define DRCMR18		__REG(0x40000148)  /* Request to Channel Map Register for ICP transmit Request */
 #define DRCMR19		__REG(0x4000014c)  /* Request to Channel Map Register for STUART receive Request */
 #define DRCMR20		__REG(0x40000150)  /* Request to Channel Map Register for STUART transmit Request */
 #define DRCMR21		__REG(0x40000154)  /* Request to Channel Map Register for MMC receive Request */
 #define DRCMR22		__REG(0x40000158)  /* Request to Channel Map Register for MMC transmit Request */
-#define DRCMR23		__REG(0x4000015c)  /* Reserved */
-#define DRCMR24		__REG(0x40000160)  /* Reserved */
+#define DRCMR23		__REG(0x4000015c)  /* Request to Channel Map Register for ASSP receive Request */
+#define DRCMR24		__REG(0x40000160)  /* Request to Channel Map Register for ASSP transmit Request */
 #define DRCMR25		__REG(0x40000164)  /* Request to Channel Map Register for USB endpoint 1 Request */
 #define DRCMR26		__REG(0x40000168)  /* Request to Channel Map Register for USB endpoint 2 Request */
 #define DRCMR27		__REG(0x4000016C)  /* Request to Channel Map Register for USB endpoint 3 Request */
 #define DRCMR28		__REG(0x40000170)  /* Request to Channel Map Register for USB endpoint 4 Request */
-#define DRCMR29		__REG(0x40000174)  /* Reserved */
+#define DRCMR29		__REG(0x40000174)  /* Request to Channel Map Register for HWUART receive Request */
 #define DRCMR30		__REG(0x40000178)  /* Request to Channel Map Register for USB endpoint 6 Request */
 #define DRCMR31		__REG(0x4000017C)  /* Request to Channel Map Register for USB endpoint 7 Request */
 #define DRCMR32		__REG(0x40000180)  /* Request to Channel Map Register for USB endpoint 8 Request */
 #define DRCMR33		__REG(0x40000184)  /* Request to Channel Map Register for USB endpoint 9 Request */
-#define DRCMR34		__REG(0x40000188)  /* Reserved */
+#define DRCMR34		__REG(0x40000188)  /* Request to Channel Map Register for HWUART transmit Request */
 #define DRCMR35		__REG(0x4000018C)  /* Request to Channel Map Register for USB endpoint 11 Request */
 #define DRCMR36		__REG(0x40000190)  /* Request to Channel Map Register for USB endpoint 12 Request */
 #define DRCMR37		__REG(0x40000194)  /* Request to Channel Map Register for USB endpoint 13 Request */
@@ -163,12 +163,16 @@
 #define DRCMRTXPCDR	DRCMR12
 #define DRCMRRXSSDR	DRCMR13
 #define DRCMRTXSSDR	DRCMR14
+#define DRCMRRXNSSPDR	DRCMR15
+#define DRCMRTXNSSPDR	DRCMR16
 #define DRCMRRXICDR	DRCMR17
 #define DRCMRTXICDR	DRCMR18
 #define DRCMRRXSTRBR	DRCMR19
 #define DRCMRTXSTTHR	DRCMR20
 #define DRCMRRXMMC	DRCMR21
 #define DRCMRTXMMC	DRCMR22
+#define DRCMRRXASSPDR	DRCMR23
+#define DRCMRTXASSPDR	DRCMR24
 
 #define DRCMR_MAPVLD	(1 << 7)	/* Map Valid (read / write) */
 #define DRCMR_CHLNUM	0x0f		/* mask for Channel Number (read / write) */
@@ -303,6 +307,22 @@
 #define BTDLL		__REG(0x40200000)  /* Divisor Latch Low Register (DLAB = 1) (read/write) */
 #define BTDLH		__REG(0x40200004)  /* Divisor Latch High Register (DLAB = 1) (read/write) */
 
+/* Hardware UART (HWUART) */
+#define HWUART          HWRBR
+#define HWRBR           __REG(0x41600000)  /* Receive Buffer Register (read only) */
+#define HWTHR           __REG(0x41600000)  /* Transmit Holding Register (write only) */
+#define HWIER           __REG(0x41600004)  /* Interrupt Enable Register (read/write) */
+#define HWIIR           __REG(0x41600008)  /* Interrupt ID Register (read only) */
+#define HWFCR           __REG(0x41600008)  /* FIFO Control Register (write only) */
+#define HWLCR           __REG(0x4160000C)  /* Line Control Register (read/write) */
+#define HWMCR           __REG(0x41600010)  /* Modem Control Register (read/write) */
+#define HWLSR           __REG(0x41600014)  /* Line Status Register (read only) */
+#define HWMSR           __REG(0x41600018)  /* Reserved */
+#define HWSPR           __REG(0x4160001C)  /* Scratch Pad Register (read/write) */
+#define HWISR           __REG(0x41600020)  /* Infrared Selection Register (read/write) */
+#define HWDLL           __REG(0x41600000)  /* Divisor Latch Low Register (DLAB = 1) (read/write) */
+#define HWDLH           __REG(0x41600004)  /* Divisor Latch High Register (DLAB = 1) (read/write) */
+
 /* Standard UART (STUART) */
 #define STUART		STRBR
 #define STRBR		__REG(0x40700000)  /* Receive Buffer Register (read only) */
@@ -1078,6 +1098,111 @@
 
 
 /*
+ * NSSP Serial Port Registers (Network SSP)
+ */
+
+#define NSSCR0		__REG(0x41400000)  /* NSSP Control Register 0 */
+#define NSSCR1		__REG(0x41400004)  /* NSSP Control Register 1 */
+#define NSSSR		__REG(0x41400008)  /* NSSP Status Register */
+#define NSSITR		__REG(0x4140000C)  /* NSSP Interrupt Test Register */
+#define NSSDR		__REG(0x41400010)  /* (Write / Read) NSSP Data Write Register/NSSP Data Read Register */
+#define NSSTO		__REG(0x41400028)  /* NSSP Time Out Register */
+#define NSSPSP		__REG(0x4140002C)  /* NSSP Programable Serial Port Register*/
+
+
+/*
+ * ASSP Serial Port Registers (Audio SSP)
+ */
+
+#define ASSCR0		__REG(0x41500000)  /* ASSP Control Register 0 */
+#define ASSCR1		__REG(0x41500004)  /* ASSP Control Register 1 */
+#define ASSSR		__REG(0x41500008)  /* ASSP Status Register */
+#define ASSITR		__REG(0x4150000C)  /* ASSP Interrupt Test Register */
+#define ASSDR		__REG(0x41500010)  /* (Write / Read) ASSP Data Write Register/ASSP Data Read Register */
+#define ASSTO		__REG(0x41500028)  /* ASSP Time Out Register */
+#define ASSPSP		__REG(0x4150002C)  /* ASSP Programable Serial Port Register*/
+
+
+/* 
+ * Bit definitions for SSP, NSSP and ASSP registers 
+ *  - note that some bits are only available on the NSSP and ASSP
+ */
+
+#define SSCR0_EDSS		(1 << 20)   /* ext. data size select */
+#define SSCR0_SCR_MASK		0x000fff00  /* [19:8] secrial clock rate */
+#define SSCR0_SCR(x)		(((x)<<8) & XSSCR0_SCR_MASK)
+#define SSCR0_SSE		(1 << 7)    /* sync ser port enable */
+#define SSCR0_FRF_MASK		0x00000030  /* [5:4] frame format */
+#define SSCR0_FRF(x)		(((x)<<4) & XSSCR0_FRF_MASK)
+#define SSCR0_FRF_SPI		0x00000000     /* ser peripheral i/f */
+#define SSCR0_FRF_TISSP		0x00000010     /* TI sync ser port */
+#define SSCR0_FRF_MICROWAVE	0x00000020     /* microwire */
+#define SSCR0_FRF_PSP		0x00000030     /* prog ser protocol */
+#define SSCR0_DSS_MASK		0x0000000f  /* data size select */
+#define SSCR0_DSS(x)		((x) & XSSCR0_DSS_MASK)
+
+#define SSCR1_TTELP		(1 << 31)   /* tx hi-z later phase */
+#define SSCR1_TTE		(1 << 30)   /* tx hi-z enable */
+#define SSCR1_EBCEI		(1 << 29)   /* bit count error int mask */
+#define SSCR1_SCFR		(1 << 28)   /* slave clock free running */
+#define SSCR1_SCLKDIR		(1 << 25)   /* ssp clock direction */
+#define SSCR1_SFRMDIR		(1 << 24)   /* ssp frame direction */
+#define SSCR1_RWOT		(1 << 23)   /* rx without transmit */
+#define SSCR1_TSRE		(1 << 21)   /* tx req enable */
+#define SSCR1_RSRE		(1 << 20)   /* rx req enable */
+#define SSCR1_TINTE		(1 << 19)   /* timeout int enable */
+#define SSCR1_STRF		(1 << 15)   /* select fifo for efwr */
+#define SSCR1_EFWR		(1 << 14)   /* fifo write/read enable */
+#define SSCR1_RFT_MASK		0x00003c00  /* [13:10] rx fifo threshold */
+#define SSCR1_RFT(x)		(((x)<<10) & XSSCR1_RFT_MASK)
+#define SSCR1_TFT_MASK		0x000003c0  /* [9:6] tx fifo threshold */
+#define SSCR1_TFT(x)		(((x)<<6) & XSSCR1_TFT_MASK)
+#define SSCR1_MWDS		(1 << 5)    /* microwire tx data size */
+#define SSCR1_SPH		(1 << 4)    /* SPI SSPSCLK phase */
+#define SSCR1_SPO		(1 << 3)    /* motorolla SPI polarity */
+#define SSCR1_LBM		(1 << 2)    /* loop-back mode */
+#define SSCR1_TIE		(1 << 1)    /* tx fifo int enable */
+#define SSCR1_RIE		(1 << 0)    /* rx fifo int enable */
+
+#define SSPSP_DMYSTOP_MASK	0x01800000  /* [24:23] dummy stop */
+#define SSPSP_DMYSTOP(x)	(((x)<<23) & XSSPSP_DMYSTOP_MASK)
+#define SSPSP_SFRMWDTH_MASK	0x007f0000  /* [22:16] serial frame width */
+#define SSPSP_SFRMWDTH(x)	(((x)<<16) & XSSPSP_SFRMWDTH_MASK)
+#define SSPSP_SFRMDLY_MASK	0x0000fe00  /* [15:9] serial frame delay */
+#define SSPSP_SFRMDLY(x)	(((x)<<9) & XSSPSP_SFRMDLY_MASK)
+#define SSPSP_DMYSTRT_MASK	0x00000180  /* [8:7] dummy start */
+#define SSPSP_DMYSTRT(x)	(((x)<<7) & XSSPSP_DMYSTRT_MASK)
+#define SSPSP_STRTDLY_MASK	0x00000070  /* [6:4] three-bit start delay */
+#define SSPSP_STRTDLY(x)	(((x)<<4) & XSSPSP_STRTDLY_MASK)
+#define SSPSP_ETDS		(1 << 3)    /* end of tx data state */
+#define SSPSP_SFRMP		(1 << 2)    /* serial frame polarity */
+#define SSPSP_SCMODE_MASK	0x00000003  /* bit-rate clock mode */
+#define SSPSP_SCMODE(x)		((x) & XSSPSP_SCMODE_MASK)
+
+#define SSTO_TIMEOUT_MASK	0x00ffffff  /* [23:0] timeout */
+#define SSTO_TIMEOUT(x)		((x) & XSSTO_TIMEOUT_MASK)
+
+#define SSITR_TROR		(1 << 7)    /* test rx fifo overrun */
+#define SSITR_TRFS		(1 << 6)    /* test rx fifo serv req */
+#define SSITR_TTFS		(1 << 5)    /* test tx fifo serv req */
+
+#define SSSR_BCE		(1 << 23)   /* bit count error */
+#define SSSR_CSS		(1 << 22)   /* clock sync stat */
+#define SSSR_TUR		(1 << 21)   /* tx fifo underrun */
+#define SSSR_TINT		(1 << 19)   /* rx timeout int */
+#define SSSR_RFL_MASK		0x0000f000  /* rx fifo level */
+#define SSSR_RFL(x)		(((x)<<16) & XSSSR_RFL_MASK)
+#define SSSR_TFL_MASK		0x00000f00  /* tx fifo level */
+#define SSSR_TFL(x)		(((x)<<8) & XSSSR_TFL_MASK)
+#define SSSR_ROR		(1 << 7)    /* rx fifo overrun */
+#define SSSR_RFS		(1 << 6)    /* rx fifo serv request */
+#define SSSR_TFS		(1 << 5)    /* tx fifo serv req */
+#define SSSR_BSY		(1 << 4)    /* SSP busy */
+#define SSSR_RNE		(1 << 3)    /* rx fifo not empty */
+#define SSSR_TNF		(1 << 2)    /* tx fifo not full */
+
+
+/*
  * MultiMediaCard (MMC) controller
  */
 
@@ -1122,6 +1247,7 @@
 #define CKEN7_BTUART	(1 << 7)	/* BTUART Unit Clock Enable */
 #define CKEN6_FFUART	(1 << 6)	/* FFUART Unit Clock Enable */
 #define CKEN5_STUART	(1 << 5)	/* STUART Unit Clock Enable */
+#define CKEN4_HWUART	(1 << 4)        /* HWUART Unit Clock Enable */
 #define CKEN3_SSP	(1 << 3)	/* SSP Unit Clock Enable */
 #define CKEN2_AC97	(1 << 2)	/* AC97 Unit Clock Enable */
 #define CKEN1_PWM1	(1 << 1)	/* PWM1 Clock Enable */
--- linux-2.6.5/include/asm-arm/page.h~heh	2004-04-03 22:36:25.000000000 -0500
+++ linux-2.6.5/include/asm-arm/page.h	2004-04-30 20:57:36.000000000 -0400
@@ -92,6 +92,14 @@
 # endif
 #endif
 
+#ifdef CONFIG_CPU_COPY_V6
+# ifdef _USER
+#  define MULTI_USER 1
+# else
+#  define _USER v6
+# endif
+#endif
+
 #ifndef _USER
 #error Unknown user operations model
 #endif
--- linux-2.6.5/include/asm-arm/thread_info.h~heh	2004-04-03 22:37:06.000000000 -0500
+++ linux-2.6.5/include/asm-arm/thread_info.h	2004-04-30 20:57:36.000000000 -0400
@@ -108,8 +108,8 @@
 #define TI_CPU		20
 #define TI_CPU_DOMAIN	24
 #define TI_CPU_SAVE	28
-#define TI_USED_MATH	76
-#define TI_FPSTATE	(TI_USED_MATH+16)
+#define TI_USED_CP	76
+#define TI_FPSTATE	(TI_USED_CP+16)
 
 #endif
 
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/include/asm-arm/rtc.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,45 @@
+/*
+ *  linux/include/asm-arm/rtc.h
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef ASMARM_RTC_H
+#define ASMARM_RTC_H
+
+struct module;
+
+struct rtc_ops {
+	struct module	*owner;
+	int		(*open)(void);
+	void		(*release)(void);
+	int		(*ioctl)(unsigned int, unsigned long);
+
+	void		(*read_time)(struct rtc_time *);
+	int		(*set_time)(struct rtc_time *);
+	void		(*read_alarm)(struct rtc_wkalrm *);
+	int		(*set_alarm)(struct rtc_wkalrm *);
+	int		(*proc)(char *buf);
+};
+
+void rtc_time_to_tm(unsigned long, struct rtc_time *);
+int rtc_tm_to_time(struct rtc_time *, unsigned long *);
+void rtc_next_alarm_time(struct rtc_time *, struct rtc_time *, struct rtc_time *);
+void rtc_update(unsigned long, unsigned long);
+int register_rtc(struct rtc_ops *);
+void unregister_rtc(struct rtc_ops *);
+
+static inline int rtc_periodic_alarm(struct rtc_time *tm)
+{
+	return  (tm->tm_year == -1) ||
+		((unsigned)tm->tm_mon >= 12) ||
+		((unsigned)(tm->tm_mday - 1) >= 31) ||
+		((unsigned)tm->tm_hour > 23) ||
+		((unsigned)tm->tm_min > 59) ||
+		((unsigned)tm->tm_sec > 59);
+}
+
+#endif
--- linux-2.6.5/include/linux/serial.h~heh	2004-04-03 22:36:26.000000000 -0500
+++ linux-2.6.5/include/linux/serial.h	2004-04-30 20:57:36.000000000 -0400
@@ -81,17 +81,6 @@
 #define SERIAL_IO_HUB6	1
 #define SERIAL_IO_MEM	2
 
-struct serial_uart_config {
-	char	*name;
-	int	dfl_xmit_fifo_size;
-	int	flags;
-};
-
-#define UART_CLEAR_FIFO		0x01
-#define UART_USE_FIFO		0x02
-#define UART_STARTECH		0x04
-#define UART_NATSEMI		0x08
-
 /*
  * Definitions for async_struct (and serial_struct) flags field
  */
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/include/linux/i2c-pxa.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,76 @@
+/*
+ *  i2c_pxa.h
+ *
+ *  Copyright (C) 2002 Intrinsyc Software Inc.
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ */
+#ifndef _I2C_PXA_H_
+#define _I2C_PXA_H_
+
+struct i2c_algo_pxa_data
+{
+        void (*write_byte) (u8 value);
+        u8   (*read_byte) (void);
+        void (*start) (void);
+        void (*repeat_start) (void);
+        void (*stop) (void);
+        void (*abort) (void);
+        int  (*wait_bus_not_busy) (void);
+        int  (*wait_for_interrupt) (int wait_type);
+        void (*transfer) (int lastbyte, int receive, int midbyte);
+        void (*reset) (void);
+
+	int udelay;
+	int timeout;
+};
+
+#define DEF_TIMEOUT             3
+#define BUS_ERROR               (-EREMOTEIO)
+#define ACK_DELAY               0       /* time to delay before checking bus error */
+#define MAX_MESSAGES            65536   /* maximum number of messages to send */
+
+#define I2C_SLEEP_TIMEOUT       2       /* time to sleep for on i2c transactions */
+#define I2C_RETRY               (-2000) /* an error has occurred retry transmit */
+#define I2C_TRANSMIT		1
+#define I2C_RECEIVE		0
+#define I2C_PXA_SLAVE_ADDR      0x1    /* slave pxa unit address */
+#define I2C_ICR_INIT            (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE) /* ICR initialization value */
+/* ICR initialize bit values 
+*                       
+*  15. FM       0 (100 Khz operation)
+*  14. UR       0 (No unit reset)
+*  13. SADIE    0 (Disables the unit from interrupting on slave addresses 
+*                                       matching its slave address)
+*  12. ALDIE    0 (Disables the unit from interrupt when it loses arbitration 
+*                                       in master mode)
+*  11. SSDIE    0 (Disables interrupts from a slave stop detected, in slave mode)  
+*  10. BEIE     1 (Enable interrupts from detected bus errors, no ACK sent)
+*  9.  IRFIE    1 (Enable interrupts from full buffer received)
+*  8.  ITEIE    1 (Enables the I2C unit to interrupt when transmit buffer empty)
+*  7.  GCD      1 (Disables i2c unit response to general call messages as a slave) 
+*  6.  IUE      0 (Disable unit until we change settings)
+*  5.  SCLE     1 (Enables the i2c clock output for master mode (drives SCL)   
+*  4.  MA       0 (Only send stop with the ICR stop bit)
+*  3.  TB       0 (We are not transmitting a byte initially)
+*  2.  ACKNAK   0 (Send an ACK after the unit receives a byte)
+*  1.  STOP     0 (Do not send a STOP)
+*  0.  START    0 (Do not send a START)
+*
+*/
+
+#define I2C_ISR_INIT            0x7FF  /* status register init */
+/* I2C status register init values 
+ *
+ * 10. BED      1 (Clear bus error detected)
+ * 9.  SAD      1 (Clear slave address detected)
+ * 7.  IRF      1 (Clear IDBR Receive Full)
+ * 6.  ITE      1 (Clear IDBR Transmit Empty)
+ * 5.  ALD      1 (Clear Arbitration Loss Detected)
+ * 4.  SSD      1 (Clear Slave Stop Detected)
+ */
+
+#endif
--- linux-2.6.5/include/linux/ioport.h~heh	2004-04-03 22:36:26.000000000 -0500
+++ linux-2.6.5/include/linux/ioport.h	2004-04-30 20:57:36.000000000 -0400
@@ -99,6 +99,7 @@
 			     void (*alignf)(void *, struct resource *,
 					    unsigned long, unsigned long),
 			     void *alignf_data);
+extern int reallocate_resource(struct resource *res, unsigned long start, unsigned long size);
 int adjust_resource(struct resource *res, unsigned long start,
 		    unsigned long size);
 
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/include/linux/switches.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,74 @@
+/*
+ *  linux/include/linux/switches.h
+ *
+ *  Copyright (C) 2000 John Dorsey
+ *
+ *  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.
+ *
+ *  23 October 2000 - created.
+ */
+
+#if !defined(_LINUX_SWITCHES_H)
+#define _LINUX_SWITCHES_H
+
+#define SWITCHES_MASK_SIZE  (128)
+
+typedef unsigned long switches_bitfield;
+
+#define SWITCHES_BITS            (sizeof(switches_bitfield) * 8)
+#define SWITCHES_NUM_FIELDS      (SWITCHES_MASK_SIZE /  SWITCHES_BITS)
+#define SWITCHES_FIELD_SELECT(i) ((i) / SWITCHES_BITS)
+#define SWITCHES_FIELD_MASK(i)   ((switches_bitfield)(1 << (i) % \
+                                  SWITCHES_BITS))
+
+typedef struct switches_mask_t {
+	unsigned int count;
+	switches_bitfield events[SWITCHES_NUM_FIELDS];
+	switches_bitfield states[SWITCHES_NUM_FIELDS];
+} switches_mask_t;
+
+#define SWITCHES_ZERO(m) \
+do { \
+	unsigned int sz_i; \
+	(m)->count = 0; \
+	for(sz_i = 0; sz_i < SWITCHES_NUM_FIELDS; ++sz_i) \
+		(m)->events[sz_i] = (m)->states[sz_i] = 0; \
+} while (0)
+
+/* `s' is the state of the switch, either 0 or non-zero: */
+#define SWITCHES_SET(m, i, s) \
+do { \
+	((m)->events[SWITCHES_FIELD_SELECT((i))] |= \
+         SWITCHES_FIELD_MASK((i))); \
+	if(s) \
+		((m)->states[SWITCHES_FIELD_SELECT((i))] |= \
+                 SWITCHES_FIELD_MASK((i))); \
+	else \
+		((m)->states[SWITCHES_FIELD_SELECT((i))] &= \
+                 ~SWITCHES_FIELD_MASK((i))); \
+	++((m)->count); \
+} while (0)
+
+/* Should only use to clear an event set by SWITCHES_SET(): */
+#define SWITCHES_CLEAR(m, i) \
+do { \
+	((m)->events[SWITCHES_FIELD_SELECT((i))] &= \
+         ~SWITCHES_FIELD_MASK((i))); \
+	((m)->states[SWITCHES_FIELD_SELECT((i))] &= \
+         ~SWITCHES_FIELD_MASK((i))); \
+	--((m)->count); \
+}
+
+#define SWITCHES_COUNT(m) ((m)->count)
+
+/* Returns 0 or non-zero: */
+#define SWITCHES_EVENT(m, i) \
+((m)->events[SWITCHES_FIELD_SELECT((i))] & SWITCHES_FIELD_MASK((i)))
+
+/* Returns 0 or non-zero: */
+#define SWITCHES_STATE(m, i) \
+((m)->states[SWITCHES_FIELD_SELECT((i))] & SWITCHES_FIELD_MASK((i)))
+
+#endif  /* !defined(_LINUX_SWITCHES_H) */
--- linux-2.6.5/include/linux/serial_reg.h~heh	2004-04-03 22:37:38.000000000 -0500
+++ linux-2.6.5/include/linux/serial_reg.h	2004-04-30 20:57:36.000000000 -0400
@@ -121,6 +121,7 @@
 /*
  * These are the definitions for the Modem Control Register
  */
+#define UART_MCR_AFE	0x20	/* Enable auto-RTS/CTS (TI16C750) */
 #define UART_MCR_LOOP	0x10	/* Enable loopback test mode */
 #define UART_MCR_OUT2	0x08	/* Out2 complement */
 #define UART_MCR_OUT1	0x04	/* Out1 complement */
@@ -156,6 +157,21 @@
 #define UART_FCR_PXAR32	0xc0	/* receive FIFO treshold = 32 */
 
 /*
+ * The Intel PXA2xx chip defines those bits
+ */
+#define UART_IER_DMAE	0x80	/* DMA Requests Enable */
+#define UART_IER_UUE	0x40	/* UART Unit Enable */
+#define UART_IER_NRZE	0x20	/* NRZ coding Enable */
+#define UART_IER_RTOIE	0x10	/* Receiver Time Out Interrupt Enable */
+
+#define UART_IIR_TOD	0x08	/* Character Timeout Indication Detected */
+
+#define UART_FCR_PXAR1	0x00	/* receive FIFO treshold = 1 */ 
+#define UART_FCR_PXAR8	0x40	/* receive FIFO treshold = 8 */
+#define UART_FCR_PXAR16	0x80	/* receive FIFO treshold = 16 */
+#define UART_FCR_PXAR32	0xc0	/* receive FIFO treshold = 32 */
+
+/*
  * These are the definitions for the Extended Features Register
  * (StarTech 16C660 only, when DLAB=1)
  */
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/include/linux/mmc/card.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,83 @@
+/*
+ *  linux/include/linux/mmc/card.h
+ *
+ * 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.
+ *
+ *  Card driver specific definitions.
+ */
+#ifndef LINUX_MMC_CARD_H
+#define LINUX_MMC_CARD_H
+
+#include <linux/mmc/mmc.h>
+
+struct mmc_cid {
+	unsigned int		manfid;
+	unsigned int		serial;
+	char			prod_name[8];
+	unsigned char		hwrev;
+	unsigned char		fwrev;
+	unsigned char		month;
+	unsigned char		year;
+};
+
+struct mmc_csd {
+	unsigned char		mmc_prot;
+	unsigned short		cmdclass;
+	unsigned short		tacc_clks;
+	unsigned int		tacc_ns;
+	unsigned int		max_dtr;
+	unsigned int		read_blkbits;
+	unsigned int		capacity;
+};
+
+struct mmc_host;
+
+/*
+ * MMC device
+ */
+struct mmc_card {
+	struct list_head	node;		/* node in hosts devices list */
+	struct mmc_host		*host;		/* the host this device belongs to */
+	struct device		dev;		/* the device */
+	unsigned int		rca;		/* relative card address of device */
+	unsigned int		state;		/* (our) card state */
+#define MMC_STATE_PRESENT	(1<<0)
+#define MMC_STATE_DEAD		(1<<1)
+	struct mmc_cid		cid;		/* card identification */
+	struct mmc_csd		csd;		/* card specific */
+};
+
+#define mmc_card_dead(c)	((c)->state & MMC_STATE_DEAD)
+#define mmc_card_present(c)	((c)->state & MMC_STATE_PRESENT)
+
+#define mmc_card_name(c)	((c)->cid.prod_name)
+#define mmc_card_id(c)		((c)->dev.bus_id)
+
+#define mmc_list_to_card(l)	container_of(l, struct mmc_card, node)
+#define mmc_get_drvdata(c)	dev_get_drvdata(&(c)->dev)
+#define mmc_set_drvdata(c,d)	dev_set_drvdata(&(c)->dev, d)
+
+/*
+ * MMC device driver (e.g., Flash card, I/O card...)
+ */
+struct mmc_driver {
+	struct device_driver drv;
+	int (*probe)(struct mmc_card *);
+	void (*remove)(struct mmc_card *);
+	int (*suspend)(struct mmc_card *, u32);
+	int (*resume)(struct mmc_card *);
+};
+
+extern int mmc_register_driver(struct mmc_driver *);
+extern void mmc_unregister_driver(struct mmc_driver *);
+
+static inline int mmc_card_claim_host(struct mmc_card *card)
+{
+	return __mmc_claim_host(card->host, card);
+}
+
+#define mmc_card_release_host(c)	mmc_release_host((c)->host)
+
+#endif
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/include/linux/mmc/mmc.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,88 @@
+/*
+ *  linux/include/linux/mmc/mmc.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef MMC_H
+#define MMC_H
+
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+struct request;
+struct mmc_data;
+struct mmc_request;
+
+struct mmc_command {
+	u32			opcode;
+	u32			arg;
+	u32			resp[4];
+	unsigned int		flags;		/* expected response type */
+#define MMC_RSP_NONE	(0 << 0)
+#define MMC_RSP_SHORT	(1 << 0)
+#define MMC_RSP_LONG	(2 << 0)
+#define MMC_RSP_MASK	(3 << 0)
+#define MMC_RSP_CRC	(1 << 3)		/* expect valid crc */
+#define MMC_RSP_BUSY	(1 << 4)		/* card may send busy */
+
+	unsigned int		retries;	/* max number of retries */
+	unsigned int		error;		/* command error */
+
+#define MMC_ERR_NONE	0
+#define MMC_ERR_TIMEOUT	1
+#define MMC_ERR_BADCRC	2
+#define MMC_ERR_FIFO	3
+#define MMC_ERR_FAILED	4
+#define MMC_ERR_INVALID	5
+
+	struct mmc_data		*data;		/* data segment associated with cmd */
+	struct mmc_request	*req;		/* assoicated request */
+};
+
+struct mmc_data {
+	unsigned int		timeout_ns;	/* data timeout (in ns, max 80ms) */
+	unsigned int		timeout_clks;	/* data timeout (in clocks) */
+	unsigned int		blksz_bits;	/* data block size */
+	unsigned int		blocks;		/* number of blocks */
+	struct request		*rq;		/* request structure */
+	unsigned int		error;		/* data error */
+	unsigned int		flags;
+
+#define MMC_DATA_WRITE	(1 << 8)
+#define MMC_DATA_READ	(1 << 9)
+#define MMC_DATA_STREAM	(1 << 10)
+
+	unsigned int		bytes_xfered;
+
+	struct mmc_command	*stop;		/* stop command */
+	struct mmc_request	*req;		/* assoicated request */
+};
+
+struct mmc_request {
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+	struct mmc_command	*stop;
+
+	void			*done_data;	/* completion data */
+	void			(*done)(struct mmc_request *);/* completion function */
+};
+
+struct mmc_host;
+struct mmc_card;
+
+extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
+extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
+
+extern int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card);
+
+static inline void mmc_claim_host(struct mmc_host *host)
+{
+	__mmc_claim_host(host, (struct mmc_card *)-1);
+}
+
+extern void mmc_release_host(struct mmc_host *host);
+
+#endif
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/include/linux/mmc/host.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,67 @@
+/*
+ *  linux/include/linux/mmc/host.h
+ *
+ * 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.
+ *
+ *  Host driver specific definitions.
+ */
+#ifndef LINUX_MMC_HOST_H
+#define LINUX_MMC_HOST_H
+
+#include <linux/mmc/mmc.h>
+
+struct mmc_ios {
+	unsigned int	clock;			/* clock rate */
+	unsigned short	vdd;			/* supply (units of 10mV) */
+	unsigned char	bus_mode;		/* command output mode */
+
+#define MMC_BUSMODE_OPENDRAIN	1
+#define MMC_BUSMODE_PUSHPULL	2
+
+	unsigned char	power_mode;		/* power supply mode */
+
+#define MMC_POWER_OFF		0
+#define MMC_POWER_UP		1
+#define MMC_POWER_ON		2
+};
+
+struct mmc_host_ops {
+	void	(*request)(struct mmc_host *host, struct mmc_request *req);
+	void	(*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
+};
+
+struct mmc_card;
+
+struct mmc_host {
+	struct device		*dev;
+	struct mmc_host_ops	*ops;
+	unsigned int		f_min;
+	unsigned int		f_max;
+	u32			ocr_avail;
+
+	/* private data */
+	unsigned int		host_num;	/* host number */
+	struct mmc_ios		ios;		/* current io bus settings */
+	u32			ocr;		/* the current OCR setting */
+
+	struct list_head	cards;		/* devices attached to this host */
+
+	wait_queue_head_t	wq;
+	spinlock_t		lock;		/* card_busy lock */
+	struct mmc_card		*card_busy;	/* the MMC card claiming host */
+	struct mmc_card		*card_selected;	/* the selected MMC card */
+};
+
+extern int mmc_init_host(struct mmc_host *);
+extern int mmc_add_host(struct mmc_host *);
+extern void mmc_remove_host(struct mmc_host *);
+extern int mmc_suspend_host(struct mmc_host *, u32);
+extern int mmc_resume_host(struct mmc_host *);
+
+extern void mmc_detect_change(struct mmc_host *);
+extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
+
+#endif
+
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/include/linux/mmc/protocol.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,203 @@
+/*
+ * Header for MultiMediaCard (MMC)
+ *
+ * Copyright 2002 Hewlett-Packard Company
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Based strongly on code by:
+ *
+ * Author: Yong-iL Joh <tolkien@mizi.com>
+ * Date  : $Date: 2002/06/18 12:37:30 $
+ *
+ * Author:  Andrew Christian
+ *          15 May 2002
+ */
+
+#ifndef MMC_MMC_PROTOCOL_H
+#define MMC_MMC_PROTOCOL_H
+
+/* Standard MMC commands (3.1)           type  argument     response */
+   /* class 1 */
+#define	MMC_GO_IDLE_STATE         0   /* bc                          */
+#define MMC_SEND_OP_COND          1   /* bcr  [31:0] OCR         R3  */
+#define MMC_ALL_SEND_CID          2   /* bcr                     R2  */
+#define MMC_SET_RELATIVE_ADDR     3   /* ac   [31:16] RCA        R1  */
+#define MMC_SET_DSR               4   /* bc   [31:16] RCA            */
+#define MMC_SELECT_CARD           7   /* ac   [31:16] RCA        R1  */
+#define MMC_SEND_CSD              9   /* ac   [31:16] RCA        R2  */
+#define MMC_SEND_CID             10   /* ac   [31:16] RCA        R2  */
+#define MMC_READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */
+#define MMC_STOP_TRANSMISSION    12   /* ac                      R1b */
+#define MMC_SEND_STATUS	         13   /* ac   [31:16] RCA        R1  */
+#define MMC_GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */
+
+  /* class 2 */
+#define MMC_SET_BLOCKLEN         16   /* ac   [31:0] block len   R1  */
+#define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1  */
+#define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1  */
+
+  /* class 3 */
+#define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1  */
+
+  /* class 4 */
+#define MMC_SET_BLOCK_COUNT      23   /* adtc [31:0] data addr   R1  */
+#define MMC_WRITE_BLOCK          24   /* adtc [31:0] data addr   R1  */
+#define MMC_WRITE_MULTIPLE_BLOCK 25   /* adtc                    R1  */
+#define MMC_PROGRAM_CID          26   /* adtc                    R1  */
+#define MMC_PROGRAM_CSD          27   /* adtc                    R1  */
+
+  /* class 6 */
+#define MMC_SET_WRITE_PROT       28   /* ac   [31:0] data addr   R1b */
+#define MMC_CLR_WRITE_PROT       29   /* ac   [31:0] data addr   R1b */
+#define MMC_SEND_WRITE_PROT      30   /* adtc [31:0] wpdata addr R1  */
+
+  /* class 5 */
+#define MMC_ERASE_GROUP_START    35   /* ac   [31:0] data addr   R1  */
+#define MMC_ERASE_GROUP_END      36   /* ac   [31:0] data addr   R1  */
+#define MMC_ERASE                37   /* ac                      R1b */
+
+  /* class 9 */
+#define MMC_FAST_IO              39   /* ac   <Complex>          R4  */
+#define MMC_GO_IRQ_STATE         40   /* bcr                     R5  */
+
+  /* class 7 */
+#define MMC_LOCK_UNLOCK          42   /* adtc                    R1b */
+
+  /* class 8 */
+#define MMC_APP_CMD              55   /* ac   [31:16] RCA        R1  */
+#define MMC_GEN_CMD              56   /* adtc [0] RD/WR          R1b */
+
+/*
+  MMC status in R1
+  Type
+  	e : error bit
+	s : status bit
+	r : detected and set for the actual command response
+	x : detected and set during command execution. the host must poll
+            the card by sending status command in order to read these bits.
+  Clear condition
+  	a : according to the card state
+	b : always related to the previous command. Reception of
+            a valid command will clear it (with a delay of one command)
+	c : clear by read
+ */
+
+#define R1_OUT_OF_RANGE		(1 << 31)	/* er, c */
+#define R1_ADDRESS_ERROR	(1 << 30)	/* erx, c */
+#define R1_BLOCK_LEN_ERROR	(1 << 29)	/* er, c */
+#define R1_ERASE_SEQ_ERROR      (1 << 28)	/* er, c */
+#define R1_ERASE_PARAM		(1 << 27)	/* ex, c */
+#define R1_WP_VIOLATION		(1 << 26)	/* erx, c */
+#define R1_CARD_IS_LOCKED	(1 << 25)	/* sx, a */
+#define R1_LOCK_UNLOCK_FAILED	(1 << 24)	/* erx, c */
+#define R1_COM_CRC_ERROR	(1 << 23)	/* er, b */
+#define R1_ILLEGAL_COMMAND	(1 << 22)	/* er, b */
+#define R1_CARD_ECC_FAILED	(1 << 21)	/* ex, c */
+#define R1_CC_ERROR		(1 << 20)	/* erx, c */
+#define R1_ERROR		(1 << 19)	/* erx, c */
+#define R1_UNDERRUN		(1 << 18)	/* ex, c */
+#define R1_OVERRUN		(1 << 17)	/* ex, c */
+#define R1_CID_CSD_OVERWRITE	(1 << 16)	/* erx, c, CID/CSD overwrite */
+#define R1_WP_ERASE_SKIP	(1 << 15)	/* sx, c */
+#define R1_CARD_ECC_DISABLED	(1 << 14)	/* sx, a */
+#define R1_ERASE_RESET		(1 << 13)	/* sr, c */
+#define R1_STATUS(x)            (x & 0xFFFFE000)
+#define R1_CURRENT_STATE(x)    	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
+#define R1_READY_FOR_DATA	(1 << 8)	/* sx, a */
+#define R1_APP_CMD		(1 << 7)	/* sr, c */
+
+/* These are unpacked versions of the actual responses */
+
+struct _mmc_csd {
+	u8  csd_structure;
+	u8  spec_vers;
+	u8  taac;
+	u8  nsac;
+	u8  tran_speed;
+	u16 ccc;
+	u8  read_bl_len;
+	u8  read_bl_partial;
+	u8  write_blk_misalign;
+	u8  read_blk_misalign;
+	u8  dsr_imp;
+	u16 c_size;
+	u8  vdd_r_curr_min;
+	u8  vdd_r_curr_max;
+	u8  vdd_w_curr_min;
+	u8  vdd_w_curr_max;
+	u8  c_size_mult;
+	union {
+		struct { /* MMC system specification version 3.1 */
+			u8  erase_grp_size;
+			u8  erase_grp_mult;
+		} v31;
+		struct { /* MMC system specification version 2.2 */
+			u8  sector_size;
+			u8  erase_grp_size;
+		} v22;
+	} erase;
+	u8  wp_grp_size;
+	u8  wp_grp_enable;
+	u8  default_ecc;
+	u8  r2w_factor;
+	u8  write_bl_len;
+	u8  write_bl_partial;
+	u8  file_format_grp;
+	u8  copy;
+	u8  perm_write_protect;
+	u8  tmp_write_protect;
+	u8  file_format;
+	u8  ecc;
+};
+
+#define MMC_VDD_145_150	0x00000001	/* VDD voltage 1.45 - 1.50 */
+#define MMC_VDD_150_155	0x00000002	/* VDD voltage 1.50 - 1.55 */
+#define MMC_VDD_155_160	0x00000004	/* VDD voltage 1.55 - 1.60 */
+#define MMC_VDD_160_165	0x00000008	/* VDD voltage 1.60 - 1.65 */
+#define MMC_VDD_165_170	0x00000010	/* VDD voltage 1.65 - 1.70 */
+#define MMC_VDD_17_18	0x00000020	/* VDD voltage 1.7 - 1.8 */
+#define MMC_VDD_18_19	0x00000040	/* VDD voltage 1.8 - 1.9 */
+#define MMC_VDD_19_20	0x00000080	/* VDD voltage 1.9 - 2.0 */
+#define MMC_VDD_20_21	0x00000100	/* VDD voltage 2.0 ~ 2.1 */
+#define MMC_VDD_21_22	0x00000200	/* VDD voltage 2.1 ~ 2.2 */
+#define MMC_VDD_22_23	0x00000400	/* VDD voltage 2.2 ~ 2.3 */
+#define MMC_VDD_23_24	0x00000800	/* VDD voltage 2.3 ~ 2.4 */
+#define MMC_VDD_24_25	0x00001000	/* VDD voltage 2.4 ~ 2.5 */
+#define MMC_VDD_25_26	0x00002000	/* VDD voltage 2.5 ~ 2.6 */
+#define MMC_VDD_26_27	0x00004000	/* VDD voltage 2.6 ~ 2.7 */
+#define MMC_VDD_27_28	0x00008000	/* VDD voltage 2.7 ~ 2.8 */
+#define MMC_VDD_28_29	0x00010000	/* VDD voltage 2.8 ~ 2.9 */
+#define MMC_VDD_29_30	0x00020000	/* VDD voltage 2.9 ~ 3.0 */
+#define MMC_VDD_30_31	0x00040000	/* VDD voltage 3.0 ~ 3.1 */
+#define MMC_VDD_31_32	0x00080000	/* VDD voltage 3.1 ~ 3.2 */
+#define MMC_VDD_32_33	0x00100000	/* VDD voltage 3.2 ~ 3.3 */
+#define MMC_VDD_33_34	0x00200000	/* VDD voltage 3.3 ~ 3.4 */
+#define MMC_VDD_34_35	0x00400000	/* VDD voltage 3.4 ~ 3.5 */
+#define MMC_VDD_35_36	0x00800000	/* VDD voltage 3.5 ~ 3.6 */
+#define MMC_CARD_BUSY	0x80000000	/* Card Power up status bit */
+
+
+/*
+ * CSD field definitions
+ */
+
+#define CSD_STRUCT_VER_1_0  0           /* Valid for system specification 1.0 - 1.2 */
+#define CSD_STRUCT_VER_1_1  1           /* Valid for system specification 1.4 - 2.2 */
+#define CSD_STRUCT_VER_1_2  2           /* Valid for system specification 3.1       */
+
+#define CSD_SPEC_VER_0      0           /* Implements system specification 1.0 - 1.2 */
+#define CSD_SPEC_VER_1      1           /* Implements system specification 1.4 */
+#define CSD_SPEC_VER_2      2           /* Implements system specification 2.0 - 2.2 */
+#define CSD_SPEC_VER_3      3           /* Implements system specification 3.1 */
+
+#endif  /* MMC_MMC_PROTOCOL_H */
+
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/include/linux/l3/algo-bit.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,39 @@
+/*
+ *  linux/include/linux/l3/algo-bit.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.
+ *
+ * L3 Bus bit-banging algorithm.  Derived from i2c-algo-bit.h by
+ * Simon G. Vogl.
+ */
+#ifndef L3_ALGO_BIT_H
+#define L3_ALGO_BIT_H 1
+
+#include <linux/l3/l3.h>
+
+struct l3_algo_bit_data {
+	void (*setdat) (void *data, int state);
+	void (*setclk) (void *data, int state);
+	void (*setmode)(void *data, int state);
+	void (*setdir) (void *data, int in);	/* set data direction */
+	int  (*getdat) (void *data);
+
+	void *data;
+
+	/* bus timings (us) */
+	int	data_hold;
+	int	data_setup;
+	int	clock_high;
+	int	mode_hold;
+	int	mode_setup;
+	int	mode;
+};
+
+int l3_bit_add_bus(struct l3_adapter *);
+int l3_bit_del_bus(struct l3_adapter *);
+
+#endif
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/include/linux/l3/l3.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,95 @@
+/*
+ *  linux/include/linux/l3/l3.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.
+ *
+ * Derived from i2c.h by Simon G. Vogl
+ */
+#ifndef L3_H
+#define L3_H
+
+struct l3_msg {
+	unsigned char	addr;	/* slave address	*/
+	unsigned char	flags;		
+#define L3_M_RD		0x01
+#define L3_M_NOADDR	0x02
+	unsigned short	len;	/* msg length		*/
+	unsigned char	*buf;	/* pointer to msg data	*/
+};
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+struct l3_adapter;
+
+struct l3_algorithm {
+	/* textual description */
+	char name[32];
+
+	/* perform bus transactions */
+	int (*xfer)(struct l3_adapter *, struct l3_msg msgs[], int num);
+};
+
+struct semaphore;
+
+/*
+ * l3_adapter is the structure used to identify a physical L3 bus along
+ * with the access algorithms necessary to access it.
+ */
+struct l3_adapter {
+	/*
+	 * This name is used to uniquely identify the adapter.
+	 * It should be the same as the module name.
+	 */
+	char			name[32];
+
+	/*
+	 * the algorithm to access the bus
+	 */
+	struct l3_algorithm	*algo;
+
+	/*
+	 * Algorithm specific data
+	 */
+	void			*algo_data;
+
+	/*
+	 * This may be NULL, or should point to the module struct
+	 */
+	struct module		*owner;
+
+	/*
+	 * private data for the adapter
+	 */
+	void			*data;
+
+	/*
+	 * Our lock.  Unlike the i2c layer, we allow this to be used for
+	 * other stuff, like the i2c layer lock.  Some people implement
+	 * i2c stuff using the same signals as the l3 bus.
+	 */
+	struct semaphore	*lock;
+
+	/*
+	 * List of all adapters.
+	 */
+	struct list_head	adapters;
+};
+
+extern int l3_add_adapter(struct l3_adapter *);
+extern int l3_del_adapter(struct l3_adapter *);
+extern void l3_put_adapter(struct l3_adapter *);
+extern struct l3_adapter *l3_get_adapter(const char *name);
+
+extern int l3_write(struct l3_adapter *, int, const char *, int);
+extern int l3_read(struct l3_adapter *, int, char *, int);
+
+#endif
+
+#endif /* L3_H */
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/include/linux/l3/uda1341.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,61 @@
+/*
+ *  linux/include/linux/l3/uda1341.h
+ *
+ * Philips UDA1341 mixer device driver
+ *
+ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ */
+
+#define UDA1341_NAME "uda1341"
+
+struct uda1341_cfg {
+	unsigned int fs:16;
+	unsigned int format:3;
+};
+
+#define FMT_I2S		0
+#define FMT_LSB16	1
+#define FMT_LSB18	2
+#define FMT_LSB20	3
+#define FMT_MSB		4
+#define FMT_LSB16MSB	5
+#define FMT_LSB18MSB	6
+#define FMT_LSB20MSB	7
+
+#define L3_UDA1341_CONFIGURE	0x13410001
+
+struct l3_gain {
+	unsigned int	left:8;
+	unsigned int	right:8;
+	unsigned int	unused:8;
+	unsigned int	channel:8;
+};
+
+#define L3_SET_VOLUME		0x13410002
+#define L3_SET_TREBLE		0x13410003
+#define L3_SET_BASS		0x13410004
+#define L3_SET_GAIN		0x13410005
+
+struct l3_agc {
+	unsigned int	level:8;
+	unsigned int	enable:1;
+	unsigned int	attack:7;
+	unsigned int	decay:8;
+	unsigned int	channel:8;
+};
+
+#define L3_INPUT_AGC		0x13410006
+
+struct uda1341;
+
+int uda1341_configure(struct uda1341 *uda, struct uda1341_cfg *conf);
+int uda1341_mixer_ctl(struct uda1341 *uda, int cmd, void *arg);
+int uda1341_open(struct uda1341 *uda);
+void uda1341_close(struct uda1341 *uda);
+
+struct uda1341 *uda1341_attach(const char *adapter);
+void uda1341_detach(struct uda1341 *uda);
+
--- linux-2.6.5/include/linux/i2c-id.h~heh	2004-04-03 22:36:16.000000000 -0500
+++ linux-2.6.5/include/linux/i2c-id.h	2004-04-30 20:57:36.000000000 -0400
@@ -187,6 +187,7 @@
 #define I2C_ALGO_BITHS	0x130000	/* enhanced bit style adapters	*/
 #define I2C_ALGO_OCP_IOP3XX  0x140000	/* XSCALE IOP3XX On-chip I2C alg */
 
+#define I2C_ALGO_PXA	0x200000	/* Intel PXA I2C algorithm  */
 #define I2C_ALGO_EXP	0x800000	/* experimental			*/
 
 #define I2C_ALGO_MASK	0xff0000	/* Mask for algorithms		*/
--- linux-2.6.5/include/linux/serial_core.h~heh	2004-04-03 22:36:18.000000000 -0500
+++ linux-2.6.5/include/linux/serial_core.h	2004-04-30 20:57:36.000000000 -0400
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- *  $Id: serial_core.h,v 1.49 2002/07/20 18:06:32 rmk Exp $
+ *  $Id: serial_core.h,v 1.53 2002/08/02 12:55:08 rmk Exp $
  */
 
 /*
@@ -159,8 +159,6 @@
 	spinlock_t		lock;			/* port lock */
 	unsigned int		iobase;			/* in/out[bwl] */
 	char			*membase;		/* read/write[bwl] */
-	unsigned int		irq;			/* irq number */
-	unsigned int		uartclk;		/* base uart clock */
 	unsigned char		fifosize;		/* tx fifo size */
 	unsigned char		x_char;			/* xon/xoff char */
 	unsigned char		regshift;		/* reg offset shift */
@@ -172,6 +170,7 @@
 
 	unsigned int		read_status_mask;	/* driver specific */
 	unsigned int		ignore_status_mask;	/* driver specific */
+
 	struct uart_info	*info;			/* pointer to parent info */
 	struct uart_icount	icount;			/* statistics */
 
@@ -182,7 +181,6 @@
 
 	unsigned int		flags;
 
-#define UPF_HUP_NOTIFY		(1 << 0)
 #define UPF_FOURPORT		(1 << 1)
 #define UPF_SAK			(1 << 2)
 #define UPF_SPD_MASK		(0x1030)
@@ -212,9 +210,12 @@
 	unsigned int		timeout;		/* character-based timeout */
 	unsigned int		type;			/* port type */
 	struct uart_ops		*ops;
+	unsigned int		uartclk;		/* base uart clock */
 	unsigned int		custom_divisor;
+	unsigned int		irq;			/* irq number */
 	unsigned int		line;			/* port index */
 	unsigned long		mapbase;		/* for ioremap */
+	struct device		*dev;			/* parent device */
 	unsigned char		hub6;			/* this should be in the 8250 driver */
 	unsigned char		unused[3];
 };
--- linux-2.6.5/include/linux/vmalloc.h~heh	2004-04-03 22:38:23.000000000 -0500
+++ linux-2.6.5/include/linux/vmalloc.h	2004-04-30 20:57:36.000000000 -0400
@@ -35,6 +35,8 @@
  *	Lowlevel-APIs (not for driver use!)
  */
 extern struct vm_struct *get_vm_area(unsigned long size, unsigned long flags);
+extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
+					unsigned long start, unsigned long end);
 extern struct vm_struct *remove_vm_area(void *addr);
 extern int map_vm_area(struct vm_struct *area, pgprot_t prot,
 			struct page ***pages);
--- linux-2.6.5/include/linux/mm.h~heh	2004-04-03 22:36:15.000000000 -0500
+++ linux-2.6.5/include/linux/mm.h	2004-04-30 20:57:36.000000000 -0400
@@ -655,5 +655,12 @@
 int in_gate_area(struct task_struct *task, unsigned long addr);
 #endif
 
+#ifndef __arm__
+#define memc_update_addr(x,y,z)
+#define memc_update_mm(x)
+#define memc_clear(x,y)
+#endif
+
 #endif /* __KERNEL__ */
 #endif /* _LINUX_MM_H */
+
--- linux-2.6.5/init/do_mounts.c~heh	2004-04-03 22:36:56.000000000 -0500
+++ linux-2.6.5/init/do_mounts.c	2004-04-30 20:57:36.000000000 -0400
@@ -391,7 +391,7 @@
 			root_device_name += 5;
 	}
 
-	is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
+	is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR || MAJOR(ROOT_DEV) == 31;
 
 	if (initrd_load())
 		goto out;
--- linux-2.6.5/fs/binfmt_aout.c~heh	2004-04-03 22:36:26.000000000 -0500
+++ linux-2.6.5/fs/binfmt_aout.c	2004-04-30 20:57:36.000000000 -0400
@@ -432,7 +432,11 @@
 		else
 			send_sig(SIGTRAP, current, 0);
 	}
+#ifndef __arm__
 	return 0;
+#else
+	return regs->ARM_r0;
+#endif
 }
 
 static int load_aout_library(struct file *file)
@@ -462,8 +466,11 @@
 
 	/* For  QMAGIC, the starting address is 0x20 into the page.  We mask
 	   this off to get the starting address for the page */
-
-	start_addr =  ex.a_entry & 0xfffff000;
+#ifndef __arm__
+	start_addr = ex.a_entry & 0xfffff000;
+#else
+	start_addr = ex.a_entry & 0xffff8000;
+#endif
 
 	if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
 		static unsigned long error_time;
--- linux-2.6.5/mm/slab.c~heh	2004-04-03 22:37:41.000000000 -0500
+++ linux-2.6.5/mm/slab.c	2004-04-30 20:57:36.000000000 -0400
@@ -2115,12 +2115,12 @@
  *
  * Called with disabled ints.
  */
-static inline void __cache_free (kmem_cache_t *cachep, void* objp)
+static inline void __cache_free (kmem_cache_t *cachep, void* objp, void *caller)
 {
 	struct array_cache *ac = ac_data(cachep);
 
 	check_irq_off();
-	objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));
+	objp = cache_free_debugcheck(cachep, objp, caller /*__builtin_return_address(0)*/);
 
 	if (likely(ac->avail < ac->limit)) {
 		STATS_INC_FREEHIT(cachep);
@@ -2289,7 +2289,7 @@
 	unsigned long flags;
 
 	local_irq_save(flags);
-	__cache_free(cachep, objp);
+	__cache_free(cachep, objp, __builtin_return_address(0));
 	local_irq_restore(flags);
 }
 
@@ -2312,7 +2312,7 @@
 	local_irq_save(flags);
 	kfree_debugcheck(objp);
 	c = GET_PAGE_CACHE(virt_to_page(objp));
-	__cache_free(c, (void*)objp);
+	__cache_free(c, (void*)objp, __builtin_return_address(0));
 	local_irq_restore(flags);
 }
 
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/Documentation/l3/structure	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,36 @@
+L3 Bus Driver
+-------------
+
+The structure of the driver is as follows:
+
+	+----------+	+----------+	+----------+
+	| client 1 |	| client 2 |	| client 3 |
+	+-----^----+	+----^-----+	+----^-----+
+	      |		     |               |
+	+-----v--------------v---------------v-----+
+	|                                          |
+	+-----^-------+              +-------^-----+
+	      |	      |     core     |       |
+	+-----v----+  |	             |  +----v-----+
+	| device   |  |	             |  | device   |
+	| driver 1 |  |              |	| driver 2 |
+	+-----^----+  |              |  +----^-----+
+	      |	      |	  services   |       |
+	+-----v-------+              +-------v-----+
+	|                                          |
+	+-----------------^----^-------------------+
+			  |    |
+			  |  +-v---------+
+			  |  | algorithm |
+			  |  |  driver   |
+			  |  +-v---------+
+			  |    |
+			+-v----v-+
+			|  bus   |
+			| driver |
+			+--------+
+
+Clients talk to the core to attach device drivers and bus adapters, and
+to instruct device drivers to perform actions.  Device drivers then talk
+to the core to perform L3 bus transactions via the algorithm driver and
+ultimately bus driver.
--- linux-2.6.5/arch/arm/kernel/debug.S~heh	2004-04-03 22:36:55.000000000 -0500
+++ linux-2.6.5/arch/arm/kernel/debug.S	2004-04-30 20:57:36.000000000 -0400
@@ -192,6 +192,20 @@
 
 		@ if all ports are inactive, then there is nothing we can do
 		moveq	pc, lr
+		ldr	r1, [\rx, #UTCR2]
+		teq	r1, #5
+		movne	r1, #0
+		strne	r1, [\rx, #UTCR3]
+		movne	r1, #8
+		strne	r1, [\rx, #UTCR0]
+		movne	r1, #5
+		strne	r1, [\rx, #UTCR2]
+		movne	r1, #0
+		strne	r1, [\rx, #UTCR1]
+		movne	r1, #3
+		strne	r1, [\rx, #UTCR3]
+		movne	r1, #255
+		strne	r1, [\rx, #UTSR0]
 		.endm
 
 		.macro	senduart,rd,rx
@@ -287,7 +301,7 @@
 
 #elif defined(CONFIG_ARCH_INTEGRATOR)
 
-#include <asm/hardware/serial_amba.h>
+#include <asm/hardware/amba_serial.h>
 
 		.macro	addruart,rx
 		mrc	p15, 0, \rx, c1, c0
@@ -298,7 +312,7 @@
 		.endm
 
 		.macro	senduart,rd,rx
-		strb	\rd, [\rx, #AMBA_UARTDR]
+		strb	\rd, [\rx, #UART01x_DR]
 		.endm
 
 		.macro	waituart,rd,rx
--- linux-2.6.5/arch/arm/kernel/entry-armv.S~heh	2004-04-03 22:36:54.000000000 -0500
+++ linux-2.6.5/arch/arm/kernel/entry-armv.S	2004-04-30 20:57:36.000000000 -0400
@@ -1181,6 +1181,9 @@
  * get out of that mode without clobbering one register.
  */
 vector_FIQ:	disable_fiq
+		mrs	r13, spsr
+		orr	r13, r13, #PSR_F_BIT
+		msr	spsr, r13
 		subs	pc, lr, #4
 
 /*=============================================================================
--- linux-2.6.5/arch/arm/kernel/irq.c~heh	2004-04-03 22:36:12.000000000 -0500
+++ linux-2.6.5/arch/arm/kernel/irq.c	2004-04-30 20:57:36.000000000 -0400
@@ -47,12 +47,36 @@
 #define MAX_IRQ_CNT	100000
 
 static volatile unsigned long irq_err_count;
-static spinlock_t irq_controller_lock;
 static LIST_HEAD(irq_pending);
 
 struct irqdesc irq_desc[NR_IRQS];
 void (*init_arch_irq)(void) __initdata = NULL;
 
+#if NR_IRQ_DEVICES > 1
+struct irq_device {
+	spinlock_t	lock;
+	int		nr_irqs;
+	struct irq_desc	*irqs;
+};
+
+static struct irq_device irq_devices[NR_IRQ_DEVICES] = {
+	[0] = {
+		.lock		= SPIN_LOCK_UNLOCKED,
+		.nr_irqs	= NR_IRQS,
+		.irqs		= irq_desc,
+	},
+};
+#define IRQ_LOCK(irq)	(&irq_devices[IRQ_DEVICE(irq)].lock)
+#define IRQ_DESC(irq)	(&irq_devices[IRQ_DEVICE(irq)].irqs[IRQ_INDEX(irq)])
+#define IRQ_VALID(irq)	(IRQ_DEVICE(irq) < NR_IRQ_DEVICES && \
+			 IRQ_INDEX(irq) < irq_devices[IRQ_DEVICE(irq)].nr_irqs)
+#else
+static spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;
+#define IRQ_LOCK(irq)	(&irq_controller_lock)
+#define IRQ_DESC(irq)	(&irq_desc[irq])
+#define IRQ_VALID(irq)	((irq) < NR_IRQS)
+#endif
+
 /*
  * Dummy mask/unmask handler
  */
@@ -95,13 +119,13 @@
  */
 void disable_irq(unsigned int irq)
 {
-	struct irqdesc *desc = irq_desc + irq;
+	struct irqdesc *desc = IRQ_DESC(irq);
 	unsigned long flags;
 
-	spin_lock_irqsave(&irq_controller_lock, flags);
+	spin_lock_irqsave(IRQ_LOCK(irq), flags);
 	desc->disable_depth++;
 	list_del_init(&desc->pend);
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 }
 
 /**
@@ -116,10 +140,10 @@
  */
 void enable_irq(unsigned int irq)
 {
-	struct irqdesc *desc = irq_desc + irq;
+	struct irqdesc *desc = IRQ_DESC(irq);
 	unsigned long flags;
 
-	spin_lock_irqsave(&irq_controller_lock, flags);
+	spin_lock_irqsave(IRQ_LOCK(irq), flags);
 	if (unlikely(!desc->disable_depth)) {
 		printk("enable_irq(%u) unbalanced from %p\n", irq,
 			__builtin_return_address(0));
@@ -140,7 +164,7 @@
 				list_add(&desc->pend, &irq_pending);
 		}
 	}
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 }
 
 /*
@@ -148,24 +172,24 @@
  */
 void enable_irq_wake(unsigned int irq)
 {
-	struct irqdesc *desc = irq_desc + irq;
+	struct irqdesc *desc = IRQ_DESC(irq);
 	unsigned long flags;
 
-	spin_lock_irqsave(&irq_controller_lock, flags);
+	spin_lock_irqsave(IRQ_LOCK(irq), flags);
 	if (desc->chip->wake)
 		desc->chip->wake(irq, 1);
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 }
 
 void disable_irq_wake(unsigned int irq)
 {
-	struct irqdesc *desc = irq_desc + irq;
+	struct irqdesc *desc = IRQ_DESC(irq);
 	unsigned long flags;
 
-	spin_lock_irqsave(&irq_controller_lock, flags);
+	spin_lock_irqsave(IRQ_LOCK(irq), flags);
 	if (desc->chip->wake)
 		desc->chip->wake(irq, 0);
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 }
 
 int show_interrupts(struct seq_file *p, void *v)
@@ -175,8 +199,8 @@
 	unsigned long flags;
 
 	if (i < NR_IRQS) {
-		spin_lock_irqsave(&irq_controller_lock, flags);
-	    	action = irq_desc[i].action;
+		spin_lock_irqsave(IRQ_LOCK(irq), flags);
+	    	action = IRQ_DESC(i)->action;
 		if (!action)
 			goto unlock;
 
@@ -187,7 +211,7 @@
 
 		seq_putc(p, '\n');
 unlock:
-		spin_unlock_irqrestore(&irq_controller_lock, flags);
+		spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 	} else if (i == NR_IRQS) {
 #ifdef CONFIG_ARCH_ACORN
 		show_fiq_list(p, v);
@@ -259,7 +283,7 @@
 	unsigned int status;
 	int retval = 0;
 
-	spin_unlock(&irq_controller_lock);
+	spin_unlock(IRQ_LOCK(irq));
 
 	if (!(action->flags & SA_INTERRUPT))
 		local_irq_enable();
@@ -274,7 +298,7 @@
 	if (status & SA_SAMPLE_RANDOM)
 		add_interrupt_randomness(irq);
 
-	spin_lock_irq(&irq_controller_lock);
+	spin_lock_irq(IRQ_LOCK(irq));
 
 	return retval;
 }
@@ -455,7 +479,7 @@
 		desc = &bad_irq_desc;
 
 	irq_enter();
-	spin_lock(&irq_controller_lock);
+	spin_lock(IRQ_LOCK(irq));
 	desc->handle(irq, desc, regs);
 
 	/*
@@ -464,7 +488,7 @@
 	if (!list_empty(&irq_pending))
 		do_pending_irqs(regs);
 
-	spin_unlock(&irq_controller_lock);
+	spin_unlock(IRQ_LOCK(irq));
 	irq_exit();
 }
 
@@ -473,7 +497,7 @@
 	struct irqdesc *desc;
 	unsigned long flags;
 
-	if (irq >= NR_IRQS) {
+	if (!IRQ_VALID(irq)) {
 		printk(KERN_ERR "Trying to install handler for IRQ%d\n", irq);
 		return;
 	}
@@ -481,12 +505,12 @@
 	if (handle == NULL)
 		handle = do_bad_IRQ;
 
-	desc = irq_desc + irq;
+	desc = IRQ_DESC(irq);
 
 	if (is_chained && desc->chip == &bad_chip)
 		printk(KERN_WARNING "Trying to install chained handler for IRQ%d\n", irq);
 
-	spin_lock_irqsave(&irq_controller_lock, flags);
+	spin_lock_irqsave(IRQ_LOCK(irq), flags);
 	if (handle == do_bad_IRQ) {
 		desc->chip->mask(irq);
 		desc->chip->ack(irq);
@@ -499,7 +523,7 @@
 		desc->disable_depth = 0;
 		desc->chip->unmask(irq);
 	}
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 }
 
 void set_irq_chip(unsigned int irq, struct irqchip *chip)
@@ -507,7 +531,7 @@
 	struct irqdesc *desc;
 	unsigned long flags;
 
-	if (irq >= NR_IRQS) {
+	if (!IRQ_VALID(irq)) {
 		printk(KERN_ERR "Trying to install chip for IRQ%d\n", irq);
 		return;
 	}
@@ -515,10 +539,10 @@
 	if (chip == NULL)
 		chip = &bad_chip;
 
-	desc = irq_desc + irq;
-	spin_lock_irqsave(&irq_controller_lock, flags);
+	desc = IRQ_DESC(irq);
+	spin_lock_irqsave(IRQ_LOCK(irq), flags);
 	desc->chip = chip;
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 }
 
 int set_irq_type(unsigned int irq, unsigned int type)
@@ -527,16 +551,21 @@
 	unsigned long flags;
 	int ret = -ENXIO;
 
-	if (irq >= NR_IRQS) {
+	if (!IRQ_VALID(irq)) {
 		printk(KERN_ERR "Trying to set irq type for IRQ%d\n", irq);
 		return -ENODEV;
 	}
 
-	desc = irq_desc + irq;
+	desc = IRQ_DESC(irq);
+	if (!desc->action && desc->handle != do_bad_IRQ) {
+		printk(KERN_ERR "Setting type of unclaimed IRQ%d from ", irq);
+		print_symbol("%s\n", (unsigned long)__builtin_return_address(0));
+	}
+
 	if (desc->chip->type) {
-		spin_lock_irqsave(&irq_controller_lock, flags);
+		spin_lock_irqsave(IRQ_LOCK(irq), flags);
 		ret = desc->chip->type(irq, type);
-		spin_unlock_irqrestore(&irq_controller_lock, flags);
+		spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 	}
 
 	return ret;
@@ -547,17 +576,17 @@
 	struct irqdesc *desc;
 	unsigned long flags;
 
-	if (irq >= NR_IRQS) {
+	if (!IRQ_VALID(irq)) {
 		printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
 		return;
 	}
 
-	desc = irq_desc + irq;
-	spin_lock_irqsave(&irq_controller_lock, flags);
+	desc = IRQ_DESC(irq);
+	spin_lock_irqsave(IRQ_LOCK(irq), flags);
 	desc->valid = (iflags & IRQF_VALID) != 0;
 	desc->probe_ok = (iflags & IRQF_PROBE) != 0;
 	desc->noautoenable = (iflags & IRQF_NOAUTOEN) != 0;
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 }
 
 int setup_irq(unsigned int irq, struct irqaction *new)
@@ -587,13 +616,13 @@
 	/*
 	 * The following block of code has to be executed atomically
 	 */
-	desc = irq_desc + irq;
-	spin_lock_irqsave(&irq_controller_lock, flags);
+	desc = IRQ_DESC(irq);
+	spin_lock_irqsave(IRQ_LOCK(irq), flags);
 	p = &desc->action;
 	if ((old = *p) != NULL) {
 		/* Can't share interrupts unless both agree to */
 		if (!(old->flags & new->flags & SA_SHIRQ)) {
-			spin_unlock_irqrestore(&irq_controller_lock, flags);
+			spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 			return -EBUSY;
 		}
 
@@ -618,7 +647,7 @@
 		}
 	}
 
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 	return 0;
 }
 
@@ -659,7 +688,7 @@
 	unsigned long retval;
 	struct irqaction *action;
 
-	if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler ||
+	if (!IRQ_VALID(irq) || !IRQ_DESC(irq)->valid || !handler ||
 	    (irq_flags & SA_SHIRQ && !dev_id))
 		return -EINVAL;
 
@@ -700,14 +729,14 @@
 	struct irqaction * action, **p;
 	unsigned long flags;
 
-	if (irq >= NR_IRQS || !irq_desc[irq].valid) {
+	if (!IRQ_VALID(irq) || !IRQ_DESC(irq)->valid) {
 		printk(KERN_ERR "Trying to free IRQ%d\n",irq);
 		dump_stack();
 		return;
 	}
 
-	spin_lock_irqsave(&irq_controller_lock, flags);
-	for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
+	spin_lock_irqsave(IRQ_LOCK(irq), flags);
+	for (p = &IRQ_DESC(irq)->action; (action = *p) != NULL; p = &action->next) {
 		if (action->dev_id != dev_id)
 			continue;
 
@@ -715,7 +744,7 @@
 		*p = action->next;
 		break;
 	}
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	spin_unlock_irqrestore(IRQ_LOCK(irq), flags);
 
 	if (!action) {
 		printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
@@ -747,19 +776,18 @@
 	 * first snaffle up any unassigned but
 	 * probe-able interrupts
 	 */
-	spin_lock_irq(&irq_controller_lock);
 	for (i = 0; i < NR_IRQS; i++) {
-		if (!irq_desc[i].probe_ok || irq_desc[i].action)
-			continue;
-
-		irq_desc[i].probing = 1;
-		irq_desc[i].triggered = 0;
-		if (irq_desc[i].chip->type)
-			irq_desc[i].chip->type(i, IRQT_PROBE);
-		irq_desc[i].chip->unmask(i);
-		irqs += 1;
+		spin_lock_irq(IRQ_LOCK(i));
+		if (irq_desc[i].probe_ok && !irq_desc[i].action) {
+			irq_desc[i].probing = 1;
+			irq_desc[i].triggered = 0;
+			if (irq_desc[i].chip->type)
+				irq_desc[i].chip->type(i, IRQT_PROBE);
+			irq_desc[i].chip->unmask(i);
+			irqs += 1;
+		}
+		spin_unlock_irq(IRQ_LOCK(i));
 	}
-	spin_unlock_irq(&irq_controller_lock);
 
 	/*
 	 * wait for spurious interrupts to mask themselves out again
@@ -770,14 +798,14 @@
 	/*
 	 * now filter out any obviously spurious interrupts
 	 */
-	spin_lock_irq(&irq_controller_lock);
 	for (i = 0; i < NR_IRQS; i++) {
+		spin_lock_irq(IRQ_LOCK(i));
 		if (irq_desc[i].probing && irq_desc[i].triggered) {
 			irq_desc[i].probing = 0;
 			irqs -= 1;
 		}
+		spin_unlock_irq(IRQ_LOCK(i));
 	}
-	spin_unlock_irq(&irq_controller_lock);
 
 	return irqs;
 }
@@ -788,11 +816,13 @@
 {
 	unsigned int mask = 0, i;
 
-	spin_lock_irq(&irq_controller_lock);
-	for (i = 0; i < 16 && i < NR_IRQS; i++)
-		if (irq_desc[i].probing && irq_desc[i].triggered)
+	for (i = 0; i < 16 && i < NR_IRQS; i++) {
+		struct irqdesc *desc = IRQ_DESC(i);
+		spin_lock_irq(IRQ_LOCK(i));
+		if (desc->probing && desc->triggered)
 			mask |= 1 << i;
-	spin_unlock_irq(&irq_controller_lock);
+		spin_unlock_irq(IRQ_LOCK(i));
+	}
 
 	up(&probe_sem);
 
@@ -813,23 +843,21 @@
 	 * look at the interrupts, and find exactly one
 	 * that we were probing has been triggered
 	 */
-	spin_lock_irq(&irq_controller_lock);
 	for (i = 0; i < NR_IRQS; i++) {
-		if (irq_desc[i].probing &&
-		    irq_desc[i].triggered) {
+		struct irqdesc *desc = IRQ_DESC(i);
+
+		spin_lock_irq(IRQ_LOCK(i));
+		if (desc->probing && desc->triggered) {
 			if (irq_found != NO_IRQ) {
+				spin_unlock_irq(IRQ_LOCK(i));
 				irq_found = NO_IRQ;
-				goto out;
+				break;
 			}
 			irq_found = i;
 		}
+		spin_unlock_irq(IRQ_LOCK(i));
 	}
 
-	if (irq_found == -1)
-		irq_found = NO_IRQ;
-out:
-	spin_unlock_irq(&irq_controller_lock);
-
 	up(&probe_sem);
 
 	return irq_found;
--- linux-2.6.5/arch/arm/kernel/bios32.c~heh	2004-04-03 22:36:56.000000000 -0500
+++ linux-2.6.5/arch/arm/kernel/bios32.c	2004-04-30 20:57:36.000000000 -0400
@@ -565,8 +565,6 @@
 	if (hw->postinit)
 		hw->postinit();
 
-	pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq);
-
 	list_for_each_entry(sys, &hw->buses, node) {
 		struct pci_bus *bus = sys->bus;
 
@@ -581,6 +579,11 @@
 		pci_bus_assign_resources(bus);
 
 		/*
+		 * Fixup IRQs.
+		 */
+		pci_bus_fixup_irqs(bus, pcibios_swizzle, pcibios_map_irq);
+
+		/*
 		 * Tell drivers about devices found.
 		 */
 		pci_bus_add_devices(bus);
--- linux-2.6.5/arch/arm/kernel/traps.c~heh	2004-04-03 22:36:57.000000000 -0500
+++ linux-2.6.5/arch/arm/kernel/traps.c	2004-04-30 20:57:36.000000000 -0400
@@ -206,33 +206,43 @@
 	c_backtrace(fp, 0x10);
 }
 
-spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
-
-/*
- * This function is protected against re-entrancy.
- */
-NORET_TYPE void die(const char *str, struct pt_regs *regs, int err)
+static void __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs)
 {
-	struct task_struct *tsk = current;
+	struct task_struct *tsk = thread->task;
 	static int die_counter;
 
-	console_verbose();
-	spin_lock_irq(&die_lock);
-	bust_spinlocks(1);
-
 	printk("Internal error: %s: %x [#%d]\n", str, err, ++die_counter);
 	print_modules();
 	printk("CPU: %d\n", smp_processor_id());
 	show_regs(regs);
 	printk("Process %s (pid: %d, stack limit = 0x%p)\n",
-		tsk->comm, tsk->pid, tsk->thread_info + 1);
+		tsk->comm, tsk->pid, thread + 1);
 
 	if (!user_mode(regs) || in_interrupt()) {
 		dump_mem("Stack: ", regs->ARM_sp, 8192+(unsigned long)tsk->thread_info);
 		dump_backtrace(regs, tsk);
 		dump_instr(regs);
 	}
+}
+
+void nmi_watchdog(struct thread_info *thread, struct pt_regs *regs)
+{
+	__die("NMI watchdog", 0, thread, regs);
+}
 
+spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * This function is protected against re-entrancy.
+ */
+NORET_TYPE void die(const char *str, struct pt_regs *regs, int err)
+{
+	struct thread_info *thread = current_thread_info();
+
+	console_verbose();
+	spin_lock_irq(&die_lock);
+	bust_spinlocks(1);
+	__die(str, err, thread, regs);
 	bust_spinlocks(0);
 	spin_unlock_irq(&die_lock);
 	do_exit(SIGSEGV);
--- linux-2.6.5/arch/arm/Kconfig~heh	2004-04-03 22:37:07.000000000 -0500
+++ linux-2.6.5/arch/arm/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -297,7 +297,7 @@
 
 config CPU_FREQ
 	bool "Support CPU clock change (EXPERIMENTAL)"
-	depends on (ARCH_SA1100 || ARCH_INTEGRATOR) && EXPERIMENTAL
+	depends on (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_PXA) && EXPERIMENTAL
 	help
 	  CPU clock scaling allows you to change the clock speed of the
 	  running CPU on the fly. This is a nice method to save battery power,
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/fastfpe/entry.S	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,294 @@
+/*
+At entry the registers contain the following information:
+
+r14	return address for undefined exception return
+r9	return address for return from exception
+r13	user registers on stack, offset 0 up to offset 4*15 contains
+	registers r0..15, then the psr
+r10	FP workspace 35 words (init, reg[8][4], fpsr, fpcr)
+ 
+*/
+
+/*---------------------------------------------------------------------------*/
+
+	.data
+fp_const:
+	.word	0, 0x00000000, 0, 0x80000000	@ 0
+	.word	0, 0x80000000, 0,          0	@ 1
+	.word	0, 0x80000000, 0,          1	@ 2
+	.word	0, 0xc0000000, 0,          1	@ 3
+	.word	0, 0x80000000, 0,          2	@ 4
+	.word	0, 0xa0000000, 0,          2	@ 5
+	.word	0, 0x80000000, 0,	  -1	@ 0.5
+	.word	0, 0xa0000000, 0,          3	@ 10
+fp_undef:
+	.word	0
+fp_cond:
+	.word	0xf0f0	@ eq
+	.word	0x0f0f	@ ne
+	.word	0xcccc	@ cs
+	.word	0x3333	@ cc
+	.word	0xff00	@ mi
+	.word	0x00ff	@ pl
+	.word	0xaaaa	@ vs
+	.word	0x5555	@ vc
+	.word	0x0c0c	@ hi
+	.word	0xf3f3	@ ls
+	.word	0xaa55	@ ge
+	.word	0x55aa	@ lt
+	.word	0x0a05	@ gt
+	.word	0xf5fa	@ le
+	.word	0xffff	@ al
+	.word	0x0000	@ nv
+	
+/*---------------------------------------------------------------------------*/
+	
+	.text
+	.globl	fastfpe_enter
+fastfpe_enter:
+	ldr	r4,=fp_undef
+	str	r14,[r4]		@ to free one register
+	add	r10,r10,#4		@ to make the code simpler
+	mov	r4, r0			@ r4=trapped instruction
+	and	r1,r4,#0x00000f00	@ r1=coprocessor << 8
+next_enter:
+	cmp	r1,#1<<8		@ copro 1 ?
+	beq	copro_1
+	cmp	r1,#2<<8
+	movne	pc,r14
+
+copro_2:
+	and	r1,r4,#0x0f000000
+	cmp	r1,#0x0c000000          @ CPDT with post indexing
+        cmpne   r1,#0x0d000000          @ CPDT with pre indexing
+        beq     CPDT_M_enter
+	mov	pc,r14
+
+copro_1:
+	and	r1,r4,#0x0f000000
+	cmp	r1,#0x0e000000		@ CPDO
+	beq	CPDO_CPRT_enter
+	cmp	r1,#0x0c000000		@ CPDT with post indexing
+	cmpne	r1,#0x0d000000		@ CPDT with pre indexing
+	beq	CPDT_1_enter
+	mov	pc,r14
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	fastfpe_next
+fastfpe_next:
+	ldr	r5,[r13,#60]
+next_after_cond:
+__x1:
+	ldrt	r4,[r5],#4
+	
+	ldr	r0,=fp_cond		@ check condition of next instruction
+	ldr	r1,[r13,#64]		@ psr containing flags
+	mov	r2,r4,lsr#28
+	mov	r1,r1,lsr#28
+	ldr	r0,[r0,r2,lsl#2]
+	mov	r0,r0,lsr r1
+	tst	r0,#1
+	beq	next_after_cond		@ must not necessarily have been an
+					@ FP instruction !
+	and	r1,r4,#0x0f000000	@ Test for copro instruction
+	cmp	r1,#0x0c000000
+	rsbgts	r0,r1,#0x0e000000	@ cmpgt #0x0e000000,r1
+	movlt	pc,r9			@ next is no copro instruction, return
+	
+	ands	r1,r4,#0x00000f00	@ r1 = coprocessor << 8
+	cmpne	r1,#3<<8
+	movge	pc,r9			@ copro = 0 or >=3, return
+		
+	str	r5,[r13,#60]		@ save updated pc
+	b	next_enter
+
+/*---------------------------------------------------------------------------*/
+
+undefined:
+	ldr	r4,=fp_undef
+	ldr	pc,[r4]	
+
+/*---------------------------------------------------------------------------*/
+
+CPDT_1_enter:
+	and	r5,r4,#0x000f0000	@ r5=base register number << 16
+	ldr	r6,[r13,r5,lsr#14]	@ r6=base address
+	cmp	r5,#0x000f0000		@ base register = pc ?
+	addeq	r6,r6,#4
+	and	r7,r4,#0x000000ff	@ r7=offset value
+
+	tst	r4,#0x00800000		@ up or down?
+	addne	r7,r6,r7,lsl#2
+	subeq	r7,r6,r7,lsl#2		@ r6=base address +/- offset
+	tst	r4,#0x01000000		@ preindexing ?
+	movne	r6,r7
+	tst	r4,#0x00200000		@ write back ?
+	cmpne	r5,#0x000f0000		@ base register = pc ?
+	strne	r7,[r13,r5,lsr#14]
+
+	and	r0,r4,#0x00007000	@ r0=fp register number << 12
+	add	r0,r10,r0,lsr#8		@ r0=address of fp register
+	mov	r1,#0
+	tst	r4,#0x00008000
+	orrne	r1,r1,#1		@ T0
+	tst	r4,#0x00400000
+	orrne	r1,r1,#2		@ T1
+	tst	r4,#0x00100000
+	orrne	r1,r1,#4		@ L/S
+
+	add	pc,pc,r1,lsl#2
+	mov	r0,r0
+	b	CPDT_store_single	@ these functions get 
+	b	CPDT_store_double	@ r0=address of fp register
+	b	CPDT_store_extended	@ r6=address of data
+	b	undefined		@ CPDT_store_decimal
+        b       CPDT_load_single
+        b       CPDT_load_double
+        b       CPDT_load_extended
+        b       undefined		@ CPDT_load_decimal
+
+/*---------------------------------------------------------------------------*/
+
+CPDT_M_enter:
+	and	r5,r4,#0x000f0000	@ r5=base register number << 16
+	ldr	r6,[r13,r5,lsr#14]	@ r6=base address
+	cmp	r5,#0x000f0000		@ base register = pc ?
+	addeq	r6,r6,#4
+	and	r7,r4,#0x000000ff	@ r7=offset value
+
+	tst	r4,#0x00800000		@ up or down?
+	addne	r7,r6,r7,lsl#2
+	subeq	r7,r6,r7,lsl#2		@ r7=base address +/- offset
+	tst	r4,#0x01000000		@ preindexing ?
+	movne	r6,r7
+	tst	r4,#0x00200000		@ write back ?
+	cmpne	r5,#0x000f0000		@ base register = pc ?
+	strne	r7,[r13,r5,lsr#14]
+
+	and	r0,r4,#0x00007000	@ r0=fp register number << 12
+	and	r1,r4,#0x00008000
+	mov	r1,r1,lsr#15		@ N0
+	and	r2,r4,#0x00400000
+	orrs	r1,r1,r2,lsr#21		@ N1
+	addeq	r1,r1,#4		@ r1=register count
+
+	tst	r4,#0x00100000		@ load/store
+	beq	CPDT_sfm
+	b	CPDT_lfm
+
+/*---------------------------------------------------------------------------*/
+
+CPDO_CPRT_enter:
+	tst	r4,#0x00000010
+	bne	CPRT_enter
+	
+	and	r0,r4,#0x00007000
+	add	r0,r10,r0,lsr#8		@ r0=address of Fd
+	and	r1,r4,#0x00070000
+	add	r1,r10,r1,lsr#12	@ r1=address of Fn
+	tst	r4,#0x00000008
+	bne	CPDO_const
+	and	r2,r4,#0x00000007
+	add	r2,r10,r2,lsl#4		@ r2=address of Fm
+	
+CPDO_constback:
+	and	r3,r4,#0x00f00000
+	tst	r4,#0x00008000
+	orrne	r3,r3,#0x01000000
+		
+	add	pc,pc,r3,lsr#18
+	mov	r0,r0
+	b	CPDO_adf
+	b	CPDO_muf
+	b	CPDO_suf
+	b	CPDO_rsf
+	b	CPDO_dvf
+	b	CPDO_rdf
+	b	undefined
+	b	undefined
+	b	undefined		@ CPDO_rmf
+	b	CPDO_muf
+	b	CPDO_dvf
+	b	CPDO_rdf
+	b	undefined
+	b	undefined
+	b	undefined
+	b	undefined
+	b	CPDO_mvf
+	b	CPDO_mnf
+	b	CPDO_abs
+	b	CPDO_rnd
+	b	CPDO_sqt
+	b	undefined
+	b	undefined
+	b	undefined
+	b	undefined
+	b	undefined
+	b	undefined
+	b	undefined
+	b	undefined
+	b	undefined
+	b	CPDO_rnd
+	b	fastfpe_next
+
+CPDO_const:
+	ldr	r2,=fp_const
+	and	r3,r4,#0x00000007
+	add	r2,r2,r3,lsl#4	
+	b	CPDO_constback
+	
+/*---------------------------------------------------------------------------*/
+
+CPRT_enter:
+	and	r0,r4,#0x0000f000	@ r0=Rd<<12
+	and	r1,r4,#0x00070000
+	add	r1,r10,r1,lsr#12	@ r1=address of Fn
+	tst	r4,#0x00000008
+	bne	CPRT_const
+	and	r2,r4,#0x00000007
+	add	r2,r10,r2,lsl#4		@ r2=address of Fm
+	
+CPRT_constback:
+	and	r3,r4,#0x00f00000
+		
+	add	pc,pc,r3,lsr#18
+	mov	r0,r0
+	b	CPRT_flt
+	b	CPRT_fix
+	b	CPRT_wfs
+	b	CPRT_rfs
+	b	undefined
+	b	undefined
+	b	undefined
+	b	undefined
+	b	undefined
+	b	CPRT_cmf
+	b	undefined
+	b	CPRT_cnf
+	b	undefined
+	b	CPRT_cmf
+	b	undefined
+	b	CPRT_cnf
+
+CPRT_const:
+	ldr	r2,=fp_const
+	and	r3,r4,#0x00000007
+	add	r2,r2,r3,lsl#4	
+	b	CPRT_constback
+	
+/*---------------------------------------------------------------------------*/
+
+	@ The fetch of the next instruction to emulate could fault
+
+	.section .fixup,"ax"
+	.align
+__f1:
+	mov	pc,r9
+	.previous
+	.section __ex_table,"a"
+	.align 3
+	.long	__x1,__f1
+	.previous
+	
+/*---------------------------------------------------------------------------*/
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/fastfpe/module.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,62 @@
+/*
+    Fast Floating Point Emulator
+    (c) Peter Teichmann <mail@peter-teichmann.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+
+#ifndef MODULE
+#define kern_fp_enter	fp_enter
+
+extern char fpe_type[];
+#endif
+
+static void (*orig_fp_enter)(void);	/* old kern_fp_enter value */
+extern void (*kern_fp_enter)(void);	/* current FP handler */
+extern void fastfpe_enter(void);	/* forward declarations */
+
+static int __init fpe_init(void)
+{
+  if (fpe_type[0] && strcmp(fpe_type, "fastfpe"))
+    return 0;
+
+  printk("Fast Floating Point Emulator V0.9 (c) Peter Teichmann.\n");
+
+  /* Save pointer to the old FP handler and then patch ourselves in */
+  orig_fp_enter = kern_fp_enter;
+  kern_fp_enter = fastfpe_enter;
+
+  return 0;
+}
+
+static void __exit fpe_exit(void)
+{
+  /* Restore the values we saved earlier. */
+  kern_fp_enter = orig_fp_enter;
+}
+
+module_init(fpe_init);
+module_exit(fpe_exit);
+
+MODULE_AUTHOR("Peter Teichmann <mail@peter-teichmann.de>");
+MODULE_DESCRIPTION("Fast floating point emulator with full precision");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/fastfpe/CPDO.S	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,682 @@
+/*
+The FP structure has 4 words reserved for each register, the first is used just
+for the sign in bit 31, the second and third are for the mantissa (unsigned
+integer, high 32 bit first) and the fourth is the exponent (signed integer).
+The mantissa is always normalized.
+
+If the exponent is 0x80000000, that is the most negative value, the number
+represented is 0 and both mantissa words are also 0.
+
+If the exponent is 0x7fffffff, that is the biggest positive value, the number
+represented is infinity if the high 32 mantissa bit are also 0, otherwise it is
+a NaN. The low 32 mantissa bit are 0 if the number represented is infinity.
+
+Decimal and packed decimal numbers are not supported yet.
+
+The parameters to these functions are r0=destination pointer, r1 and r2
+source pointers. r4 is the instruction. They may use r0-r8 and r14. They return
+to fastfpe_next, except CPDO_rnf_core which expects the return address in r14.
+*/
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDO_adf
+CPDO_adf:
+	ldmia	r1,{r1,r3,r5,r7}
+	ldmia	r2,{r2,r4,r6,r8}
+
+	cmp	r7,#0x7fffffff
+	cmpne	r8,#0x7fffffff
+	beq	CPDO_adf_extra
+
+	cmp	r1,r2
+	bne	CPDO_suf_s
+
+CPDO_adf_s:
+	subs	r2,r7,r8
+	bge	CPDO_adf_2nd
+	
+	mov	r7,r8
+	rsb	r2,r2,#0
+	cmp	r2,#32
+	ble	CPDO_adf_1st2
+
+	sub	r2,r2,#32
+	cmp	r2,#32
+	movgt	r2,#32
+	mov	r5,r3,lsr r2
+	mov	r3,#0
+	b	CPDO_adf_add
+
+CPDO_adf_1st2:
+	rsb	r8,r2,#32
+	mov	r5,r5,lsr r2
+	orr	r5,r5,r3,lsl r8
+	mov	r3,r3,lsr r2	@ 1. op normalized
+	b	CPDO_adf_add
+
+CPDO_adf_2nd:
+	cmp	r2,#32
+	ble	CPDO_adf_2nd2
+
+	sub	r2,r2,#32
+	cmp	r2,#32
+	movgt	r2,#32
+	mov	r6,r4,lsr r2
+	mov	r4,#0
+	b	CPDO_adf_add
+
+CPDO_adf_2nd2:
+	rsb	r8,r2,#32
+	mov	r6,r6,lsr r2
+	orr	r6,r6,r4,lsl r8
+	mov	r4,r4,lsr r2	@ 2. op normalized
+
+CPDO_adf_add:
+	adds	r5,r5,r6
+	adcs	r3,r3,r4	@ do addition
+	bcc	CPDO_adf_end
+
+	add	r7,r7,#1
+	movs	r3,r3,rrx
+	mov	r5,r5,rrx	@ correct for overflow
+
+CPDO_adf_end:
+	cmp	r7,#0x20000000
+	bge	CPDO_inf
+
+	stmia	r0,{r1,r3,r5,r7}
+	b	fastfpe_next
+
+CPDO_adf_extra:
+	cmp	r7,#0x7fffffff		@ was it the 1st ?
+	bne	CPDO_infnan_2		@ no it was the 2nd
+	cmp	r8,#0x7fffffff		@ if 1st, 2nd too ?
+	bne	CPDO_infnan_1		@ no only 1st
+	cmp	r3,#0
+	cmpeq	r4,#0
+	bne	CPDO_nan_12
+	b	CPDO_inf
+
+/*---------------------------------------------------------------------------*/
+
+CPDO_infnan_1:
+	stmia	r0,{r1,r3,r5,r7}
+	b	fastfpe_next
+
+CPDO_infnan_2:
+	stmia	r0,{r2,r4,r6,r8}
+	b	fastfpe_next
+	
+CPDO_nan_12:
+	orr	r2,r3,r4
+	b	CPDO_inf_1
+
+CPDO_nan:
+	mov	r2,#0x40000000		@ create non signalling NaN
+	b	CPDO_inf_1
+
+CPDO_inf:
+	mov	r2,#0
+CPDO_inf_1:
+	mov	r3,#0
+	mov	r4,#0x7fffffff
+CPDO_store_1234:
+	stmia	r0,{r1,r2,r3,r4}
+	b	fastfpe_next
+
+CPDO_zero:
+	mov	r1,#0
+CPDO_zero_1:
+	mov	r2,#0
+	mov	r3,#0
+	mov	r4,#0x80000000
+	stmia	r0,{r1,r2,r3,r4}
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDO_suf
+CPDO_suf:
+	ldmia	r1,{r1,r3,r5,r7}
+	ldmia	r2,{r2,r4,r6,r8}
+
+CPDO_suf_l:
+	cmp	r7,#0x7fffffff
+	cmpne	r8,#0x7fffffff
+	beq	CPDO_suf_extra
+
+	cmp	r1,r2
+	bne	CPDO_adf_s
+
+CPDO_suf_s:
+	subs	r2,r7,r8		@ determine greater number
+	bgt	CPDO_suf_2nd		@ first number is greater
+	blt	CPDO_suf_1st		@ second number is greater
+	cmp	r3,r4			@ also mantissa is important
+	cmpeq	r5,r6
+	bhi	CPDO_suf_2nd		@ first number is greater
+	beq	CPDO_zero
+
+CPDO_suf_1st:
+	eor	r1,r1,#0x80000000	@ second number is greater, invert sign
+	mov	r7,r8
+	rsb	r2,r2,#0
+	cmp	r2,#32
+	ble	CPDO_suf_1st2
+
+	sub	r2,r2,#32
+	cmp	r2,#32
+	movgt	r2,#32
+	mov	r5,r3,lsr r2
+	mov	r3,#0
+	b	CPDO_suf_1st_sub
+
+CPDO_suf_1st2:
+	rsb	r8,r2,#32
+	mov	r5,r5,lsr r2
+	orr	r5,r5,r3,lsl r8
+	mov	r3,r3,lsr r2	@ 1. op normalized
+
+CPDO_suf_1st_sub:
+	subs	r5,r6,r5	@ do subtraction
+	sbc	r3,r4,r3
+	b	CPDO_suf_norm
+
+CPDO_suf_2nd:
+	cmp	r2,#32
+	ble	CPDO_suf_2nd2
+
+	sub	r2,r2,#32
+	cmp	r2,#32
+	movgt	r2,#32
+	mov	r6,r4,lsr r2
+	mov	r4,#0
+	b	CPDO_suf_2nd_sub
+
+CPDO_suf_2nd2:
+	rsb	r8,r2,#32
+	mov	r6,r6,lsr r2
+	orr	r6,r6,r4,lsl r8
+	mov	r4,r4,lsr r2	@ 2. op normalized
+
+CPDO_suf_2nd_sub:
+	subs	r5,r5,r6
+	sbc	r3,r3,r4	@ do subtraction
+
+CPDO_suf_norm:
+	teq	r3,#0		@ normalize 32bit
+	moveq	r3,r5
+	moveq	r5,#0
+	subeq	r7,r7,#32
+	
+	cmp	r3,#0x00010000	@ 16bit
+	movcc	r3,r3,lsl#16
+	orrcc	r3,r3,r5,lsr#16
+	movcc	r5,r5,lsl#16
+	subcc	r7,r7,#16
+	
+	cmp	r3,#0x01000000	@ 8bit
+	movcc	r3,r3,lsl#8
+	orrcc	r3,r3,r5,lsr#24
+	movcc	r5,r5,lsl#8
+	subcc	r7,r7,#8
+	
+	cmp	r3,#0x10000000	@ 4bit
+	movcc	r3,r3,lsl#4
+	orrcc	r3,r3,r5,lsr#28
+	movcc	r5,r5,lsl#4
+	subcc	r7,r7,#4
+	
+	cmp	r3,#0x40000000	@ 2bit
+	movcc	r3,r3,lsl#2
+	orrcc	r3,r3,r5,lsr#30
+	movcc	r5,r5,lsl#2
+	subcc	r7,r7,#2
+	
+	cmp	r3,#0x80000000	@ 1bit
+	movcc	r3,r3,lsl#1
+	orrcc	r3,r3,r5,lsr#31
+	movcc	r5,r5,lsl#1
+	subcc	r7,r7,#1
+
+	cmp	r7,#0xe0000000
+	ble	CPDO_zero_1
+
+	stmia	r0,{r1,r3,r5,r7}
+	b	fastfpe_next
+
+CPDO_suf_extra:
+	cmp	r7,#0x7fffffff		@ was it the 1st ?
+	eorne	r2,r2,#0x80000000	@ change sign, might have been INF
+	bne	CPDO_infnan_2		@ no it was the 2nd
+	cmp	r8,#0x7fffffff		@ if 1st, 2nd too ?
+	bne	CPDO_infnan_1		@ no only 1st
+	cmp	r3,#0
+	cmpeq	r4,#0
+	bne	CPDO_nan_12
+	b	CPDO_nan		@ here is difference with adf !
+
+/*---------------------------------------------------------------------------*/
+
+	.globl CPDO_rsf
+CPDO_rsf:
+	mov	r3,r2
+	ldmia	r1,{r2,r4,r6,r8}
+	ldmia	r3,{r1,r3,r5,r7}
+	b	CPDO_suf_l
+	
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDO_muf
+CPDO_muf:
+	ldmia	r1,{r1,r3,r5,r7}
+	ldmia	r2,{r2,r4,r6,r8}
+
+	cmp	r7,#0x7fffffff
+	cmpne	r8,#0x7fffffff
+	beq	CPDO_muf_extra
+	
+	eor	r1,r1,r2
+	adds	r8,r7,r8
+	bvs	CPDO_zero_1
+
+	umull	r7,r2,r3,r4
+	umull	r14,r3,r6,r3
+	adds	r7,r7,r3	@ r2|r7|r14 = r2|r7|#0 + #0|r3|r14
+	adc	r2,r2,#0
+	umull	r4,r3,r5,r4
+	adds	r14,r14,r4	@ r2|r7|r14 += #0|r3|r4
+	adcs	r7,r7,r3
+	adc	r2,r2,#0
+	umull	r4,r3,r5,r6
+	adds	r14,r14,r3	@ r2|r7|r14 += #0|#0|r3
+	adcs	r7,r7,#0
+	adcs	r2,r2,#0
+
+	bpl	CPDO_muf_norm
+	
+	add	r8,r8,#1
+	b	CPDO_muf_end
+	
+CPDO_muf_norm:
+	adds	r14,r14,r14
+	adcs	r7,r7,r7
+	adcs	r2,r2,r2
+
+CPDO_muf_end:
+	cmp	r8,#0x20000000
+	bge	CPDO_inf
+	cmp	r8,#0xe0000000
+	ble	CPDO_zero_1
+	stmia	r0,{r1,r2,r7,r8}
+	b	fastfpe_next
+
+CPDO_muf_extra:
+	cmp	r7,#0x7fffffff		@ was it the first?
+	bne	CPDO_muf_extra_2nd	@ no, so it was the second
+	cmp	r8,#0x7fffffff		@ yes, second too?
+	bne	CPDO_muf_extra_1st	@ no, only first
+	orr	r3,r3,r4		@ if both inf -> inf, otherwise nan
+	eor	r1,r1,r2		@ sign for the inf case
+	b	CPDO_infnan_1
+
+CPDO_muf_extra_1st:
+	cmp	r3,#0			@ is it a nan?
+	bne	CPDO_infnan_1
+	cmp	r8,#0x80000000		@ is the second 0?
+	beq	CPDO_nan
+	eor	r1,r1,r2		@ correct sign for inf
+	b	CPDO_inf
+
+CPDO_muf_extra_2nd:
+	cmp	r4,#0			@ is it a nan?
+	bne	CPDO_infnan_2
+	cmp	r7,#0x80000000		@ is the first 0?
+	beq	CPDO_nan
+	eor	r1,r1,r2		@ correct sign for inf
+	b	CPDO_inf
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDO_dvf
+CPDO_dvf:
+	ldmia	r1,{r1,r3,r5,r7}
+	ldmia	r2,{r2,r4,r6,r8}
+
+CPDO_dvf_l:
+	cmp	r7,#0x7fffffff
+	cmpne	r8,#0x7fffffff
+	beq	CPDO_dvf_extra
+	cmp	r8,#0x80000000
+	beq	CPDO_dvf_by0
+
+	eor	r1,r1,r2
+	cmp	r7,#0x80000000
+	beq	CPDO_zero_1
+	
+	sub	r8,r7,r8
+	
+	mov	r2,#0
+	mov	r7,#1
+
+	cmp	r3,r4
+	cmpeq	r5,r6
+	bcs	CPDO_dvf_loop_
+
+	sub	r8,r8,#1
+
+CPDO_dvf_loop:
+	adds	r5,r5,r5
+	adcs	r3,r3,r3
+	bcs	CPDO_dvf_anyway
+CPDO_dvf_loop_:
+	subs	r5,r5,r6
+	sbcs	r3,r3,r4
+	bcs	CPDO_dvf_okay
+
+	adds	r5,r5,r6
+	adc	r3,r3,r4
+	adds	r7,r7,r7
+	adcs	r2,r2,r2
+	bcc	CPDO_dvf_loop
+	b	CPDO_dvf_end
+
+CPDO_dvf_anyway:
+	adcs	r7,r7,r7
+	adcs	r2,r2,r2
+	bcs	CPDO_dvf_end
+	subs	r5,r5,r6
+	sbc	r3,r3,r4
+	b	CPDO_dvf_loop
+
+CPDO_dvf_okay:
+	adcs	r7,r7,r7
+	adcs	r2,r2,r2
+	bcc	CPDO_dvf_loop
+
+CPDO_dvf_end:
+	b	CPDO_muf_end
+
+CPDO_dvf_by0:
+	cmp	R7,#0x80000000
+	beq	CPDO_nan		@ first also 0 -> nan
+	eor	r1,r1,r2		@ otherwise calculatesign for inf
+	b	CPDO_inf
+
+CPDO_dvf_extra:
+	cmp	r7,#0x7fffffff		@ was it the first?
+	bne	CPDO_dvf_extra_2nd	@ no, so it was the second
+	cmp	r8,#0x7fffffff		@ yes, second too?
+	bne	CPDO_dvf_extra_1st	@ no, only first
+	orrs	r3,r3,r4
+	beq	CPDO_nan		@ if both inf -> create nan
+	b	CPDO_nan_12		@ otherwise keep nan
+
+CPDO_dvf_extra_1st:
+	eor	r1,r1,r2		@ correct sign for inf
+	b	CPDO_infnan_1
+
+CPDO_dvf_extra_2nd:
+	cmp	r4,#0			@ is it a nan?
+	bne	CPDO_infnan_2
+	eor	r1,r1,r2		@ correct sign for zero
+	b	CPDO_zero_1
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDO_rdf
+CPDO_rdf:
+	mov	r3,r2
+	ldmia	r1,{r2,r4,r6,r8}
+	ldmia	r3,{r1,r3,r5,r7}
+	b	CPDO_dvf_l
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDO_rmf
+CPDO_rmf:
+	b	fastfpe_next
+	
+/*---------------------------------------------------------------------------*/
+
+
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDO_mvf
+CPDO_mvf:
+	ldmia	r2,{r1,r2,r3,r4}
+	stmia	r0,{r1,r2,r3,r4}
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDO_mnf
+CPDO_mnf:
+	ldmia	r2,{r1,r2,r3,r4}
+	eor	r1,r1,#0x80000000
+	stmia	r0,{r1,r2,r3,r4}
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDO_abs
+CPDO_abs:
+	ldmia	r2,{r1,r2,r3,r4}
+	bic	r1,r1,#0x80000000
+	stmia	r0,{r1,r2,r3,r4}
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+	
+	.globl	CPDO_sqt
+CPDO_sqt:
+	ldmia	r2,{r1,r2,r3,r4}
+	cmp	r1,#0
+	bne	CPDO_nan
+	cmp	r4,#0x7fffffff
+	beq	CPDO_store_1234
+
+	tst	r4,r4,lsr#1		@carry=exponent bit 0
+	bcc	CPDO_sqt_exponenteven
+	adds	r3,r3,r3
+	adcs	r2,r2,r2		@carry is needed in loop!
+CPDO_sqt_exponenteven:
+	mov	r4,r4,asr #1
+	str	r4,[r0,#12]
+
+	mov	r4,#0x80000000
+	mov	r5,#0
+	sub	r2,r2,#0x80000000
+
+	mov	r8,#0x40000000
+	mov	r14,#0x80000000
+
+	mov	r1,#1
+	b	CPDO_sqt_loop1_first
+CPDO_sqt_loop1:
+	adds	r3,r3,r3
+	adcs	r2,r2,r2
+CPDO_sqt_loop1_first:
+	add	r6,r4,r8,lsr r1		@r7 const = r5
+	bcs	CPDO_sqt_loop1_1
+	cmp	r2,r6
+	cmpeq	r3,r5			@r5 for r7
+	bcc	CPDO_sqt_loop1_0
+CPDO_sqt_loop1_1:
+	orr	r4,r4,r14,lsr r1
+	subs	r3,r3,r5		@r5 for r7
+	sbc	r2,r2,r6
+CPDO_sqt_loop1_0:
+	add	r1,r1,#1
+	cmp	r1,#30
+	ble	CPDO_sqt_loop1
+
+	adds	r3,r3,r3
+	adcs	r2,r2,r2
+	bcs	CPDO_sqt_between_1
+	adds	r7,r5,#0x80000000
+	adc	r6,r4,#0
+	cmp	r2,r6
+	cmpeq	r3,r7
+	bcc	CPDO_sqt_between_0
+CPDO_sqt_between_1:
+	orr	r4,r4,#0x00000001
+	subs	r3,r3,r5
+	sbc	r2,r2,r4
+	subs	r3,r3,#0x80000000
+	sbc	r2,r2,#0
+CPDO_sqt_between_0:
+	mov	r1,#0
+
+CPDO_sqt_loop2:
+	adds	r3,r3,r3
+	adcs	r2,r2,r2
+	bcs	CPDO_sqt_loop2_1
+	adds	r7,r5,r8,lsr r1
+	adc	r6,r4,#0
+	cmp	r2,r6
+	cmpeq	r3,r7
+	bcc	CPDO_sqt_loop2_0
+CPDO_sqt_loop2_1:
+	orr	r5,r5,r14,lsr r1
+	subs	r3,r3,r5
+	sbc	r2,r2,r4
+	subs	r3,r3,r8,lsr r1
+	sbc	r2,r2,#0
+CPDO_sqt_loop2_0:
+	add	r1,r1,#1
+	cmp	r1,#30
+	ble	CPDO_sqt_loop2
+
+	adds	r3,r3,r3
+	adcs	r2,r2,r2
+	bcs	CPDO_sqt_after_1
+	cmp	r2,r6
+	cmpeq	r3,r7
+	bcc	CPDO_sqt_after_0
+CPDO_sqt_after_1:
+	orr	r5,r5,#0x00000001
+CPDO_sqt_after_0:
+
+	mov	r1,#0
+	stmia	r0,{r1,r4,r5}
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+	
+	.globl	CPDO_rnd
+CPDO_rnd:
+	ldmia	r2,{r1,r2,r3,r5}
+        bl      CPDO_rnd_core
+
+CPDO_rnd_store:
+	stmia	r0,{r1,r2,r3,r5}
+    	b	fastfpe_next
+	
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDO_rnd_core
+CPDO_rnd_core:
+	and	r4,r4,#0x00000060
+	add	pc,pc,r4,lsr#3
+	mov	r0,r0
+	b	CPDO_rnd_N
+	b	CPDO_rnd_P
+	b	CPDO_rnd_M
+	b	CPDO_rnd_Z
+	
+CPDO_rnd_N:
+	cmp	r5,#-1
+	blt	CPDO_rnd_zero
+	cmp	r5,#63
+	movge	pc,r14
+	mov	r4,#0x40000000
+	cmp	r5,#31
+	bge	CPDO_rnd_N_2
+
+	adds	r2,r2,r4,lsr r5
+	bcc	CPDO_rnd_end
+	b	CPDO_rnd_end_norm
+
+CPDO_rnd_N_2:
+CPDO_rnd_P_2:
+	sub	r6,r5,#32
+	adds	r3,r3,r4,ror r6	@ror ist needed to handle a -1 correctly
+	adcs	r2,r2,#0
+	bcc	CPDO_rnd_end
+	b	CPDO_rnd_end_norm
+
+CPDO_rnd_P:
+	tst	r1,#0x80000000
+	bne	CPDO_rnd_M_entry
+CPDO_rnd_P_entry:
+	cmp	r5,#0
+	blt	CPDO_rnd_P_small
+	cmp	r5,#63
+	movge	pc,r14
+	mov	r4,#0x7fffffff
+	cmp	r5,#32
+	bge	CPDO_rnd_P_2
+
+	adds	r3,r3,#0xffffffff
+	adcs	r2,r2,r4,lsr r5
+	bcc	CPDO_rnd_end
+	b	CPDO_rnd_end_norm
+
+CPDO_rnd_P_small:
+	cmp	r5,#0x80000000
+	moveq	pc,r14
+	b	CPDO_rnd_one
+
+CPDO_rnd_M:
+	tst	r1,#0x80000000
+	bne	CPDO_rnd_P_entry
+CPDO_rnd_M_entry:
+	cmp	r5,#0
+	blt	CPDO_rnd_zero
+	cmp	r5,#63
+	movge	pc,r14
+
+	b	CPDO_rnd_end
+	
+CPDO_rnd_Z:
+	cmp	r5,#0
+	blt	CPDO_rnd_zero
+	cmp	r5,#63
+	movge	pc,r14
+	b	CPDO_rnd_end
+
+CPDO_rnd_end_norm:
+	add	r5,r5,#1
+	movs	r2,r2,rrx
+	mov	r3,r3,rrx
+CPDO_rnd_end:
+	rsbs	r4,r5,#31
+	bmi	CPDO_rnd_end_2
+	mov     r3,#0
+	mov     r2,r2,lsr r4
+	mov	r2,r2,lsl r4
+	mov	pc,r14
+
+CPDO_rnd_end_2:
+	rsb	r4,r5,#63
+	mov	r3,r3,lsr r4
+	mov	r3,r3,lsl r4
+	mov	pc,r14
+
+CPDO_rnd_one:
+	mov	r2,#0x80000000
+	mov	r3,#0
+	mov	r5,#0
+	mov	pc,r14
+	
+CPDO_rnd_zero:
+	mov	r1,#0
+	mov	r2,#0
+	mov	r3,#0
+	mov	r5,#0x80000000
+	mov	pc,r14
+
+/*---------------------------------------------------------------------------*/
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/fastfpe/CPRT.S	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,185 @@
+/*
+The FP structure has 4 words reserved for each register, the first is used
+just
+for the sign in bit 31, the second and third are for the mantissa (unsigned
+integer, high 32 bit first) and the fourth is the exponent (signed integer).
+The mantissa is always normalized.
+
+If the exponent is 0x80000000, that is the most negative value, the number
+represented is 0 and both mantissa words are also 0.
+
+If the exponent is 0x7fffffff, that is the biggest positive value, the
+number
+represented is infinity if the high 32 mantissa bit are also 0, otherwise it
+is
+a NaN. The low 32 mantissa bit are 0 if the number represented is infinity.
+
+Decimal and packed decimal numbers are not supported yet.
+*/
+
+/*---------------------------------------------------------------------------*/
+
+	.text
+	.globl	CPRT_flt
+CPRT_flt:
+	add	r0,r13,r0,lsr#10
+	ldr	r2,[r0]
+	mov	r3,#0
+	cmp	r2,#0
+	beq	CPRT_flt_zero
+	
+	ands	r0,r2,#0x80000000
+	rsbne	r2,r2,#0
+	mov	r4,#31
+	
+	cmp	r2,#0x00010000
+	movcc	r2,r2,lsl#16
+	subcc	r4,r4,#16
+	
+	cmp	r2,#0x01000000
+	movcc	r2,r2,lsl#8
+	subcc	r4,r4,#8
+	
+	cmp	r2,#0x10000000
+	movcc	r2,r2,lsl#4
+	subcc	r4,r4,#4
+	
+	cmp	r2,#0x40000000
+	movcc	r2,r2,lsl#2
+	subcc	r4,r4,#2
+	
+	cmp	r2,#0x80000000
+	movcc	r2,r2,lsl#1
+	subcc	r4,r4,#1
+
+	stmia	r1,{r0,r2,r3,r4}
+	b	fastfpe_next
+
+CPRT_flt_zero:
+	mov	r0,#0
+	mov	r4,#0x80000000
+	stmia	r1,{r0,r2,r3,r4}
+	b	fastfpe_next
+	
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPRT_fix
+CPRT_fix:
+	ldmia	r2,{r1,r2,r3,r5}
+	bl	CPDO_rnd_core
+	
+CPRT_back:
+	add	r0,r13,r0,lsr#10
+	cmp	r5,#0
+	blt	CPRT_int_zero
+	cmp	r5,#30
+	bgt	CPRT_overflow
+	
+	rsb	r5,r5,#31
+	mov	r2,r2,lsr r5
+	tst	r1,#0x80000000
+	rsbne	r2,r2,#0
+	
+	str	r2,[r0]
+	b	fastfpe_next
+
+CPRT_int_zero:
+	mov	r2,#0
+	str	r2,[r0]
+	b	fastfpe_next
+
+CPRT_overflow:
+	mov	r2,#0x80000000
+	tst	r1,#0x80000000
+	subeq	r2,r2,#1
+	str	r2,[r0]
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPRT_wfs
+CPRT_wfs:
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPRT_rfs
+CPRT_rfs:
+	add	r0,r13,r0,lsr#10
+	mov	r1,#0x02000000		@ Software Emulation, not Acorn FPE
+	str	r1,[r0]
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPRT_cmf
+CPRT_cmf:
+	ldmia	r1,{r1,r3,r5,r7}
+	ldmia	r2,{r2,r4,r6,r8}
+
+CPRT_cmf_e:
+	ldr	r0,[r13,#16*4]
+	
+	cmp	r7,#0x7fffffff
+	bic	r0,r0,#0xf0000000
+
+	cmpeq	r3,#0xffffffff
+	beq	CPRT_cmf_unordered
+	cmp	r8,#0x7fffffff
+	cmpeq	r4,#0xffffffff
+	beq	CPRT_cmf_unordered
+
+	cmp	r1,r2
+	beq	CPRT_cmf_equalsign
+	b	CPRT_cmf_sign
+
+CPRT_cmf_equalsign:
+	cmp	r7,r8
+	beq	CPRT_cmf_equalexponent
+	bgt	CPRT_cmf_sign
+	b	CPRT_cmf_signb	
+
+CPRT_cmf_equalexponent:
+	cmp	r3,r4
+	cmpeq	r5,r6
+	beq	CPRT_cmf_equal
+	bhi	CPRT_cmf_sign
+	b	CPRT_cmf_signb
+
+CPRT_cmf_sign:
+	cmp     r7,#0x80000000		@ (0.0 == -0.0)?
+	cmpeq   r7,r8
+	beq     CPRT_cmf_equal
+	tst	r1,#0x80000000
+	orreq	r0,r0,#0x20000000
+	orrne	r0,r0,#0x80000000
+	str	r0,[r13,#16*4]
+	b	fastfpe_next
+
+CPRT_cmf_signb:
+	tst	r1,#0x80000000
+	orrne	r0,r0,#0x20000000
+	orreq	r0,r0,#0x80000000
+	str	r0,[r13,#16*4] 
+	b	fastfpe_next
+
+CPRT_cmf_equal:
+	orr	r0,r0,#0x60000000
+	str	r0,[r13,#16*4]
+	b	fastfpe_next
+
+CPRT_cmf_unordered:
+	orr	r0,r0,#0x10000000
+        str     r0,[r13,#16*4]
+        b       fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPRT_cnf
+CPRT_cnf:
+	ldmia	r1,{r1,r3,r5,r7}
+	ldmia	r2,{r2,r4,r6,r8}
+	eor	r2,r2,#0x80000000
+	b	CPRT_cmf_e
+
+/*---------------------------------------------------------------------------*/
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/fastfpe/CPDT.S	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,430 @@
+/*
+The FP structure has 4 words reserved for each register, the first is used just
+for the sign in bit 31, the second and third are for the mantissa (unsigned
+integer, high 32 bit first) and the fourth is the exponent (signed integer).
+The mantissa is always normalized.
+
+If the exponent is 0x80000000, that is the most negative value, the number
+represented is 0 and both mantissa words are also 0.
+
+If the exponent is 0x7fffffff, that is the biggest positive value, the number
+represented is infinity if the high 32 mantissa bit are also 0, otherwise it is
+a NaN. The low 32 mantissa bit are 0 if the number represented is infinity.
+
+Decimal and packed decimal numbers are not supported yet.
+*/
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDT_load_single
+CPDT_load_single:
+	ldr	r1,[r6]
+	
+	and	r2,r1,#0x80000000	@ r2 = sign
+	
+	mov	r5,r1,lsr#23
+	bics	r5,r5,#0x100
+	beq	CPDT_ls_e0		@ exponent = 0; zero/denormalized
+	teq	r5,#255
+	beq	CPDT_ls_e255		@ exponent = 255; infinity/NaN
+
+	sub     r5,r5,#127		@ r5 = exponent, remove normalized bias
+
+	mov	r3,r1,lsl#8
+	orr	r3,r3,#0x80000000
+	mov	r4,#0			@ r3,r4 = mantissa
+
+	stmia	r0,{r2-r5}
+	b	fastfpe_next
+
+CPDT_ls_e0:
+	movs	r3,r1,lsl#9
+	beq	CPDT_load_zero
+
+	mov	r5,#-127
+
+CPDT_ls_e0_norm:
+	tst	r3,#0x80000000
+	subeq	r5,r5,#1
+	moveq	r3,r3,lsl#1
+	beq	CPDT_ls_e0_norm
+
+	mov	r4,#0
+	stmia	r0,{r2-r5}
+	b	fastfpe_next
+
+CPDT_ls_e255:
+	mov	r3,r1,lsl#9
+	mov	r4,#0
+	mov	r5,#0x7fffffff
+	stmia	r0,{r2-r5}
+	b	fastfpe_next
+
+CPDT_load_zero:
+	mov	r3,#0
+	mov	r4,#0
+	mov	r5,#0x80000000
+	stmia	r0,{r2-r5}
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDT_load_double
+CPDT_load_double:
+	ldr	r1,[r6]
+	ldr	r6,[r6,#4]
+
+	and	r2,r1,#0x80000000	@ r2 = sign
+
+	mov	r5,r1,lsr#20
+	bics	r5,r5,#0x800
+	beq	CPDT_ld_e0		@ exponent = 0; zero/denormalized
+	add	r4,r5,#1
+	teq	r4,#2048
+	beq	CPDT_ld_e2047		@ exponent = 2047; infinity/NaN
+
+	add     r5,r5,#1
+	sub	r5,r5,#1024		@ r5 = exponent, remove normalized bias
+
+	mov	r3,r1,lsl#11
+	orr	r3,r3,#0x80000000
+	orr	r3,r3,r6,lsr #21
+	mov	r4,r6,lsl#11		@ r3,r4 = mantissa
+
+	stmia	r0,{r2-r5}
+	b	fastfpe_next
+
+CPDT_ld_e0:
+	mov	r3,r1,lsl#12
+	orr	r3,r3,r6,lsr#20
+	movs	r4,r6,lsl#12
+	teqeq	r3,#0
+	beq	CPDT_load_zero
+	
+	mov	r5,#1
+	sub	r5,r5,#1024
+
+CPDT_ld_e0_norm:
+	tst	r3,#0x80000000
+	subeq	r5,r5,#1
+	moveqs	r4,r4,lsl#1
+	adceq	r3,r3,r3
+	beq	CPDT_ld_e0_norm
+
+	stmia	r0,{r2-r5}
+	b	fastfpe_next
+
+CPDT_ld_e2047:
+	mov	r3,r1,lsl#12
+	orr	r3,r3,r6,lsr#1
+	bic	r6,r6,#0x80000000
+	orr	r3,r3,r6		@ to get all fraction bits !
+	mov	r4,#0
+	mov	r5,#0x7fffffff
+	stmia	r0,{r2-r5}
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDT_load_extended
+CPDT_load_extended:
+	ldr	r1,[r6]
+	ldr	r3,[r6,#4]
+	ldr	r4,[r6,#8]
+	
+	and	r2,r1,#0x80000000
+	bics	r5,r1,#0x80000000
+	beq	CPDT_le_e0
+	add	r1,r5,#1
+	teq	r4,#32768
+	beq	CPDT_le_e32767
+
+	add	r5,r5,#1
+	sub	r5,r5,#16384
+
+	stmia	r0,{r2-r5}
+	b	fastfpe_next
+
+CPDT_le_e0:
+	teq	r3,#0
+	teqeq	r4,#0
+	beq	CPDT_load_zero
+	
+	mov	r5,#2
+	sub	r5,r5,#16384
+	b	CPDT_ld_e0_norm
+
+CPDT_le_e32767:
+	mov	r3,r3,lsl#1
+	orr	r3,r3,r4,lsr#1
+	bic	r4,r4,#0x80000000
+	orr	r3,r3,r4
+	mov	r5,#0x7fffffff
+	stmia	r0,{r2-r5}
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDT_load_decimal
+CPDT_load_decimal:
+	
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDT_store_single
+CPDT_store_single:
+	ldmia	r0,{r1-r4}
+	
+	cmp	r4,#-127
+	ble	CPDT_ss_e0
+	cmp	r4,#128
+	bge	CPDT_ss_e255
+
+	adds	r2,r2,#1<<7		@ round to nearest
+	bcs	CPDT_ss_rnd_ovfl	@ very very seldom taken
+
+CPDT_ss_store:
+	add	r4,r4,#127
+	orr	r1,r1,r4,lsl#23
+	
+	bic	r2,r2,#0x80000000
+	orr	r1,r1,r2,lsr#8
+
+	str	r1,[r6]
+	b	fastfpe_next
+
+CPDT_ss_rnd_ovfl:
+	add	r4,r4,#1
+	cmp	r4,#128
+	bge	CPDT_ss_e255
+
+	mov	r2,#0x80000000
+	mov	r3,#0
+	b	CPDT_ss_store
+
+CPDT_ss_e0:
+	cmp	r4,#-150
+	ble	CPDT_ss_zero
+
+	add	r4,r4,#126
+CPDT_ss_unnormalize:
+	mov	r2,r2,lsr#1
+	adds	r4,r4,#1
+	bne	CPDT_ss_unnormalize
+
+	orr	r1,r1,r2,lsr#8
+
+CPDT_ss_zero:
+	str	r1,[r6]
+	b	fastfpe_next
+
+CPDT_ss_e255:
+	cmp	r4,#0x7fffffff
+	bne	CPDT_ss_inf
+	cmp	r2,#0
+	beq	CPDT_ss_inf
+
+	orr	r1,r1,#0x00200000	@ for safety so that it is not INF
+	orr	r1,r1,r2,lsr#9		@ get highest bit of mantissa
+
+CPDT_ss_inf:
+	orr	r1,r1,#0x7f000000
+	orr	r1,r1,#0x00800000
+	str	r1,[r6]
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDT_store_double
+CPDT_store_double:
+	ldmia	r0,{r1-r4}
+	
+	cmp	r4,#1024		@ this check has to be first, or
+	bge	CPDT_sd_e2047		@ overflow can occur on second !
+	add	r0,r4,#3
+	cmp	r0,#-1023+3		@ cmp with -1023
+	ble	CPDT_sd_e0
+
+	adds	r3,r3,#1<<10		@ round to nearest
+	adcs	r2,r2,#0
+	bcs	CPDT_sd_rnd_ovfl	@ very very seldom taken
+
+CPDT_sd_store:
+	sub	r4,r4,#1
+	add	r4,r4,#1024
+	orr	r1,r1,r4,lsl#20
+	
+	bic	r2,r2,#0x80000000
+	orr	r1,r1,r2,lsr#11
+
+	mov	r2,r2,lsl#21
+	orr	r2,r2,r3,lsr#11
+
+	stmia	r6,{r1,r2}
+	b	fastfpe_next
+
+CPDT_sd_rnd_ovfl:
+	add	r4,r4,#1
+	cmp	r4,#1024
+	bge	CPDT_sd_e2047
+
+	mov	r2,#0x80000000
+	mov	r3,#0
+	b	CPDT_sd_store
+
+CPDT_sd_e0:
+	add	r0,r4,#1075-1024
+	cmp	r0,#-1024
+	ble	CPDT_sd_zero
+
+	add	r4,r4,#1024
+	sub	r4,r4,#2
+CPDT_sd_unnormalize:
+	movs	r2,r2,lsr#1
+	mov	r3,r3,rrx
+	adds	r4,r4,#1
+	bne	CPDT_sd_unnormalize
+
+	orr	r1,r1,r2,lsr#11
+	mov	r2,r2,lsl#21
+	orr	r2,r2,r3,lsr#11
+
+	stmia	r6,{r1,r2}
+	b	fastfpe_next
+
+CPDT_sd_zero:
+	mov	r2,#0
+	stmia	r6,{r1,r2}
+	b	fastfpe_next
+
+CPDT_sd_e2047:
+	cmp	r4,#0x7fffffff
+	bne	CPDT_sd_inf
+	cmp	r2,#0
+	beq	CPDT_sd_inf
+
+	orr	r1,r1,#0x00040000	@ for safety so that it is not INF
+	orr	r1,r1,r2,lsr#12		@ get highest bit of mantissa
+
+CPDT_sd_inf:
+	orr	r1,r1,#0x7f000000
+	orr	r1,r1,#0x00f00000
+	stmia	r6,{r1,r2}
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDT_store_extended
+CPDT_store_extended:
+	ldmia	r0,{r1-r4}
+	
+	cmp	r4,#16384		@ this check has to be first, or
+	bge	CPDT_se_e32767		@ overflow can occur with second !
+	add	r0,r4,#63
+	cmp	r0,#-16383+63
+	ble	CPDT_se_e0
+
+	sub	r4,r4,#1
+	add	r4,r4,#16384
+	orr	r1,r1,r4
+	
+	stmia	r6,{r1-r3}
+	b	fastfpe_next
+
+CPDT_se_e0:
+	add	r0,r4,#16446-16384
+	cmp	r0,#-16384
+	ble	CPDT_se_zero
+
+	add	r4,r4,#16384
+	sub	r4,r4,#2
+CPDT_se_unnormalize:
+	movs	r2,r2,lsr#1
+	mov	r3,r3,rrx
+	adds	r4,r4,#1
+	bne	CPDT_se_unnormalize
+
+	stmia	r6,{r1-r3}
+	b	fastfpe_next
+	
+CPDT_se_zero:
+	mov	r2,#0
+	mov	r3,#0
+	stmia	r6,{r1-r3}
+	b	fastfpe_next
+
+CPDT_se_e32767:
+	cmp	r4,#0x7fffffff
+	bne	CPDT_se_inf
+	cmp	r2,#0
+	beq	CPDT_se_inf
+
+	mov	r2,r2,lsl#1
+	orr	r2,r2,#0x20000000
+
+CPDT_se_inf:
+	orr	r1,r1,#0x00007f00
+	orr	r1,r1,#0x000000ff
+	stmia	r6,{r1-r3}
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDT_store_decimal
+CPDT_store_decimal:
+
+	b	fastfpe_next
+
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDT_sfm
+CPDT_sfm:
+	add	r2,r10,r0,lsr#8
+	ldr	r4,[r2,#0]
+	ldr	r3,[r2,#4]
+	bic	r3,r3,#0x80000000
+	orr	r3,r3,r4
+	str	r3,[r6],#4
+	ldr	r3,[r2,#8]
+	str	r3,[r6],#4
+	ldr	r3,[r2,#12]
+	str	r3,[r6],#4
+
+	add	r0,r0,#1<<12
+	and	r0,r0,#7<<12
+	subs	r1,r1,#1
+	bne	CPDT_sfm
+	b	fastfpe_next
+	
+/*---------------------------------------------------------------------------*/
+
+	.globl	CPDT_lfm
+CPDT_lfm:
+	add	r2,r10,r0,lsr#8
+	ldr	r4,[r6],#4
+	and	r3,r4,#0x80000000
+	str	r3,[r2,#0]
+	ldr	r3,[r6],#4
+	str	r3,[r2,#8]
+	ldr	r3,[r6],#4
+	str	r3,[r2,#12]
+
+	cmp	r3,#0x80000000		@ does the exp indicate zero?
+	biceq	r4,r4,#0x80000000	@ if so, indicate 'denormalized'
+	beq	CPDT_lfm_storer4
+	cmp	r3,#0x7fffffff		@ does the exp indicate inf or NaN?
+	biceq	r4,r4,#0x80000000	@ if so, indicate 'denormalized'
+	beq	CPDT_lfm_storer4
+	orrne	r4,r4,#0x80000000	@ otherwise, set normalized bit
+
+CPDT_lfm_storer4:
+	str	r4,[r2,#4]
+
+	add	r0,r0,#1<<12
+	and	r0,r0,#7<<12
+	subs	r1,r1,#1
+	bne	CPDT_lfm
+	b	fastfpe_next
+	
+/*---------------------------------------------------------------------------*/
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/fastfpe/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,14 @@
+#
+# linux/arch/arm/fastfpe/Makefile
+#
+# Copyright (C) Peter Teichmann
+#
+
+obj-y	:=
+obj-m	:=
+obj-n	:=
+obj-	:=
+
+fastfpe-objs := module.o entry.o CPDO.o CPRT.o CPDT.o
+
+obj-$(CONFIG_FPE_FASTFPE) += fastfpe.o
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/common/rtctime.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,482 @@
+/*
+ *  linux/arch/arm/common/rtctime.c
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions Ltd.
+ *  Based on sa1100-rtc.c, Nils Faerber, CIH, Nicolas Pitre.
+ *  Based on rtc.c by Paul Gortmaker
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#include <asm/rtc.h>
+#include <asm/semaphore.h>
+
+static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
+static struct fasync_struct *rtc_async_queue;
+
+/*
+ * rtc_lock protects rtc_irq_data
+ */
+static spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+static unsigned long rtc_irq_data;
+
+/*
+ * rtc_sem protects rtc_inuse and rtc_ops
+ */
+static DECLARE_MUTEX(rtc_sem);
+static unsigned long rtc_inuse;
+static struct rtc_ops *rtc_ops;
+
+#define rtc_epoch 1900UL
+
+static const unsigned char days_in_month[] = {
+	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
+#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
+
+static int month_days(unsigned int month, unsigned int year)
+{
+	return days_in_month[month] + (LEAP_YEAR(year) && month == 1);
+}
+
+/*
+ * Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
+ */
+void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
+{
+	int days, month, year;
+
+	days = time / 86400;
+	time -= days * 86400;
+
+	tm->tm_wday = (days + 4) % 7;
+
+	year = 1970 + days / 365;
+	days -= (year - 1970) * 365
+	        + LEAPS_THRU_END_OF(year - 1)
+	        - LEAPS_THRU_END_OF(1970 - 1);
+	if (days < 0) {
+		year -= 1;
+		days += 365 + LEAP_YEAR(year);
+	}
+	tm->tm_year = year - 1900;
+	tm->tm_yday = days + 1;
+
+	for (month = 0; month < 11; month++) {
+		int newdays;
+
+		newdays = days - month_days(month, year);
+		if (newdays < 0)
+			break;
+		days = newdays;
+	}
+	tm->tm_mon = month;
+	tm->tm_mday = days + 1;
+
+	tm->tm_hour = time / 3600;
+	time -= tm->tm_hour * 3600;
+	tm->tm_min = time / 60;
+	tm->tm_sec = time - tm->tm_min * 60;
+}
+
+/*
+ * Convert Gregorian date to seconds since 01-01-1970 00:00:00.
+ */
+int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
+{
+	unsigned int yrs = tm->tm_year + 1900;
+
+	*time = 0;
+
+	if (yrs < 1970 ||
+	    tm->tm_mon >= 12 ||
+	    tm->tm_mday < 1 ||
+	    tm->tm_mday > month_days(tm->tm_mon, yrs) ||
+	    tm->tm_hour >= 24 ||
+	    tm->tm_min >= 60 ||
+	    tm->tm_sec >= 60)
+		return -EINVAL;
+
+	*time = mktime(yrs, tm->tm_mon + 1, tm->tm_mday,
+		       tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+	return 0;
+}
+
+/*
+ * Calculate the next alarm time given the requested alarm time mask
+ * and the current time.
+ *
+ * FIXME: for now, we just copy the alarm time because we're lazy (and
+ * is therefore buggy - setting a 10am alarm at 8pm will not result in
+ * the alarm triggering.)
+ */
+void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm)
+{
+	next->tm_year = now->tm_year;
+	next->tm_mon = now->tm_mon;
+	next->tm_mday = now->tm_mday;
+	next->tm_hour = alrm->tm_hour;
+	next->tm_min = alrm->tm_min;
+	next->tm_sec = alrm->tm_sec;
+}
+
+static inline void rtc_read_time(struct rtc_ops *ops, struct rtc_time *tm)
+{
+	memset(tm, 0, sizeof(struct rtc_time));
+	ops->read_time(tm);
+}
+
+static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm)
+{
+	return ops->set_time(tm);
+}
+
+static inline void rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
+{
+	memset(alrm, 0, sizeof(struct rtc_wkalrm));
+	ops->read_alarm(alrm);
+}
+
+static inline int rtc_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
+{
+	return ops->set_alarm(alrm);
+}
+
+void rtc_update(unsigned long num, unsigned long events)
+{
+	spin_lock(&rtc_lock);
+	rtc_irq_data = (rtc_irq_data + (num << 8)) | events;
+	spin_unlock(&rtc_lock);
+
+	wake_up_interruptible(&rtc_wait);
+	kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
+}
+
+
+static ssize_t
+rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long data;
+	ssize_t ret;
+
+	if (count < sizeof(unsigned long))
+		return -EINVAL;
+
+	add_wait_queue(&rtc_wait, &wait);
+	do {
+		__set_current_state(TASK_INTERRUPTIBLE);
+
+		spin_lock_irq(&rtc_lock);
+		data = rtc_irq_data;
+		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+
+		if (data != 0) {
+			ret = 0;
+			break;
+		}
+		if (file->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	} while (1);
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&rtc_wait, &wait);
+
+	if (ret == 0) {
+		ret = put_user(data, (unsigned long *)buf);
+		if (ret == 0)
+			ret = sizeof(unsigned long);
+	}
+	return ret;
+}
+
+static unsigned int rtc_poll(struct file *file, poll_table *wait)
+{
+	unsigned long data;
+
+	poll_wait(file, &rtc_wait, wait);
+
+	spin_lock_irq(&rtc_lock);
+	data = rtc_irq_data;
+	spin_unlock_irq(&rtc_lock);
+
+	return data != 0 ? POLLIN | POLLRDNORM : 0;
+}
+
+static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		     unsigned long arg)
+{
+	struct rtc_ops *ops = file->private_data;
+	struct rtc_time tm;
+	struct rtc_wkalrm alrm;
+	int ret;
+
+	switch (cmd) {
+	case RTC_ALM_READ:
+		rtc_read_alarm(ops, &alrm);
+		ret = copy_to_user((void *)arg, &alrm.time, sizeof(tm));
+		if (ret)
+			ret = -EFAULT;
+		break;
+
+	case RTC_ALM_SET:
+		ret = copy_from_user(&alrm.time, (void *)arg, sizeof(tm));
+		alrm.enabled = 0;
+		alrm.pending = 0;
+		alrm.time.tm_mday = -1;
+		alrm.time.tm_mon = -1;
+		alrm.time.tm_year = -1;
+		alrm.time.tm_wday = -1;
+		alrm.time.tm_yday = -1;
+		alrm.time.tm_isdst = -1;
+		if (ret == 0)
+			ret = rtc_set_alarm(ops, &alrm);
+		else
+			ret = -EFAULT;
+		break;
+
+	case RTC_RD_TIME:
+		rtc_read_time(ops, &tm);
+		ret = copy_to_user((void *)arg, &tm, sizeof(tm));
+		if (ret)
+			ret = -EFAULT;
+		break;
+
+	case RTC_SET_TIME:
+		if (!capable(CAP_SYS_TIME)) {
+			ret = -EACCES;
+			break;
+		}
+		ret = copy_from_user(&tm, (void *)arg, sizeof(tm));
+		if (ret == 0)
+			ret = rtc_set_time(ops, &tm);
+		else
+			ret = -EFAULT;
+		break;
+
+#ifndef rtc_epoch
+	case RTC_EPOCH_SET:
+		/*
+		 * There were no RTC clocks before 1900.
+		 */
+		if (arg < 1900) {
+			ret = -EINVAL;
+			break;
+		}
+		if (!capable(CAP_SYS_TIME)) {
+			ret = -EACCES;
+			break;
+		}
+		rtc_epoch = arg;
+		ret = 0;
+		break;
+#endif
+
+	case RTC_EPOCH_READ:
+		ret = put_user(rtc_epoch, (unsigned long *)arg);
+		break;
+
+	case RTC_WKALM_SET:
+		ret = copy_from_user(&alrm, (void *)arg, sizeof(alrm));
+		if (ret == 0)
+			ret = rtc_set_alarm(ops, &alrm);
+		else
+			ret = -EFAULT;
+		break;
+
+	case RTC_WKALM_RD:
+		rtc_read_alarm(ops, &alrm);
+		ret = copy_to_user((void *)arg, &alrm, sizeof(alrm));
+		if (ret)
+			ret = -EFAULT;
+		break;
+
+	default:
+		ret = ops->ioctl(cmd, arg);
+	}
+	return ret;
+}
+
+static int rtc_open(struct inode *inode, struct file *file)
+{
+	int ret;
+
+	down(&rtc_sem);
+
+	if (rtc_inuse) {
+		ret = -EBUSY;
+	} else if (!rtc_ops || !try_module_get(rtc_ops->owner)) {
+		ret = -ENODEV;
+	} else {
+		file->private_data = rtc_ops;
+
+		ret = rtc_ops->open ? rtc_ops->open() : 0;
+		if (ret == 0) {
+			spin_lock_irq(&rtc_lock);
+			rtc_irq_data = 0;
+			spin_unlock_irq(&rtc_lock);
+
+			rtc_inuse = 1;
+		}
+	}
+	up(&rtc_sem);
+
+	return ret;
+}
+
+static int rtc_release(struct inode *inode, struct file *file)
+{
+	struct rtc_ops *ops = file->private_data;
+
+	if (ops->release)
+		ops->release();
+
+	spin_lock_irq(&rtc_lock);
+	rtc_irq_data = 0;
+	spin_unlock_irq(&rtc_lock);
+
+	module_put(rtc_ops->owner);
+	rtc_inuse = 0;
+
+	return 0;
+}
+
+static int rtc_fasync(int fd, struct file *file, int on)
+{
+	return fasync_helper(fd, file, on, &rtc_async_queue);
+}
+
+static struct file_operations rtc_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= rtc_read,
+	.poll		= rtc_poll,
+	.ioctl		= rtc_ioctl,
+	.open		= rtc_open,
+	.release	= rtc_release,
+	.fasync		= rtc_fasync,
+};
+
+static struct miscdevice rtc_miscdev = {
+	.minor		= RTC_MINOR,
+	.name		= "rtc",
+	.fops		= &rtc_fops,
+};
+
+
+static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	struct rtc_ops *ops = data;
+	struct rtc_wkalrm alrm;
+	struct rtc_time tm;
+	char *p = page;
+	int len;
+
+	rtc_read_time(ops, &tm);
+
+	p += sprintf(p,
+		"rtc_time\t: %02d:%02d:%02d\n"
+		"rtc_date\t: %04d-%02d-%02d\n"
+		"rtc_epoch\t: %04lu\n",
+		tm.tm_hour, tm.tm_min, tm.tm_sec,
+		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+		rtc_epoch);
+
+	rtc_read_alarm(ops, &alrm);
+	p += sprintf(p, "alrm_time\t: ");
+	if ((unsigned int)alrm.time.tm_hour <= 24)
+		p += sprintf(p, "%02d:", alrm.time.tm_hour);
+	else
+		p += sprintf(p, "**:");
+	if ((unsigned int)alrm.time.tm_min <= 59)
+		p += sprintf(p, "%02d:", alrm.time.tm_min);
+	else
+		p += sprintf(p, "**:");
+	if ((unsigned int)alrm.time.tm_sec <= 59)
+		p += sprintf(p, "%02d\n", alrm.time.tm_sec);
+	else
+		p += sprintf(p, "**\n");
+
+	p += sprintf(p, "alrm_date\t: ");
+	if ((unsigned int)alrm.time.tm_year <= 200)
+		p += sprintf(p, "%04d-", alrm.time.tm_year + 1900);
+	else
+		p += sprintf(p, "****-");
+	if ((unsigned int)alrm.time.tm_mon <= 11)
+		p += sprintf(p, "%02d-", alrm.time.tm_mon + 1);
+	else
+		p += sprintf(p, "**-");
+	if ((unsigned int)alrm.time.tm_mday <= 31)
+		p += sprintf(p, "%02d\n", alrm.time.tm_mday);
+	else
+		p += sprintf(p, "**\n");
+	p += sprintf(p, "alrm_wakeup\t: %s\n", alrm.enabled ? "yes" : "no");
+	p += sprintf(p, "alrm_pending\t: %s\n", alrm.pending ? "yes" : "no");
+
+	if (ops->proc)
+		p += ops->proc(p);
+
+	len = (p - page) - off;
+	if (len < 0)
+		len = 0;
+	*eof = len <= count;
+	*start = page + off;
+
+	return len;
+}
+
+int register_rtc(struct rtc_ops *ops)
+{
+	int ret = -EBUSY;
+
+	down(&rtc_sem);
+	if (rtc_ops == NULL) {
+		rtc_ops = ops;
+
+		ret = misc_register(&rtc_miscdev);
+		if (ret == 0)
+			create_proc_read_entry("driver/rtc", 0, 0,
+					       rtc_read_proc, ops);
+	}
+	up(&rtc_sem);
+
+	return ret;
+}
+
+void unregister_rtc(struct rtc_ops *rtc)
+{
+	down(&rtc_sem);
+	if (rtc == rtc_ops) {
+		remove_proc_entry("driver/rtc", NULL);
+		misc_deregister(&rtc_miscdev);
+		rtc_ops = NULL;
+	}
+	up(&rtc_sem);
+}
+
+EXPORT_SYMBOL(rtc_time_to_tm);
+EXPORT_SYMBOL(rtc_tm_to_time);
+EXPORT_SYMBOL(rtc_update);
+EXPORT_SYMBOL(register_rtc);
+EXPORT_SYMBOL(unregister_rtc);
--- linux-2.6.5/arch/arm/common/Makefile~heh	2004-04-03 22:36:57.000000000 -0500
+++ linux-2.6.5/arch/arm/common/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -2,7 +2,7 @@
 # Makefile for the linux kernel.
 #
 
-obj-y				+= platform.o
+obj-y				+= platform.o rtctime.o
 obj-$(CONFIG_ARM_AMBA)		+= amba.o
 obj-$(CONFIG_ICST525)		+= icst525.o
 obj-$(CONFIG_SA1111)		+= sa1111.o sa1111-pcibuf.o
--- linux-2.6.5/arch/arm/mach-pxa/generic.c~heh	2004-04-03 22:36:53.000000000 -0500
+++ linux-2.6.5/arch/arm/mach-pxa/generic.c	2004-04-30 20:57:36.000000000 -0400
@@ -132,7 +132,7 @@
  /* virtual     physical    length      type */
   { 0xf6000000, 0x20000000, 0x01000000, MT_DEVICE }, /* PCMCIA0 IO */
   { 0xf7000000, 0x30000000, 0x01000000, MT_DEVICE }, /* PCMCIA1 IO */
-  { 0xf8000000, 0x40000000, 0x01400000, MT_DEVICE }, /* Devs */
+  { 0xf8000000, 0x40000000, 0x01800000, MT_DEVICE }, /* Devs */
   { 0xfa000000, 0x44000000, 0x00100000, MT_DEVICE }, /* LCD */
   { 0xfc000000, 0x48000000, 0x00100000, MT_DEVICE }, /* Mem Ctl */
   { 0xff000000, 0x00000000, 0x00100000, MT_DEVICE }  /* UNCACHED_PHYS_0 */
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-pxa/cpu-pxa.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,322 @@
+/*
+ *  linux/arch/arm/mach-pxa/cpu-pxa.c
+ *
+ *  Copyright (C) 2002,2003 Intrinsyc Software
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ *   31-Jul-2002 : Initial version [FB]
+ *   29-Jan-2003 : added PXA255 support [FB]
+ *   20-Apr-2003 : ported to v2.5 (Dustin McIntire, Sensoria Corp.)
+ * 
+ * Note:
+ *   This driver may change the memory bus clock rate, but will not do any
+ *   platform specific access timing changes... for example if you have flash
+ *   memory connected to CS0, you will need to register a platform specific
+ *   notifier which will adjust the memory access strobes to maintain a 
+ *   minimum strobe width.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+
+#include <asm/hardware.h>
+
+#define DEBUG  0
+
+#ifdef DEBUG
+  static unsigned int freq_debug = DEBUG;
+  MODULE_PARM(freq_debug, "i");
+  MODULE_PARM_DESC(freq_debug, "Set the debug messages to on=1/off=0");
+#else
+  #define freq_debug  0
+#endif  
+
+typedef struct
+{
+    unsigned int khz;
+    unsigned int membus;
+    unsigned int cccr;
+    unsigned int div2;
+} pxa_freqs_t;
+
+/* Define the refresh period in mSec for the SDRAM and the number of rows */
+#define SDRAM_TREF          64      /* standard 64ms SDRAM */
+#define SDRAM_ROWS          4096    /* 64MB=8192 32MB=4096 */ 
+#define MDREFR_DRI(x)       ((x*SDRAM_TREF)/(SDRAM_ROWS*32))
+
+#define CCLKCFG_TURBO       0x1
+#define CCLKCFG_FCS         0x2
+#define PXA25x_MIN_FREQ     99500
+#define PXA25x_MAX_FREQ     398100
+#define MDREFR_DB2_MASK     (MDREFR_K2DB2 | MDREFR_K1DB2)
+#define MDREFR_DRI_MASK     0xFFF
+
+
+/* Use the run mode frequencies for the CPUFREQ_POLICY_PERFORMANCE policy */
+static pxa_freqs_t pxa255_run_freqs[] =
+{
+    /* CPU   MEMBUS  CCCR  DIV2*/
+    { 99500,  99500, 0x121, 1}, /* run= 99, turbo= 99, PXbus=50,  SDRAM=50 */
+    {132700, 132700, 0x123, 1}, /* run=133, turbo=133, PXbus=66,  SDRAM=66 */
+    {199100,  99500, 0x141, 0}, /* run=199, turbo=199, PXbus=99,  SDRAM=99 */
+    {265400, 132700, 0x143, 1}, /* run=265, turbo=265, PXbus=133, SDRAM=66 */
+    {331800, 165900, 0x145, 1}, /* run=331, turbo=331, PXbus=166, SDRAM=83 */
+    {398100,  99500, 0x161, 0}, /* run=398, turbo=398, PXbus=196, SDRAM=99 */
+    {0,}
+};
+#define NUM_RUN_FREQS (sizeof(pxa255_run_freqs)/sizeof(pxa_freqs_t))
+
+static struct cpufreq_frequency_table pxa255_run_freq_table[NUM_RUN_FREQS+1];
+
+/* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */
+static pxa_freqs_t pxa255_turbo_freqs[] =
+{
+    /* CPU   MEMBUS  CCCR  DIV2*/
+    { 99500, 99500,  0x121, 1}, /* run=99,  turbo= 99, PXbus=50, SDRAM=50 */
+    {199100, 99500,  0x221, 0}, /* run=99,  turbo=199, PXbus=50, SDRAM=99 */
+    {298500, 99500,  0x321, 0}, /* run=99,  turbo=287, PXbus=50, SDRAM=99 */
+    {298600, 99500,  0x1c1, 0}, /* run=199, turbo=287, PXbus=99, SDRAM=99 */
+    {398100, 99500,  0x241, 0}, /* run=199, turbo=398, PXbus=99, SDRAM=99 */
+    {0,}
+};
+#define NUM_TURBO_FREQS (sizeof(pxa255_turbo_freqs)/sizeof(pxa_freqs_t))
+
+static struct cpufreq_frequency_table pxa255_turbo_freq_table[NUM_TURBO_FREQS+1];
+
+/* find a valid frequency point */
+static int pxa_verify_policy(struct cpufreq_policy *policy)
+{
+    int ret;
+    struct cpufreq_frequency_table *pxa_freqs_table;
+
+    if(policy->policy == CPUFREQ_POLICY_PERFORMANCE) {
+        pxa_freqs_table = pxa255_run_freq_table;
+    } else if (policy->policy == CPUFREQ_POLICY_POWERSAVE) {
+        pxa_freqs_table = pxa255_turbo_freq_table;
+    } else if (policy->policy == CPUFREQ_POLICY_GOVERNOR) {
+        pxa_freqs_table = pxa255_run_freq_table;
+    } else {
+        printk("CPU PXA: Unknown policy found. "
+               "Using CPUFREQ_POLICY_PERFORMANCE\n");
+        pxa_freqs_table = pxa255_run_freq_table;
+    } 
+	ret=cpufreq_frequency_table_verify(policy, pxa_freqs_table);
+    
+    if(freq_debug) {
+        printk("Verified CPU policy: %dKhz min to %dKhz max\n",
+            policy->min, policy->max);
+    }
+
+    return ret;
+}
+
+static int pxa_set_target(struct cpufreq_policy *policy,
+                 unsigned int target_freq,
+                 unsigned int relation)
+{
+    int idx;
+    unsigned long cpus_allowed;
+    int cpu = policy->cpu;
+    struct cpufreq_freqs freqs;
+    pxa_freqs_t *pxa_freq_settings;
+    struct cpufreq_frequency_table *pxa_freqs_table;
+    unsigned long flags;
+    unsigned int unused;
+    unsigned int preset_mdrefr, postset_mdrefr;
+
+    /*
+     * Save this threads cpus_allowed mask.
+     */
+    cpus_allowed = current->cpus_allowed;
+
+    /*
+     * Bind to the specified CPU.  When this call returns,
+     * we should be running on the right CPU.
+     */
+    set_cpus_allowed(current, 1 << cpu);
+    BUG_ON(cpu != smp_processor_id());
+
+    /* Get the current policy */
+    if(policy->policy == CPUFREQ_POLICY_PERFORMANCE) {
+        pxa_freq_settings = pxa255_run_freqs;
+        pxa_freqs_table   = pxa255_run_freq_table;
+    }else if (policy->policy == CPUFREQ_POLICY_POWERSAVE) {
+        pxa_freq_settings = pxa255_turbo_freqs;
+        pxa_freqs_table   = pxa255_turbo_freq_table;
+    }else if (policy->policy == CPUFREQ_POLICY_GOVERNOR) {
+        pxa_freq_settings = pxa255_run_freqs;
+        pxa_freqs_table   = pxa255_run_freq_table;
+    }else {
+        printk("CPU PXA: Unknown policy found. "
+               "Using CPUFREQ_POLICY_PERFORMANCE\n");
+        pxa_freq_settings = pxa255_run_freqs;
+        pxa_freqs_table   = pxa255_run_freq_table;
+    } 
+
+    /* Lookup the next frequency */
+	if (cpufreq_frequency_table_target(policy, pxa_freqs_table, 
+	                                   target_freq, relation, &idx)) {
+		return -EINVAL;
+    }
+
+    freqs.old = policy->cur;
+    freqs.new = pxa_freq_settings[idx].khz;
+    freqs.cpu = policy->cpu;  
+    if(freq_debug) {
+        printk(KERN_INFO "Changing CPU frequency to %d Mhz, (SDRAM %d Mhz)\n", 
+            freqs.new/1000, (pxa_freq_settings[idx].div2) ? 
+            (pxa_freq_settings[idx].membus/2000) : 
+            (pxa_freq_settings[idx].membus/1000));
+    }
+
+    void *ramstart = phys_to_virt(0xa0000000);
+
+    /* 
+     * Tell everyone what we're about to do... 
+     * you should add a notify client with any platform specific 
+     * Vcc changing capability
+     */
+    cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+    /* Calculate the next MDREFR.  If we're slowing down the SDRAM clock
+     * we need to preset the smaller DRI before the change.  If we're speeding
+     * up we need to set the larger DRI value after the change.  
+     */
+    preset_mdrefr = postset_mdrefr = MDREFR;
+    if((MDREFR & MDREFR_DRI_MASK) > MDREFR_DRI(pxa_freq_settings[idx].membus)) {    
+        preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK) | 
+                        MDREFR_DRI(pxa_freq_settings[idx].membus);
+    }
+    postset_mdrefr = (postset_mdrefr & ~MDREFR_DRI_MASK) | 
+                    MDREFR_DRI(pxa_freq_settings[idx].membus);
+    
+    /* If we're dividing the memory clock by two for the SDRAM clock, this
+     * must be set prior to the change.  Clearing the divide must be done
+     * after the change.
+     */
+    if(pxa_freq_settings[idx].div2) { 
+        preset_mdrefr  |= MDREFR_DB2_MASK;
+        postset_mdrefr |= MDREFR_DB2_MASK;
+    } else { 
+        postset_mdrefr &= ~MDREFR_DB2_MASK; 
+    }
+    
+    local_irq_save(flags);
+    
+    /* Set new the CCCR */
+    CCCR = pxa_freq_settings[idx].cccr;
+
+    __asm__ __volatile__("                                  \
+        ldr r4, [%1] ;  /* load MDREFR */                   \
+        b   2f ;                                            \
+        .align  5 ;                                         \
+1:                                                          \
+        str %4, [%1] ;          /* preset the MDREFR */     \
+        mcr p14, 0, %2, c6, c0, 0 ; /* set CCLKCFG[FCS] */  \
+        str %5, [%1] ;          /* postset the MDREFR */    \
+                                                            \
+        b   3f       ;                                      \
+2:      b   1b       ;                                      \
+3:      nop          ;                                      \
+        "                                                                            
+        : "=&r" (unused)                                                             
+        : "r" (&MDREFR), "r" (CCLKCFG_TURBO|CCLKCFG_FCS), "r" (ramstart), \
+          "r" (preset_mdrefr), "r" (postset_mdrefr)             
+        : "r4", "r5");
+    local_irq_restore(flags);
+
+    /*
+     * Restore the CPUs allowed mask.
+     */
+    set_cpus_allowed(current, cpus_allowed);
+
+    /* 
+     * Tell everyone what we've just done... 
+     * you should add a notify client with any platform specific 
+     * SDRAM refresh timer adjustments
+     */
+    cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+    return 0;
+}
+
+static int pxa_cpufreq_init(struct cpufreq_policy *policy)
+{
+    unsigned long cpus_allowed;
+    unsigned int cpu = policy->cpu;
+    int i;
+
+	cpus_allowed = current->cpus_allowed;
+
+	set_cpus_allowed(current, 1 << cpu);
+	BUG_ON(cpu != smp_processor_id());
+
+    /* set default policy and cpuinfo */
+    policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+    policy->cpuinfo.max_freq = PXA25x_MAX_FREQ;
+    policy->cpuinfo.min_freq = PXA25x_MIN_FREQ;
+    policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */
+    policy->cur = get_clk_frequency_khz(0); /* current freq */
+    policy->min = policy->max = policy->cur;
+
+    /* Generate the run cpufreq_frequency_table struct */
+    for(i=0;i<NUM_RUN_FREQS;i++) {
+        pxa255_run_freq_table[i].frequency = pxa255_run_freqs[i].khz;
+        pxa255_run_freq_table[i].index = i;    
+    }
+    pxa255_run_freq_table[i].frequency = CPUFREQ_TABLE_END;
+    /* Generate the turbo cpufreq_frequency_table struct */
+    for(i=0;i<NUM_TURBO_FREQS;i++) {
+        pxa255_turbo_freq_table[i].frequency = pxa255_turbo_freqs[i].khz;
+        pxa255_turbo_freq_table[i].index = i;    
+    }
+    pxa255_turbo_freq_table[i].frequency = CPUFREQ_TABLE_END;
+    
+    set_cpus_allowed(current, cpus_allowed);
+    printk(KERN_INFO "PXA CPU frequency change support initialized\n");
+
+    return 0;
+}
+
+static struct cpufreq_driver pxa_cpufreq_driver = {
+    .verify     = pxa_verify_policy,
+    .target     = pxa_set_target,
+    .init       = pxa_cpufreq_init,
+    .name       = "PXA25x",
+};
+
+static int __init pxa_cpu_init(void)
+{
+    return cpufreq_register_driver(&pxa_cpufreq_driver);
+}
+
+static void __exit pxa_cpu_exit(void)
+{
+    cpufreq_unregister_driver(&pxa_cpufreq_driver);
+}
+
+
+MODULE_AUTHOR ("Intrinsyc Software Inc.");
+MODULE_DESCRIPTION ("CPU frequency changing driver for the PXA architecture");
+MODULE_LICENSE("GPL");
+module_init(pxa_cpu_init);
+module_exit(pxa_cpu_exit);
+
--- linux-2.6.5/arch/arm/boot/compressed/misc.c~heh	2004-04-03 22:37:37.000000000 -0500
+++ linux-2.6.5/arch/arm/boot/compressed/misc.c	2004-04-30 20:57:36.000000000 -0400
@@ -113,7 +113,7 @@
  * gzip delarations
  */
 #define OF(args)  args
-#define STATIC static
+#define STATIC
 
 typedef unsigned char  uch;
 typedef unsigned short ush;
@@ -122,12 +122,12 @@
 #define WSIZE 0x8000		/* Window size must be at least 32k, */
 				/* and a power of two */
 
-static uch *inbuf;		/* input buffer */
-static uch window[WSIZE];	/* Sliding window buffer */
+unsigned char *inbuf;		/* input buffer */
+unsigned char window[WSIZE];	/* Sliding window buffer */
 
-static unsigned insize;		/* valid bytes in inbuf */
-static unsigned inptr;		/* index of next byte to be processed in inbuf */
-static unsigned outcnt;		/* bytes in output buffer */
+unsigned int insize;		/* valid bytes in inbuf */
+unsigned int inptr;		/* index of next byte to be processed in inbuf */
+unsigned int outcnt;		/* bytes in output buffer */
 
 /* gzip flag byte */
 #define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
@@ -166,9 +166,9 @@
 extern char input_data[];
 extern char input_data_end[];
 
-static uch *output_data;
-static ulg output_ptr;
-static ulg bytes_out;
+unsigned char *output_data;
+unsigned long output_ptr;
+unsigned long bytes_out;
 
 static void *malloc(int size);
 static void free(void *where);
@@ -179,8 +179,8 @@
 static void puts(const char *);
 
 extern int end;
-static ulg free_mem_ptr;
-static ulg free_mem_ptr_end;
+unsigned long free_mem_ptr;
+unsigned long free_mem_ptr_end;
 
 #define HEAP_SIZE 0x2000
 
--- linux-2.6.5/arch/arm/mm/ioremap.c~heh	2004-04-03 22:37:36.000000000 -0500
+++ linux-2.6.5/arch/arm/mm/ioremap.c	2004-04-30 20:57:36.000000000 -0400
@@ -13,15 +13,18 @@
  * virtual space.  One should *only* use readl, writel, memcpy_toio and
  * so on with such remapped areas.
  *
- * Because the ARM only has a 32-bit address space we can't address the
- * whole of the (physical) PCI space at once.  PCI huge-mode addressing
- * allows us to circumvent this restriction by splitting PCI space into
- * two 2GB chunks and mapping only one at a time into processor memory.
- * We use MMU protection domains to trap any attempt to access the bank
- * that is not currently mapped.  (This isn't fully implemented yet.)
+ * ioremap support tweaked to allow support for large page mappings.  We
+ * have several issues that needs to be resolved first however:
+ *
+ *  1. We need set_pte, or something like set_pte to understand large
+ *     page mappings.
+ *
+ *  2. we need the unmap_* functions to likewise understand large page
+ *     mappings.
  */
 #include <linux/errno.h>
 #include <linux/mm.h>
+#include <linux/slab.h>
 #include <linux/vmalloc.h>
 
 #include <asm/page.h>
@@ -29,31 +32,162 @@
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 
-static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
-	unsigned long phys_addr, pgprot_t pgprot)
+extern rwlock_t vmlist_lock;
+extern struct vm_struct *vmlist;
+
+static struct vm_struct *
+get_io_vm_area(unsigned long size, unsigned long align, unsigned long flags)
 {
+	struct vm_struct **p, *tmp, *area;
+	unsigned long addr;
+
+	area = (struct vm_struct *)kmalloc(sizeof(*area), GFP_KERNEL);
+	if (!area)
+		return NULL;
+
+	align -= 1;
+
+	size += PAGE_SIZE;
+	addr = VMALLOC_START;
+	write_lock(&vmlist_lock);
+	for (p = &vmlist; (tmp = *p); p = &tmp->next) {
+		if ((unsigned long)tmp->addr < addr)
+			continue;
+		if ((size + addr) < addr)
+			goto out;
+		if (size + addr <= (unsigned long) tmp->addr)
+			break;
+		addr = tmp->size + (unsigned long) tmp->addr;
+		if ((addr + align) < addr)
+			goto out;
+		addr = (addr + align) & ~align;
+		if (addr > VMALLOC_END - size)
+			goto out;
+	}
+	area->flags = flags;
+	area->addr = (void *)addr;
+	area->size = size;
+	area->next = *p;
+	*p = area;
+	write_unlock(&vmlist_lock);
+	return area;
+
+out:
+	write_unlock(&vmlist_lock);
+	kfree(area);
+	return NULL;
+}
+
+static inline void unmap_area_pte(pmd_t *pmd, unsigned long address, unsigned long size)
+{
+	pte_t *ptep;
 	unsigned long end;
 
+	if (pmd_none(*pmd))
+		return;
+	if (pmd_bad(*pmd)) {
+		pmd_ERROR(*pmd);
+		pmd_clear(pmd);
+		return;
+	}
+	ptep = pte_offset_kernel(pmd, address);
 	address &= ~PMD_MASK;
 	end = address + size;
 	if (end > PMD_SIZE)
 		end = PMD_SIZE;
-	if (address >= end)
-		BUG();
 	do {
-		if (!pte_none(*pte)) {
-			printk("remap_area_pte: page already exists\n");
-			BUG();
+		pte_t pte;
+		pte = ptep_get_and_clear(ptep);
+		address += PAGE_SIZE;
+		ptep++;
+		if (pte_none(pte))
+			continue;
+		if (pte_present(pte)) {
+			unsigned long pfn = pte_pfn(pte);
+			struct page *page;
+
+			if (!pfn_valid(pfn))
+				continue;
+			page = pfn_to_page(pfn);
+			if (!PageReserved(page))
+				__free_page(page);
+			continue;
 		}
-		set_pte(pte, pfn_pte(phys_addr >> PAGE_SHIFT, pgprot));
+		printk(KERN_CRIT "Whee.. Swapped out page in kernel page table\n");
+	} while (address < end);
+}
+
+static inline void unmap_area_pmd(pgd_t *dir, unsigned long address, unsigned long size)
+{
+	pmd_t *pmd;
+	unsigned long end;
+
+	if (pgd_none(*dir))
+		return;
+	if (pgd_bad(*dir)) {
+		pgd_ERROR(*dir);
+		pgd_clear(dir);
+		return;
+	}
+	pmd = pmd_offset(dir, address);
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	do {
+		unmap_area_pte(pmd, address, end - address);
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	} while (address < end);
+}
+
+static void
+unmap_area_pages(unsigned long address, unsigned long size)
+{
+	unsigned long start = address;
+	unsigned long end = address + size;
+	pgd_t *dir;
+
+	dir = pgd_offset_k(address);
+	flush_cache_vunmap(start, end);
+	do {
+		unmap_area_pmd(dir, address, end - address);
+		address = (address + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	} while (address && (address < end));
+	flush_tlb_kernel_range(start, end);
+}
+
+static inline void
+remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
+	       unsigned long pfn, pgprot_t pgprot)
+{
+	unsigned long end;
+
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	BUG_ON(address >= end);
+	do {
+		if (!pte_none(*pte))
+			goto bad;
+
+		set_pte(pte, pfn_pte(pfn, pgprot));
 		address += PAGE_SIZE;
-		phys_addr += PAGE_SIZE;
+		pfn++;
 		pte++;
 	} while (address && (address < end));
+	return;
+
+ bad:
+	printk("remap_area_pte: page already exists\n");
+	BUG();
 }
 
-static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
-	unsigned long phys_addr, unsigned long flags)
+static inline int
+remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
+	       unsigned long pfn, unsigned long flags)
 {
 	unsigned long end;
 	pgprot_t pgprot;
@@ -64,51 +198,53 @@
 	if (end > PGDIR_SIZE)
 		end = PGDIR_SIZE;
 
-	phys_addr -= address;
-	if (address >= end)
-		BUG();
+	pfn -= address >> PAGE_SHIFT;
+	BUG_ON(address >= end);
 
 	pgprot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE | flags);
 	do {
 		pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address);
 		if (!pte)
 			return -ENOMEM;
-		remap_area_pte(pte, address, end - address, address + phys_addr, pgprot);
+		remap_area_pte(pte, address, end - address, pfn + (address >> PAGE_SHIFT), pgprot);
 		address = (address + PMD_SIZE) & PMD_MASK;
 		pmd++;
 	} while (address && (address < end));
 	return 0;
 }
 
-static int remap_area_pages(unsigned long address, unsigned long phys_addr,
-				 unsigned long size, unsigned long flags)
+static int
+remap_area_pages(unsigned long start, unsigned long pfn,
+		 unsigned long size, unsigned long flags)
 {
-	int error;
+	unsigned long address = start;
+	unsigned long end = start + size;
+	int err = 0;
 	pgd_t * dir;
-	unsigned long end = address + size;
 
-	phys_addr -= address;
+	pfn -= address >> PAGE_SHIFT;
 	dir = pgd_offset(&init_mm, address);
-	flush_cache_all();
-	if (address >= end)
-		BUG();
+	BUG_ON(address >= end);
 	spin_lock(&init_mm.page_table_lock);
 	do {
-		pmd_t *pmd;
-		pmd = pmd_alloc(&init_mm, dir, address);
-		error = -ENOMEM;
-		if (!pmd)
+		pmd_t *pmd = pmd_alloc(&init_mm, dir, address);
+		if (!pmd) {
+			err = -ENOMEM;
 			break;
+		}
 		if (remap_area_pmd(pmd, address, end - address,
-					 phys_addr + address, flags))
+				   pfn + (address >> PAGE_SHIFT), flags)) {
+			err = -ENOMEM;
 			break;
-		error = 0;
+		}
+
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
 	} while (address && (address < end));
+
 	spin_unlock(&init_mm.page_table_lock);
-	flush_tlb_all();
-	return error;
+	flush_cache_vmap(start, end);
+	return err;
 }
 
 /*
@@ -146,11 +282,11 @@
 	/*
 	 * Ok, go for it..
 	 */
-	area = get_vm_area(size, VM_IOREMAP);
+	area = get_io_vm_area(size, align, VM_IOREMAP);
 	if (!area)
 		return NULL;
 	addr = area->addr;
-	if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) {
+	if (remap_area_pages((unsigned long) addr, phys_addr >> PAGE_SHIFT, size, flags)) {
 		vfree(addr);
 		return NULL;
 	}
@@ -159,5 +295,26 @@
 
 void __iounmap(void *addr)
 {
-	vfree((void *) (PAGE_MASK & (unsigned long) addr));
+	struct vm_struct **p, *tmp;
+
+	if (!addr)
+		return;
+
+	if ((PAGE_SIZE - 1) & (unsigned long)addr) {
+		printk(KERN_ERR "Trying to iounmap() bad address (%p)\n", addr);
+		return;
+	}
+
+	write_lock(&vmlist_lock);
+	for (p = &vmlist; (tmp = *p); p = &tmp->next) {
+		if (tmp->addr == addr) {
+			*p = tmp->next;
+			unmap_area_pages((unsigned long) tmp->addr, tmp->size);
+			write_unlock(&vmlist_lock);
+			kfree(tmp);
+			return;
+		}
+	}
+	write_unlock(&vmlist_lock);
+	printk(KERN_ERR "Trying to iounmap nonexistent area (%p)\n", addr);
 }
--- linux-2.6.5/arch/arm/mm/proc-xscale.S~heh	2004-04-03 22:38:05.000000000 -0500
+++ linux-2.6.5/arch/arm/mm/proc-xscale.S	2004-04-30 20:57:36.000000000 -0400
@@ -563,11 +563,62 @@
 	movne	r2, #0				@ no -> fault
 
 	str	r2, [r0]			@ hardware version
+
+	@ We try to map 64K page entries when possible.  
+	@ We do that for kernel space only since the usage pattern from
+	@ the setting of VM area is quite simple.  User space is not worth
+	@ the implied complexity because of ever randomly changing PTEs 
+	@ (page aging, swapout, etc) requiring constant coherency checks.
+	@ Since PTEs are usually set in increasing order, we test the
+	@ possibility for a large page only when given the last PTE of a
+	@ 64K boundary.
+	tsteq	r1, #L_PTE_USER
+	andeq	r1, r0, #(15 << 2)
+	teqeq	r1, #(15 << 2)
+	beq	1f
+
 	mov	ip, #0
 	mcr	p15, 0, r0, c7, c10, 1		@ Clean D cache line
 	mcr	p15, 0, ip, c7, c10, 4		@ Drain Write (& Fill) Buffer
 	mov	pc, lr
 
+	@ See if we have 16 identical PTEs but with consecutive base addresses
+1:	bic	r3, r2, #0x0000f000
+	mov	r1, #0x0000f000
+2:	eor	r2, r2, r3
+	teq	r2, r1
+	bne	4f
+	subs	r1, r1, #0x00001000
+	ldr	r2, [r0, #-4]!
+	bne	2b
+	eors	r2, r2, r3
+	bne	4f
+
+	@ Now create our LARGE PTE from the current EXT one.
+	bic	r3, r3, #PTE_TYPE_MASK
+	orr	r3, r3, #PTE_TYPE_LARGE
+	and	r2, r3, #0x30			@ EXT_AP --> LARGE_AP0
+	orr	r2, r2, r2, lsl #2		@ add LARGE_AP1
+	orr	r2, r2, r2, lsl #4		@ add LARGE_AP3 + LARGE_AP2
+	and	r1, r3, #0x3c0			@ EXT_TEX
+	bic	r3, r3, #0x3c0
+	orr	r2, r2, r1, lsl #(12 - 6)	@ --> LARGE_TEX
+	orr	r2, r2, r3			@ add remaining bits
+
+	@ then put it in the pagetable
+	mov	r3, r2
+3:	strd	r2, [r0], #8
+	tst	r0, #(15 << 2)
+	bne	3b
+
+	@ Then sync the 2 corresponding cache lines
+	sub	r0, r0, #(16 << 2)
+	mcr	p15, 0, r0, c7, c10, 1		@ Clean D cache line
+4:	orr	r0, r0, #(15 << 2)
+	mcr	p15, 0, r0, c7, c10, 1		@ Clean D cache line
+	mov	ip, #0
+	mcr	p15, 0, ip, c7, c10, 4		@ Drain Write (& Fill) Buffer
+	mov	pc, lr
 
 	.ltorg
 
--- linux-2.6.5/arch/arm/mach-sa1100/Makefile~heh	2004-04-03 22:38:26.000000000 -0500
+++ linux-2.6.5/arch/arm/mach-sa1100/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -3,7 +3,7 @@
 #
 
 # Common support
-obj-y := generic.o irq.o dma.o
+obj-y := generic.o irq.o dma.o nmi-oopser.o
 obj-m :=
 obj-n :=
 obj-  :=
@@ -88,7 +88,7 @@
 obj-$(CONFIG_LEDS) += $(led-y)
 
 # SA1110 USB client support
-#obj-$(CONFIG_SA1100_USB)		+= usb/
+obj-$(CONFIG_SA1100_USB)		+= usb/
 
 # Miscelaneous functions
 obj-$(CONFIG_PM)			+= pm.o sleep.o
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/strings.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,43 @@
+/*
+ * usb/strings.h
+ *
+ * Copyright (C) 2002 Russell King.
+ *
+ * USB device string handling, built upon usb buffers.
+ */
+#ifndef USBDEV_STRINGS_H
+#define USBDEV_STRINGS_H
+
+#include <linux/spinlock.h>
+
+struct usb_buf;
+
+#define NR_STRINGS 8
+
+struct usb_string_descriptor;
+
+struct usbc_strs {
+	spinlock_t lock;
+	struct usb_buf *buf[NR_STRINGS];
+};
+
+#define usbc_string_desc(buf)	((struct usb_string_descriptor *)(buf)->data)
+
+void usbc_string_from_cstr(struct usb_buf *buf, const char *str);
+struct usb_buf *usbc_string_alloc(int len);
+void usbc_string_free(struct usb_buf *buf);
+
+int usbc_string_add(struct usbc_strs *table, struct usb_buf *buf);
+void usbc_string_del(struct usbc_strs *table, int nr);
+
+/*
+ * Note: usbc_string_find() increments the buffer use count.
+ * You must call usbb_put() after use.
+ */
+struct usb_buf *
+usbc_string_find(struct usbc_strs *table, unsigned int lang, unsigned int idx);
+
+void usbc_string_free_all(struct usbc_strs *table);
+void usbc_string_init(struct usbc_strs *table);
+
+#endif
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb_ctl.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,114 @@
+/*
+ *	Copyright (C)  Compaq Computer Corporation, 1998, 1999
+ *	Copyright (C)  Extenex Corporation 2001
+ *
+ *  usb_ctl.h
+ *
+ *  PRIVATE interface used to share info among components of the SA-1100 USB
+ *  core: usb_ctl, usb_ep0, usb_recv and usb_send. Clients of the USB core
+ *  should use sa1100_usb.h.
+ *
+ */
+
+#ifndef _USB_CTL_H
+#define _USB_CTL_H
+
+#include <asm/dma.h>  /* dmach_t */
+
+struct usb_client;
+
+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
+{
+	struct usb_client *client;
+	dma_regs_t *dmach_tx, *dmach_rx;
+	int state;
+	unsigned char address;
+	struct usb_stats_t stats;
+};
+
+/* in usb_ctl.c */
+extern struct usb_info_t usbd_info;
+
+/*
+ * Function Prototypes
+ */
+enum { kError=-1, kEvSuspend=0, kEvReset=1,
+	   kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 };
+int usbctl_next_state_on_event( int event );
+
+/* endpoint zero */
+void ep0_reset(void);
+void ep0_int_hndlr(void);
+
+/* receiver */
+int  ep1_recv(void);
+int  ep1_init(dma_regs_t *dma);
+void ep1_int_hndlr(int status);
+void ep1_reset(void);
+void ep1_stall(void);
+
+/* xmitter */
+void ep2_reset(void);
+int  ep2_init(dma_regs_t *dma);
+void ep2_int_hndlr(int status);
+void ep2_stall(void);
+
+#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))); \
+}
+
+
+#define CHECK_ADDRESS { if ( Ser0UDCAR == 1 ) { printk("%s:%d I lost my address!!!\n",__FUNCTION__, __LINE__);}}
+#endif /* _USB_CTL_H */
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb_send.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,302 @@
+/*
+ * Generic xmit layer for the SA1100 USB client function
+ * Copyright (c) 2001 by Nicolas Pitre
+ *
+ * This code was loosely inspired by the original version which was
+ * Copyright (c) Compaq Computer Corporation, 1998-1999
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This is still work in progress...
+ *
+ * Please see linux/Documentation/arm/SA1100/SA1100_USB for details.
+ * 15/03/2001 - ep2_start now sets UDCAR to overcome something that is hardware
+ * 		bug, I think. green@iXcelerator.com
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/delay.h>	// for the massive_attack hack 28Feb01ww
+#include <linux/usb_ch9.h>
+
+#include <asm/dma.h>
+
+#include "usbdev.h"
+#include "sa1100_usb.h"
+#include "sa1100usb.h"
+
+static unsigned int ep2_curdmalen;
+static unsigned int ep2_remain;
+
+static struct sausb_dev *ep2_dev;
+
+static void udc_set_cs2(u32 val, u32 mask, u32 check)
+{
+	int i = 0;
+
+	do {
+		Ser0UDCCS2 = val;
+		udelay(1);
+		if ((Ser0UDCCS2 & mask) == check)
+			return;
+	} while (i++ < 10000);
+
+	printk("UDC: UDCCS2 write timed out: val=0x%08x\n", val);
+}
+
+/* set feature stall executing, async */
+static void ep2_start(struct sausb_dev *usb)
+{
+	ep2_curdmalen = min(ep2_remain, usb->ep[1].maxpktsize);
+	if (ep2_curdmalen == 0)
+		return;
+
+	/*
+	 * must do this _before_ queue buffer..
+	 * stop NAKing IN tokens
+	 */
+	udc_set_cs2(usb->ep[1].udccs | UDCCS2_TPC, UDCCS2_TPC, 0);
+
+	UDC_write(Ser0UDCIMP, ep2_curdmalen - 1);
+
+	/* 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 */
+
+	/*
+	 * fight stupid silicon bug
+	 */
+	Ser0UDCAR = usb->ctl->address;
+
+	sa1100_start_dma(usb->ep[1].dmach, usb->ep[1].pktdma, ep2_curdmalen);
+}
+
+static void udc_ep2_done(struct sausb_dev *usb, int flag)
+{
+	int size = usb->ep[1].buflen - ep2_remain;
+
+	if (!usb->ep[1].buflen)
+		return;
+
+	dma_unmap_single(usb->dev, usb->ep[1].bufdma, usb->ep[1].buflen,
+			 DMA_TO_DEVICE);
+
+	usb->ep[1].bufdma = 0;
+	usb->ep[1].buflen = 0;
+	usb->ep[1].pktdma = 0;
+
+	if (usb->ep[1].cb_func)
+		usb->ep[1].cb_func(usb->ep[1].cb_data, flag, size);
+}
+
+/*
+ * Initialisation.  Clear out the status.
+ */
+void udc_ep2_init(struct sausb_dev *usb)
+{
+	ep2_dev = usb;
+
+	usb->ep[1].udccs = UDCCS2_FST;
+
+	BUG_ON(usb->ep[1].buflen);
+	BUG_ON(usb->ep[1].pktlen);
+
+	sa1100_reset_dma(usb->ep[1].dmach);
+}
+
+/*
+ * Note: rev A0-B2 chips don't like FST
+ */
+void udc_ep2_halt(struct sausb_dev *usb, int halt)
+{
+	usb->ep[1].host_halt = halt;
+
+	if (halt) {
+		usb->ep[1].udccs |= UDCCS2_FST;
+		udc_set_cs2(UDCCS2_FST, UDCCS2_FST, UDCCS2_FST);
+	} else {
+		sa1100_clear_dma(usb->ep[1].dmach);
+
+		udc_set_cs2(UDCCS2_FST, UDCCS2_FST, UDCCS2_FST);
+		udc_set_cs2(0, UDCCS2_FST, 0);
+		udc_set_cs2(UDCCS2_SST, UDCCS2_SST, 0);
+
+		usb->ep[1].udccs &= ~UDCCS2_FST;
+
+		udc_ep2_done(usb, -EINTR);
+	}
+}
+
+/*
+ * This gets called when we receive a SET_CONFIGURATION packet to EP0.
+ * We were configured.  We can now send packets to the host.
+ */
+void udc_ep2_config(struct sausb_dev *usb, unsigned int maxpktsize)
+{
+	/*
+	 * We shouldn't be transmitting anything...
+	 */
+	BUG_ON(usb->ep[1].buflen);
+	BUG_ON(usb->ep[1].pktlen);
+
+	/*
+	 * Set our configuration.
+	 */
+	usb->ep[1].maxpktsize = maxpktsize;
+	usb->ep[1].configured = 1;
+
+	/*
+	 * Clear any pending TPC status.
+	 */
+	udc_set_cs2(UDCCS2_TPC, UDCCS2_TPC, 0);
+
+	/*
+	 * Enable EP2 interrupts.
+	 */
+	usb->udccr &= ~UDCCR_TIM;
+	UDC_write(Ser0UDCCR, usb->udccr);
+
+	usb->ep[1].udccs = 0;
+}
+
+/*
+ * We saw a reset from the attached hub, or were deconfigured.
+ * This means we are no longer configured.
+ */
+void udc_ep2_reset(struct sausb_dev *usb)
+{
+	/*
+	 * Disable EP2 interrupts.
+	 */
+	usb->udccr |= UDCCR_TIM;
+	UDC_write(Ser0UDCCR, usb->udccr);
+
+	usb->ep[1].configured = 0;
+	usb->ep[1].maxpktsize = 0;
+
+	sa1100_reset_dma(usb->ep[1].dmach);
+	udc_ep2_done(usb, -EINTR);
+}
+
+void udc_ep2_int_hndlr(struct sausb_dev *usb)
+{
+	u32 status = Ser0UDCCS2;
+
+	// check for stupid silicon bug.
+	if (Ser0UDCAR != usb->ctl->address)
+		Ser0UDCAR = usb->ctl->address;
+
+	udc_set_cs2(usb->ep[1].udccs | UDCCS2_SST, UDCCS2_SST, 0);
+
+	if (!(status & UDCCS2_TPC)) {
+		printk("usb_send: Not TPC: UDCCS2 = %x\n", status);
+		return;
+	}
+
+	sa1100_stop_dma(usb->ep[1].dmach);
+
+	if (status & (UDCCS2_TPE | UDCCS2_TUR)) {
+		printk("usb_send: transmit error %x\n", status);
+		usb->ep[1].fifo_errs ++;
+		udc_ep2_done(usb, -EIO);
+	} else {
+		unsigned int imp;
+#if 1		// 22Feb01ww/Oleg
+		imp = ep2_curdmalen;
+#else
+		// this is workaround for case when setting
+		// of Ser0UDCIMP was failed
+		imp = Ser0UDCIMP + 1;
+#endif
+		usb->ep[1].pktdma += imp;
+		ep2_remain -= imp;
+
+		usb->ep[1].bytes += imp;
+		usb->ep[1].packets++;
+
+		sa1100_clear_dma(usb->ep[1].dmach);
+
+		if (ep2_remain != 0) {
+			ep2_start(usb);
+		} else {
+			udc_ep2_done(usb, 0);
+		}
+	}
+}
+
+int udc_ep2_send(struct sausb_dev *usb, char *buf, int len)
+{
+	unsigned long flags;
+	dma_addr_t dma;
+	int ret;
+
+	if (!buf || len == 0)
+		return -EINVAL;
+
+	dma = dma_map_single(usb->dev, buf, len, DMA_TO_DEVICE);
+
+	spin_lock_irqsave(&usb->lock, flags);
+	do {
+		if (!usb->ep[1].configured) {
+			ret = -ENODEV;
+			break;
+		}
+
+		if (usb->ep[1].buflen) {
+			ret = -EBUSY;
+			break;
+		}
+
+		usb->ep[1].bufdma = dma;
+		usb->ep[1].buflen = len;
+		usb->ep[1].pktdma = dma;
+		ep2_remain = len;
+
+		sa1100_clear_dma(usb->ep[1].dmach);
+
+		ep2_start(usb);
+		ret = 0;
+	} while (0);
+	spin_unlock_irqrestore(&usb->lock, flags);
+
+	if (ret)
+		dma_unmap_single(usb->dev, dma, len, DMA_TO_DEVICE);
+
+	return ret;
+}
+
+void udc_ep2_send_reset(struct sausb_dev *usb)
+{
+	sa1100_reset_dma(usb->ep[1].dmach);
+	udc_ep2_done(usb, -EINTR);
+}
+
+int udc_ep2_idle(struct sausb_dev *usb)
+{
+	if (!usb->ep[1].configured)
+		return -ENODEV;
+
+	if (usb->ep[1].buflen)
+		return -EBUSY;
+
+	return 0;
+}
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb-char.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,709 @@
+/*
+ * (C) Copyright 2000-2001 Extenex Corporation
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  usb-char.c
+ *
+ *  Miscellaneous character device interface for SA1100 USB function
+ *	driver.
+ *
+ *  Background:
+ *  The SA1100 function driver ported from the Compaq Itsy project
+ *  has an interface, usb-eth.c, to feed network packets over the
+ *  usb wire and into the Linux TCP/IP stack.
+ *
+ *  This file replaces that one with a simple character device
+ *  interface that allows unstructured "byte pipe" style reads and
+ *  writes over the USB bulk endpoints by userspace programs.
+ *
+ *  A new define, CONFIG_SA1100_USB_NETLINK, has been created that,
+ *  when set, (the default) causes the ethernet interface to be used.
+ *  When not set, this more pedestrian character interface is linked
+ *  in instead.
+ *
+ *  Please see linux/Documentation/arm/SA1100/SA1100_USB for details.
+ *
+ *  ward.willats@extenex.com
+ *
+ *  To do:
+ *  - Can't dma into ring buffer directly with dma_map/unmap usb_recv
+ *    uses and get bytes out at the same time DMA is going on. Investigate:
+ *    a) changing usb_recv to use alloc_consistent() at client request; or
+ *    b) non-ring-buffer based data structures. In the meantime, I am using
+ *    a bounce buffer. Simple, but wasteful.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/cache.h>
+#include <linux/poll.h>
+#include <linux/circ_buf.h>
+#include <linux/timer.h>
+#include <linux/usb_ch9.h>
+
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <asm/page.h>
+#include <asm/mach-types.h>
+
+#include "usb-char.h"
+#include "client.h"
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Driver  Options
+//////////////////////////////////////////////////////////////////////////////
+
+#define VERSION 	"0.4"
+
+
+#define VERBOSITY 1
+
+#if VERBOSITY
+# define	PRINTK(x, a...)	printk (x, ## a)
+#else
+# define	PRINTK(x, a...)	/**/
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+// Globals - Macros - Enums - Structures
+//////////////////////////////////////////////////////////////////////////////
+#ifndef MIN
+#define MIN( a, b ) ((a)<(b)?(a):(b))
+#endif
+
+typedef int bool; enum { false = 0, true = 1 };
+
+static const char pszMe[] = "usbchr: ";
+
+static wait_queue_head_t wq_read;
+static wait_queue_head_t wq_write;
+static wait_queue_head_t wq_poll;
+
+/* Serialze multiple writers onto the transmit hardware
+.. since we sleep the writer during transmit to stay in
+.. sync. (Multiple writers don't make much sense, but..) */
+static DECLARE_MUTEX( xmit_sem );
+
+// size of usb DATA0/1 packets. 64 is standard maximum
+// for bulk transport, though most hosts seem to be able
+// to handle larger.
+#define TX_PACKET_SIZE 64
+#define RX_PACKET_SIZE 64
+#define RBUF_SIZE  (4*PAGE_SIZE)
+
+static struct wcirc_buf {
+  char *buf;
+  int in;
+  int out;
+} rx_ring = { NULL, 0, 0 };
+
+static struct {
+	 unsigned long  cnt_rx_complete;
+	 unsigned long  cnt_rx_errors;
+	 unsigned long  bytes_rx;
+	 unsigned long  cnt_tx_timeouts;
+	 unsigned long  cnt_tx_errors;
+	 unsigned long  bytes_tx;
+} charstats;
+
+
+static char * tx_buf = NULL;
+static char * packet_buffer = NULL;
+static int sending = 0;
+static int usb_ref_count = 0;
+static int last_tx_result = 0;
+static int last_rx_result = 0;
+static int last_tx_size = 0;
+static struct timer_list tx_timer;
+
+//////////////////////////////////////////////////////////////////////////////
+// Prototypes
+//////////////////////////////////////////////////////////////////////////////
+static char * 	what_the_f( int e );
+static void 	free_txrx_buffers( void );
+static void     twiddle_descriptors(struct usb_client *client);
+static int      usbc_open( struct inode *pInode, struct file *pFile );
+static void     rx_done_callback_packet_buffer(void *data, int flag, int size );
+
+static void     tx_timeout( unsigned long );
+static void     tx_done_callback(void *data, int flag, int size );
+
+static ssize_t  usbc_read( struct file *, char *, size_t, loff_t * );
+static ssize_t  usbc_write( struct file *, const char *, size_t, loff_t * );
+static unsigned int usbc_poll( struct file *pFile, poll_table * pWait );
+static int usbc_ioctl( struct inode *pInode, struct file *pFile,
+                       unsigned int nCmd, unsigned long argument );
+static int      usbc_close( struct inode *pInode, struct file *pFile );
+
+#ifdef CONFIG_SA1100_EXTENEX1
+static void     extenex_configured_notify_proc( void );
+#endif
+//////////////////////////////////////////////////////////////////////////////
+// Private Helpers
+//////////////////////////////////////////////////////////////////////////////
+
+static char * what_the_f( int e )
+{
+	 char * p;
+	 switch( e ) {
+	 case 0:
+		  p = "noErr";
+		  break;
+	 case -ENODEV:
+		  p = "ENODEV - usb not in config state";
+		  break;
+	 case -EBUSY:
+		  p = "EBUSY - another request on the hardware";
+		  break;
+	 case -EAGAIN:
+		  p = "EAGAIN";
+		  break;
+	 case -EINTR:
+		  p = "EINTR - interrupted\n";
+		  break;
+	 case -EPIPE:
+		  p = "EPIPE - zero length xfer\n";
+		  break;
+	 default:
+		  p = "????";
+		  break;
+	 }
+	 return p;
+}
+
+static void free_txrx_buffers( void )
+{
+	 if ( rx_ring.buf != NULL ) {
+		  kfree( rx_ring.buf );
+		  rx_ring.buf = NULL;
+	 }
+	 if ( packet_buffer != NULL ) {
+		  kfree( packet_buffer );
+		  packet_buffer = NULL;
+	 }
+	 if ( tx_buf != NULL ) {
+		  kfree( tx_buf );
+		  tx_buf = NULL;
+	 }
+}
+
+/* twiddle_descriptors()
+ * It is between open() and start(). Setup descriptors.
+ */
+static void twiddle_descriptors(struct usb_client *client)
+{
+	struct cdb *cdb = sa1100_usb_get_descriptor_ptr();
+
+	cdb->ep1.wMaxPacketSize = cpu_to_le16(RX_PACKET_SIZE);
+	cdb->ep2.wMaxPacketSize = cpu_to_le16(TX_PACKET_SIZE);
+
+#ifdef CONFIG_SA1100_EXTENEX1
+	if (machine_is_extenex1()) {
+		int nr;
+
+		cdb->cfg.bmAttributes = USB_CONFIG_SELFPOWERED;
+		cdb->cfg.MaxPower = 0;
+
+		nr = sa1100_usb_add_string(client->ctl, "HHT Bulk Transfer");
+
+		if (nr > 0)
+			cdb->intf.iInterface = nr;
+	}
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// ASYNCHRONOUS
+//////////////////////////////////////////////////////////////////////////////
+static  void kick_start_rx( void )
+{
+	 if ( usb_ref_count ) {
+		  int total_space  = CIRC_SPACE( rx_ring.in, rx_ring.out, RBUF_SIZE );
+		  if ( total_space >= RX_PACKET_SIZE ) {
+		           sa1100_usb_recv_set_callback(rx_done_callback_packet_buffer, NULL);
+			   sa1100_usb_recv( packet_buffer, RX_PACKET_SIZE);
+		  }
+	 }
+}
+/*
+ * rx_done_callback_packet_buffer()
+ * We have completed a DMA xfer into the temp packet buffer.
+ * Move to ring.
+ *
+ * flag values:
+ * on init,  -EAGAIN
+ * on reset, -EINTR
+ * on RPE, -EIO
+ * on short packet -EPIPE
+ */
+static void
+rx_done_callback_packet_buffer(void *data, int flag, int size )
+{
+	 charstats.cnt_rx_complete++;
+
+	 if ( flag == 0 || flag == -EPIPE ) {
+		  size_t n;
+
+		  charstats.bytes_rx += size;
+
+		  n = CIRC_SPACE_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE );
+		  n = MIN( n, size );
+		  size -= n;
+
+		  memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer, n );
+		  rx_ring.in = (rx_ring.in + n) & (RBUF_SIZE-1);
+		  memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer + n, size );
+		  rx_ring.in = (rx_ring.in + size) & (RBUF_SIZE-1);
+
+		  wake_up_interruptible( &wq_read );
+		  wake_up_interruptible( &wq_poll );
+
+		  last_rx_result = 0;
+
+		  kick_start_rx();
+
+	 } else if ( flag != -EAGAIN ) {
+		  charstats.cnt_rx_errors++;
+		  last_rx_result = flag;
+		  wake_up_interruptible( &wq_read );
+		  wake_up_interruptible( &wq_poll );
+	 }
+	 else  /* init, start a read */
+		  kick_start_rx();
+}
+
+
+static void tx_timeout( unsigned long unused )
+{
+	printk( "%stx timeout\n", pszMe );
+	sa1100_usb_send_reset();
+	charstats.cnt_tx_timeouts++;
+}
+
+
+// on init, -EAGAIN
+// on reset, -EINTR
+// on TPE, -EIO
+static void tx_done_callback(void *data, int flags, int size )
+{
+	 if ( flags == 0 )
+		  charstats.bytes_tx += size;
+	 else
+		  charstats.cnt_tx_errors++;
+	 last_tx_size = size;
+	 last_tx_result = flags;
+	 sending = 0;
+	 wake_up_interruptible( &wq_write );
+	 wake_up_interruptible( &wq_poll );
+}
+
+
+static struct usb_client usbc_client = {
+	.name		= "usb-char",
+
+	/*
+	 * USB client identification for host use in CPU endian.
+	 */
+	.vendor		= 0,
+	.product	= 0,
+	.version	= 0,
+	.class		= 0xff,
+	.subclass	= 0,
+	.protocol	= 0,
+};
+
+#ifdef CONFIG_SA1100_EXTENEX1
+#include "../../../drivers/char/ex_gpio.h"
+static void extenex_state_change(void *data, int state, int oldstate)
+{
+	if (exgpio_play_string( "440,1:698,1") == -EAGAIN)
+		printk( "%sWanted to BEEP but ex_gpio not open\n", pszMe );
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+// Workers
+//////////////////////////////////////////////////////////////////////////////
+
+static int usbc_open(struct inode *pInode, struct file *pFile)
+{
+	int retval = 0;
+
+	PRINTK( KERN_DEBUG "%sopen()\n", pszMe );
+
+#ifdef CONFIG_SA1100_EXTENEX1
+	if (machine_is_extenex1()) {
+		usbc_client.vendor           = 0x0c9f;
+		usbc_client.product          = 0x0100;
+		usbc_client.version          = 0x0001;
+		usbc_client.manufacturer_str = "Extenex";
+		usbc_client.product_str      = "Handheld Theater";
+		usbc_client.serial_str       = "00000000";
+		usbc_client.state_change     = extenex_state_change;
+	}
+#endif
+
+	 /* start usb core */
+	 retval = usbctl_open(&usbc_client);
+	 if (retval)
+		return retval;
+
+	 /* allocate memory */
+	 if ( usb_ref_count == 0 ) {
+		  tx_buf = (char*) kmalloc( TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA );
+		  if ( tx_buf == NULL ) {
+			   printk( "%sARGHH! COULD NOT ALLOCATE TX BUFFER\n", pszMe );
+			   goto malloc_fail;
+		  }
+		  rx_ring.buf =
+			(char*) kmalloc( RBUF_SIZE, GFP_KERNEL );
+
+		  if ( rx_ring.buf == NULL ) {
+			   printk( "%sARGHH! COULD NOT ALLOCATE RX BUFFER\n", pszMe );
+			   goto malloc_fail;
+		  }
+
+		  packet_buffer =
+			(char*) kmalloc( RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA  );
+
+		  if ( packet_buffer == NULL ) {
+			   printk( "%sARGHH! COULD NOT ALLOCATE RX PACKET BUFFER\n", pszMe );
+			   goto malloc_fail;
+		  }
+		  rx_ring.in = rx_ring.out = 0;
+		  memset( &charstats, 0, sizeof( charstats ) );
+		  sending = 0;
+		  last_tx_result = 0;
+		  last_tx_size = 0;
+	 }
+
+	 /* modify default descriptors */
+	 twiddle_descriptors(&usbc_client);
+
+	 retval = usbctl_start(&usbc_client);
+	 if ( retval ) {
+		  printk( "%sAGHH! Could not USB core\n", pszMe );
+		  free_txrx_buffers();
+		  return retval;
+	 }
+	 usb_ref_count++;   /* must do _before_ kick_start() */
+	 MOD_INC_USE_COUNT;
+	 kick_start_rx();
+	 return 0;
+
+ malloc_fail:
+	 free_txrx_buffers();
+	 return -ENOMEM;
+}
+
+/*
+ * Read endpoint. Note that you can issue a read to an
+ * unconfigured endpoint. Eventually, the host may come along
+ * and configure underneath this module and data will appear.
+ */
+static ssize_t usbc_read( struct file *pFile, char *pUserBuffer,
+                        size_t stCount, loff_t *pPos )
+{
+	 ssize_t retval;
+	 unsigned long flags;
+	 DECLARE_WAITQUEUE( wait, current );
+
+	 PRINTK( KERN_DEBUG "%sread()\n", pszMe );
+
+	 local_irq_save(flags);
+	 if ( last_rx_result == 0 ) {
+		  local_irq_restore( flags );
+	 } else {  /* an error happended and receiver is paused */
+		  local_irq_restore( flags );
+		  last_rx_result = 0;
+		  kick_start_rx();
+	 }
+
+	 add_wait_queue( &wq_read, &wait );
+	 while( 1 ) {
+		  ssize_t bytes_avail;
+		  ssize_t bytes_to_end;
+
+		  set_current_state( TASK_INTERRUPTIBLE );
+
+		  /* snap ring buf state */
+		  local_irq_save( flags );
+		  bytes_avail  = CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE );
+		  bytes_to_end = CIRC_CNT_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE );
+		  local_irq_restore( flags );
+
+		  if ( bytes_avail != 0 ) {
+			   ssize_t bytes_to_move = MIN( stCount, bytes_avail );
+			   retval = 0;		// will be bytes transfered
+			   if ( bytes_to_move != 0 ) {
+					size_t n = MIN( bytes_to_end, bytes_to_move );
+					if ( copy_to_user( pUserBuffer,
+									   &rx_ring.buf[ rx_ring.out ],
+									   n ) ) {
+						 retval = -EFAULT;
+						 break;
+					}
+					bytes_to_move -= n;
+ 					retval += n;
+					// might go 1 char off end, so wrap
+					rx_ring.out = ( rx_ring.out + n ) & (RBUF_SIZE-1);
+					if ( copy_to_user( pUserBuffer + n,
+									   &rx_ring.buf[ rx_ring.out ],
+									   bytes_to_move )
+						 ) {
+						 retval = -EFAULT;
+						 break;
+					}
+					rx_ring.out += bytes_to_move;		// cannot wrap
+					retval += bytes_to_move;
+					kick_start_rx();
+			   }
+			   break;
+		  }
+		  else if ( last_rx_result ) {
+			   retval = last_rx_result;
+			   break;
+		  }
+		  else if ( pFile->f_flags & O_NONBLOCK ) {  // no data, can't sleep
+			   retval = -EAGAIN;
+			   break;
+		  }
+		  else if ( signal_pending( current ) ) {   // no data, can sleep, but signal
+			   retval = -ERESTARTSYS;
+			   break;
+		  }
+		  schedule();					   			// no data, can sleep
+	 }
+	 set_current_state( TASK_RUNNING );
+	 remove_wait_queue( &wq_read, &wait );
+
+	 if ( retval < 0 )
+		  printk( "%sread error %d - %s\n", pszMe, retval, what_the_f( retval ) );
+	 return retval;
+}
+
+/*
+ * Write endpoint. This routine attempts to break the passed in buffer
+ * into usb DATA0/1 packet size chunks and send them to the host.
+ * (The lower-level driver tries to do this too, but easier for us
+ * to manage things here.)
+ *
+ * We are at the mercy of the host here, in that it must send an IN
+ * token to us to pull this data back, so hopefully some higher level
+ * protocol is expecting traffic to flow in that direction so the host
+ * is actually polling us. To guard against hangs, a 5 second timeout
+ * is used.
+ *
+ * This routine takes some care to only report bytes sent that have
+ * actually made it across the wire. Thus we try to stay in lockstep
+ * with the completion routine and only have one packet on the xmit
+ * hardware at a time. Multiple simultaneous writers will get
+ * "undefined" results.
+ *
+  */
+static ssize_t  usbc_write( struct file *pFile, const char * pUserBuffer,
+							 size_t stCount, loff_t *pPos )
+{
+	 ssize_t retval = 0;
+	 ssize_t stSent = 0;
+
+	 DECLARE_WAITQUEUE( wait, current );
+
+	 PRINTK( KERN_DEBUG "%swrite() %d bytes\n", pszMe, stCount );
+
+	 down( &xmit_sem );  // only one thread onto the hardware at a time
+
+	 while( stCount != 0 && retval == 0 ) {
+		  int nThisTime  = MIN( TX_PACKET_SIZE, stCount );
+		  copy_from_user( tx_buf, pUserBuffer, nThisTime );
+		  sending = nThisTime;
+		  sa1100_usb_send_set_callback(tx_done_callback, NULL);
+ 		  retval = sa1100_usb_send( tx_buf, nThisTime);
+		  if ( retval < 0 ) {
+			   char * p = what_the_f( retval );
+			   printk( "%sCould not queue xmission. rc=%d - %s\n",
+					   pszMe, retval, p );
+			   sending = 0;
+			   break;
+		  }
+		  /* now have something on the diving board */
+		  add_wait_queue( &wq_write, &wait );
+		  tx_timer.expires = jiffies + ( HZ * 5 );
+		  add_timer( &tx_timer );
+		  while( 1 ) {
+			   set_current_state( TASK_INTERRUPTIBLE );
+			   if ( sending == 0 ) {  /* it jumped into the pool */
+					del_timer( &tx_timer );
+					retval = last_tx_result;
+					if ( retval == 0 ) {
+						 stSent		 += last_tx_size;
+						 pUserBuffer += last_tx_size;
+						 stCount     -= last_tx_size;
+					}
+					else
+						 printk( "%sxmission error rc=%d - %s\n",
+								 pszMe, retval, what_the_f(retval) );
+					break;
+			   }
+			   else if ( signal_pending( current ) ) {
+					del_timer( &tx_timer );
+					printk( "%ssignal\n", pszMe  );
+					retval = -ERESTARTSYS;
+					break;
+			   }
+			   schedule();
+		  }
+		  set_current_state( TASK_RUNNING );
+		  remove_wait_queue( &wq_write, &wait );
+	 }
+
+	 up( &xmit_sem );
+
+	 if ( 0 == retval )
+		  retval = stSent;
+	 return retval;
+}
+
+static unsigned int usbc_poll( struct file *pFile, poll_table * pWait )
+{
+	 unsigned int retval = 0;
+
+	 PRINTK( KERN_DEBUG "%poll()\n", pszMe );
+
+	 poll_wait( pFile, &wq_poll, pWait );
+
+	 if ( CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ) )
+		  retval |= POLLIN | POLLRDNORM;
+	 if ( sa1100_usb_xmitter_avail() )
+		  retval |= POLLOUT | POLLWRNORM;
+	 return retval;
+}
+
+static int usbc_ioctl( struct inode *pInode, struct file *pFile,
+                       unsigned int nCmd, unsigned long argument )
+{
+	 int retval = 0;
+
+	 switch( nCmd ) {
+
+	 case USBC_IOC_FLUSH_RECEIVER:
+		  sa1100_usb_recv_reset();
+		  rx_ring.in = rx_ring.out = 0;
+		  break;
+
+	 case USBC_IOC_FLUSH_TRANSMITTER:
+		  sa1100_usb_send_reset();
+		  break;
+
+	 case USBC_IOC_FLUSH_ALL:
+		  sa1100_usb_recv_reset();
+		  rx_ring.in = rx_ring.out = 0;
+		  sa1100_usb_send_reset();
+		  break;
+
+	 default:
+		  retval = -ENOIOCTLCMD;
+		  break;
+
+	 }
+	 return retval;
+}
+
+
+static int usbc_close( struct inode *pInode, struct file * pFile )
+{
+	PRINTK( KERN_DEBUG "%sclose()\n", pszMe );
+	if ( --usb_ref_count == 0 ) {
+		 down( &xmit_sem );
+		 usbctl_stop(&usbc_client);
+		 free_txrx_buffers();
+		 del_timer( &tx_timer );
+		 usbctl_close(&usbc_client);
+		 up(&xmit_sem);
+	}
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Initialization
+//////////////////////////////////////////////////////////////////////////////
+
+static struct file_operations usbc_fops = {
+	owner:		THIS_MODULE,
+	open:		usbc_open,
+	read:		usbc_read,
+	write:		usbc_write,
+	poll:		usbc_poll,
+	ioctl:		usbc_ioctl,
+	release:	usbc_close,
+};
+
+static struct miscdevice usbc_misc_device = {
+	USBC_MINOR,
+	"usb_char",
+	&usbc_fops
+};
+
+/*
+ * usbc_init()
+ */
+
+static int __init usbc_init( void )
+{
+	 int rc;
+
+#if !defined( CONFIG_ARCH_SA1100 )
+	 return -ENODEV;
+#endif
+
+	 if ( (rc = misc_register( &usbc_misc_device )) != 0 ) {
+		  printk( KERN_WARNING "%sCould not register device 10, "
+				  "%d. (%d)\n", pszMe, USBC_MINOR, rc );
+		  return -EBUSY;
+	 }
+
+	 // initialize wait queues
+	 init_waitqueue_head( &wq_read );
+	 init_waitqueue_head( &wq_write );
+	 init_waitqueue_head( &wq_poll );
+
+	 // initialize tx timeout timer
+	 init_timer( &tx_timer );
+	 tx_timer.function = tx_timeout;
+
+	  printk( KERN_INFO "USB Function Character Driver Interface"
+				  " - %s, (C) 2001, Extenex Corp.\n", VERSION
+		   );
+
+	 return rc;
+}
+
+static void __exit usbc_exit( void )
+{
+}
+
+module_init(usbc_init);
+module_exit(usbc_exit);
+
+// end: usb-char.c
+
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb-eth.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,535 @@
+/*
+ * Network driver for the SA1100 USB client function
+ * Copyright (c) 2001 by Nicolas Pitre
+ *
+ * This code was loosely inspired by the original initial ethernet test driver
+ * Copyright (c) Compaq Computer Corporation, 1999
+ *
+ * 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.
+ *
+ * Issues:
+ *  - DMA needs 8 byte aligned buffer, but causes inefficiencies
+ *    in the IP code.
+ *  - stall endpoint operations appeared to be very unstable.
+ */
+
+/*
+ * Define RX_NO_COPY if you want data to arrive directly into the
+ * receive network buffers, instead of arriving into bounce buffer
+ * and then get copied to network buffer.
+ *
+ * Since the SA1100 DMA engine is unable to cope with unaligned
+ * buffer addresses, we need to use bounce buffers or suffer the
+ * alignment trap performance hit.
+ */
+#undef RX_NO_COPY
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/random.h>
+#include <linux/usb_ch9.h>
+
+#include "client.h"
+
+
+#define ETHERNET_VENDOR_ID  0x049f
+#define ETHERNET_PRODUCT_ID 0x505A
+#define MAX_PACKET 32768
+
+/*
+ * This is our usb "packet size", and must match the host "packet size".
+ */
+static int usb_rsize = 64;
+static int usb_wsize = 64;
+
+struct usbe_info {
+	struct net_device	dev;
+	struct usb_client	client;
+	struct sk_buff		*cur_tx_skb;
+	struct sk_buff		*next_tx_skb;
+	struct sk_buff		*cur_rx_skb;
+	struct sk_buff		*next_rx_skb;
+#ifndef RX_NO_COPY
+	char *dmabuf;	// dma expects it's buffers to be aligned on 8 bytes boundary
+#endif
+	struct net_device_stats	stats;
+};
+
+
+static int usbeth_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if (new_mtu <= sizeof(struct ethhdr) || new_mtu > MAX_PACKET)
+		return -EINVAL;
+
+	// no second zero-length packet read wanted after mtu-sized packets
+	if (((new_mtu + sizeof(struct ethhdr)) % usb_rsize) == 0)
+		return -EDOM;
+
+	dev->mtu = new_mtu;
+	return 0;
+}
+
+static struct sk_buff *usb_new_recv_skb(struct usbe_info *usbe)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(2 + sizeof(struct ethhdr) + usbe->dev.mtu,
+			GFP_ATOMIC);
+
+	if (skb)
+		skb_reserve(skb, 2);
+
+	return skb;
+}
+
+static void usbeth_recv_callback(void *data, int flag, int len)
+{
+	struct usbe_info *usbe = data;
+	struct sk_buff *skb;
+	unsigned int size;
+	char *buf;
+
+	skb = usbe->cur_rx_skb;
+
+	/* flag validation */
+	if (flag != 0)
+		goto error;
+
+	/*
+	 * Make sure we have enough room left in the buffer.
+	 */
+	if (len > skb_tailroom(skb)) {
+		usbe->stats.rx_over_errors++;
+		usbe->stats.rx_errors++;
+		goto oversize;
+	}
+
+	/*
+	 * If the packet is smaller than usb_rsize bytes, the packet
+	 * is complete, and we need to use the next receive buffer.
+	 */
+	if (len != usb_rsize)
+		usbe->cur_rx_skb = usbe->next_rx_skb;
+
+	/*
+	 * Put the data onto the socket buffer and resume USB receive.
+	 */
+#ifndef RX_NO_COPY
+	memcpy(skb_put(skb, len), usbe->dmabuf, len);
+	buf = usbe->dmabuf;
+	size = usb_rsize;
+#else
+	skb_put(skb, len);
+	buf = usbe->cur_rx_skb->tail;
+	size = skb_tailroom(usbe->cur_rx_skb);
+#endif
+	usbctl_ep_queue_buffer(usbe->client.ctl, 1, buf, size);
+
+	if (len == usb_rsize)
+		return;
+
+	/*
+	 * A frame must contain at least an ethernet header.
+	 */
+	if (skb->len < sizeof(struct ethhdr)) {
+		usbe->stats.rx_length_errors++;
+		usbe->stats.rx_errors++;
+		goto recycle;
+	}
+
+	/*
+	 * MAC must match our address or the broadcast address.
+	 * Really, we should let any packet through, otherwise
+	 * things that rely on multicast won't work.
+	 */
+	if (memcmp(skb->data, usbe->dev.dev_addr, ETH_ALEN) &&
+	    memcmp(skb->data, usbe->dev.broadcast, ETH_ALEN)) {
+		usbe->stats.rx_frame_errors++;
+		usbe->stats.rx_errors++;
+		goto recycle;
+	}
+
+	/*
+	 * We're going to consume this SKB.  Get a new skb to
+	 * replace it with.  IF this fails, we'd better recycle
+	 * the one we have.
+	 */
+	usbe->next_rx_skb = usb_new_recv_skb(usbe);
+	if (!usbe->next_rx_skb) {
+		if (net_ratelimit())
+			printk(KERN_ERR "%s: can't allocate new rx skb\n",
+				usbe->dev.name);
+		usbe->stats.rx_dropped++;
+		goto recycle;
+	}
+
+// FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ?
+
+	usbe->stats.rx_packets++;
+	usbe->stats.rx_bytes += skb->len;
+	usbe->dev.last_rx = jiffies;
+
+	skb->dev = &usbe->dev;
+	skb->protocol = eth_type_trans(skb, &usbe->dev);
+	skb->ip_summed = CHECKSUM_NONE;
+
+	if (netif_rx(skb) == NET_RX_DROP)
+		usbe->stats.rx_dropped++;
+	return;
+
+ error:
+	/*
+	 * Oops, IO error, or stalled.
+	 */
+	switch (flag) {
+	case -EIO:	/* aborted transfer */
+		usbe->stats.rx_errors++;
+		break;
+
+	case -EPIPE:	/* fifo screwed/no data */
+		usbe->stats.rx_fifo_errors++;
+		usbe->stats.rx_errors++;
+		break;
+
+	case -EINTR:	/* reset */
+		break;
+
+	case -EAGAIN:	/* initialisation */
+		break;
+	}
+
+ oversize:
+	skb_trim(skb, 0);
+
+#ifndef RX_NO_COPY
+	buf = usbe->dmabuf;
+	size = usb_rsize;
+#else
+	buf = skb->tail;
+	size = skb_tailroom(skb);
+#endif
+	usbctl_ep_queue_buffer(usbe->client.ctl, 1, buf, size);
+	return;
+
+ recycle:
+	skb_trim(skb, 0);
+	usbe->next_rx_skb = skb;
+	return;
+}
+
+/*
+ * Send a skb.
+ *
+ * Note that the receiver expects the last packet to be a non-multiple
+ * of its rsize.  If the packet length is a muliple of wsize (and
+ * therefore the remote rsize) tweak the length.
+ */
+static void usbeth_send(struct sk_buff *skb, struct usbe_info *usbe)
+{
+	unsigned int len = skb->len;
+	int ret;
+
+	if ((len % usb_wsize) == 0)
+		len++;
+
+	ret = usbctl_ep_queue_buffer(usbe->client.ctl, 2, skb->data, len);
+	if (ret) {
+		printk(KERN_ERR "%s: tx dropping packet: %d\n",
+		       usbe->dev.name, ret);
+
+		/*
+		 * If the USB core can't accept the packet, we drop it.
+		 */
+		dev_kfree_skb_irq(skb);
+
+		usbe->cur_tx_skb = NULL;
+		usbe->stats.tx_carrier_errors++;
+	} else {
+		usbe->dev.trans_start = jiffies;
+	}
+}
+
+static void usbeth_send_callback(void *data, int flag, int size)
+{
+	struct usbe_info *usbe = data;
+	struct sk_buff *skb = usbe->cur_tx_skb;
+
+	switch (flag) {
+	case 0:
+		usbe->stats.tx_packets++;
+		usbe->stats.tx_bytes += skb->len;
+		break;
+	case -EIO:
+		usbe->stats.tx_errors++;
+		break;
+	default:
+		usbe->stats.tx_dropped++;
+		break;
+	}
+
+	dev_kfree_skb_irq(skb);
+
+	skb = usbe->cur_tx_skb = usbe->next_tx_skb;
+	usbe->next_tx_skb = NULL;
+
+	if (skb)
+		usbeth_send(skb, usbe);
+
+	netif_wake_queue(&usbe->dev);
+}
+
+static int usbeth_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct usbe_info *usbe = dev->priv;
+	unsigned long flags;
+
+	if (usbe->next_tx_skb) {
+		printk(KERN_ERR "%s: called with next_tx_skb != NULL\n",
+		       usbe->dev.name);
+		return 1;
+	}
+
+	local_irq_save(flags);
+	if (usbe->cur_tx_skb) {
+		usbe->next_tx_skb = skb;
+		netif_stop_queue(dev);
+	} else {
+		usbe->cur_tx_skb = skb;
+
+		usbeth_send(skb, usbe);
+	}
+	local_irq_restore(flags);
+	return 0;
+}
+
+/*
+ * Transmit timed out.  Reset the endpoint, and re-queue the pending
+ * packet.  If we have a free transmit slot, wake the transmit queue.
+ */
+static void usbeth_xmit_timeout(struct net_device *dev)
+{
+	struct usbe_info *usbe = dev->priv;
+	unsigned long flags;
+
+	usbctl_ep_reset(usbe->client.ctl, 2);
+
+	local_irq_save(flags);
+	if (usbe->cur_tx_skb)
+		usbeth_send(usbe->cur_tx_skb, usbe);
+
+	if (usbe->next_tx_skb == NULL)
+		netif_wake_queue(dev);
+
+	usbe->stats.tx_errors++;
+	local_irq_restore(flags);
+}
+
+static int usbeth_open(struct net_device *dev)
+{
+	struct usbe_info *usbe = dev->priv;
+	unsigned char *buf;
+	unsigned int size;
+
+	usbctl_ep_set_callback(usbe->client.ctl, 2, usbeth_send_callback, usbe);
+	usbctl_ep_set_callback(usbe->client.ctl, 1, usbeth_recv_callback, usbe);
+
+	usbe->cur_tx_skb = usbe->next_tx_skb = NULL;
+	usbe->cur_rx_skb = usb_new_recv_skb(usbe);
+	usbe->next_rx_skb = usb_new_recv_skb(usbe);
+	if (!usbe->cur_rx_skb || !usbe->next_rx_skb) {
+		printk(KERN_ERR "%s: can't allocate new skb\n",
+			usbe->dev.name);
+		if (usbe->cur_rx_skb)
+			kfree_skb(usbe->cur_rx_skb);
+		if (usbe->next_rx_skb)
+			kfree_skb(usbe->next_rx_skb);
+		return -ENOMEM;;
+	}
+#ifndef RX_NO_COPY
+	buf =  usbe->dmabuf;
+	size = usb_rsize;
+#else
+	buf = usbe->cur_rx_skb->tail;
+	size = skb_tailroom(usbe->cur_rx_skb);
+#endif
+	usbctl_ep_queue_buffer(usbe->client.ctl, 1, buf, size);
+
+	if (netif_carrier_ok(dev))
+		netif_start_queue(dev);
+
+	return 0;
+}
+
+static int usbeth_close(struct net_device *dev)
+{
+	struct usbe_info *usbe = dev->priv;
+
+	netif_stop_queue(dev);
+
+	usbctl_ep_set_callback(usbe->client.ctl, 2, NULL, NULL);
+	usbctl_ep_set_callback(usbe->client.ctl, 1, NULL, NULL);
+	usbctl_ep_reset(usbe->client.ctl, 2);
+	usbctl_ep_reset(usbe->client.ctl, 1);
+
+	if (usbe->cur_tx_skb)
+		kfree_skb(usbe->cur_tx_skb);
+	if (usbe->next_tx_skb)
+		kfree_skb(usbe->next_tx_skb);
+	if (usbe->cur_rx_skb)
+		kfree_skb(usbe->cur_rx_skb);
+	if (usbe->next_rx_skb)
+		kfree_skb(usbe->next_rx_skb);
+
+	return 0;
+}
+
+static struct net_device_stats *usbeth_stats(struct net_device *dev)
+{
+	struct usbe_info *usbe = dev->priv;
+
+	return &usbe->stats;
+}
+
+static int __init usbeth_probe(struct net_device *dev)
+{
+	u8 node_id[ETH_ALEN];
+
+	SET_MODULE_OWNER(dev);
+
+	/*
+	 * Assign the hardware address of the board:
+	 * generate it randomly, as there can be many such
+	 * devices on the bus.
+	 */
+	get_random_bytes(node_id, sizeof node_id);
+	node_id[0] &= 0xfe;	// clear multicast bit
+	memcpy(dev->dev_addr, node_id, sizeof node_id);
+
+	ether_setup(dev);
+	dev->flags &= ~IFF_MULTICAST;
+	dev->flags &= ~IFF_BROADCAST;
+	//dev->flags |= IFF_NOARP;
+
+	return 0;
+}
+
+/*
+ * This is called when something in the upper usb client layers
+ * changes that affects the endpoint connectivity state (eg,
+ * connection or disconnection from the host.)  We probably want
+ * to do some more handling here, like kicking off a pending
+ * transmission if we're running?
+ */
+static void usbeth_state_change(void *data, int state, int oldstate)
+{
+	struct usbe_info *usbe = data;
+
+	if (state == USB_STATE_CONFIGURED) {
+		netif_carrier_on(&usbe->dev);
+		if (netif_running(&usbe->dev))
+			netif_wake_queue(&usbe->dev);
+	} else {
+		if (netif_running(&usbe->dev))
+			netif_stop_queue(&usbe->dev);
+		netif_carrier_off(&usbe->dev);
+	}
+}
+
+static struct usbe_info usbe_info = {
+	.dev	= {
+		.name			= "usbf",
+		.init			= usbeth_probe,
+		.get_stats		= usbeth_stats,
+		.watchdog_timeo		= 1 * HZ,
+		.open			= usbeth_open,
+		.stop			= usbeth_close,
+		.hard_start_xmit	= usbeth_xmit,
+		.change_mtu		= usbeth_change_mtu,
+		.tx_timeout		= usbeth_xmit_timeout,
+		.priv			= &usbe_info,
+	},
+	.client	= {
+		.name			= "usbeth",
+		.priv			= &usbe_info,
+		.state_change		= usbeth_state_change,
+
+		/*
+		 * USB client identification for host use in CPU endian.
+		 */
+		.vendor			= ETHERNET_VENDOR_ID,
+		.product		= ETHERNET_PRODUCT_ID,
+		.version		= 0,
+		.class			= 0xff,	/* vendor specific */
+		.subclass		= 0,
+		.protocol		= 0,
+
+		.product_str		= "SA1100 USB NIC",
+	},
+};
+
+static int __init usbeth_init(void)
+{
+	int rc;
+
+#ifndef RX_NO_COPY
+	usbe_info.dmabuf = kmalloc(usb_rsize, GFP_KERNEL | GFP_DMA);
+	if (!usbe_info.dmabuf)
+		return -ENOMEM;
+#endif
+
+	if (register_netdev(&usbe_info.dev) != 0) {
+#ifndef RX_NO_COPY
+		kfree(usbe_info.dmabuf);
+#endif
+		return -EIO;
+	}
+
+	rc = usbctl_open(&usbe_info.client);
+	if (rc == 0) {
+		struct cdb *cdb = sa1100_usb_get_descriptor_ptr();
+
+		cdb->ep1.wMaxPacketSize = cpu_to_le16(usb_rsize);
+		cdb->ep2.wMaxPacketSize = cpu_to_le16(usb_wsize);
+
+		rc = usbctl_start(&usbe_info.client);
+		if (rc)
+			usbctl_close(&usbe_info.client);
+	}
+
+	if (rc) {
+		unregister_netdev(&usbe_info.dev);
+#ifndef RX_NO_COPY
+		kfree(usbe_info.dmabuf);
+#endif
+	}
+
+	return rc;
+}
+
+static void __exit usbeth_cleanup(void)
+{
+	usbctl_stop(&usbe_info.client);
+	usbctl_close(&usbe_info.client);
+
+	unregister_netdev(&usbe_info.dev);
+#ifndef RX_NO_COPY
+	kfree(usbe_info.dmabuf);
+#endif
+}
+
+module_init(usbeth_init);
+module_exit(usbeth_cleanup);
+
+MODULE_DESCRIPTION("USB client ethernet driver");
+MODULE_PARM(usb_rsize, "1i");
+MODULE_PARM_DESC(usb_rsize, "number of bytes in packets from host to sa11x0");
+MODULE_PARM(usb_wsize, "1i");
+MODULE_PARM_DESC(usb_wsize, "number of bytes in packets from sa11x0 to host");
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb_recv.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,318 @@
+/*
+ * Generic receive layer for the SA1100 USB client function
+ * Copyright (c) 2001 by Nicolas Pitre
+ *
+ * This code was loosely inspired by the original version which was
+ * Copyright (c) Compaq Computer Corporation, 1998-1999
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This is still work in progress...
+ *
+ * Please see linux/Documentation/arm/SA1100/SA1100_USB for details.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/usb_ch9.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+
+#include "sa1100_usb.h"
+#include "sa1100usb.h"
+
+static int naking;
+
+#if 1
+static void dump_buf(struct sausb_dev *usb, const char *prefix)
+{
+	printk("%s: buf [dma=%08x len=%3d] pkt [cpu=%08x dma=%08x len=%3d rem=%3d]\n",
+		prefix,
+		usb->ep[0].bufdma,
+		usb->ep[0].buflen,
+		(unsigned int)usb->ep[0].pktcpu,
+		usb->ep[0].pktdma,
+		usb->ep[0].pktlen,
+		usb->ep[0].pktrem);
+}
+#endif
+
+static void udc_ep1_done(struct sausb_dev *usb, int flag, int size)
+{
+//	printk("UDC: rxd: %3d %3d\n", flag, size);
+	dump_buf(usb, "UDC: rxd");
+
+	if (!usb->ep[0].buflen)
+		return;
+
+	dma_unmap_single(usb->dev, usb->ep[0].bufdma, usb->ep[0].buflen,
+			 DMA_FROM_DEVICE);
+
+	usb->ep[0].bufdma = 0;
+	usb->ep[0].buflen = 0;
+	usb->ep[0].pktcpu = NULL;
+	usb->ep[0].pktdma = 0;
+	usb->ep[0].pktlen = 0;
+	usb->ep[0].pktrem = 0;
+
+	if (usb->ep[0].cb_func)
+		usb->ep[0].cb_func(usb->ep[0].cb_data, flag, size);
+}
+
+/*
+ * Initialisation.  Clear out the status, and set FST.
+ */
+void udc_ep1_init(struct sausb_dev *usb)
+{
+	sa1100_reset_dma(usb->ep[0].dmach);
+
+	UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC);
+
+	BUG_ON(usb->ep[0].buflen);
+	BUG_ON(usb->ep[0].pktlen);
+}
+
+void udc_ep1_halt(struct sausb_dev *usb, int halt)
+{
+	if (halt) {
+		/* force stall at UDC */
+		UDC_set(Ser0UDCCS1, UDCCS1_FST);
+	} else {
+		sa1100_reset_dma(usb->ep[0].dmach);
+
+		UDC_clear(Ser0UDCCS1, UDCCS1_FST);
+
+		udc_ep1_done(usb, -EINTR, 0);
+	}
+}
+
+/*
+ * This gets called when we receive a SET_CONFIGURATION packet to EP0.
+ * We were configured.  We can now accept packets from the host.
+ */
+void udc_ep1_config(struct sausb_dev *usb, unsigned int maxpktsize)
+{
+	usb->ep[0].maxpktsize = maxpktsize;
+	usb->ep[0].configured = 1;
+
+	Ser0UDCOMP = maxpktsize - 1;
+
+	sa1100_reset_dma(usb->ep[0].dmach);
+	udc_ep1_done(usb, -EINTR, 0);
+
+	/*
+	 * Enable EP1 interrupts.
+	 */
+	usb->udccr &= ~UDCCR_RIM;
+	UDC_write(Ser0UDCCR, usb->udccr);
+}
+
+/*
+ * We saw a reset from the attached hub.  This means we are no
+ * longer configured, and as far as the rest of the world is
+ * concerned, we don't exist.
+ */
+void udc_ep1_reset(struct sausb_dev *usb)
+{
+	/*
+	 * Disable EP1 interrupts.
+	 */
+	usb->udccr |= UDCCR_RIM;
+	UDC_write(Ser0UDCCR, usb->udccr);
+
+	usb->ep[0].configured = 0;
+	usb->ep[0].maxpktsize = 0;
+
+	sa1100_reset_dma(usb->ep[0].dmach);
+	udc_ep1_done(usb, -EINTR, 0);
+}
+
+void udc_ep1_int_hndlr(struct sausb_dev *usb)
+{
+	dma_addr_t dma_addr;
+	unsigned int len;
+	u32 status = Ser0UDCCS1;
+
+	dump_buf(usb, "UDC: int");
+
+	if (naking) {
+		printk("UDC: usbrx: in ISR but naking [0x%02x]\n", status);
+		return;
+	}
+
+	if (!(status & UDCCS1_RPC))
+		/* you can get here if we are holding NAK */
+		return;
+
+	if (!usb->ep[0].buflen) {
+		printk("UDC: usb_recv: RPC for non-existent buffer [0x%02x]\n", status);
+		naking = 1;
+		return;
+	}
+
+	sa1100_stop_dma(usb->ep[0].dmach);
+
+	dma_addr = sa1100_get_dma_pos(usb->ep[0].dmach);
+
+	/*
+	 * We've finished with the DMA for this packet.
+	 */
+	sa1100_clear_dma(usb->ep[0].dmach);
+
+	if (status & UDCCS1_SST) {
+		printk("UDC: usb_recv: stall sent\n");
+		UDC_flip(Ser0UDCCS1, UDCCS1_SST);
+
+		/*
+		 * UDC aborted current transfer, so we do.
+		 *
+		 * It would be better to re-queue this buffer IMHO.  It
+		 * hasn't gone anywhere yet. --rmk
+		 */
+		UDC_flip(Ser0UDCCS1, UDCCS1_RPC);
+		udc_ep1_done(usb, -EIO, 0);
+		return;
+	}
+
+	if (status & UDCCS1_RPE) {
+		printk("UDC: usb_recv: RPError %x\n", status);
+		UDC_flip(Ser0UDCCS1, UDCCS1_RPC);
+		udc_ep1_done(usb, -EIO, 0);
+		return;
+	}
+
+	len = dma_addr - usb->ep[0].pktdma;
+	if (len < 0) {
+		printk("UDC: usb_recv: dma_addr (%x) < pktdma (%x)\n",
+			dma_addr, usb->ep[0].pktdma);
+		len = 0;
+	}
+
+	if (len > usb->ep[0].pktlen)
+		len = usb->ep[0].pktlen;
+
+	/*
+	 * If our transfer was smaller, and we have bytes left in
+	 * the FIFO, we need to read them out manually.
+	 */
+	if (len < usb->ep[0].pktlen && (Ser0UDCCS1 & UDCCS1_RNE)) {
+		char *buf;
+
+		dma_sync_single(usb->dev, usb->ep[0].pktdma + len,
+				usb->ep[0].pktlen - len, DMA_FROM_DEVICE);
+
+		buf = (char *)usb->ep[0].pktcpu + len;
+
+		do {
+			*buf++ = Ser0UDCDR;
+			len++;
+		} while (len < usb->ep[0].pktlen && (Ser0UDCCS1 & UDCCS1_RNE));
+
+		/*
+		 * Note: knowing the internals of this macro is BAD, but we
+		 * need this to cause the data to be written back to memory.
+		 */
+		dma_sync_single(usb->dev, usb->ep[0].pktdma + len,
+				usb->ep[0].pktlen - len, DMA_TO_DEVICE);
+	}
+
+	/*
+	 * If the FIFO still contains data, something's definitely wrong.
+	 */
+	if (Ser0UDCCS1 & UDCCS1_RNE) {
+		printk("UDC: usb_recv: fifo screwed, shouldn't contain data\n");
+		usb->ep[0].fifo_errs++;
+		naking = 1;
+		udc_ep1_done(usb, -EPIPE, 0);
+		return;
+	}
+
+	/*
+	 * Do statistics.
+	 */
+	if (len) {
+		usb->ep[0].bytes += len;
+		usb->ep[0].packets ++;
+	}
+
+	/*
+	 * Update remaining byte count for this buffer.
+	 */
+	usb->ep[0].pktrem -= len;
+
+	/*
+	 * If we received a full-sized packet, and there's more
+	 * data remaining, th, queue up another receive.
+	 */
+	if (len == usb->ep[0].pktlen && usb->ep[0].pktrem != 0) {
+		usb->ep[0].pktcpu += len;
+		usb->ep[0].pktdma += len;
+		usb->ep[0].pktlen = min(usb->ep[0].pktrem, usb->ep[0].maxpktsize);
+		sa1100_start_dma(usb->ep[0].dmach, usb->ep[0].pktdma, usb->ep[0].pktlen);
+		/*
+		 * Clear RPC to receive next packet.
+		 */
+		UDC_flip(Ser0UDCCS1, UDCCS1_RPC);
+		dump_buf(usb, "UDC: req");
+		return;
+	}
+
+	naking = 1;
+	udc_ep1_done(usb, 0, usb->ep[0].buflen - usb->ep[0].pktrem);
+}
+
+int udc_ep1_queue_buffer(struct sausb_dev *usb, char *buf, unsigned int len)
+{
+	unsigned long flags;
+	dma_addr_t dma;
+	int ret;
+
+	if (!buf || len == 0)
+		return -EINVAL;
+
+	dma = dma_map_single(usb->dev, buf, len, DMA_FROM_DEVICE);
+
+	spin_lock_irqsave(&usb->lock, flags);
+	do {
+		if (usb->ep[0].buflen) {
+			ret = -EBUSY;
+			break;
+		}
+
+		sa1100_clear_dma(usb->ep[0].dmach);
+
+		usb->ep[0].bufdma = dma;
+		usb->ep[0].buflen = len;
+		usb->ep[0].pktcpu = buf;
+		usb->ep[0].pktdma = dma;
+		usb->ep[0].pktlen = min(len, usb->ep[0].maxpktsize);
+		usb->ep[0].pktrem = len;
+
+		sa1100_start_dma(usb->ep[0].dmach, usb->ep[0].bufdma, usb->ep[0].buflen);
+		dump_buf(usb, "UDC: que");
+
+		if (naking) {
+			/* turn off NAK of OUT packets, if set */
+			UDC_flip(Ser0UDCCS1, UDCCS1_RPC);
+			naking = 0;
+		}
+
+		ret = 0;
+	} while (0);
+	spin_unlock_irqrestore(&usb->lock, flags);
+
+	if (ret)
+		dma_unmap_single(usb->dev, dma, len, DMA_FROM_DEVICE);
+
+	return 0;
+}
+
+void udc_ep1_recv_reset(struct sausb_dev *usb)
+{
+	sa1100_reset_dma(usb->ep[0].dmach);
+	udc_ep1_done(usb, -EINTR, 0);
+}
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/buffer.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,63 @@
+/*
+ * usb/buffer.c
+ *
+ * Copyright (C) 2002 Russell King.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include "buffer.h"
+
+static LIST_HEAD(buffers);
+
+struct usb_buf *usbb_alloc(int size, int gfp)
+{
+	unsigned long flags;
+	struct usb_buf *buf;
+
+	buf = kmalloc(sizeof(struct usb_buf) + size, gfp);
+	if (buf) {
+		atomic_set(&buf->users, 1);
+		local_irq_save(flags);
+		list_add(&buf->list, &buffers);
+		local_irq_restore(flags);
+		buf->len  = 0;
+		buf->data = (unsigned char *) (buf + 1);
+		buf->head = (unsigned char *) (buf + 1);
+	}
+
+	return buf;
+}
+
+void __usbb_free(struct usb_buf *buf)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	list_del(&buf->list);
+	local_irq_restore(flags);
+	kfree(buf);
+}
+
+EXPORT_SYMBOL(usbb_alloc);
+EXPORT_SYMBOL(__usbb_free);
+
+static void __exit usbb_exit(void)
+{
+	if (!list_empty(&buffers)) {
+		struct list_head *l, *n;
+		printk("usbb: buffers not freed:\n");
+
+		list_for_each_safe(l, n, &buffers) {
+			struct usb_buf *b = list_entry(l, struct usb_buf, list);
+
+			printk(" %p: alloced from %p count %d\n",
+				b, b->alloced_by, atomic_read(&b->users));
+
+			__usbb_free(b);
+		}
+	}
+}
+
+module_exit(usbb_exit);
+
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb-char.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2001 Extenex Corporation
+ *
+ * usb-char.h
+ *
+ * Character device emulation client for SA-1100 client usb core.
+ *
+ *
+ *
+ */
+#ifndef _USB_CHAR_H
+#define _USB_CHAR_H
+
+#define USBC_MAJOR 10      /* miscellaneous character device */
+#define USBC_MINOR 240     /* in the "reserved for local use" range */
+
+#define USBC_MAGIC 0x8E
+
+/* zap everything in receive ring buffer */
+#define USBC_IOC_FLUSH_RECEIVER    _IO( USBC_MAGIC, 0x01 )
+
+/* reset transmitter */
+#define USBC_IOC_FLUSH_TRANSMITTER _IO( USBC_MAGIC, 0x02 )
+
+/* do both of above */
+#define USBC_IOC_FLUSH_ALL         _IO( USBC_MAGIC, 0x03 )
+
+
+
+
+
+
+#endif /* _USB_CHAR_H */
+
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/buffer.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,45 @@
+/*
+ * usb/buffer.h: USB client buffers
+ *
+ * Copyright (C) 2002 Russell King.
+ *
+ * Loosely based on linux/skbuff.h
+ */
+#ifndef USBDEV_BUFFER_H
+#define USBDEV_BUFFER_H
+
+#include <linux/list.h>
+
+struct usb_buf {
+	atomic_t	users;
+	struct list_head list;
+	void		*alloced_by;
+	unsigned char	*data;
+	unsigned char	*head;
+	unsigned int	len;
+};
+
+extern struct usb_buf *usbb_alloc(int size, int gfp);
+extern void __usbb_free(struct usb_buf *);
+
+static inline struct usb_buf *usbb_get(struct usb_buf *buf)
+{
+	atomic_inc(&buf->users);
+	return buf;
+}
+
+static inline void usbb_put(struct usb_buf *buf)
+{
+	if (atomic_dec_and_test(&buf->users))
+		__usbb_free(buf);
+}
+
+static inline void *usbb_push(struct usb_buf *buf, int len)
+{
+	unsigned char *b = buf->head;
+	buf->head += len;
+	buf->len += len;
+	return b;
+}
+
+#endif
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/sa1100usb.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,1160 @@
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/usb_ch9.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#include "buffer.h"
+#include "usbdev.h"
+#include "sa1100_usb.h"
+#include "sa1100usb.h"
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk( fmt , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+static inline void pcs(const char *prefix)
+{
+#ifdef DEBUG
+	__u32 foo = Ser0UDCCS0;
+
+	DPRINTK("%s UDCAR: %d\n", prefix, Ser0UDCAR);
+
+	printk("UDC: %s: %08x [ %s%s%s%s%s%s]\n", prefix,
+	       foo,
+	       foo & UDCCS0_SE ? "SE " : "",
+	       foo & UDCCS0_DE ? "DE " : "",
+	       foo & UDCCS0_FST ? "FST " : "",
+	       foo & UDCCS0_SST ? "SST " : "",
+	       foo & UDCCS0_IPR ? "IPR " : "",
+	       foo & UDCCS0_OPR ? "OPR " : "");
+#endif
+}
+
+/*
+ * 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.
+ */
+static inline void soft_connect_hook(int enable)
+{
+#ifdef CONFIG_SA1100_EXTENEX1
+	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;
+		}
+	}
+#endif
+}
+
+/*
+ * disable the UDC at the source
+ */
+static inline void udc_disable(struct sausb_dev *usb)
+{
+	soft_connect_hook(0);
+
+	usb->udccr = UDCCR_UDD | UDCCR_SUSIM;
+
+	UDC_write(Ser0UDCCR, usb->udccr);
+}
+
+/*
+ * Clear any pending write from the EP0 write buffer.
+ */
+static void ep0_clear_write(struct sausb_dev *usb)
+{
+	struct usb_buf *buf;
+
+	buf = usb->wrbuf;
+	usb->wrint = NULL;
+	usb->wrbuf = NULL;
+	usb->wrptr = NULL;
+	usb->wrlen = 0;
+
+	if (buf)
+		usbb_put(buf);
+}
+
+static int udc_start(void *priv)
+{
+	struct sausb_dev *usb = priv;
+
+	usb->ep[0].maxpktsize = 0;
+	usb->ep[1].maxpktsize = 0;
+
+	/*
+	 * start UDC internal machinery running, but mask interrupts.
+	 */
+	usb->udccr = UDCCR_SUSIM | UDCCR_TIM | UDCCR_RIM | UDCCR_EIM |
+		     UDCCR_RESIM;
+	UDC_write(Ser0UDCCR, usb->udccr);
+
+	udelay(100);
+
+	/*
+	 * clear all interrupt sources
+	 */
+	Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR |
+		    UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR;
+
+	/*
+	 * flush DMA and fire through some -EAGAINs
+	 */
+	udc_ep1_init(usb);
+	udc_ep2_init(usb);
+
+	/*
+	 * enable any platform specific hardware
+	 */
+	soft_connect_hook(1);
+
+	/*
+	 * Enable resume, suspend and endpoint 0 interrupts.  Leave
+	 * endpoint 1 and 2 interrupts masked.
+	 *
+	 * 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.
+	 */
+	usb->udccr &= ~(UDCCR_SUSIM | UDCCR_EIM | UDCCR_RESIM);
+	UDC_write(Ser0UDCCR, usb->udccr);
+
+	return 0;
+}
+
+static int udc_stop(void *priv)
+{
+	struct sausb_dev *usb = priv;
+
+	ep0_clear_write(usb);
+
+	/* mask everything */
+	Ser0UDCCR = 0xFC;
+
+	udc_ep1_reset(usb);
+	udc_ep2_reset(usb);
+
+	udc_disable(usb);
+
+	return 0;
+}
+
+
+
+
+
+/*
+ * 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_de(void)
+{
+	int i = 1;
+
+	while (1) {
+		if (OK_TO_WRITE) {
+			Ser0UDCCS0 |= UDCCS0_DE;
+		} else {
+			DPRINTK("UDC: quitting set DE because SST or SE set\n");
+			break;
+		}
+		if (Ser0UDCCS0 & UDCCS0_DE)
+			break;
+		udelay(i);
+		if (++i == 50) {
+			printk("UDC: Dangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8X)\n",
+			       UDCCS0_DE, Ser0UDCCS0);
+			break;
+		}
+	}
+}
+
+static void set_ipr(void)
+{
+	int i = 1;
+
+	while (1) {
+		if (OK_TO_WRITE) {
+			Ser0UDCCS0 |= UDCCS0_IPR;
+		} else {
+			DPRINTK("UDC: Quitting set IPR because SST or SE set\n");
+			break;
+		}
+		if (Ser0UDCCS0 & UDCCS0_IPR)
+			break;
+		udelay(i);
+		if (++i == 50) {
+			printk("UDC: Dangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8X)\n",
+			       UDCCS0_IPR, Ser0UDCCS0);
+			break;
+		}
+	}
+}
+
+static void set_ipr_and_de(void)
+{
+	int i = 1;
+
+	while (1) {
+		if (OK_TO_WRITE) {
+			Ser0UDCCS0 |= BOTH_BITS;
+		} else {
+			DPRINTK("UDC: Quitting set IPR/DE because SST or SE set\n");
+			break;
+		}
+		if ((Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS)
+			break;
+		udelay(i);
+		if (++i == 50) {
+			printk("UDC: Dangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8X)\n",
+			       UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0);
+			break;
+		}
+	}
+}
+
+static inline 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();
+}
+
+/*
+ * udc_ep0_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 udc_ep0_write_fifo(struct sausb_dev *usb)
+{
+	unsigned int bytes_this_time = min(usb->wrlen, 8U);
+	int bytes_written = 0;
+
+	DPRINTK("WF=%d: ", bytes_this_time);
+
+	while (bytes_this_time--) {
+		unsigned int cwc;
+		int i;
+
+		DPRINTK("%2.2X ", *usb->wrptr);
+
+		cwc = Ser0UDCWC & 15;
+
+		i = 10;
+		do {
+			Ser0UDCD0 = *usb->wrptr;
+			udelay(20);	/* voodo 28Feb01ww */
+		} while ((Ser0UDCWC & 15) == cwc && --i);
+
+		if (i == 0) {
+			printk("UDC: udc_ep0_write_fifo: write failure\n");
+			usb->ep0_wr_fifo_errs++;
+		}
+
+		usb->wrptr++;
+		bytes_written++;
+	}
+	usb->wrlen -= bytes_written;
+
+	/* following propagation voodo so maybe caller writing IPR in
+	   ..a moment might actually get it to stick 28Feb01ww */
+	udelay(300);
+
+	usb->ep0_wr_bytes += bytes_written;
+	DPRINTK("L=%d WCR=%8.8X\n", usb->wrlen, 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
+ * against the count register with an overall timeout.
+ */
+static int
+udc_ep0_read_fifo(struct sausb_dev *usb, struct usb_ctrlrequest *request, int sz)
+{
+	unsigned char *pOut = (unsigned char *) request;
+	unsigned int fifo_count, bytes_read = 0;
+
+	fifo_count = Ser0UDCWC & 15;
+
+	DPRINTK("RF=%d ", fifo_count);
+	BUG_ON(fifo_count > sz);
+
+	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(KERN_ERR "UDC: udc_ep0_read_fifo: read failure\n");
+			usb->ep0_rd_fifo_errs++;
+			break;
+		}
+		pOut++;
+		bytes_read++;
+	}
+
+	DPRINTK("fc=%d\n", bytes_read);
+	usb->ep0_rd_bytes += bytes_read;
+	usb->ep0_rd_packets ++;
+	return bytes_read;
+}
+
+static void ep0_sh_write_data(struct sausb_dev *usb)
+{
+	/*
+	 * 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
+	 */
+	set_cs_bits(UDCCS0_DE);
+}
+
+static void ep0_sh_write_with_empty_packet(struct sausb_dev *usb)
+{
+	/*
+	 * 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
+	 */
+	set_cs_bits(UDCCS0_IPR | UDCCS0_DE);
+	DPRINTK("UDC: sh_write_empty: Sent empty packet\n");
+}
+
+static int udc_clear_opr(void)
+{
+	int i = 10000;
+	int is_clear;
+
+	/*FIXME*/
+	do {
+		Ser0UDCCS0 = UDCCS0_SO;
+		is_clear = !(Ser0UDCCS0 & UDCCS0_OPR);
+		if (i-- <= 0)
+			break;
+	} while (!is_clear);
+
+	return is_clear;
+}
+
+static int udc_ep0_queue(void *priv, struct usb_buf *buf,
+			 unsigned int req_len)
+{
+	struct sausb_dev *usb = priv;
+	__u32 cs_reg_bits = UDCCS0_IPR;
+
+	DPRINTK("a=%d r=%d\n", buf->len, req_len);
+
+	/*
+	 * thou shalt not enter data phase until
+	 * Out Packet Ready is clear
+	 */
+	if (!udc_clear_opr()) {
+		printk("UDC: SO did not clear OPR\n");
+		set_cs_bits(UDCCS0_DE | UDCCS0_SO);
+		usbb_put(buf);
+		return 1;
+	}
+
+	usb->ep0_wr_packets++;
+
+	usb->wrbuf = buf;
+	usb->wrptr = buf->data;
+	usb->wrlen = min(buf->len, req_len);
+
+	udc_ep0_write_fifo(usb);
+
+	if (usb->wrlen == 0) {
+		/*
+		 * out in one, so data end
+		 */
+		cs_reg_bits |= UDCCS0_DE;
+		ep0_clear_write(usb);
+	} else if (buf->len < req_len) {
+		/*
+		 * we are going to short-change host
+		 * so need nul to not stall
+		 */
+		usb->wrint = ep0_sh_write_with_empty_packet;
+	} else {
+		/*
+		 * we have as much or more than requested
+		 */
+		usb->wrint = ep0_sh_write_data;
+	}
+
+	/*
+	 * note: IPR was set uncondtionally at start of routine
+	 */
+	set_cs_bits(cs_reg_bits);
+	return 0;
+}
+
+/*
+ * 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 sometimes work!  The rest of the time it would never propagate and
+ * we'd spin forever.  So now I just set it and pray...
+ */
+static void udc_set_address(void *priv, unsigned int addr)
+{
+	Ser0UDCAR = addr;
+}
+
+static void udc_set_config(void *priv, struct cdb *cdb)
+{
+	struct sausb_dev *usb = priv;
+
+	if (cdb) {
+		udc_ep1_config(usb, le16_to_cpu(cdb->ep1.wMaxPacketSize));
+		udc_ep2_config(usb, le16_to_cpu(cdb->ep2.wMaxPacketSize));
+	} else {
+		udc_ep1_reset(usb);
+		udc_ep2_reset(usb);
+	}
+}
+
+static unsigned int udc_ep_get_status(void *priv, unsigned int ep)
+{
+	unsigned int status;
+
+	switch (ep) {
+	case 0:
+		status = (Ser0UDCCS0 & UDCCS0_FST) ? 1 : 0;
+		break;
+
+	case 1:
+		status = (Ser0UDCCS1 & UDCCS1_FST) ? 1 : 0;
+		break;
+
+	case 2:
+		status = (Ser0UDCCS2 & UDCCS2_FST) ? 1 : 0;
+		break;
+
+	default:
+		printk(KERN_ERR "UDC: get_status: bad end point %d\n", ep);
+		status = 0;
+		break;
+	}
+
+	return status;
+}
+
+static void udc_ep_halt(void *priv, unsigned int ep, int halt)
+{
+	struct sausb_dev *usb = priv;
+
+	printk("UDC: ep%d %s halt\n", ep, halt ? "set" : "clear");
+
+	switch (ep) {
+	case 1:
+		udc_ep1_halt(usb, halt);
+		break;
+
+	case 2:
+		udc_ep2_halt(usb, halt);
+		break;
+	}
+}
+
+static int udc_ep_queue(void *priv, unsigned int ep, char *buf, unsigned int len)
+{
+	struct sausb_dev *usb = priv;
+	int ret = -EINVAL;
+
+	switch (ep) {
+	case 1:
+		ret = udc_ep1_queue_buffer(usb, buf, len);
+		break;
+	case 2:
+		ret = udc_ep2_send(usb, buf, len);
+		break;
+	}
+
+	return ret;
+}
+
+static void udc_ep_reset(void *priv, unsigned int ep)
+{
+	struct sausb_dev *usb = priv;
+
+	switch (ep) {
+	case 1:
+		udc_ep1_recv_reset(usb);
+		break;
+	case 2:
+		udc_ep2_send_reset(usb);
+		break;
+	}
+}
+
+static void udc_ep_callback(void *priv, unsigned int ep, usb_callback_t cb, void *data)
+{
+	struct sausb_dev *usb = priv;
+	unsigned long flags;
+
+	if (ep == 1 || ep == 2) {
+		ep -= 1;
+
+		spin_lock_irqsave(&usb->lock, flags);
+		usb->ep[ep].cb_func = cb;
+		usb->ep[ep].cb_data = data;
+		spin_unlock_irqrestore(&usb->lock, flags);
+	}
+}
+
+static int udc_ep_idle(void *priv, unsigned int ep)
+{
+	struct sausb_dev *usb = priv;
+	int ret = -EINVAL;
+
+	switch (ep) {
+	case 1:
+		break;
+	case 2:
+		ret = udc_ep2_idle(usb);
+		break;
+	}
+
+	return ret;
+}
+
+static struct usbc_driver usb_sa1100_drv = {
+	.owner		= THIS_MODULE,
+	.name		= "SA1100",
+	.start		= udc_start,
+	.stop		= udc_stop,
+	.ep0_queue	= udc_ep0_queue,
+	.set_address	= udc_set_address,
+	.set_config	= udc_set_config,
+	.ep_get_status	= udc_ep_get_status,
+	.ep_halt	= udc_ep_halt,
+	.ep_queue	= udc_ep_queue,
+	.ep_reset	= udc_ep_reset,
+	.ep_callback	= udc_ep_callback,
+	.ep_idle	= udc_ep_idle,
+};
+
+
+/*
+ * udc_ep0_read_packet()
+ *
+ * This setup handler is the "idle" state of endpoint zero. It looks for
+ * OPR (OUT packet ready) to see if a setup request has been been received
+ * from the host. Requests without a return data phase are immediately
+ * handled. Otherwise, the handler may be set to one of the sh_write_xxxx
+ * data pumpers if more than 8 bytes need to get back to the host.
+ */
+static void udc_ep0_read_packet(struct sausb_dev *usb, u32 cs_reg_in)
+{
+	struct usb_ctrlrequest req;
+	int n, ret = RET_NOACTION;
+
+	/*
+	 * A control request has been received by EP0.
+	 * Read the request.
+	 */
+	n = udc_ep0_read_fifo(usb, &req, sizeof(req));
+
+	if (n == sizeof(req)) {
+		ret = usbctl_parse_request(usb->ctl, &req);
+	} else {
+		/*
+		 * The request wasn't fully received.  Force a
+		 * stall.
+		 */
+		set_cs_bits(UDCCS0_FST | UDCCS0_SO);
+		printk("UDC: fifo read error: wanted %d bytes got %d\n",
+		       sizeof(req), n);
+	}
+
+	switch (ret) {
+	case RET_ERROR:
+	case RET_NOACTION:
+		break;
+
+	case RET_ACK:
+		set_cs_bits(UDCCS0_DE | UDCCS0_SO);
+		break;
+
+	case RET_REQERROR:
+		/*
+		 * Send stall PID to host.
+		 */
+		set_cs_bits(UDCCS0_DE | UDCCS0_SO | UDCCS0_FST);
+		break;
+	}
+}
+
+/*
+ * HACK DEBUG  3Mar01ww
+ * Well, maybe not, it really seems to help!  08Mar01ww
+ */
+static void core_kicker(struct sausb_dev *usb)
+{
+	__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;
+}
+
+static void enable_resume_mask_suspend(struct sausb_dev *usb)
+{
+	int i;
+
+	usb->udccr |= UDCCR_SUSIM;
+
+	i = 1;
+	do {
+		Ser0UDCCR = usb->udccr;
+		udelay(i);
+		if (Ser0UDCCR == usb->udccr)
+			break;
+		if (Ser0UDCSR & UDCSR_RSTIR)
+			break;
+	} while (i++ < 50);
+
+	if (i == 50)
+		printk("UDC: enable_resume: could not set SUSIM 0x%08x\n",
+		       Ser0UDCCR);
+
+	usb->udccr &= ~UDCCR_RESIM;
+
+	i = 1;	
+	do {
+		Ser0UDCCR = usb->udccr;
+		udelay(i);
+		if (Ser0UDCCR == usb->udccr)
+			break;
+		if (Ser0UDCSR & UDCSR_RSTIR)
+			break;
+	} while (i++ < 50);
+
+	if (i == 50)
+		printk("UDC: enable_resume: could not clear RESIM 0x%08x\n",
+		       Ser0UDCCR);
+}
+
+static void enable_suspend_mask_resume(struct sausb_dev *usb)
+{
+	int i;
+
+	usb->udccr |= UDCCR_RESIM;
+
+	i = 1;
+	do {
+		Ser0UDCCR = usb->udccr;
+		udelay(i);
+		if (Ser0UDCCR == usb->udccr)
+			break;
+		if (Ser0UDCSR & UDCSR_RSTIR)
+			break;
+	} while (i++ < 50);
+
+	if (i == 50)
+		printk("UDC: enable_resume: could not set RESIM 0x%08x\n",
+		       Ser0UDCCR);
+
+	usb->udccr &= ~UDCCR_SUSIM;
+
+	i = 1;	
+	do {
+		Ser0UDCCR = usb->udccr;
+		udelay(i);
+		if (Ser0UDCCR == usb->udccr)
+			break;
+		if (Ser0UDCSR & UDCSR_RSTIR)
+			break;
+	} while (i++ < 50);
+
+	if (i == 50)
+		printk("UDC: enable_resume: could not clear SUSIM 0x%08x\n",
+		       Ser0UDCCR);
+}
+
+/*
+ * Reset received from HUB (or controller just went nuts and reset by
+ * itself!) so UDC core has been reset, track this state here
+ */
+static void udc_reset(struct sausb_dev *usb)
+{
+	if (usbctl_reset(usb->ctl)) {
+		ep0_clear_write(usb);
+
+		/*
+		 * Clean up endpoints.
+		 */
+		udc_ep1_reset(usb);
+		udc_ep2_reset(usb);
+	}
+
+	/*
+	 * mask reset ints, they flood during sequence, enable
+	 * suspend and resume
+	 */
+	usb->udccr = (usb->udccr & ~(UDCCR_SUSIM | UDCCR_RESIM)) | UDCCR_REM;
+	Ser0UDCCR = usb->udccr;
+}
+
+/*
+ * handle interrupt for endpoint zero
+ */
+static void udc_ep0_int_hndlr(struct sausb_dev *usb)
+{
+	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) {
+		DPRINTK("UDC: early termination of setup\n");
+
+		/*
+		 * Clear setup end
+		 */
+		set_cs_bits(UDCCS0_SSE);
+
+		/*
+		 * Clear any pending write.
+		 */
+		ep0_clear_write(usb);
+	}
+
+	/*
+	 * UDC sent a stall due to a protocol violation.
+	 */
+	if (cs_reg_in & UDCCS0_SST) {
+		usb->ep0_stall_sent++;
+
+		DPRINTK("UDC: write_preamble: UDC sent stall\n");
+
+		/*
+		 * Clear sent stall
+		 */
+		set_cs_bits(UDCCS0_SST);
+
+		/*
+		 * Clear any pending write.
+		 */
+		ep0_clear_write(usb);
+	}
+
+	switch (cs_reg_in & (UDCCS0_OPR | UDCCS0_IPR)) {
+	case UDCCS0_OPR | UDCCS0_IPR:
+		DPRINTK("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(usb);
+
+		/*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.
+		 */
+		udc_ep0_read_packet(usb, cs_reg_in);
+		break;
+
+	case 0:
+		if (usb->wrint) {
+			if (usb->wrlen != 0) {
+				/*
+				 * More data to go
+				 */
+				udc_ep0_write_fifo(usb);
+				set_ipr();
+			}
+
+			if (usb->wrlen == 0) {
+				/*
+				 * All data sent.
+				 */
+				usb->wrint(usb);
+
+				ep0_clear_write(usb);
+			}
+		}
+		break;
+
+	case UDCCS0_IPR:
+		DPRINTK("UDC: IPR set, not writing\n");
+		usb->ep0_early_irqs++;
+		break;
+	}
+
+	pcs("<--");
+}
+
+static irqreturn_t udc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct sausb_dev *usb = dev_id;
+	u32 status = Ser0UDCSR;
+
+	/*
+	 * ReSeT Interrupt Request - UDC has been reset
+	 */
+	if (status & UDCSR_RSTIR) {
+		udc_reset(usb);
+
+		/*
+		 * clear all pending sources
+		 */
+		UDC_flip(Ser0UDCSR, status);
+		return IRQ_HANDLED;
+	}
+
+	/*
+	 * else we have done something other than reset,
+	 * so be sure reset enabled
+	 */
+	usb->udccr &= ~UDCCR_REM;
+	UDC_write(Ser0UDCCR, usb->udccr);
+
+	/*
+	 * RESume Interrupt Request
+	 */
+	if (status & UDCSR_RESIR) {
+		usbctl_resume(usb->ctl);
+		core_kicker(usb);
+		enable_suspend_mask_resume(usb);
+	}
+
+	/*
+	 * SUSpend Interrupt Request
+	 */
+	if (status & UDCSR_SUSIR) {
+		usbctl_suspend(usb->ctl);
+		enable_resume_mask_suspend(usb);
+	}
+
+	/*
+	 * clear all pending sources
+	 */
+	UDC_flip(Ser0UDCSR, status);
+
+	if (status & UDCSR_EIR)
+		udc_ep0_int_hndlr(usb);
+
+	if (status & UDCSR_RIR)
+		udc_ep1_int_hndlr(usb);
+
+	if (status & UDCSR_TIR)
+		udc_ep2_int_hndlr(usb);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef 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
+udc_read_proc(char *page, char **start, off_t off, int cnt, int *eof,
+	      void *data)
+{
+	struct sausb_dev *usb = data;
+	char *p = page;
+	u32 v;
+	int len, i;
+
+	p += usbctl_proc_info(usb->ctl, p);
+	p += sprintf(p, "\nUDC:\n");
+	v = Ser0UDCAR;
+	p += sprintf(p, "Address\t: %d (0x%02x)\n", v, v);
+	v = Ser0UDCIMP;
+	p += sprintf(p, "IN max\t: %d (0x%02x)\n", v + 1, v);
+	v = Ser0UDCOMP;
+	p += sprintf(p, "OUT max\t: %d (0x%02x)\n", v + 1, v);
+	v = Ser0UDCCR;
+	p += sprintf(p, "UDCCR\t: 0x%02x "
+			"[ %cSUSIM %cTIM %cRIM %cEIM %cRESIM %cUDA %cUDD ] "
+			"(0x%02x)\n",
+			v,
+			v & UDCCR_SUSIM ? '+' : '-', v & UDCCR_TIM ? '+' : '-',
+			v & UDCCR_RIM   ? '+' : '-', v & UDCCR_EIM ? '+' : '-',
+			v & UDCCR_RESIM ? '+' : '-', v & UDCCR_UDA ? '+' : '-',
+			v & UDCCR_UDD   ? '+' : '-', usb->udccr);
+	v = Ser0UDCCS0;
+	p += sprintf(p, "UDCCS0\t: 0x%02x "
+			"[ %cSO    %cSE  %cDE  %cFST %cSST   %cIPR %cOPR ]\n",
+			v,
+			v & UDCCS0_SO  ? '+' : '-', v & UDCCS0_SE  ? '+' : '-',
+			v & UDCCS0_DE  ? '+' : '-', v & UDCCS0_FST ? '+' : '-',
+			v & UDCCS0_SST ? '+' : '-', v & UDCCS0_IPR ? '+' : '-',
+			v & UDCCS0_OPR ? '+' : '-');
+	v = Ser0UDCCS1;
+	p += sprintf(p, "UDCCS1\t: 0x%02x "
+			"[         %cRNE %cFST %cSST %cRPE   %cRPC %cRFS ]\n",
+			v,
+			v & UDCCS1_RNE ? '+' : '-', v & UDCCS1_FST ? '+' : '-',
+			v & UDCCS1_SST ? '+' : '-', v & UDCCS1_RPE ? '+' : '-',
+			v & UDCCS1_RPC ? '+' : '-', v & UDCCS1_RFS ? '+' : '-');
+	v = Ser0UDCCS2;
+	p += sprintf(p, "UDCCS2\t: 0x%02x "
+			"[         %cFST %cSST %cTUR %cTPE   %cTPC %cTFS ]\n",
+			v,
+			v & UDCCS2_FST ? '+' : '-', v & UDCCS2_SST ? '+' : '-',
+			v & UDCCS2_TUR ? '+' : '-', v & UDCCS2_TPE ? '+' : '-',
+			v & UDCCS2_TPC ? '+' : '-', v & UDCCS2_TFS ? '+' : '-');
+
+	p += sprintf(p, "\n");
+	p += sprintf(p, "             Bytes    Packets  FIFO errs Max Sz\n");
+	p += sprintf(p, "EP0 Rd: %10ld %10ld %10ld      -\n",
+		     usb->ep0_rd_bytes,
+		     usb->ep0_rd_packets,
+		     usb->ep0_rd_fifo_errs);
+	p += sprintf(p, "EP0 Wr: %10ld %10ld %10ld      -\n",
+		     usb->ep0_wr_bytes,
+		     usb->ep0_wr_packets,
+		     usb->ep0_wr_fifo_errs);
+
+	for (i = 0; i < 2; i++)
+		p += sprintf(p, "EP%d   : %10ld %10ld %10ld %6d\n",
+			     i + 1,
+			     usb->ep[i].bytes,
+			     usb->ep[i].packets,
+			     usb->ep[i].fifo_errs,
+			     usb->ep[i].maxpktsize);
+
+	p += sprintf(p, "Stalls sent\t: %ld\n", usb->ep0_stall_sent);
+	p += sprintf(p, "Early ints\t: %ld\n", usb->ep0_early_irqs);
+
+#if 0
+	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
+#endif
+
+	len = (p - page) - off;
+	if (len < 0)
+		len = 0;
+	*eof = (len <= cnt) ? 1 : 0;
+	*start = page + off;
+
+	return len;
+}
+
+#endif
+
+extern struct usbctl usbctl;
+
+static int __devinit udc_probe(struct device *dev)
+{
+	struct sausb_dev *usb;
+	int retval;
+
+	if (!request_mem_region(0x80000000, 0x10000, "sa11x0-udc"))
+		return -EBUSY;
+
+	usb = kmalloc(sizeof(struct sausb_dev), GFP_KERNEL);
+	if (!usb)
+		return -ENOMEM;
+
+	memset(usb, 0, sizeof(struct sausb_dev));
+	dev_set_drvdata(dev, usb);
+
+	usb_sa1100_drv.priv = usb;
+
+	usb->dev = dev;
+	usb->ctl = &usbctl;
+
+	spin_lock_init(&usb->lock);
+
+	udc_disable(usb);
+
+	usbctl_init(usb->ctl, &usb_sa1100_drv);
+
+#ifdef CONFIG_PROC_FS
+	create_proc_read_entry("sausb", 0, NULL, udc_read_proc, usb);
+#endif
+
+	/* setup rx dma */
+	retval = sa1100_request_dma(DMA_Ser0UDCRd, "USB receive",
+				    NULL, NULL, &usb->ep[0].dmach);
+	if (retval) {
+		printk("UDC: unable to register for rx dma rc=%d\n",
+		       retval);
+		goto err;
+	}
+
+	/* setup tx dma */
+	retval = sa1100_request_dma(DMA_Ser0UDCWr, "USB transmit",
+				    NULL, NULL, &usb->ep[1].dmach);
+	if (retval) {
+		printk("UDC: unable to register for tx dma rc=%d\n",
+		       retval);
+		goto err;
+	}
+
+	/* now allocate the IRQ. */
+	retval = request_irq(IRQ_Ser0UDC, udc_interrupt, SA_INTERRUPT,
+			     "SA USB core", usb);
+	if (retval) {
+		printk("UDC: couldn't request USB irq rc=%d\n", retval);
+		goto err;
+	}
+
+	return retval;
+
+ err:
+	if (usb->ep[2].dmach) {
+		sa1100_free_dma(usb->ep[2].dmach);
+		usb->ep[2].dmach = NULL;
+	}
+	if (usb->ep[1].dmach) {
+		sa1100_free_dma(usb->ep[1].dmach);
+		usb->ep[1].dmach = NULL;
+	}
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("sausb", NULL);
+#endif
+	release_mem_region(0x80000000, 0x10000);
+	return retval;
+}
+
+/*
+ * Release DMA and interrupt resources
+ */
+static int __devexit udc_remove(struct device *dev)
+{
+	struct sausb_dev *usb = dev_get_drvdata(dev);
+
+	dev_set_drvdata(dev, NULL);
+
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("sausb", NULL);
+#endif
+
+	udc_disable(usb);
+
+	free_irq(IRQ_Ser0UDC, usb);
+	sa1100_free_dma(usb->ep[1].dmach);
+	sa1100_free_dma(usb->ep[0].dmach);
+
+	usbctl_exit(usb->ctl);
+
+	release_mem_region(0x80000000, 0x10000);
+
+	return 0;
+}
+
+static struct device_driver sa11x0usb_driver = {
+	.name		= "sa11x0-udc",
+	.bus		= &platform_bus_type,
+	.probe		= udc_probe,
+	.remove		= __devexit_p(udc_remove),
+};
+
+static int __init udc_init(void)
+{
+	return driver_register(&sa11x0usb_driver);
+}
+
+static void __exit udc_exit(void)
+{
+	driver_unregister(&sa11x0usb_driver);
+}
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SA1100 USB Gadget driver");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/control.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,933 @@
+/*
+ * usb/control.c
+ *
+ * This parses and handles all the control messages to/from endpoint 0.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/usb_ch9.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
+
+#include "buffer.h"
+#include "client.h"
+#include "usbdev.h"
+
+#include "sa1100_usb.h"
+
+#define USB_ENDPOINT_HALT		0
+#define USB_DEVICE_REMOTE_WAKEUP	1
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG fmt , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+/*
+ * print string descriptor
+ */
+static char * __attribute__((unused))
+psdesc(char *str, int len, struct usb_string_descriptor *desc)
+{
+	char *start = str;
+	int nchars = (desc->bLength - 2) / sizeof(__u16) + 2;
+	int i;
+
+	if (nchars >= len)
+		nchars = len - 1;
+
+	nchars -= 2;
+
+	*str++ = '"';
+	for(i = 0; i < nchars; i++)
+	 	*str++ = le16_to_cpu(desc->wData[i]);
+	*str++ = '"';
+	*str = '\0';
+
+	return start;
+}
+
+enum {
+	kError		= -1,
+	kEvSuspend	= 0,
+	kEvReset	= 1,
+	kEvResume	= 2,
+	kEvAddress	= 3,
+	kEvConfig	= 4,
+	kEvDeConfig	= 5
+};
+
+enum {
+	kStateZombie		= 0,
+	kStateZombieSuspend	= 1,
+	kStateDefault		= 2,
+	kStateDefaultSuspend	= 3,
+	kStateAddr		= 4,
+	kStateAddrSuspend	= 5,
+	kStateConfig		= 6,
+	kStateConfigSuspend	= 7
+};
+
+#define kE	kError
+#define kSZ	kStateZombie
+#define kSZS	kStateZombieSuspend
+#define kSD	kStateDefault
+#define kSDS	kStateDefaultSuspend
+#define kSA	kStateAddr
+#define kSAS	kStateAddrSuspend
+#define kSC	kStateConfig
+#define kSCS	kStateConfigSuspend
+
+/*
+ * Fig 9-1 P192
+ *  Zombie == Attached | Powered
+ */
+static int device_state_machine[8][6] = {
+// 	suspend	reset	resume	addr	config	deconfig
+{	kSZS, 	kSD,	kE,	kE,	kE,	kE  }, /* zombie */
+{	kE,	kSD,	kSZ,	kE,	kE,	kE  }, /* zom sus */ 
+{	kSDS,	kError,	kSD,	kSA,	kE,	kE  }, /* default */
+{	kE,	kSD,	kSD,	kE,	kE,	kE  }, /* def sus */
+{	kSAS,	kSD,	kE,	kE,	kSC,	kE  }, /* addr */
+{	kE,	kSD,	kSA,	kE,	kE,	kE  }, /* addr sus */
+{	kSCS,	kSD,	kE,	kE,	kE,	kSA }, /* config */
+{	kE,	kSD, 	kSC,	kE,	kE,	kE  }  /* cfg sus */
+};
+
+/*
+ * "device state" is the usb device framework state, as opposed to the
+ * "state machine state" which is whatever the driver needs and is much
+ * more fine grained
+ */
+static int sm_state_to_device_state[8] = {
+	USB_STATE_POWERED,	/* zombie */
+	USB_STATE_SUSPENDED,	/* zombie suspended */
+	USB_STATE_DEFAULT,	/* default */
+	USB_STATE_SUSPENDED,	/* default suspended */
+	USB_STATE_ADDRESS,	/* address */
+	USB_STATE_SUSPENDED,	/* address suspended */
+	USB_STATE_CONFIGURED,	/* config */
+	USB_STATE_SUSPENDED	/* config suspended */
+};
+
+static char * state_names[8] = {
+	"zombie",
+	"zombie suspended",
+	"default",
+	"default suspended",
+	"address",
+	"address suspended",
+	"configured",
+	"config suspended"
+};
+
+static char * event_names[6] = {
+	"suspend",
+	"reset",
+	"resume",
+	"address assigned",
+	"configure",
+	"de-configure"
+};
+
+static char * device_state_names[] = {
+	"not attached",
+	"attached",
+	"powered",
+	"default",
+	"address",
+	"configured",
+	"suspended"
+};
+
+static void usbctl_callbacks(struct usbctl *ctl, int state, int oldstate)
+{
+	struct usb_client *clnt = ctl->clnt;
+
+	/*
+	 * Inform any clients currently attached
+	 * that the connectivity state changed.
+	 */
+	if (clnt && clnt->state_change)
+		clnt->state_change(clnt->priv, state, oldstate);
+}
+
+/*
+ * called by the interrupt handler here and the two endpoint
+ * files when interesting .."events" happen
+ */
+static int usbctl_next_state_on_event(struct usbctl *ctl, int event)
+{
+	int next_state, next_dev_state, old_dev_state;
+
+	printk(KERN_DEBUG "usbctl: %s --[%s]--> ", state_names[ctl->sm_state],
+		event_names[event]);
+
+	next_state = device_state_machine[ctl->sm_state][event];
+	if (next_state != kError) {
+		next_dev_state = sm_state_to_device_state[next_state];
+
+		printk("%s. Device in %s state.\n",
+			state_names[next_state],
+			device_state_names[next_dev_state]);
+
+		old_dev_state = ctl->state;
+		ctl->sm_state = next_state;
+		ctl->state = next_dev_state;
+
+		if (old_dev_state != next_dev_state)
+			usbctl_callbacks(ctl, next_dev_state, old_dev_state);
+	} else
+		printk("(error)\n");
+
+	return next_state;
+}
+
+/*
+ * Driver detected USB HUB reset.
+ */
+int usbctl_reset(struct usbctl *ctl)
+{
+	int ret;
+
+	ret = usbctl_next_state_on_event(ctl, kEvReset) == kError;
+
+	if (!ret) {
+		ctl->address = 0;
+	}
+	return ret;
+}
+
+EXPORT_SYMBOL(usbctl_reset);
+
+void usbctl_suspend(struct usbctl *ctl)
+{
+	usbctl_next_state_on_event(ctl, kEvSuspend);
+}
+
+EXPORT_SYMBOL(usbctl_suspend);
+
+void usbctl_resume(struct usbctl *ctl)
+{
+	usbctl_next_state_on_event(ctl, kEvResume);
+}
+
+EXPORT_SYMBOL(usbctl_resume);
+
+static struct usb_interface_descriptor *
+usbctl_get_interface_descriptor(struct usbctl *ctl, unsigned int interface)
+{
+	/*FIXME*/
+	struct cdb *cdb = sa1100_usb_get_descriptor_ptr();
+
+	return (struct usb_interface_descriptor *)&cdb->intf;
+}
+
+static inline int
+__usbctl_queue(struct usbctl *ctl, struct usb_ctrlrequest *req,
+	       struct usb_buf *buf)
+{
+	unsigned int reqlen = le16_to_cpu(req->wLength);
+
+	return ctl->driver->ep0_queue(ctl->driver->priv, buf, reqlen) ?
+		RET_ERROR : RET_QUEUED;
+}
+
+static int
+usbctl_queue(struct usbctl *ctl, struct usb_ctrlrequest *req,
+	     void *data, unsigned int len)
+{
+	struct usb_buf *buf;
+
+	buf = usbb_alloc(len, GFP_ATOMIC);
+	if (!buf) {
+		printk(KERN_ERR "usb: out of memory\n");
+		return RET_ERROR;
+	}
+
+	if (data)
+		memcpy(usbb_push(buf, len), data, len);
+
+	return __usbctl_queue(ctl, req, buf);
+}
+
+/*
+ * 9.4.5: Get Status (device)
+ */
+static int
+usbctl_parse_dev_get_status(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	u16 status;
+
+	status = /* self_powered_hook() ? 1 : 0 */1;
+
+	status = cpu_to_le16(status);
+
+	return usbctl_queue(ctl, req, &status, 2);
+}
+
+/*
+ * Send USB device description to the host.
+ */
+static int
+usbctl_desc_device(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	return __usbctl_queue(ctl, req, usbb_get(ctl->dev_desc_buf));
+}
+
+/*
+ * Send USB configuration information to the host.
+ */
+static int
+usbctl_desc_config(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	/*FIXME*/
+	struct cdb *cdb = sa1100_usb_get_descriptor_ptr();
+
+	return usbctl_queue(ctl, req, cdb, sizeof(struct cdb));
+}
+
+/*
+ * Send a string to the host from the string table.
+ */
+static int
+usbctl_desc_string(struct usbctl *ctl, struct usb_ctrlrequest *req,
+		   unsigned int idx)
+{
+	struct usb_buf *buf;
+	unsigned int lang = le16_to_cpu(req->wIndex);
+	char string[32] __attribute__((unused));
+	int ret;
+
+	DPRINTK("usbctl: desc_string (index %u, lang 0x%04x): ", idx, lang);
+
+	buf = usbc_string_find(&ctl->strings, lang, idx);
+	if (buf) {
+		DPRINTK("%s\n", idx == 0 ? "language" :
+			 psdesc(string, sizeof(string), usbc_string_desc(buf)));
+
+		ret = __usbctl_queue(ctl, req, buf);
+	} else {
+		DPRINTK("not found -> stall\n");
+		ret = RET_REQERROR;
+	}
+	return ret;
+}
+
+/*
+ * Send an interface description (and endpoints) to the host.
+ */
+static int
+usbctl_desc_interface(struct usbctl *ctl, struct usb_ctrlrequest *req,
+		      unsigned int idx)
+{
+	struct usb_interface_descriptor *desc;
+	int ret;
+
+	DPRINTK("usbctl: desc_interface (index %d)\n", idx);
+
+	desc = usbctl_get_interface_descriptor(ctl, idx);
+
+	if (desc) {
+		ret = usbctl_queue(ctl, req, desc, desc->bLength);
+	} else {
+		printk("usbctl: unknown interface %d\n", idx);
+		ret = RET_REQERROR;
+	}
+
+	return ret;
+}
+
+/*
+ * Send an endpoint (1 .. n) to the host.
+ */
+static int
+usbctl_desc_endpoint(struct usbctl *ctl, struct usb_ctrlrequest *req,
+		     unsigned int idx)
+{
+	int ret;
+
+	DPRINTK("usbctl: desc_endpoint (index %d)\n", idx);
+
+	if (idx >= 1 && idx <= ctl->nr_ep) {
+		struct usb_endpoint_descriptor *ep = ctl->ep_desc[idx - 1];
+
+		ret = usbctl_queue(ctl, req, ep, ep->bLength);
+	} else {
+		printk("usbctl: unknown endpoint %d\n", idx);
+		ret = RET_REQERROR;
+	}
+
+	return ret;
+}
+
+/*
+ * 9.4.3: Parse a request for a descriptor.
+ *  Unspecified conditions:
+ *   None
+ *  Valid states: default, address, configured.
+ */
+static int
+usbctl_parse_dev_descriptor(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	unsigned int idx = le16_to_cpu(req->wValue) & 255;
+	unsigned int type = le16_to_cpu(req->wValue) >> 8;
+	int ret;
+
+	switch (type) {
+	case USB_DT_DEVICE:		/* check if idx matters */
+		ret = usbctl_desc_device(ctl, req);
+		break;
+
+	case USB_DT_CONFIG:		/* check if idx matters */
+		ret = usbctl_desc_config(ctl, req);
+		break;
+
+	case USB_DT_STRING:
+		ret = usbctl_desc_string(ctl, req, idx);
+		break;
+
+	case USB_DT_INTERFACE:
+		ret = usbctl_desc_interface(ctl, req, idx);
+		break;
+
+	case USB_DT_ENDPOINT:
+		ret = usbctl_desc_endpoint(ctl, req, idx);
+		break;
+
+	case USB_DT_DEVICE_QUALIFIER:
+	case USB_DT_OTHER_SPEED_CONFIG:
+	case USB_DT_INTERFACE_POWER:
+	default:
+		printk(KERN_ERR "usbctl: unknown descriptor: "
+			"wValue = 0x%04x wIndex = 0x%04x\n",
+			le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex));
+		ret = RET_REQERROR;
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * 9.4.6: Set Address
+ * The USB1.1 spec says the response to SetAddress() with value 0
+ * is undefined.  It then goes on to define the response.  We
+ * acknowledge addresses of zero, but take no further action.
+ */
+static int
+usbctl_parse_dev_set_address(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	unsigned int address = le16_to_cpu(req->wValue) & 0x7f;
+
+	if (ctl->state == USB_STATE_CONFIGURED)
+		return RET_REQERROR;
+
+	if (address != 0) {
+		ctl->address = address;
+
+		usbctl_next_state_on_event(ctl, kEvAddress);
+
+		ctl->driver->set_address(ctl->driver->priv, address);
+	}
+
+	return RET_ACK;
+}
+
+/*
+ * 9.4.2: Get Configuration.
+ *  Unspecified conditions:
+ *   - non-zero wIndex, wValue or wLength (ignored)
+ *   - default state (request error)
+ *  Valid states: address, configured.
+ */
+static int
+usbctl_parse_dev_get_config(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	u8 status = 0;
+
+	if (ctl->state == USB_STATE_CONFIGURED)
+		status = 1;
+
+	return usbctl_queue(ctl, req, &status, 1);
+}
+
+/*
+ * 9.4.7: Set Configuration.
+ *  Unspecified conditions:
+ *   - default state (request error)
+ */
+static int
+usbctl_parse_dev_set_config(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	unsigned int cfg = le16_to_cpu(req->wValue);
+	int ret = RET_REQERROR;
+
+	if (ctl->state == USB_STATE_DEFAULT)
+		return ret;
+
+	if (cfg == 0) {
+		/* enter address state, or remain in address state */
+		usbctl_next_state_on_event(ctl, kEvDeConfig);
+
+		ctl->driver->set_config(ctl->driver->priv, NULL);
+
+		ret = RET_ACK;
+	} else if (cfg == 1) {
+		/* enter configured state, and set configuration */
+		/*FIXME*/
+		struct cdb *cdb = sa1100_usb_get_descriptor_ptr();
+
+		usbctl_next_state_on_event(ctl, kEvConfig);
+
+		ctl->driver->set_config(ctl->driver->priv, cdb);
+		ret = RET_ACK;
+	}
+
+	return ret;
+}
+
+/*
+ * Interface handling
+ */
+
+/*
+ * 9.4.5: Get Status (interface)
+ */
+static int
+usbctl_parse_int_get_status(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	unsigned int interface = le16_to_cpu(req->wIndex) & 255;
+	u16 status;
+
+	switch (ctl->state) {
+	case USB_STATE_DEFAULT:
+		return RET_REQERROR;
+
+	case USB_STATE_ADDRESS:
+		if (interface != 0)
+			return RET_REQERROR;
+		break;
+
+	case USB_STATE_CONFIGURED:
+		if (interface != 1)
+			return RET_REQERROR;
+		break;
+	}
+
+	status = cpu_to_le16(0);
+
+	return usbctl_queue(ctl, req, &status, 2);
+}
+
+/*
+ * 9.4.4: Get Interface
+ *  Unspecified conditions:
+ *   - 
+ *  States: Default (unspecified), Address (Request Error), Configured (ok)
+ */
+static int
+usbctl_parse_int_get_interface(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	unsigned int interface = le16_to_cpu(req->wIndex) & 255;
+	u8 null = 0;
+
+	if (ctl->state != USB_STATE_CONFIGURED)
+		return RET_REQERROR;
+
+	/*
+	 * If the interface doesn't exist, respond with request error
+	 */
+	if (interface != 1)
+		return RET_REQERROR;
+
+	printk("usbctl: get interface %d not supported\n", interface);
+
+	return usbctl_queue(ctl, req, &null, 1);
+}
+
+static int
+usbctl_parse_int_set_interface(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	unsigned int interface = le16_to_cpu(req->wIndex) & 255;
+
+	if (interface != 0)
+		printk("usbctl: set interface %d not supported (ignored)\n",
+			interface);
+
+	return RET_ACK;
+}
+
+/*
+ * Endpoint handling
+ */
+
+/*
+ * 9.4.5: Get Status (endpoint)
+ */
+static int
+usbctl_parse_ep_get_status(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	unsigned int ep = le16_to_cpu(req->wIndex) & 15;
+	u16 status;
+
+	if ((ep != 0 && ctl->state != USB_STATE_CONFIGURED) ||
+	    ep <= ctl->nr_ep)
+		return RET_REQERROR;
+
+	status = ctl->driver->ep_get_status(ctl->driver->priv, ep);
+	status = cpu_to_le16(status);
+
+	return usbctl_queue(ctl, req, &status, 2);
+}
+
+/*
+ * 9.4.1: Clear an endpoint feature.  We only support ENDPOINT_HALT.
+ *  Unspecified conditions:
+ *   - non-zero wLength is not specified (ignored)
+ *  Valid states: Address, Configured.
+ */
+static int
+usbctl_parse_ep_clear_feature(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	unsigned int feature = le16_to_cpu(req->wValue);
+	unsigned int ep = le16_to_cpu(req->wIndex) & 15;
+	int ret;
+
+	if ((ep != 0 && ctl->state != USB_STATE_CONFIGURED) ||
+	    ep <= ctl->nr_ep)
+		return RET_REQERROR;
+
+	if (feature == USB_ENDPOINT_HALT) {
+		ctl->driver->ep_halt(ctl->driver->priv, ep, 0);
+		ret = RET_ACK;
+	} else {
+		printk(KERN_ERR "usbctl: unsupported clear feature: "
+			"wValue = 0x%04x wIndex = 0x%04x\n",
+			feature, ep);
+
+		ret = RET_REQERROR;
+	}
+	return ret;
+}
+
+/*
+ * 9.4.9: Set Feature (endpoint)
+ */
+static int
+usbctl_parse_ep_set_feature(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	unsigned int feature = le16_to_cpu(req->wValue);
+	unsigned int ep = le16_to_cpu(req->wIndex) & 15;
+	int ret;
+
+	if ((ep != 0 && ctl->state != USB_STATE_CONFIGURED) ||
+	    ep <= ctl->nr_ep)
+		return RET_REQERROR;
+
+	if (feature == USB_ENDPOINT_HALT) {
+		ctl->driver->ep_halt(ctl->driver->priv, ep, 1);
+		ret = RET_ACK;
+	} else {
+		printk(KERN_ERR "usbctl: unsupported set feature "
+			"wValue = 0x%04x wIndex = 0x%04x\n",
+			feature, ep);
+
+		ret = RET_REQERROR;
+	}
+	return ret;
+}
+
+/*
+ * This reflects Table 9.3 (p186) in the USB1.1 spec.
+ *
+ * Some notes:
+ *  - USB1.1 specifies remote wakeup feature, so we don't implement
+ *    USB_RECIP_DEVICE USB_REQ_{SET,CLEAR}_FEATURE
+ *  - USB1.1 doesn't actually specify any interface features, so we
+ *    don't implement USB_RECIP_INTERFACE USB_REQ_{SET,CLEAR}_FEATURE
+ */
+static int (*request_fns[4][16])(struct usbctl *, struct usb_ctrlrequest *) = {
+	[USB_RECIP_DEVICE] = {
+		[USB_REQ_GET_STATUS]	    = usbctl_parse_dev_get_status,
+		[USB_REQ_CLEAR_FEATURE]	    = NULL,
+		[USB_REQ_SET_FEATURE]	    = NULL,
+		[USB_REQ_SET_ADDRESS]	    = usbctl_parse_dev_set_address,
+		[USB_REQ_GET_DESCRIPTOR]    = usbctl_parse_dev_descriptor,
+		[USB_REQ_SET_DESCRIPTOR]    = NULL,
+		[USB_REQ_GET_CONFIGURATION] = usbctl_parse_dev_get_config,
+		[USB_REQ_SET_CONFIGURATION] = usbctl_parse_dev_set_config,
+	},
+
+	[USB_RECIP_INTERFACE] = {
+		[USB_REQ_GET_STATUS]	    = usbctl_parse_int_get_status,
+		[USB_REQ_CLEAR_FEATURE]	    = NULL,
+		[USB_REQ_SET_FEATURE]	    = NULL,
+		[USB_REQ_GET_INTERFACE]	    = usbctl_parse_int_get_interface,
+		[USB_REQ_SET_INTERFACE]	    = usbctl_parse_int_set_interface,
+	},
+
+	[USB_RECIP_ENDPOINT] = {
+		[USB_REQ_GET_STATUS]	    = usbctl_parse_ep_get_status,
+		[USB_REQ_CLEAR_FEATURE]	    = usbctl_parse_ep_clear_feature,
+		[USB_REQ_SET_FEATURE]	    = usbctl_parse_ep_set_feature,
+	},
+};
+
+static void __attribute__((unused))
+usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req)
+{
+	printk("%sbRequestType=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));
+}
+
+int usbctl_parse_request(struct usbctl *ctl, struct usb_ctrlrequest *req)
+{
+	unsigned int type;
+	int (*fn)(struct usbctl *, struct usb_ctrlrequest *) = NULL;
+	int ret = RET_REQERROR;
+
+	//usbctl_dump_request("usbctl: ", req);
+
+	type = req->bRequestType & USB_TYPE_MASK;
+	if (type == USB_TYPE_STANDARD) {
+		unsigned int recip;
+
+		recip = req->bRequestType & USB_RECIP_MASK;
+		if (recip < ARRAY_SIZE(request_fns) &&
+		    req->bRequest < ARRAY_SIZE(request_fns[0]))
+			fn = request_fns[recip][req->bRequest];
+	}
+
+	if (fn)
+		ret = fn(ctl, req);
+	else
+		usbctl_dump_request(KERN_ERR "usbctl: unknown request: ",
+				    req);
+
+	/*
+	 * Make sure we're doing the right thing.
+	 */
+	if (req->bRequestType & USB_DIR_IN) {
+		if (ret != RET_QUEUED && ret != RET_REQERROR)
+			printk("Error: device to host transfer expected\n");
+	} else {
+		if (ret == RET_QUEUED)
+			printk("Error: no device to host transfer expected\n");
+	}
+
+	return ret;
+}
+
+EXPORT_SYMBOL(usbctl_parse_request);
+
+/* Start running. Must have called usb_open (above) first */
+int usbctl_start(struct usb_client *client)
+{
+	struct usbctl *ctl = client->ctl;
+
+	if (ctl == NULL || ctl->clnt != client) {
+		printk("usbctl: start: no client registered\n");
+		return -EPERM;
+	}
+
+	ctl->sm_state = kStateZombie;
+	ctl->state = USB_STATE_POWERED;
+
+	/*
+	 * Notify the client as to our state.
+	 */
+	usbctl_callbacks(ctl, USB_STATE_POWERED, USB_STATE_SUSPENDED);
+
+	return ctl->driver->start(ctl->driver->priv);
+}
+
+EXPORT_SYMBOL(usbctl_start);
+
+/*
+ * Stop USB core from running
+ */
+void usbctl_stop(struct usb_client *client)
+{
+	struct usbctl *ctl = client->ctl;
+
+	if (ctl == NULL || ctl->clnt != client) {
+		printk("USBDEV: stop: no client/driver registered\n");
+		return;
+	}
+
+	ctl->driver->stop(ctl->driver->priv);
+}
+
+EXPORT_SYMBOL(usbctl_stop);
+
+struct usbctl usbctl;
+
+EXPORT_SYMBOL(usbctl);
+
+/* Open SA usb core on behalf of a client, but don't start running */
+
+int usbctl_open(struct usb_client *client)
+{
+	struct usbctl *ctl = &usbctl;
+	int ret;
+printk("usbctl_open: ctl %p driver %p\n", ctl, ctl->driver);
+	if (!ctl->driver || !try_module_get(ctl->driver->owner))
+		return -ENODEV;
+
+	if (ctl->clnt != NULL) {
+		ret = -EBUSY;
+		goto err;
+	}
+
+	ctl->clnt     = client;
+	ctl->state    = USB_STATE_SUSPENDED;
+	ctl->nr_ep    = 2;
+	/* start in zombie suspended state */
+	ctl->sm_state = kStateZombieSuspend;
+	ctl->state    = USB_STATE_SUSPENDED;
+	client->ctl   = ctl;
+
+	ctl->dev_desc_buf = usbb_alloc(sizeof(struct usb_device_descriptor),
+				       GFP_KERNEL);
+	if (!ctl->dev_desc_buf) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ctl->dev_desc = usbb_push(ctl->dev_desc_buf,
+				  sizeof(struct usb_device_descriptor));
+
+	/* create descriptors for enumeration */
+	initialize_descriptors(ctl);
+
+	return 0;
+
+ err:
+	module_put(ctl->driver->owner);
+	return ret;
+}
+
+EXPORT_SYMBOL(usbctl_open);
+
+/* Tell SA core client is through using it */
+void usbctl_close(struct usb_client *client)
+{
+	struct usbctl *ctl = client->ctl;
+
+	if (ctl == NULL || ctl->clnt != client) {
+		printk("usbctl: close: no client registered\n");
+		return;
+	}
+
+	usbb_put(ctl->dev_desc_buf);
+
+	client->ctl       = NULL;
+	ctl->clnt         = NULL;
+	ctl->dev_desc     = NULL;
+	ctl->dev_desc_buf = NULL;
+	/* reset to zombie suspended state */
+	ctl->sm_state     = kStateZombieSuspend;
+	ctl->state        = USB_STATE_SUSPENDED;
+
+	usbc_string_free_all(&ctl->strings);
+
+	if (ctl->driver->owner)
+		module_put(ctl->driver->owner);
+}
+
+EXPORT_SYMBOL(usbctl_close);
+
+int usbctl_proc_info(struct usbctl *ctl, char *buf)
+{
+	char *p = buf;
+
+	p += sprintf(p, "USB Gadget Core:\n");
+	p += sprintf(p, "Driver\t: %s\n",
+		ctl->driver ? ctl->driver->name : "none");
+	p += sprintf(p, "Client\t: %s\n",
+		ctl->clnt ? ctl->clnt->name : "none");
+	p += sprintf(p, "State\t: %s (%s) %d\n",
+		device_state_names[sm_state_to_device_state[ctl->sm_state]],
+		state_names[ctl->sm_state],
+		ctl->sm_state);
+	p += sprintf(p, "Address\t: %d\n", ctl->address);
+
+	return p - buf;
+}
+
+EXPORT_SYMBOL(usbctl_proc_info);
+
+int
+usbctl_ep_queue_buffer(struct usbctl *ctl, unsigned int ep,
+		       char *buf, unsigned int len)
+{
+	return ctl->driver->ep_queue(ctl->driver->priv, ep, buf, len);
+}
+
+EXPORT_SYMBOL(usbctl_ep_queue_buffer);
+
+void usbctl_ep_reset(struct usbctl *ctl, unsigned int ep)
+{
+	return ctl->driver->ep_reset(ctl->driver->priv, ep);
+}
+
+EXPORT_SYMBOL(usbctl_ep_reset);
+
+void
+usbctl_ep_set_callback(struct usbctl *ctl, unsigned int ep,
+		       usb_callback_t callback, void *data)
+{
+	ctl->driver->ep_callback(ctl->driver->priv, ep, callback, data);
+}
+
+EXPORT_SYMBOL(usbctl_ep_set_callback);
+
+int usbctl_ep_idle(struct usbctl *ctl, unsigned int ep)
+{
+	return ctl->driver->ep_idle(ctl->driver->priv, ep);
+}
+
+EXPORT_SYMBOL(usbctl_ep_idle);
+
+/*
+ * usbctl_init()
+ * Module load time. Allocate dma and interrupt resources. Setup /proc fs
+ * entry. Leave UDC disabled.
+ */
+int usbctl_init(struct usbctl *ctl, struct usbc_driver *drv)
+{
+	usbc_string_init(&ctl->strings);
+printk("usbctl_init: %p %p\n", ctl, drv);
+	/*
+	 * start in zombie suspended state
+	 */
+	ctl->sm_state	= kStateZombieSuspend;
+	ctl->state	= USB_STATE_SUSPENDED;
+	ctl->driver	= drv;
+
+	return 0;
+}
+
+/*
+ * usbctl_exit()
+ */
+void usbctl_exit(struct usbctl *ctl)
+{
+	usbc_string_free_all(&ctl->strings);
+
+	ctl->driver	= NULL;
+}
+
+EXPORT_SYMBOL(usbctl_init);
+EXPORT_SYMBOL(usbctl_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB gadget core");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/client.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,40 @@
+#ifndef USBDEV_CLIENT_H
+#define USBDEV_CLIENT_H
+
+#include "sa1100_usb.h" /* grr */
+
+struct usbctl;
+
+struct usb_client {
+	struct usbctl	*ctl;
+	const char	*name;		/* Client name		*/
+	void		*priv;		/* Client-private data	*/
+	void		(*state_change)(void *priv, int state, int oldstate);
+	__u16		vendor;		/* USB vendor ID	*/
+	__u16		product;	/* USB product ID	*/
+	__u16		version;	/* USB version ID	*/
+	__u8		class;		/* USB class		*/
+	__u8		subclass;	/* USB subclass		*/
+	__u8		protocol;	/* USB protocol		*/
+	__u8		unused1;
+	__u16		unused2;
+	const char	*manufacturer_str;
+	const char	*product_str;
+	const char	*serial_str;
+};
+
+int usbctl_start(struct usb_client *client);
+void usbctl_stop(struct usb_client *client);
+int usbctl_open(struct usb_client *client);
+void usbctl_close(struct usb_client *client);
+
+int
+usbctl_ep_queue_buffer(struct usbctl *ctl, unsigned int ep,
+		       char *buf, unsigned int len);
+void usbctl_ep_reset(struct usbctl *ctl, unsigned int ep);
+void
+usbctl_ep_set_callback(struct usbctl *ctl, unsigned int ep,
+		       usb_callback_t callback, void *data);
+int usbctl_ep_idle(struct usbctl *ctl, unsigned int ep);
+
+#endif
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/sa1100_usb.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,50 @@
+/*
+ * sa1100_usb.h
+ *
+ * Public interface to the sa1100 USB core. For use by client modules
+ * like usb-eth and usb-char.
+ *
+ */
+#ifndef _SA1100_USB_H
+#define _SA1100_USB_H
+
+typedef void (*usb_callback_t)(void *data, int flag, int size);
+
+/* in usb_send.c */
+int sa1100_usb_xmitter_avail( void );
+int sa1100_usb_send(char *buf, int len);
+void sa1100_usb_send_set_callback(usb_callback_t callback, void *data);
+void sa1100_usb_send_reset(void);
+
+/* in usb_recev.c */
+int sa1100_usb_recv(char *buf, int len);
+void sa1100_usb_recv_set_callback(usb_callback_t callback, void *data);
+void sa1100_usb_recv_reset(void);
+
+//////////////////////////////////////////////////////////////////////////////
+// Descriptor Management
+//////////////////////////////////////////////////////////////////////////////
+
+// MaxPower:
+#define USB_POWER(x)  ((x)>>1) /* convert mA to descriptor units of A for MaxPower */
+
+/* "config descriptor buffer" - that is, one config,
+   ..one interface and 2 endpoints */
+struct cdb {
+	 struct usb_config_descriptor	 	cfg;
+	 struct usb_interface_descriptor	intf;
+	 struct usb_endpoint_descriptor		ep1, ep2;
+} __attribute__ ((packed));
+
+
+/*=======================================================
+ * Descriptor API
+ */
+
+/* Get the address of the statically allocated desc_t structure
+   in the usb core driver. Clients can modify this between
+   the time they call sa1100_usb_open() and sa1100_usb_start()
+*/
+struct cdb *sa1100_usb_get_descriptor_ptr(void);
+
+#endif /* _SA1100_USB_H */
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/sa1100usb.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,136 @@
+/*
+ *	Copyright (C)  Compaq Computer Corporation, 1998, 1999
+ *	Copyright (C)  Extenex Corporation 2001
+ *
+ *  usb_ctl.h
+ *
+ *  PRIVATE interface used to share info among components of the SA-1100 USB
+ *  core: usb_ctl, usb_ep0, usb_recv and usb_send. Clients of the USB core
+ *  should use sa1100_usb.h.
+ *
+ */
+#ifndef SA1100USB_H
+#define SA1100USB_H
+
+struct usbctl;
+
+struct sausb_dev {
+	struct device	*dev;
+	struct usbctl	*ctl;
+	spinlock_t	lock;
+
+	u32		udccr;
+
+	/*
+	 * EP0 write thread.
+	 */
+	void		(*wrint)(struct sausb_dev *);
+	struct usb_buf	*wrbuf;
+	unsigned char	*wrptr;
+	unsigned int	wrlen;
+
+	/*
+	 * EP0 statistics.
+	 */
+	unsigned long	ep0_wr_fifo_errs;
+	unsigned long	ep0_wr_bytes;
+	unsigned long	ep0_wr_packets;
+	unsigned long	ep0_rd_fifo_errs;
+	unsigned long	ep0_rd_bytes;
+	unsigned long	ep0_rd_packets;
+	unsigned long	ep0_stall_sent;
+	unsigned long	ep0_early_irqs;
+
+	/*
+	 * EP1 .. n
+	 */
+	struct {
+		dma_regs_t	*dmach;
+
+		dma_addr_t	bufdma;
+		unsigned int	buflen;
+		void		*pktcpu;
+		dma_addr_t	pktdma;
+		unsigned int	pktlen;
+		unsigned int	pktrem;
+
+		void		*cb_data;
+		void		(*cb_func)(void *data, int flag, int size);
+
+		u32		udccs;
+		unsigned int	maxpktsize;
+		unsigned int	configured;
+		unsigned int	host_halt;
+		unsigned long	fifo_errs;
+		unsigned long	bytes;
+		unsigned long	packets;
+	} ep[2];
+};
+
+/* receiver */
+int  ep1_recv(void);
+void udc_ep1_init(struct sausb_dev *);
+void udc_ep1_halt(struct sausb_dev *, int);
+void udc_ep1_reset(struct sausb_dev *);
+void udc_ep1_config(struct sausb_dev *, unsigned int);
+void udc_ep1_int_hndlr(struct sausb_dev *);
+
+/* xmitter */
+void udc_ep2_init(struct sausb_dev *);
+void udc_ep2_halt(struct sausb_dev *, int);
+void udc_ep2_reset(struct sausb_dev *);
+void udc_ep2_config(struct sausb_dev *, unsigned int);
+void udc_ep2_int_hndlr(struct sausb_dev *);
+
+#define UDC_write(reg, val) do { \
+	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)); \
+} while (0)
+
+#define UDC_set(reg, val) do { \
+	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))); \
+} while (0)
+
+#define UDC_clear(reg, val) do { \
+	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)); \
+} while (0)
+
+#define UDC_flip(reg, val) do { \
+	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))); \
+} while (0)
+
+#define CHECK_ADDRESS { if ( Ser0UDCAR == 1 ) { printk("%s:%d I lost my address!!!\n",__FUNCTION__, __LINE__);}}
+
+#endif
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/strings.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,117 @@
+/*
+ * usb/strings.c
+ *
+ * Copyright (C) 2002 Russell King.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/usb_ch9.h>
+
+#include "buffer.h"
+#include "strings.h"
+
+struct usb_buf *usbc_string_alloc(int len)
+{
+	struct usb_buf *buf;
+	int tot_len;
+
+	tot_len = sizeof(struct usb_descriptor_header) + sizeof(u16) * len;
+
+	buf = usbb_alloc(tot_len, GFP_KERNEL);
+
+	if (buf) {
+		struct usb_string_descriptor *desc = usbb_push(buf, tot_len);
+
+		desc->bLength = tot_len;
+		desc->bDescriptorType = USB_DT_STRING;
+	}
+	return buf;
+}
+
+void usbc_string_free(struct usb_buf *buf)
+{
+	if (buf)
+		usbb_put(buf);
+}
+
+void usbc_string_from_cstr(struct usb_buf *buf, const char *str)
+{
+	struct usb_string_descriptor *desc = usbc_string_desc(buf);
+	int i, len;
+
+	len = strlen(str);
+	BUG_ON((sizeof(__u16) * len) > desc->bLength - sizeof(struct usb_descriptor_header));
+
+	for (i = 0; i < len; i++)
+		desc->wData[i] = cpu_to_le16(str[i]);
+}
+
+int usbc_string_add(struct usbc_strs *table, struct usb_buf *buf)
+{
+	int nr, i;
+	
+	nr = -ENOSPC;
+	spin_lock_irq(&table->lock);
+	for (i = 0; i < NR_STRINGS; i++)
+		if (table->buf[i] == NULL) {
+			table->buf[i] = buf;
+			nr = i;
+			break;
+		}
+	spin_unlock_irq(&table->lock);
+
+	return nr;
+}
+
+void usbc_string_del(struct usbc_strs *table, int nr)
+{
+	if (nr < NR_STRINGS) {
+		spin_lock_irq(&table->lock);
+		table->buf[nr] = NULL;
+		spin_unlock_irq(&table->lock);
+	}
+}
+
+struct usb_buf *
+usbc_string_find(struct usbc_strs *table, unsigned int lang, unsigned int idx)
+{
+	struct usb_buf *buf = NULL;
+
+	if (idx < NR_STRINGS) {
+		spin_lock_irq(&table->lock);
+		buf = usbb_get(table->buf[idx]);
+		spin_unlock_irq(&table->lock);
+	}
+
+	return buf;
+}
+
+void usbc_string_free_all(struct usbc_strs *table)
+{
+	int i;
+
+	spin_lock_irq(&table->lock);
+	for (i = 0; i < NR_STRINGS; i++) {
+		usbc_string_free(table->buf[i]);
+		table->buf[i] = NULL;
+	}
+	spin_unlock_irq(&table->lock);
+}
+
+void usbc_string_init(struct usbc_strs *table)
+{
+	memset(table, 0, sizeof(struct usbc_strs));
+	spin_lock_init(&table->lock);
+}
+
+EXPORT_SYMBOL(usbc_string_from_cstr);
+EXPORT_SYMBOL(usbc_string_alloc);
+EXPORT_SYMBOL(usbc_string_free);
+EXPORT_SYMBOL(usbc_string_add);
+EXPORT_SYMBOL(usbc_string_del);
+EXPORT_SYMBOL(usbc_string_find);
+EXPORT_SYMBOL(usbc_string_free_all);
+EXPORT_SYMBOL(usbc_string_init);
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb_ctl.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,171 @@
+ /*
+ *	Copyright (C) Compaq Computer Corporation, 1998, 1999
+ *  Copyright (C) Extenex Corporation, 2001
+ *
+ *  usb_ctl.c
+ *
+ *  SA1100 USB controller core driver.
+ *
+ *  This file provides interrupt routing and overall coordination
+ *  of the three endpoints in usb_ep0, usb_receive (1),  and usb_send (2).
+ *
+ *  Please see linux/Documentation/arm/SA1100/SA1100_USB for details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/usb.h>
+
+#include "buffer.h"
+#include "client.h"
+#include "usbdev.h"
+#include "sa1100_usb.h"
+
+//////////////////////////////////////////////////////////////////////////////
+// Globals
+//////////////////////////////////////////////////////////////////////////////
+
+/* device descriptors */
+static struct cdb cdb;
+
+//////////////////////////////////////////////////////////////////////////////
+// Private Helpers
+//////////////////////////////////////////////////////////////////////////////
+
+int sa1100_usb_add_string(struct usbctl *ctl, const char *str)
+{
+	int nr = 0;
+
+	if (str) {
+		struct usb_buf *buf;
+		int len;
+
+		len = strlen(str);
+
+		nr = -ENOMEM;
+		buf = usbc_string_alloc(len);
+		if (buf) {
+			usbc_string_from_cstr(buf, str);
+			nr = usbc_string_add(&ctl->strings, buf);
+
+			if (nr < 0)
+				usbc_string_free(buf);
+		}
+	}
+
+	return nr;
+}
+
+EXPORT_SYMBOL(sa1100_usb_add_string);
+
+static int sa1100_usb_add_language(struct usbctl *ctl, unsigned int lang)
+{
+	struct usb_buf *buf;
+	int nr = -ENOMEM;
+
+	buf = usbc_string_alloc(1);
+	if (buf) {
+		usbc_string_desc(buf)->wData[0] = cpu_to_le16(lang); /* American English */
+		nr = usbc_string_add(&ctl->strings, buf);
+
+		if (nr < 0)
+			usbc_string_free(buf);
+	}
+
+	return nr;
+}
+
+/* setup default descriptors */
+
+void initialize_descriptors(struct usbctl *ctl)
+{
+	struct usb_client *clnt = ctl->clnt;
+	int r;
+
+	ctl->ep_desc[0] = (struct usb_endpoint_descriptor *)&cdb.ep1;
+	ctl->ep_desc[1] = (struct usb_endpoint_descriptor *)&cdb.ep2;
+
+	cdb.cfg.bLength             = USB_DT_CONFIG_SIZE;
+	cdb.cfg.bDescriptorType     = USB_DT_CONFIG;
+	cdb.cfg.wTotalLength        = cpu_to_le16(sizeof(struct cdb));
+	cdb.cfg.bNumInterfaces      = 1;
+	cdb.cfg.bConfigurationValue = 1;
+	cdb.cfg.iConfiguration      = 0;
+	cdb.cfg.bmAttributes        = USB_CONFIG_ATT_ONE;
+	cdb.cfg.bMaxPower           = USB_POWER( 500 );
+
+	cdb.intf.bLength            = USB_DT_INTERFACE_SIZE;
+	cdb.intf.bDescriptorType    = USB_DT_INTERFACE;
+	cdb.intf.bInterfaceNumber   = 0;     /* unique intf index*/
+	cdb.intf.bAlternateSetting  = 0;
+	cdb.intf.bNumEndpoints      = 2;
+	cdb.intf.bInterfaceClass    = 0xff;  /* vendor specific */
+	cdb.intf.bInterfaceSubClass = 0;
+	cdb.intf.bInterfaceProtocol = 0;
+	cdb.intf.iInterface         = 0;
+
+	cdb.ep1.bLength             = USB_DT_INTERFACE_SIZE;
+	cdb.ep1.bDescriptorType     = USB_DT_ENDPOINT;
+	cdb.ep1.bEndpointAddress    = USB_DIR_OUT | 1;
+	cdb.ep1.bmAttributes        = USB_ENDPOINT_XFER_BULK;
+	cdb.ep1.wMaxPacketSize      = cpu_to_le16(64);
+	cdb.ep1.bInterval           = 0;
+
+	cdb.ep2.bLength             = USB_DT_INTERFACE_SIZE;
+	cdb.ep2.bDescriptorType     = USB_DT_ENDPOINT;
+	cdb.ep2.bEndpointAddress    = USB_DIR_IN | 2;
+	cdb.ep2.bmAttributes        = USB_ENDPOINT_XFER_BULK;
+	cdb.ep2.wMaxPacketSize      = cpu_to_le16(64);
+	cdb.ep2.bInterval           = 0;
+
+	ctl->dev_desc->bLength            = USB_DT_DEVICE_SIZE;
+	ctl->dev_desc->bDescriptorType    = USB_DT_DEVICE;
+	ctl->dev_desc->bcdUSB             = cpu_to_le16(0x100); /* 1.0 */
+	ctl->dev_desc->bDeviceClass       = clnt->class;
+	ctl->dev_desc->bDeviceSubClass    = clnt->subclass;
+	ctl->dev_desc->bDeviceProtocol    = clnt->protocol;
+	ctl->dev_desc->bMaxPacketSize0    = 8;	/* ep0 max fifo size */
+	ctl->dev_desc->idVendor           = cpu_to_le16(clnt->vendor);
+	ctl->dev_desc->idProduct          = cpu_to_le16(clnt->product);
+	ctl->dev_desc->bcdDevice          = cpu_to_le16(clnt->version);
+	ctl->dev_desc->bNumConfigurations = 1;
+
+	/* set language */
+	/* See: http://www.usb.org/developers/data/USB_LANGIDs.pdf */
+	r = sa1100_usb_add_language(ctl, 0x409);
+	if (r < 0)
+		printk(KERN_ERR "usbc: couldn't add language\n");
+
+	r = sa1100_usb_add_string(ctl, clnt->manufacturer_str);
+	if (r < 0)
+		printk(KERN_ERR "usbc: couldn't add manufacturer string\n");
+
+	ctl->dev_desc->iManufacturer = r > 0 ? r : 0;
+
+	r = sa1100_usb_add_string(ctl, clnt->product_str);
+	if (r < 0)
+		printk(KERN_ERR "usbc: couldn't add product string\n");
+
+	ctl->dev_desc->iProduct      = r > 0 ? r : 0;
+
+	r = sa1100_usb_add_string(ctl, clnt->serial_str);
+	if (r < 0)
+		printk(KERN_ERR "usbc: couldn't add serial string\n");
+
+	ctl->dev_desc->iSerialNumber = r > 0 ? r : 0;
+}
+
+
+/*====================================================
+ * Descriptor Manipulation.
+ * Use these between open() and start() above to setup
+ * the descriptors for your device.
+ */
+
+/* get pointer to static default descriptor */
+struct cdb *sa1100_usb_get_descriptor_ptr(void)
+{
+	return &cdb;
+}
+
+EXPORT_SYMBOL(sa1100_usb_get_descriptor_ptr);
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,12 @@
+#
+# Makefile for the USB client
+#
+
+usbdevcore-objs	:= buffer.o control.o strings.o usb_ctl.o
+
+sa1100-objs := sa1100usb.o usb_recv.o usb_send.o
+
+obj-$(CONFIG_SA1100_USB)	 += usbdevcore.o sa1100.o
+obj-$(CONFIG_SA1100_USB_NETLINK) += usb-eth.o
+obj-$(CONFIG_SA1100_USB_CHAR)	 += usb-char.o
+
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/usb/usbdev.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,91 @@
+#ifndef USBDEV_H
+#define USBDEV_H
+
+#include "strings.h"
+
+struct usb_buf;
+struct module;
+struct cdb;
+struct usb_client;
+
+struct usbc_driver {
+	struct module	*owner;
+	const char	*name;
+	void		*priv;
+	int		(*start)(void *);
+	int		(*stop)(void *);
+
+	int		(*ep0_queue)(void *, struct usb_buf *buf, unsigned int req_len);
+	void		(*set_address)(void *, unsigned int addr);
+	void		(*set_config)(void *, struct cdb *config);
+
+	/*
+	 * Get specified endpoint status, as defined in 9.4.5.
+	 */
+	unsigned int	(*ep_get_status)(void *, unsigned int ep);
+	void		(*ep_halt)(void *, unsigned int ep, int halt);
+
+	/*
+	 * Client
+	 */
+	int		(*ep_queue)(void *, unsigned int, char *, unsigned int);
+	void		(*ep_reset)(void *, unsigned int);
+	void		(*ep_callback)(void *, unsigned int, void (*)(void *, int, int), void *);
+	int		(*ep_idle)(void *, unsigned int);
+};
+
+struct usbc_endpoint {
+	struct usb_endpoint_descriptor	*desc;
+};
+
+struct usbc_interface {
+	struct usb_interface_descriptor	*desc;
+	unsigned int			nr_ep;
+	struct usbc_endpoint		*ep[0];
+};
+
+struct usbc_config {
+	struct usb_config_descriptor	*desc;
+	unsigned int			nr_interface;
+	struct usbc_interface		*interface[0];
+};
+
+struct usbctl {
+	struct usb_client		*clnt;
+	const struct usbc_driver	*driver;
+
+	/* Internal state */
+	unsigned int			address;	/* host assigned address */
+	unsigned int			state;		/* our device state */
+	unsigned int			sm_state;	/* state machine state */
+
+	struct usbc_config		*config;	/* active configuration */
+	struct usbc_strs		strings;
+
+	/* Descriptors */
+	struct usb_device_descriptor	*dev_desc;	/* device descriptor */
+	struct usb_buf			*dev_desc_buf;	/* device descriptor buffer */
+
+
+	int				nr_ep;
+	struct usb_endpoint_descriptor	*ep_desc[2];
+};
+
+/*
+ * Function Prototypes
+ */
+
+#define RET_ERROR	(-1)
+#define RET_NOACTION	(0)
+#define RET_QUEUED	(1)
+#define RET_ACK		(2)
+#define RET_REQERROR	(3)
+
+int usbctl_parse_request(struct usbctl *ctl, struct usb_ctrlrequest *req);
+
+int usbctl_reset(struct usbctl *ctl);
+void usbctl_suspend(struct usbctl *ctl);
+void usbctl_resume(struct usbctl *ctl);
+
+#endif
+
--- linux-2.6.5/arch/arm/mach-sa1100/pm.c~heh	2004-04-03 22:36:27.000000000 -0500
+++ linux-2.6.5/arch/arm/mach-sa1100/pm.c	2004-04-30 20:57:36.000000000 -0400
@@ -150,6 +150,7 @@
  */
 static int sa11x0_pm_prepare(u32 state)
 {
+	nmi_watchdog_disable();
 	return 0;
 }
 
@@ -158,6 +159,7 @@
  */
 static int sa11x0_pm_finish(u32 state)
 {
+	nmi_watchdog_enable();
 	return 0;
 }
 
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/arch/arm/mach-sa1100/nmi-oopser.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,104 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <asm/ptrace.h>
+#include <asm/domain.h>
+#include <asm/hardware.h>
+
+static void *nmi_stack;
+
+asm("						\n\
+nmi_start:					\n\
+	mrs	r8, spsr			\n\
+	ldr	r9, .Lstack			\n\
+	ldr	sp, [r9]			\n\
+	sub	sp, sp, #18 * 4			\n\
+	str	r8, [sp, #16 * 4]		\n\
+	str	lr, [sp, #15 * 4]		\n\
+	stmia	sp, {r0 - r7}			\n\
+	add	r0, sp, #8 * 4			\n\
+	mrs	r2, cpsr			\n\
+	bic	r1, r2, #0x1f			\n\
+	orr	r1, r1, #0x13			\n\
+	msr	cpsr_c, r1			\n\
+	mov	r0, r0				\n\
+	stmia	r0, {r8 - lr}			\n\
+	mov	r0, r0				\n\
+	msr	cpsr_c, r2			\n\
+	mov	r0, r0				\n\
+	mov	r0, sp				\n\
+	mov	lr, pc				\n\
+	ldr	pc, .Lfn			\n\
+	ldmia	sp, {r0 - r7}			\n\
+	ldr	r8, [sp, #16 * 4]		\n\
+	ldr	lr, [sp, #15 * 4]		\n\
+	add	sp, sp, #18 * 4			\n\
+	msr	spsr, r8			\n\
+	movs	pc, lr				\n\
+						\n\
+.Lstack: .long nmi_stack			\n\
+.Lfn:	.long	nmi_fn				\n\
+nmi_end:");
+
+extern unsigned char nmi_start, nmi_end;
+
+static void __attribute__((unused)) nmi_fn(struct pt_regs *regs)
+{
+	struct thread_info *thread;
+	unsigned long osmr0, osmr1, oscr, ossr, icmr, icip;
+
+	oscr = OSCR;
+	osmr0 = OSMR0;
+	osmr1 = OSMR1;
+	ossr = OSSR;
+	icmr = ICMR;
+	icip = ICIP;
+
+	OSSR = OSSR_M1;
+	ICMR &= ~IC_OST1;
+
+	thread = (struct thread_info *)(regs->ARM_sp & ~8191);
+
+	bust_spinlocks(1);
+	printk("OSMR0:%08lx OSMR1:%08lx OSCR:%08lx OSSR:%08lx ICMR:%08lx ICIP:%08lx\n",
+		osmr0, osmr1, oscr, ossr, icmr, icip);
+	nmi_watchdog(thread, regs);
+	bust_spinlocks(0);
+
+	OSSR = OSSR_M1;
+	OSMR1 = OSSR + 36864000;
+	ICMR |= IC_OST1;
+}
+
+static int nmi_init(void)
+{
+	unsigned char *vec_base = (unsigned char *)vectors_base();
+return 0;
+	nmi_stack = (void *)__get_free_page(GFP_KERNEL);
+	if (!nmi_stack)
+		return -ENOMEM;
+
+	nmi_stack += PAGE_SIZE;
+
+	modify_domain(DOMAIN_USER, DOMAIN_MANAGER);
+	memcpy(vec_base + 0x1c, &nmi_start, &nmi_end - &nmi_start);
+	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
+
+	/*
+	 * Ensure timer 1 is set to FIQ, and enabled.
+	 */
+	OSMR1 = OSCR - 1;
+	OSSR = OSSR_M1;
+	OIER |= OIER_E1;
+	ICLR |= IC_OST1;
+	ICMR |= IC_OST1;
+
+	return 0;
+}
+
+__initcall(nmi_init);
--- linux-2.6.5/drivers/media/Kconfig~heh	2004-04-03 22:36:51.000000000 -0500
+++ linux-2.6.5/drivers/media/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -32,6 +32,8 @@
 
 source "drivers/media/common/Kconfig"
 
+source "drivers/media/mmc/Kconfig"
+
 config VIDEO_TUNER
 	tristate
 	default y if VIDEO_BT848=y || VIDEO_SAA7134=y || VIDEO_MXB=y || VIDEO_CX88=y
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,52 @@
+#
+# MMC subsystem configuration
+#
+
+menu "MMC/SD Card support"
+
+config MMC
+	tristate "MMC support"
+	help
+	  MMC is the "multi-media card" bus protocol.
+
+	  If you want MMC support, you should say Y here and also
+	  to the specific driver for your MMC interface.
+
+config MMC_DEBUG
+	bool "MMC debugging"
+	depends on MMC != n
+	help
+	  This is an option for use by developers; most people should
+	  say N here.  This enables MMC core and driver debugging.
+
+config MMC_BLOCK
+	tristate "MMC block device driver"
+	depends on MMC
+	default y
+	help
+	  Say Y here to enable the MMC block device driver support.
+	  This provides a block device driver, which you can use to
+	  mount the filesystem. Almost everyone wishing MMC support
+	  should say Y or M here.
+
+config MMC_ARMMMCI
+	tristate "ARM AMBA Multimedia Card Interface support"
+	depends on ARM_AMBA && MMC
+	help
+	  This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card
+	  Interface (PL180 and PL181) support.  If you have an ARM(R)
+	  platform with a Multimedia Card slot, say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_PXA
+	tristate "Intel PXA255 Multimedia Card Interface support"
+	depends on ARCH_PXA && MMC
+	help
+	  This selects the Intel(R) PXA(R) Multimedia card Interface.
+	  If you have a PXA(R) platform with a Multimedia Card slot,
+	  say Y or M here.
+
+	  If unsure, say N.
+
+endmenu
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/mmc_queue.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,171 @@
+/*
+ *  linux/drivers/media/mmc/mmc_queue.c
+ *
+ *  Copyright (C) 2003 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/blkdev.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include "mmc_queue.h"
+
+/*
+ * Prepare a MMC request.  Essentially, this means passing the
+ * preparation off to the media driver.  The media driver will
+ * create a mmc_io_request in req->special.
+ */
+static int mmc_prep_request(struct request_queue *q, struct request *req)
+{
+	struct mmc_queue *mq = q->queuedata;
+	int ret = BLKPREP_KILL;
+
+	if (req->flags & REQ_SPECIAL) {
+		/*
+		 * Special commands already have the command
+		 * blocks already setup in req->special.
+		 */
+		BUG_ON(!req->special);
+
+		ret = BLKPREP_OK;
+	} else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {
+		/*
+		 * Block I/O requests need translating according
+		 * to the protocol.
+		 */
+		ret = mq->prep_fn(mq, req);
+	} else {
+		/*
+		 * Everything else is invalid.
+		 */
+		blk_dump_rq_flags(req, "MMC bad request");
+	}
+
+	if (ret == BLKPREP_OK)
+		req->flags |= REQ_DONTPREP;
+
+	return ret;
+}
+
+static int mmc_queue_thread(void *d)
+{
+	struct mmc_queue *mq = d;
+	struct request_queue *q = mq->queue;
+	DECLARE_WAITQUEUE(wait, current);
+	int ret;
+
+	/*
+	 * Set iothread to ensure that we aren't put to sleep by
+	 * the process freezing.  We handle suspension ourselves.
+	 */
+	current->flags |= PF_MEMALLOC|PF_IOTHREAD;
+
+	daemonize("mmcqd");
+
+	spin_lock_irq(&current->sighand->siglock);
+	sigfillset(&current->blocked);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	mq->thread = current;
+	complete(&mq->thread_complete);
+
+	add_wait_queue(&mq->thread_wq, &wait);
+	spin_lock_irq(q->queue_lock);
+	do {
+		struct request *req = NULL;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!blk_queue_plugged(q))
+			mq->req = req = elv_next_request(q);
+		spin_unlock(q->queue_lock);
+
+		if (!req) {
+			if (!mq->thread)
+				break;
+			schedule();
+			continue;
+		}
+		set_current_state(TASK_RUNNING);
+
+		ret = mq->issue_fn(mq, req);
+
+		spin_lock_irq(q->queue_lock);
+		end_request(req, ret);
+	} while (1);
+	remove_wait_queue(&mq->thread_wq, &wait);
+
+	complete_and_exit(&mq->thread_complete, 0);
+	return 0;
+}
+
+/*
+ * Generic MMC request handler.  This is called for any queue on a
+ * particular host.  When the host is not busy, we look for a request
+ * on any queue on this host, and attempt to issue it.  This may
+ * not be the queue we were asked to process.
+ */
+static void mmc_request(request_queue_t *q)
+{
+	struct mmc_queue *mq = q->queuedata;
+
+	if (!mq->req && !blk_queue_plugged(q))
+		wake_up(&mq->thread_wq);
+}
+
+/**
+ * mmc_init_queue - initialise a queue structure.
+ * @mq: mmc queue
+ * @card: mmc card to attach this queue
+ * @lock: queue lock
+ *
+ * Initialise a MMC card request queue.
+ */
+int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock)
+{
+	u64 limit = BLK_BOUNCE_HIGH;
+	int ret;
+
+	if (card->host->dev->dma_mask)
+		limit = *card->host->dev->dma_mask;
+
+	mq->card = card;
+	mq->queue = blk_init_queue(mmc_request, lock);
+	blk_queue_prep_rq(mq->queue, mmc_prep_request);
+	blk_queue_bounce_limit(mq->queue, limit);
+
+	mq->queue->queuedata = mq;
+	mq->req = NULL;
+
+	init_completion(&mq->thread_complete);
+	init_waitqueue_head(&mq->thread_wq);
+
+	ret = kernel_thread(mmc_queue_thread, mq, CLONE_KERNEL);
+	if (ret < 0) {
+		blk_cleanup_queue(mq->queue);
+	} else {
+		wait_for_completion(&mq->thread_complete);
+		init_completion(&mq->thread_complete);
+	}
+
+	return ret;
+}
+
+EXPORT_SYMBOL(mmc_init_queue);
+
+void mmc_cleanup_queue(struct mmc_queue *mq)
+{
+	mq->thread = NULL;
+	wake_up(&mq->thread_wq);
+	wait_for_completion(&mq->thread_complete);
+	blk_cleanup_queue(mq->queue);
+
+	mq->card = NULL;
+}
+
+EXPORT_SYMBOL(mmc_cleanup_queue);
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/pxamci.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,587 @@
+/*
+ *  linux/drivers/media/mmc/pxa.c - PXA MMCI driver
+ *
+ *  Copyright (C) 2003 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  This hardware is really sick.  No way to clear interrupts.  Have
+ *  to turn off the clock whenever we touch the device.  Yuck!
+ *
+ *	1 and 3 byte data transfers not supported
+ *	max block length up to 1023
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/sizes.h>
+
+#include "pxamci.h"
+
+#ifdef CONFIG_MMC_DEBUG
+#define DBG(x...)	printk(KERN_DEBUG x)
+#else
+#define DBG(x...)	do { } while (0)
+#endif
+
+struct pxamci_host {
+	struct mmc_host		mmc;
+	spinlock_t		lock;
+	struct resource		*res;
+	void			*base;
+	int			irq;
+	int			dma;
+	unsigned int		clkrt;
+	unsigned int		cmdat;
+	unsigned int		imask;
+	unsigned int		power_mode;
+
+	struct mmc_request	*req;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	dma_addr_t		sg_dma;
+	struct pxa_dma_desc	*sg_cpu;
+
+	dma_addr_t		dma_buf;
+	unsigned int		dma_size;
+	unsigned int		dma_dir;
+};
+
+#define to_pxamci_host(x)	container_of(x, struct pxamci_host, mmc)
+
+/*
+ * The base MMC clock rate
+ */
+#define CLOCKRATE	20000000
+
+static inline unsigned int ns_to_clocks(unsigned int ns)
+{
+	return (ns * (CLOCKRATE / 1000000) + 999) / 1000;
+}
+
+static void pxamci_stop_clock(struct pxamci_host *host)
+{
+	if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
+		unsigned long flags;
+		unsigned int v;
+
+		writel(STOP_CLOCK, host->base + MMC_STRPCL);
+
+		/*
+		 * Wait for the "clock has stopped" interrupt.
+		 * We need to unmask the interrupt to receive
+		 * the notification.  Sigh.
+		 */
+		spin_lock_irqsave(&host->lock, flags);
+		writel(host->imask & ~CLK_IS_OFF, host->base + MMC_I_MASK);
+		do {
+			v = readl(host->base + MMC_I_REG);
+		} while (!(v & CLK_IS_OFF));
+		writel(host->imask, host->base + MMC_I_MASK);
+		spin_unlock_irqrestore(&host->lock, flags);
+	}
+}
+
+static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->imask &= ~mask;
+	writel(host->imask, host->base + MMC_I_MASK);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->imask |= mask;
+	writel(host->imask, host->base + MMC_I_MASK);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
+{
+	unsigned int nob = data->blocks;
+	unsigned int timeout, size;
+	dma_addr_t dma;
+	u32 dcmd;
+	int i;
+
+	host->data = data;
+
+	if (data->flags & MMC_DATA_STREAM)
+		nob = 0xffff;
+
+	writel(nob, host->base + MMC_NOB);
+	writel(1 << data->blksz_bits, host->base + MMC_BLKLEN);
+
+	timeout = ns_to_clocks(data->timeout_ns) + data->timeout_clks;
+	writel((timeout + 255) / 256, host->base + MMC_RDTO);
+
+	if (data->flags & MMC_DATA_READ) {
+		host->dma_dir = DMA_FROM_DEVICE;
+		dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG;
+		DRCMRTXMMC = 0;
+		DRCMRRXMMC = host->dma | DRCMR_MAPVLD;
+	} else {
+		host->dma_dir = DMA_TO_DEVICE;
+		dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC;
+		DRCMRRXMMC = 0;
+		DRCMRTXMMC = host->dma | DRCMR_MAPVLD;
+	}
+
+	dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
+
+	host->dma_size = data->blocks << data->blksz_bits;
+	host->dma_buf = dma_map_single(host->mmc.dev, data->rq->buffer,
+				       host->dma_size, host->dma_dir);
+
+	for (i = 0, size = host->dma_size, dma = host->dma_buf; size; i++) {
+		u32 len = size;
+
+		if (len > DCMD_LENGTH)
+			len = 0x1000;
+
+		if (data->flags & MMC_DATA_READ) {
+			host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
+			host->sg_cpu[i].dtadr = dma;
+		} else {
+			host->sg_cpu[i].dsadr = dma;
+			host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
+		}
+		host->sg_cpu[i].dcmd = dcmd | len;
+
+		dma += len;
+		size -= len;
+
+		if (size) {
+			host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
+						 sizeof(struct pxa_dma_desc);
+		} else {
+			host->sg_cpu[i].ddadr = DDADR_STOP;
+		}
+	}
+	wmb();
+
+	DDADR(host->dma) = host->sg_dma;
+	DCSR(host->dma) = DCSR_RUN;
+}
+
+static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
+{
+	WARN_ON(host->cmd != NULL);
+	host->cmd = cmd;
+
+	if (cmd->flags & MMC_RSP_BUSY)
+		cmdat |= CMDAT_BUSY;
+
+	switch (cmd->flags & (MMC_RSP_MASK | MMC_RSP_CRC)) {
+	case MMC_RSP_SHORT | MMC_RSP_CRC:
+		cmdat |= CMDAT_RESP_SHORT;
+		break;
+	case MMC_RSP_SHORT:
+		cmdat |= CMDAT_RESP_R3;
+		break;
+	case MMC_RSP_LONG | MMC_RSP_CRC:
+		cmdat |= CMDAT_RESP_R2;
+		break;
+	default:
+		break;
+	}
+
+	writel(cmd->opcode, host->base + MMC_CMD);
+	writel(cmd->arg >> 16, host->base + MMC_ARGH);
+	writel(cmd->arg & 0xffff, host->base + MMC_ARGL);
+	writel(cmdat, host->base + MMC_CMDAT);
+	writel(host->clkrt, host->base + MMC_CLKRT);
+
+	writel(START_CLOCK, host->base + MMC_STRPCL);
+
+	pxamci_enable_irq(host, END_CMD_RES);
+}
+
+static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *req)
+{
+	DBG("PXAMCI: request done\n");
+	host->req = NULL;
+	host->cmd = NULL;
+	host->data = NULL;
+	mmc_request_done(&host->mmc, req);
+}
+
+static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
+{
+	struct mmc_command *cmd = host->cmd;
+	int i;
+	u32 v;
+
+	if (!cmd)
+		return 0;
+
+	host->cmd = NULL;
+
+	/*
+	 * Did I mention this is Sick.  We always need to
+	 * discard the upper 8 bits of the first 16-bit word.
+	 */
+	v = readl(host->base + MMC_RES) & 0xffff;
+	for (i = 0; i < 4; i++) {
+		u32 w1 = readl(host->base + MMC_RES) & 0xffff;
+		u32 w2 = readl(host->base + MMC_RES) & 0xffff;
+		cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8;
+		v = w2;
+	}
+
+	if (stat & STAT_TIME_OUT_RESPONSE) {
+		cmd->error = MMC_ERR_TIMEOUT;
+	} else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+		cmd->error = MMC_ERR_BADCRC;
+	}
+
+	pxamci_disable_irq(host, END_CMD_RES);
+	if (host->data && cmd->error == MMC_ERR_NONE) {
+		pxamci_enable_irq(host, DATA_TRAN_DONE);
+	} else {
+		pxamci_finish_request(host, host->req);
+	}
+
+	return 1;
+}
+
+static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
+{
+	struct mmc_data *data = host->data;
+
+	if (!data)
+		return 0;
+
+	DCSR(host->dma) = 0;
+	dma_unmap_single(host->mmc.dev, host->dma_buf, host->dma_size,
+			 host->dma_dir);
+
+	if (stat & STAT_READ_TIME_OUT)
+		data->error = MMC_ERR_TIMEOUT;
+	else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))
+		data->error = MMC_ERR_BADCRC;
+
+	data->bytes_xfered = (data->blocks - readl(host->base + MMC_NOB))
+			       << data->blksz_bits;
+
+	pxamci_disable_irq(host, DATA_TRAN_DONE);
+
+	host->data = NULL;
+	if (host->req->stop && data->error == MMC_ERR_NONE) {
+		pxamci_stop_clock(host);
+		pxamci_start_cmd(host, host->req->stop, 0);
+	} else {
+		pxamci_finish_request(host, host->req);
+	}
+
+	return 1;
+}
+
+static irqreturn_t pxamci_irq(int irq, void *devid, struct pt_regs *regs)
+{
+	struct pxamci_host *host = devid;
+	unsigned int ireg;
+	int handled = 0;
+
+	ireg = readl(host->base + MMC_I_REG);
+
+	DBG("PXAMCI: irq %08x\n", ireg);
+
+	if (ireg) {
+		unsigned stat = readl(host->base + MMC_STAT);
+
+		DBG("PXAMCI: stat %08x\n", stat);
+
+		if (ireg & END_CMD_RES)
+			handled |= pxamci_cmd_done(host, stat);
+		if (ireg & DATA_TRAN_DONE)
+			handled |= pxamci_data_done(host, stat);
+	}
+
+	return IRQ_RETVAL(handled);
+}
+
+static void pxamci_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+	struct pxamci_host *host = to_pxamci_host(mmc);
+	unsigned int cmdat;
+
+	WARN_ON(host->req != NULL);
+
+	host->req = req;
+
+	pxamci_stop_clock(host);
+
+	cmdat = host->cmdat;
+	host->cmdat &= ~CMDAT_INIT;
+
+	if (req->data) {
+		pxamci_setup_data(host, req->data);
+
+		cmdat &= ~CMDAT_BUSY;
+		cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;
+		if (req->data->flags & MMC_DATA_WRITE)
+			cmdat |= CMDAT_WRITE;
+
+		if (req->data->flags & MMC_DATA_STREAM)
+			cmdat |= CMDAT_STREAM;
+	}
+
+	pxamci_start_cmd(host, req->cmd, cmdat);
+}
+
+static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct pxamci_host *host = to_pxamci_host(mmc);
+
+	DBG("pxamci_set_ios: clock %u power %u vdd %u.%02u\n",
+	    ios->clock, ios->power_mode, ios->vdd / 100,
+	    ios->vdd % 100);
+
+	if (ios->clock) {
+		unsigned int clk = CLOCKRATE / ios->clock;
+		if (CLOCKRATE / clk > ios->clock)
+			clk <<= 1;
+		host->clkrt = fls(clk) - 1;
+
+		/*
+		 * we write clkrt on the next command
+		 */
+	} else if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
+		/*
+		 * Ensure that the clock is off.
+		 */
+		writel(STOP_CLOCK, host->base + MMC_STRPCL);
+	}
+
+	if (host->power_mode != ios->power_mode) {
+		host->power_mode = ios->power_mode;
+
+		/*
+		 * power control?  none on the lubbock.
+		 */
+
+		if (ios->power_mode == MMC_POWER_ON)
+			host->cmdat |= CMDAT_INIT;
+	}
+
+	DBG("pxamci_set_ios: clkrt = %x cmdat = %x\n",
+	    host->clkrt, host->cmdat);
+}
+
+static struct mmc_host_ops pxamci_ops = {
+	.request	= pxamci_request,
+	.set_ios	= pxamci_set_ios,
+};
+
+static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr)
+{
+	int i;
+
+	for (i = 0; i < dev->num_resources; i++)
+		if (dev->resource[i].flags == mask && nr-- == 0)
+			return &dev->resource[i];
+	return NULL;
+}
+
+static int platform_device_irq(struct platform_device *dev, int nr)
+{
+	int i;
+
+	for (i = 0; i < dev->num_resources; i++)
+		if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0)
+			return dev->resource[i].start;
+	return NO_IRQ;
+}
+
+static void pxamci_dma_irq(int dma, void *devid, struct pt_regs *regs)
+{
+	printk(KERN_ERR "DMA%d: IRQ???\n", dma);
+	DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
+}
+
+static int pxamci_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pxamci_host *host;
+	struct resource *r;
+	int ret, irq;
+
+	r = platform_device_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_device_irq(pdev, 0);
+	if (!r || irq == NO_IRQ)
+		return -ENXIO;
+
+	r = request_mem_region(r->start, SZ_4K, "PXAMCI");
+	if (!r)
+		return -EBUSY;
+
+	host = kmalloc(sizeof(struct pxamci_host), GFP_KERNEL);
+	if (!host) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memset(host, 0, sizeof(struct pxamci_host));
+	host->dma = -1;
+
+	host->sg_cpu = dma_alloc_coherent(dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = mmc_init_host(&host->mmc);
+	if (ret)
+		goto out;
+
+	spin_lock_init(&host->lock);
+	host->res = r;
+	host->irq = irq;
+	host->imask = TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
+		      END_CMD_RES|PRG_DONE|DATA_TRAN_DONE;
+	host->mmc.dev = dev;
+	host->mmc.ops = &pxamci_ops;
+	host->mmc.f_min	= 312500;
+	host->mmc.f_max	= 20000000;
+	host->mmc.ocr_avail = MMC_VDD_32_33;
+
+	host->base = ioremap(r->start, SZ_4K);
+	if (!host->base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/*
+	 * Ensure that the host controller is shut down, and setup
+	 * with our defaults.
+	 */
+	pxamci_stop_clock(host);
+	writel(0, host->base + MMC_SPI);
+	writel(64, host->base + MMC_RESTO);
+
+#ifdef CONFIG_PREEMPT
+#error Not Preempt-safe
+#endif
+	pxa_gpio_mode(GPIO6_MMCCLK_MD);
+	pxa_gpio_mode(GPIO8_MMCCS0_MD);
+	CKEN |= CKEN12_MMC;
+
+	host->dma = pxa_request_dma("PXAMCI", DMA_PRIO_LOW, pxamci_dma_irq, host);
+	if (host->dma < 0)
+		goto out;
+
+	ret = request_irq(host->irq, pxamci_irq, 0, "PXAMCI", host);
+	if (ret)
+		goto out;
+
+	dev_set_drvdata(dev, host);
+
+	mmc_add_host(&host->mmc);
+
+	return 0;
+
+ out:
+	if (host) {
+		if (host->dma >= 0)
+			pxa_free_dma(host->dma);
+		if (host->base)
+			iounmap(host->base);
+		if (host->sg_cpu)
+			dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+		kfree(host);
+	}
+	release_resource(r);
+	return ret;
+}
+
+static int pxamci_remove(struct device *dev)
+{
+	struct pxamci_host *host = dev_get_drvdata(dev);
+
+	dev_set_drvdata(dev, NULL);
+
+	if (host) {
+		mmc_remove_host(&host->mmc);
+
+		pxamci_stop_clock(host);
+		writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
+		       END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
+		       host->base + MMC_I_MASK);
+
+		free_irq(host->irq, host);
+		pxa_free_dma(host->dma);
+		iounmap(host->base);
+		dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+		release_resource(host->res);
+
+		kfree(host);
+	}
+	return 0;
+}
+
+static int pxamci_suspend(struct device *dev, u32 state, u32 level)
+{
+	struct pxamci_host *host = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (host && level == SUSPEND_DISABLE)
+		ret = mmc_suspend_host(&host->mmc, state);
+	return ret;
+}
+
+static int pxamci_resume(struct device *dev, u32 level)
+{
+	struct pxamci_host *host = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (host && level == RESUME_ENABLE)
+		ret = mmc_resume_host(&host->mmc);
+	return ret;
+}
+
+static struct device_driver pxamci_driver = {
+	.name		= "pxamci",
+	.bus		= &platform_bus_type,
+	.probe		= pxamci_probe,
+	.remove		= pxamci_remove,
+	.suspend	= pxamci_suspend,
+	.resume		= pxamci_resume,
+};
+
+static int __init pxamci_init(void)
+{
+	return driver_register(&pxamci_driver);
+}
+
+static void __exit pxamci_exit(void)
+{
+	driver_unregister(&pxamci_driver);
+}
+
+module_init(pxamci_init);
+module_exit(pxamci_exit);
+
+MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver");
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/mmc.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,15 @@
+/*
+ *  linux/drivers/media/mmc/mmc.h
+ *
+ *  Copyright (C) 2003 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _MMC_H
+/* core-internal functions */
+void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
+int mmc_register_card(struct mmc_card *card);
+void mmc_remove_card(struct mmc_card *card);
+#endif
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/mmc_sysfs.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,231 @@
+/*
+ *  linux/drivers/media/mmc/mmc_sysfs.c
+ *
+ *  Copyright (C) 2003 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  MMC sysfs/driver model support.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+
+#include "mmc.h"
+
+#define dev_to_mmc_card(d)	container_of(d, struct mmc_card, dev)
+#define to_mmc_driver(d)	container_of(d, struct mmc_driver, drv)
+
+static void mmc_release_card(struct device *dev)
+{
+	struct mmc_card *card = dev_to_mmc_card(dev);
+
+	kfree(card);
+}
+
+/*
+ * This currently matches any MMC driver to any MMC card - drivers
+ * themselves make the decision whether to drive this card in their
+ * probe method.
+ */
+static int mmc_bus_match(struct device *dev, struct device_driver *drv)
+{
+	return 1;
+}
+
+static int
+mmc_bus_hotplug(struct device *dev, char **envp, int num_envp, char *buf,
+		int buf_size)
+{
+	struct mmc_card *card = dev_to_mmc_card(dev);
+	char ccc[13];
+	int i = 0;
+
+#define add_env(fmt,val)						\
+	({								\
+		int len, ret = -ENOMEM;					\
+		if (i < num_envp) {					\
+			envp[i++] = buf;				\
+			len = snprintf(buf, buf_size, fmt, val) + 1;	\
+			buf_size -= len;				\
+			buf += len;					\
+			if (buf_size >= 0)				\
+				ret = 0;				\
+		}							\
+		ret;							\
+	})
+
+	for (i = 0; i < 12; i++)
+		ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0';
+	ccc[12] = '\0';
+
+	i = 0;
+	add_env("MMC_CCC=%s", ccc);
+	add_env("MMC_MANFID=%03x", card->cid.manfid);
+	add_env("MMC_SLOT_NAME=%s", card->dev.bus_id);
+
+	return 0;
+}
+
+static int mmc_bus_suspend(struct device *dev, u32 state)
+{
+	struct mmc_driver *drv = to_mmc_driver(dev->driver);
+	struct mmc_card *card = dev_to_mmc_card(dev);
+	int ret = 0;
+
+	if (dev->driver && drv->suspend)
+		ret = drv->suspend(card, state);
+	return ret;
+}
+
+static int mmc_bus_resume(struct device *dev)
+{
+	struct mmc_driver *drv = to_mmc_driver(dev->driver);
+	struct mmc_card *card = dev_to_mmc_card(dev);
+	int ret = 0;
+
+	if (dev->driver && drv->resume)
+		ret = drv->resume(card);
+	return ret;
+}
+
+static struct bus_type mmc_bus_type = {
+	.name		= "mmc",
+	.match		= mmc_bus_match,
+	.hotplug	= mmc_bus_hotplug,
+	.suspend	= mmc_bus_suspend,
+	.resume		= mmc_bus_resume,
+};
+
+
+static int mmc_drv_probe(struct device *dev)
+{
+	struct mmc_driver *drv = to_mmc_driver(dev->driver);
+	struct mmc_card *card = dev_to_mmc_card(dev);
+
+	return drv->probe(card);
+}
+
+static int mmc_drv_remove(struct device *dev)
+{
+	struct mmc_driver *drv = to_mmc_driver(dev->driver);
+	struct mmc_card *card = dev_to_mmc_card(dev);
+
+	drv->remove(card);
+
+	return 0;
+}
+
+
+/**
+ *	mmc_register_driver - register a media driver
+ *	@drv: MMC media driver
+ */
+int mmc_register_driver(struct mmc_driver *drv)
+{
+	drv->drv.bus = &mmc_bus_type;
+	drv->drv.probe = mmc_drv_probe;
+	drv->drv.remove = mmc_drv_remove;
+	return driver_register(&drv->drv);
+}
+
+EXPORT_SYMBOL(mmc_register_driver);
+
+/**
+ *	mmc_unregister_driver - unregister a media driver
+ *	@drv: MMC media driver
+ */
+void mmc_unregister_driver(struct mmc_driver *drv)
+{
+	drv->drv.bus = &mmc_bus_type;
+	driver_unregister(&drv->drv);
+}
+
+EXPORT_SYMBOL(mmc_unregister_driver);
+
+
+#define MMC_ATTR(name, fmt, args...)					\
+static ssize_t mmc_dev_show_##name (struct device *dev, char *buf)	\
+{									\
+	struct mmc_card *card = dev_to_mmc_card(dev);			\
+	return sprintf(buf, fmt, args);					\
+}									\
+static DEVICE_ATTR(name, S_IRUGO, mmc_dev_show_##name, NULL)
+
+MMC_ATTR(date, "%02d/%04d\n", card->cid.month, 1997 + card->cid.year);
+MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
+MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
+MMC_ATTR(manfid, "0x%03x\n", card->cid.manfid);
+MMC_ATTR(serial, "0x%06x\n", card->cid.serial);
+MMC_ATTR(name, "%s\n", card->cid.prod_name);
+
+static struct device_attribute *mmc_dev_attributes[] = {
+	&dev_attr_date,
+	&dev_attr_fwrev,
+	&dev_attr_hwrev,
+	&dev_attr_manfid,
+	&dev_attr_serial,
+	&dev_attr_name,
+};
+
+/*
+ * Internal function.  Initialise a MMC card structure.
+ */
+void mmc_init_card(struct mmc_card *card, struct mmc_host *host)
+{
+	memset(card, 0, sizeof(struct mmc_card));
+	card->host = host;
+	device_initialize(&card->dev);
+	card->dev.parent = card->host->dev;
+	card->dev.bus = &mmc_bus_type;
+	card->dev.release = mmc_release_card;
+}
+
+/*
+ * Internal function.  Register a new MMC card with the driver model.
+ */
+int mmc_register_card(struct mmc_card *card)
+{
+	int ret, i;
+
+	snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
+		 "mmc%02x:%04x", card->host->host_num, card->rca);
+
+	ret = device_add(&card->dev);
+	if (ret == 0)
+		for (i = 0; i < ARRAY_SIZE(mmc_dev_attributes); i++)
+			device_create_file(&card->dev, mmc_dev_attributes[i]);
+
+	return ret;
+}
+
+/*
+ * Internal function.  Unregister a new MMC card with the
+ * driver model, and (eventually) free it.
+ */
+void mmc_remove_card(struct mmc_card *card)
+{
+	if (mmc_card_present(card))
+		device_del(&card->dev);
+
+	put_device(&card->dev);
+}
+
+
+static int __init mmc_init(void)
+{
+	return bus_register(&mmc_bus_type);
+}
+
+static void __exit mmc_exit(void)
+{
+	bus_unregister(&mmc_bus_type);
+}
+
+module_init(mmc_init);
+module_exit(mmc_exit);
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/mmci.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,445 @@
+/*
+ *  linux/drivers/media/mmc/mmci.c - ARM PrimeCell MMCI PL180/1 driver
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware/amba.h>
+
+#include "mmci.h"
+
+#define DRIVER_NAME "mmci-pl18x"
+
+#ifdef CONFIG_MMC_DEBUG
+#define DBG(x...)	printk(KERN_DEBUG x)
+#else
+#define DBG(x...)	do { } while (0)
+#endif
+
+static int fmax = 515633;
+
+static void
+mmci_request_end(struct mmci_host *host, struct mmc_request *req)
+{
+	writel(0, host->base + MMCICOMMAND);
+	host->req = NULL;
+	host->cmd = NULL;
+	host->data = NULL;
+
+	if (req->data)
+		req->data->bytes_xfered = host->data_xfered;
+
+	mmc_request_done(&host->mmc, req);
+}
+
+static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
+{
+	unsigned int datactrl;
+
+	DBG("MMCI: data: blksz %04x blks %04x flags %08x\n",
+	    1 << data->blksz_bits, data->blocks, data->flags);
+
+	datactrl = MCI_DPSM_ENABLE | data->blksz_bits << 4;
+
+	if (data->flags & MMC_DATA_READ)
+		datactrl |= MCI_DPSM_DIRECTION;
+
+	host->data = data;
+	host->buffer = data->rq->buffer;
+	host->size = data->blocks << data->blksz_bits;
+	host->data_xfered = 0;
+
+	writel(0x800000, host->base + MMCIDATATIMER);
+	writel(host->size, host->base + MMCIDATALENGTH);
+	writel(datactrl, host->base + MMCIDATACTRL);
+}
+
+static void
+mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
+{
+	DBG("MMCI: cmd: op %02x arg %08x flags %08x\n",
+	    cmd->opcode, cmd->arg, cmd->flags);
+
+	if (readl(host->base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
+		writel(0, host->base + MMCICOMMAND);
+		udelay(1);
+	}
+
+	c |= cmd->opcode | MCI_CPSM_ENABLE;
+	switch (cmd->flags & MMC_RSP_MASK) {
+	case MMC_RSP_NONE:
+	default:
+		break;
+	case MMC_RSP_LONG:
+		c |= MCI_CPSM_LONGRSP;
+	case MMC_RSP_SHORT:
+		c |= MCI_CPSM_RESPONSE;
+		break;
+	}
+	if (/*interrupt*/0)
+		c |= MCI_CPSM_INTERRUPT;
+
+	host->cmd = cmd;
+
+	writel(cmd->arg, host->base + MMCIARGUMENT);
+	writel(c, host->base + MMCICOMMAND);
+}
+
+static void
+mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
+	      unsigned int status)
+{
+	if (status & MCI_DATABLOCKEND) {
+		host->data_xfered += 1 << data->blksz_bits;
+	}
+	if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
+		if (status & MCI_DATACRCFAIL)
+			data->error = MMC_ERR_BADCRC;
+		else if (status & MCI_DATATIMEOUT)
+			data->error = MMC_ERR_TIMEOUT;
+		else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
+			data->error = MMC_ERR_FIFO;
+		status |= MCI_DATAEND;
+	}
+	if (status & MCI_DATAEND) {
+		host->data = NULL;
+		if (!data->stop) {
+			mmci_request_end(host, data->req);
+		} else /*if (readl(host->base + MMCIDATACNT) > 6)*/ {
+			mmci_start_command(host, data->stop, 0);
+		}
+	}
+}
+
+static void
+mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
+	     unsigned int status)
+{
+	host->cmd = NULL;
+
+	cmd->resp[0] = readl(host->base + MMCIRESPONSE0);
+	cmd->resp[1] = readl(host->base + MMCIRESPONSE1);
+	cmd->resp[2] = readl(host->base + MMCIRESPONSE2);
+	cmd->resp[3] = readl(host->base + MMCIRESPONSE3);
+
+	if (status & MCI_CMDTIMEOUT) {
+		cmd->error = MMC_ERR_TIMEOUT;
+	} else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
+		cmd->error = MMC_ERR_BADCRC;
+	}
+
+	if (!cmd->data || cmd->error != MMC_ERR_NONE) {
+		mmci_request_end(host, cmd->req);
+	} else if (!(cmd->data->flags & MMC_DATA_READ)) {
+		mmci_start_data(host, cmd->data);
+	}
+}
+
+static irqreturn_t mmci_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mmci_host *host = dev_id;
+	u32 status;
+	int ret = 0;
+
+	do {
+		struct mmc_command *cmd;
+		struct mmc_data *data;
+
+		status = readl(host->base + MMCISTATUS);
+		writel(status, host->base + MMCICLEAR);
+
+		if (!(status & MCI_IRQMASK))
+			break;
+
+		DBG("MMCI: irq %08x\n", status);
+
+		if (status & (MCI_RXDATAAVLBL|MCI_RXFIFOHALFFULL)) {
+			int count = host->size - (readl(host->base + MMCIFIFOCNT) << 2);
+			if (count < 0)
+				count = 0;
+			if (count && host->buffer) {
+				readsl(host->base + MMCIFIFO, host->buffer, count >> 2);
+				host->buffer += count;
+				host->size -= count;
+				if (host->size == 0)
+					host->buffer = NULL;
+			} else {
+				static int first = 1;
+				if (first) {
+					first = 0;
+					printk(KERN_ERR "MMCI: sinking excessive data\n");
+				}
+				readl(host->base + MMCIFIFO);
+			}
+		}
+		if (status & (MCI_TXFIFOEMPTY|MCI_TXFIFOHALFEMPTY)) {
+			int count = host->size;
+			if (count > MCI_FIFOHALFSIZE)
+				count = MCI_FIFOHALFSIZE;
+			if (count && host->buffer) {
+				writesl(host->base + MMCIFIFO, host->buffer, count >> 2);
+				host->buffer += count;
+				host->size -= count;
+				if (host->size == 0)
+					host->buffer = NULL;
+			} else {
+				static int first = 1;
+				if (first) {
+					first = 0;
+					printk(KERN_ERR "MMCI: ran out of source data\n");
+				}
+			}
+		}
+
+		data = host->data;
+		if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|
+			      MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND))
+			mmci_data_irq(host, data, status);
+
+		cmd = host->cmd;
+		if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
+			mmci_cmd_irq(host, cmd, status);
+
+		ret = 1;
+	} while (status);
+
+	return IRQ_RETVAL(ret);
+}
+
+static void mmci_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+	struct mmci_host *host = to_mmci_host(mmc);
+
+	WARN_ON(host->req != NULL);
+
+	host->req = req;
+
+	if (req->data && req->data->flags & MMC_DATA_READ)
+		mmci_start_data(host, req->data);
+
+	mmci_start_command(host, req->cmd, 0);
+}
+
+static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct mmci_host *host = to_mmci_host(mmc);
+	u32 clk = 0, pwr = 0;
+
+	DBG("MMCI: set_ios: clock %dHz busmode %d powermode %d Vdd %d.%02d\n",
+	    ios->clock, ios->bus_mode, ios->power_mode,
+	    ios->vdd / 100, ios->vdd % 100);
+
+	if (ios->clock) {
+		clk = host->mclk / (2 * ios->clock) - 1;
+		if (clk > 256)
+			clk = 255;
+		clk |= MCI_CLK_ENABLE;
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_OFF:
+		break;
+	case MMC_POWER_UP:
+		pwr |= MCI_PWR_UP;
+		break;
+	case MMC_POWER_ON:
+		pwr |= MCI_PWR_ON;
+		break;
+	}
+
+	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+		pwr |= MCI_ROD;
+
+	writel(clk, host->base + MMCICLOCK);
+
+	if (host->pwr != pwr) {
+		host->pwr = pwr;
+		writel(pwr, host->base + MMCIPOWER);
+	}
+}
+
+static struct mmc_host_ops mmci_ops = {
+	.request	= mmci_request,
+	.set_ios	= mmci_set_ios,
+};
+
+static int mmci_probe(struct amba_device *dev, void *id)
+{
+	struct mmci_host *host;
+	int ret;
+//	void *tmp;
+
+	/* enable the interrupt via the VIC */
+//	tmp = ioremap(0xc3000000, SZ_4K);
+//	if (tmp) {
+//		u32 val = readl(tmp + 0x10);
+//		writel(val | 0x180, tmp + 0x10);
+//		iounmap(tmp);
+//	}
+
+	if (!request_mem_region(dev->res.start, SZ_4K, DRIVER_NAME))
+		return -EBUSY;
+
+	host = kmalloc(sizeof(struct mmci_host), GFP_KERNEL);
+	if (!host) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memset(host, 0, sizeof(struct mmci_host));
+
+	ret = mmc_init_host(&host->mmc);
+	if (ret)
+		goto out;
+
+	host->base = ioremap(dev->res.start, SZ_4K);
+	if (!host->base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	host->irq = dev->irq;
+	host->mclk = 33000000;	/* impd1 */
+	host->mmc.dev = &dev->dev;
+	host->mmc.ops = &mmci_ops;
+	host->mmc.f_min = (host->mclk + 511) / 512;
+	host->mmc.f_max = host->mclk / 2;
+	if (host->mmc.f_max > fmax)
+		host->mmc.f_max = fmax;
+
+	host->mmc.ocr_avail = MMC_VDD_35_36;
+
+	writel(0, host->base + MMCIMASK0);
+	writel(0, host->base + MMCIMASK1);
+	writel(0xfff, host->base + MMCICLEAR);
+
+	ret = request_irq(host->irq, mmci_irq, SA_SHIRQ, DRIVER_NAME, host);
+	if (ret)
+		goto out;
+
+	writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+
+	amba_set_drvdata(dev, host);
+
+	mmc_add_host(&host->mmc);
+
+	return 0;
+
+ out:
+	if (host) {
+		if (host->base)
+			iounmap(host->base);
+		kfree(host);
+	}
+	release_mem_region(dev->res.start, SZ_4K);
+	return ret;
+}
+
+static int mmci_remove(struct amba_device *dev)
+{
+	struct mmci_host *host = amba_get_drvdata(dev);
+
+	amba_set_drvdata(dev, NULL);
+
+	if (host) {
+		mmc_remove_host(&host->mmc);
+
+		writel(0, host->base + MMCIMASK0);
+		writel(0, host->base + MMCIMASK1);
+
+		writel(0, host->base + MMCICOMMAND);
+		writel(0, host->base + MMCIDATACTRL);
+
+		free_irq(host->irq, host);
+
+		iounmap(host->base);
+
+		kfree(host);
+
+		release_mem_region(dev->res.start, SZ_4K);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mmci_suspend(struct amba_device *dev, u32 state)
+{
+	struct mmci_host *host = amba_get_drvdata(dev);
+
+	return host ? mmc_suspend_host(&host->mmc, state) : 0;
+}
+
+static int mmci_resume(struct amba_device *dev)
+{
+	struct mmci_host *host = amba_get_drvdata(dev);
+	int ret = 0;
+
+	if (host) {
+		writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+		ret = mmc_resume_host(&host->mmc);
+	}
+
+	return ret;
+}
+#else
+#define mmci_suspend	NULL
+#define mmci_resume	NULL
+#endif
+
+static struct amba_id mmci_ids[] = {
+	{
+		.id	= 0x00041180,
+		.mask	= 0x000fffff,
+	},
+	{
+		.id	= 0x00041181,
+		.mask	= 0x000fffff,
+	},
+	{ 0, 0 },
+};
+
+static struct amba_driver mmci_driver = {
+	.drv		= {
+		.name	= DRIVER_NAME,
+	},
+	.probe		= mmci_probe,
+	.remove		= mmci_remove,
+	.suspend	= mmci_suspend,
+	.resume		= mmci_resume,
+	.id_table	= mmci_ids,
+};
+
+static int __init mmci_init(void)
+{
+	return amba_driver_register(&mmci_driver);
+}
+
+static void __exit mmci_exit(void)
+{
+	amba_driver_unregister(&mmci_driver);
+}
+
+module_init(mmci_init);
+module_exit(mmci_exit);
+module_param(fmax, int, 0444);
+
+MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver");
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/mmc_queue.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,29 @@
+#ifndef MMC_QUEUE_H
+#define MMC_QUEUE_H
+
+struct request;
+struct task_struct;
+
+struct mmc_queue {
+	struct mmc_card		*card;
+	struct completion	thread_complete;
+	wait_queue_head_t	thread_wq;
+	struct task_struct	*thread;
+	struct request		*req;
+	int			(*prep_fn)(struct mmc_queue *, struct request *);
+	int			(*issue_fn)(struct mmc_queue *, struct request *);
+	void			*data;
+	struct request_queue	*queue;
+};
+
+struct mmc_io_request {
+	struct request		*rq;
+	int			num;
+	struct mmc_command	selcmd;		/* mmc_queue private */
+	struct mmc_command	cmd[4];		/* max 4 commands */
+};
+
+extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *);
+extern void mmc_cleanup_queue(struct mmc_queue *);
+
+#endif
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/mmc_block.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,482 @@
+/*
+ * Block driver for media (i.e., flash cards)
+ *
+ * Copyright 2002 Hewlett-Packard Company
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Author:  Andrew Christian
+ *          28 May 2002
+ */
+#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>	/* printk() */
+#include <linux/fs.h>		/* everything... */
+#include <linux/errno.h>	/* error codes */
+#include <linux/hdreg.h>	/* HDIO_GETGEO */
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/protocol.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "mmc_queue.h"
+
+#define MMC_SHIFT	3	/* max 8 partitions per card */
+
+static int mmc_major;
+static int maxsectors = 8;
+
+/*
+ * There is one mmc_blk_data per slot.
+ */
+struct mmc_blk_data {
+	spinlock_t	lock;
+	struct gendisk	*disk;
+	struct mmc_queue queue;
+
+	unsigned int	usage;
+	unsigned int	block_bits;
+	unsigned int	suspended;
+};
+
+static DECLARE_MUTEX(open_lock);
+
+static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
+{
+	struct mmc_blk_data *md;
+
+	down(&open_lock);
+	md = disk->private_data;
+	if (md && md->usage == 0)
+		md = NULL;
+	if (md)
+		md->usage++;
+	up(&open_lock);
+
+	return md;
+}
+
+static void mmc_blk_put(struct mmc_blk_data *md)
+{
+	down(&open_lock);
+	md->usage--;
+	if (md->usage == 0) {
+		put_disk(md->disk);
+		mmc_cleanup_queue(&md->queue);
+		kfree(md);
+	}
+	up(&open_lock);
+}
+
+static int mmc_blk_open(struct inode *inode, struct file *filp)
+{
+	struct mmc_blk_data *md;
+	int ret = -ENXIO;
+
+	md = mmc_blk_get(inode->i_bdev->bd_disk);
+	if (md) {
+		if (md->usage == 2)
+			check_disk_change(inode->i_bdev);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int mmc_blk_release(struct inode *inode, struct file *filp)
+{
+	struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data;
+
+	mmc_blk_put(md);
+	return 0;
+}
+
+static int
+mmc_blk_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct block_device *bdev = inode->i_bdev;
+
+	if (cmd == HDIO_GETGEO) {
+		struct hd_geometry geo;
+
+		memset(&geo, 0, sizeof(struct hd_geometry));
+
+		geo.cylinders	= get_capacity(bdev->bd_disk) / (4 * 16);
+		geo.heads	= 4;
+		geo.sectors	= 16;
+		geo.start	= get_start_sect(bdev);
+
+		return copy_to_user((void *)arg, &geo, sizeof(geo))
+			? -EFAULT : 0;
+	}
+
+	return -ENOTTY;
+}
+
+static struct block_device_operations mmc_bdops = {
+	.open			= mmc_blk_open,
+	.release		= mmc_blk_release,
+	.ioctl			= mmc_blk_ioctl,
+	.owner			= THIS_MODULE,
+};
+
+struct mmc_blk_request {
+	struct mmc_request	req;
+	struct mmc_command	cmd;
+	struct mmc_command	stop;
+	struct mmc_data		data;
+};
+
+static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req)
+{
+	struct mmc_blk_data *md = mq->data;
+
+	/*
+	 * If we have no device, we haven't finished initialising.
+	 */
+	if (!md || !mq->card) {
+		printk("killing request - no device/host\n");
+		goto kill;
+	}
+
+	if (md->suspended) {
+		blk_plug_device(md->queue.queue);
+		goto defer;
+	}
+
+	/*
+	 * Check for excessive requests.
+	 */
+	if (req->sector + req->nr_sectors > get_capacity(req->rq_disk)) {
+		printk("bad request size\n");
+		goto kill;
+	}
+
+	return BLKPREP_OK;
+
+ defer:
+	return BLKPREP_DEFER;
+ kill:
+	return BLKPREP_KILL;
+}
+
+static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+{
+	struct mmc_blk_data *md = mq->data;
+	struct mmc_card *card = md->queue.card;
+	int err, sz = 0;
+
+	err = mmc_card_claim_host(card);
+	if (err)
+		goto cmd_err;
+
+	do {
+		struct mmc_blk_request rq;
+		struct mmc_command cmd;
+
+		memset(&rq, 0, sizeof(struct mmc_blk_request));
+		rq.req.cmd = &rq.cmd;
+		rq.req.data = &rq.data;
+
+		rq.cmd.arg = req->sector << 9;
+		rq.cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+		rq.data.rq = req;
+		rq.data.timeout_ns = card->csd.tacc_ns * 10;
+		rq.data.timeout_clks = card->csd.tacc_clks * 10;
+		rq.data.blksz_bits = md->block_bits;
+		rq.data.blocks = req->current_nr_sectors >> (md->block_bits - 9);
+		rq.stop.opcode = MMC_STOP_TRANSMISSION;
+		rq.stop.arg = 0;
+		rq.stop.flags = MMC_RSP_SHORT | MMC_RSP_CRC | MMC_RSP_BUSY;
+
+		if (rq_data_dir(req) == READ) {
+			rq.cmd.opcode = rq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
+			rq.data.flags |= MMC_DATA_READ;
+		} else {
+			rq.cmd.opcode = MMC_WRITE_BLOCK;
+			rq.cmd.flags |= MMC_RSP_BUSY;
+			rq.data.flags |= MMC_DATA_WRITE;
+			rq.data.blocks = 1;
+		}
+		rq.req.stop = rq.data.blocks > 1 ? &rq.stop : NULL;
+
+		mmc_wait_for_req(card->host, &rq.req);
+		if (rq.cmd.error) {
+			err = rq.cmd.error;
+			printk("error %d sending read/write command\n", err);
+			goto cmd_err;
+		}
+
+		if (rq_data_dir(req) == READ) {
+			sz = rq.data.bytes_xfered;
+		} else {
+			sz = 0;
+		}
+
+		if (rq.data.error) {
+			err = rq.data.error;
+			printk("error %d transferring data\n", err);
+			goto cmd_err;
+		}
+
+		if (rq.stop.error) {
+			err = rq.stop.error;
+			printk("error %d sending stop command\n", err);
+			goto cmd_err;
+		}
+
+		do {
+			cmd.opcode = MMC_SEND_STATUS;
+			cmd.arg = card->rca << 16;
+			cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+			err = mmc_wait_for_cmd(card->host, &cmd, 5);
+			if (err) {
+				printk("error %d requesting status\n", err);
+				goto cmd_err;
+			}
+		} while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+
+#if 0
+		if (cmd.resp[0] & ~0x00000900)
+			printk("status = %08x\n", cmd.resp[0]);
+		err = mmc_decode_status(cmd.resp);
+		if (err)
+			goto cmd_err;
+#endif
+
+		sz = rq.data.bytes_xfered;
+	} while (end_that_request_chunk(req, 1, sz));
+
+	mmc_card_release_host(card);
+
+	return 1;
+
+ cmd_err:
+	mmc_card_release_host(card);
+
+	end_that_request_chunk(req, 1, sz);
+	req->errors = err;
+
+	return 0;
+}
+
+#define MMC_NUM_MINORS	(256 >> MMC_SHIFT)
+
+static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))];
+
+static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
+{
+	struct mmc_blk_data *md;
+	int devidx;
+
+	devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);
+	if (devidx >= MMC_NUM_MINORS)
+		return NULL;
+	__set_bit(devidx, dev_use);
+
+	md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
+	if (md) {
+		memset(md, 0, sizeof(struct mmc_blk_data));
+
+		md->disk = alloc_disk(1 << MMC_SHIFT);
+		if (md->disk == NULL) {
+			kfree(md);
+			md = NULL;
+			goto out;
+		}
+
+		spin_lock_init(&md->lock);
+		md->usage = 1;
+
+		mmc_init_queue(&md->queue, card, &md->lock);
+		md->queue.prep_fn = mmc_blk_prep_rq;
+		md->queue.issue_fn = mmc_blk_issue_rq;
+		md->queue.data = md;
+
+		md->disk->major	= mmc_major;
+		md->disk->first_minor = devidx << MMC_SHIFT;
+		md->disk->fops = &mmc_bdops;
+		md->disk->private_data = md;
+		md->disk->queue = md->queue.queue;
+		md->disk->driverfs_dev = &card->dev;
+
+		sprintf(md->disk->disk_name, "mmcblk%d", devidx);
+		sprintf(md->disk->devfs_name, "mmc/blk%d", devidx);
+
+		md->block_bits = md->queue.card->csd.read_blkbits;
+
+		blk_queue_max_sectors(md->queue.queue, maxsectors);
+		blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits);
+		set_capacity(md->disk, md->queue.card->csd.capacity);
+	}
+ out:
+	return md;
+}
+
+static int
+mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
+{
+	struct mmc_command cmd;
+	int err;
+
+	mmc_card_claim_host(card);
+	cmd.opcode = MMC_SET_BLOCKLEN;
+	cmd.arg = 1 << card->csd.read_blkbits;
+	cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+	err = mmc_wait_for_cmd(card->host, &cmd, 5);
+	mmc_card_release_host(card);
+
+	if (err) {
+		printk(KERN_ERR "%s: unable to set block size to %d: %d\n",
+			md->disk->disk_name, cmd.arg, err);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mmc_blk_probe(struct mmc_card *card)
+{
+	struct mmc_blk_data *md;
+	int err;
+
+	if (card->csd.cmdclass & ~0x1ff)
+		return -ENODEV;
+
+	if (card->csd.read_blkbits < 9) {
+		printk(KERN_WARNING "%s: read blocksize too small (%u)\n",
+			mmc_card_id(card), 1 << card->csd.read_blkbits);
+		return -ENODEV;
+	}
+
+	md = mmc_blk_alloc(card);
+	if (md == NULL)
+		return -ENOMEM;
+
+	err = mmc_blk_set_blksize(md, card);
+	if (err)
+		goto out;
+
+	printk(KERN_INFO "%s: %s %s %dKiB\n",
+		md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
+		(card->csd.capacity << card->csd.read_blkbits) / 1024);
+
+	mmc_set_drvdata(card, md);
+	add_disk(md->disk);
+	return 0;
+
+ out:
+	mmc_blk_put(md);
+
+	return err;
+}
+
+static void mmc_blk_remove(struct mmc_card *card)
+{
+	struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+	if (md) {
+		int devidx;
+
+		del_gendisk(md->disk);
+
+		/*
+		 * I think this is needed.
+		 */
+		md->disk->queue = NULL;
+
+		devidx = md->disk->first_minor >> MMC_SHIFT;
+		__clear_bit(devidx, dev_use);
+
+		mmc_blk_put(md);
+	}
+	mmc_set_drvdata(card, NULL);
+}
+
+#ifdef CONFIG_PM
+static int mmc_blk_suspend(struct mmc_card *card, u32 state)
+{
+	struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+	if (md) {
+		blk_stop_queue(md->queue.queue);
+	}
+	return 0;
+}
+
+static int mmc_blk_resume(struct mmc_card *card)
+{
+	struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+	if (md) {
+		mmc_blk_set_blksize(md, md->queue.card);
+		blk_start_queue(md->queue.queue);
+	}
+	return 0;
+}
+#else
+#define	mmc_blk_suspend	NULL
+#define mmc_blk_resume	NULL
+#endif
+
+static struct mmc_driver mmc_driver = {
+	.drv		= {
+		.name	= "mmcblk",
+	},
+	.probe		= mmc_blk_probe,
+	.remove		= mmc_blk_remove,
+	.suspend	= mmc_blk_suspend,
+	.resume		= mmc_blk_resume,
+};
+
+static int __init mmc_blk_init(void)
+{
+	int res = -ENOMEM;
+
+	res = register_blkdev(mmc_major, "mmc");
+	if (res < 0) {
+		printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n",
+		       mmc_major, res);
+		goto out;
+	}
+	if (mmc_major == 0)
+		mmc_major = res;
+
+	devfs_mk_dir("mmc");
+	return mmc_register_driver(&mmc_driver);
+
+ out:
+	return res;
+}
+
+static void __exit mmc_blk_exit(void)
+{
+	mmc_unregister_driver(&mmc_driver);
+	devfs_remove("mmc");
+	unregister_blkdev(mmc_major, "mmc");
+}
+
+module_init(mmc_blk_init);
+module_exit(mmc_blk_exit);
+module_param(maxsectors, int, 0444);
+
+MODULE_PARM_DESC(maxsectors, "Maximum number of sectors for a single request");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/pxamci.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,94 @@
+#undef MMC_STRPCL
+#undef MMC_STAT
+#undef MMC_CLKRT
+#undef MMC_SPI
+#undef MMC_CMDAT
+#undef MMC_RESTO
+#undef MMC_RDTO
+#undef MMC_BLKLEN
+#undef MMC_NOB
+#undef MMC_PRTBUF
+#undef MMC_I_MASK
+#undef END_CMD_RES
+#undef PRG_DONE
+#undef DATA_TRAN_DONE
+#undef MMC_I_REG
+#undef MMC_CMD
+#undef MMC_ARGH
+#undef MMC_ARGL
+#undef MMC_RES
+#undef MMC_RXFIFO
+#undef MMC_TXFIFO
+
+#define MMC_STRPCL	0x0000
+#define STOP_CLOCK		(1 << 0)
+#define START_CLOCK		(2 << 0)
+
+#define MMC_STAT	0x0004
+#define STAT_END_CMD_RES		(1 << 13)
+#define STAT_PRG_DONE			(1 << 12)
+#define STAT_DATA_TRAN_DONE		(1 << 11)
+#define STAT_CLK_EN			(1 << 8)
+#define STAT_RECV_FIFO_FULL		(1 << 7)
+#define STAT_XMIT_FIFO_EMPTY		(1 << 6)
+#define STAT_RES_CRC_ERR		(1 << 5)
+#define STAT_SPI_READ_ERROR_TOKEN	(1 << 4)
+#define STAT_CRC_READ_ERROR		(1 << 3)
+#define STAT_CRC_WRITE_ERROR		(1 << 2)
+#define STAT_TIME_OUT_RESPONSE		(1 << 1)
+#define STAT_READ_TIME_OUT		(1 << 0)
+
+#define MMC_CLKRT	0x0008		/* 3 bit */
+
+#define MMC_SPI		0x000c
+#define SPI_CS_ADDRESS		(1 << 3)
+#define SPI_CS_EN		(1 << 2)
+#define CRC_ON			(1 << 1)
+#define SPI_EN			(1 << 0)
+
+#define MMC_CMDAT	0x0010
+#define CMDAT_DMAEN		(1 << 7)
+#define CMDAT_INIT		(1 << 6)
+#define CMDAT_BUSY		(1 << 5)
+#define CMDAT_STREAM		(1 << 4)	/* 1 = stream */
+#define CMDAT_WRITE		(1 << 3)	/* 1 = write */
+#define CMDAT_DATAEN		(1 << 2)
+#define CMDAT_RESP_NONE		(0 << 0)
+#define CMDAT_RESP_SHORT	(1 << 0)
+#define CMDAT_RESP_R2		(2 << 0)
+#define CMDAT_RESP_R3		(3 << 0)
+
+#define MMC_RESTO	0x0014	/* 7 bit */
+
+#define MMC_RDTO	0x0018	/* 16 bit */
+
+#define MMC_BLKLEN	0x001c	/* 10 bit */
+
+#define MMC_NOB		0x0020	/* 16 bit */
+
+#define MMC_PRTBUF	0x0024
+#define BUF_PART_FULL		(1 << 0)
+
+#define MMC_I_MASK	0x0028
+#define TXFIFO_WR_REQ		(1 << 6)
+#define RXFIFO_RD_REQ		(1 << 5)
+#define CLK_IS_OFF		(1 << 4)
+#define STOP_CMD		(1 << 3)
+#define END_CMD_RES		(1 << 2)
+#define PRG_DONE		(1 << 1)
+#define DATA_TRAN_DONE		(1 << 0)
+
+#define MMC_I_REG	0x002c
+/* same as MMC_I_MASK */
+
+#define MMC_CMD		0x0030
+
+#define MMC_ARGH	0x0034	/* 16 bit */
+
+#define MMC_ARGL	0x0038	/* 16 bit */
+
+#define MMC_RES		0x003c	/* 16 bit */
+
+#define MMC_RXFIFO	0x0040	/* 8 bit */
+
+#define MMC_TXFIFO	0x0044	/* 8 bit */
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/mmci.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,147 @@
+/*
+ *  linux/drivers/media/mmc/mmci.h - ARM PrimeCell MMCI PL180/1 driver
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+#define MMCIPOWER		0x000
+#define MCI_PWR_OFF		0x00
+#define MCI_PWR_UP		0x02
+#define MCI_PWR_ON		0x03
+#define MCI_OD			(1 << 6)
+#define MCI_ROD			(1 << 7)
+
+#define MMCICLOCK		0x004
+#define MCI_CLK_ENABLE		(1 << 8)
+#define MCI_PWRSAVE		(1 << 9)
+#define MCI_BYPASS		(1 << 10)
+
+#define MMCIARGUMENT		0x008
+#define MMCICOMMAND		0x00c
+#define MCI_CPSM_RESPONSE	(1 << 6)
+#define MCI_CPSM_LONGRSP	(1 << 7)
+#define MCI_CPSM_INTERRUPT	(1 << 8)
+#define MCI_CPSM_PENDING	(1 << 9)
+#define MCI_CPSM_ENABLE		(1 << 10)
+
+#define MMCIRESPCMD		0x010
+#define MMCIRESPONSE0		0x014
+#define MMCIRESPONSE1		0x018
+#define MMCIRESPONSE2		0x01c
+#define MMCIRESPONSE3		0x020
+#define MMCIDATATIMER		0x024
+#define MMCIDATALENGTH		0x028
+#define MMCIDATACTRL		0x02c
+#define MCI_DPSM_ENABLE		(1 << 0)
+#define MCI_DPSM_DIRECTION	(1 << 1)
+#define MCI_DPSM_MODE		(1 << 2)
+#define MCI_DPSM_DMAENABLE	(1 << 3)
+
+#define MMCIDATACNT		0x030
+#define MMCISTATUS		0x034
+#define MCI_CMDCRCFAIL		(1 << 0)
+#define MCI_DATACRCFAIL		(1 << 1)
+#define MCI_CMDTIMEOUT		(1 << 2)
+#define MCI_DATATIMEOUT		(1 << 3)
+#define MCI_TXUNDERRUN		(1 << 4)
+#define MCI_RXOVERRUN		(1 << 5)
+#define MCI_CMDRESPEND		(1 << 6)
+#define MCI_CMDSENT		(1 << 7)
+#define MCI_DATAEND		(1 << 8)
+#define MCI_DATABLOCKEND	(1 << 10)
+#define MCI_CMDACTIVE		(1 << 11)
+#define MCI_TXACTIVE		(1 << 12)
+#define MCI_RXACTIVE		(1 << 13)
+#define MCI_TXFIFOHALFEMPTY	(1 << 14)
+#define MCI_RXFIFOHALFFULL	(1 << 15)
+#define MCI_TXFIFOFULL		(1 << 16)
+#define MCI_RXFIFOFULL		(1 << 17)
+#define MCI_TXFIFOEMPTY		(1 << 18)
+#define MCI_RXFIFOEMPTY		(1 << 19)
+#define MCI_TXDATAAVLBL		(1 << 20)
+#define MCI_RXDATAAVLBL		(1 << 21)
+
+#define MMCICLEAR		0x038
+#define MCI_CMDCRCFAILCLR	(1 << 0)
+#define MCI_DATACRCFAILCLR	(1 << 1)
+#define MCI_CMDTIMEOUTCLR	(1 << 2)
+#define MCI_DATATIMEOUTCLR	(1 << 3)
+#define MCI_TXUNDERRUNCLR	(1 << 4)
+#define MCI_RXOVERRUNCLR	(1 << 5)
+#define MCI_CMDRESPENDCLR	(1 << 6)
+#define MCI_CMDSENTCLR		(1 << 7)
+#define MCI_DATAENDCLR		(1 << 8)
+#define MCI_DATABLOCKENDCLR	(1 << 10)
+
+#define MMCIMASK0		0x03c
+#define MCI_CMDCRCFAILMASK	(1 << 0)
+#define MCI_DATACRCFAILMASK	(1 << 1)
+#define MCI_CMDTIMEOUTMASK	(1 << 2)
+#define MCI_DATATIMEOUTMASK	(1 << 3)
+#define MCI_TXUNDERRUNMASK	(1 << 4)
+#define MCI_RXOVERRUNMASK	(1 << 5)
+#define MCI_CMDRESPENDMASK	(1 << 6)
+#define MCI_CMDSENTMASK		(1 << 7)
+#define MCI_DATAENDMASK		(1 << 8)
+#define MCI_DATABLOCKENDMASK	(1 << 10)
+#define MCI_CMDACTIVEMASK	(1 << 11)
+#define MCI_TXACTIVEMASK	(1 << 12)
+#define MCI_RXACTIVEMASK	(1 << 13)
+#define MCI_TXFIFOHALFEMPTYMASK	(1 << 14)
+#define MCI_RXFIFOHALFFULLMASK	(1 << 15)
+#define MCI_TXFIFOFULLMASK	(1 << 16)
+#define MCI_RXFIFOFULLMASK	(1 << 17)
+#define MCI_TXFIFOEMPTYMASK	(1 << 18)
+#define MCI_RXFIFOEMPTYMASK	(1 << 19)
+#define MCI_TXDATAAVLBLMASK	(1 << 20)
+#define MCI_RXDATAAVLBLMASK	(1 << 21)
+
+#define MMCIMASK1		0x040
+#define MMCIFIFOCNT		0x048
+#define MMCIFIFO		0x080 /* to 0x0bc */
+
+#define MCI_IRQMASK	\
+	(MCI_CMDCRCFAIL|MCI_DATACRCFAIL|MCI_CMDTIMEOUT|MCI_DATATIMEOUT|	\
+	MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_CMDRESPEND|MCI_CMDSENT|	\
+	MCI_DATAEND|MCI_DATABLOCKEND|					\
+	MCI_TXFIFOHALFEMPTY|MCI_RXFIFOHALFFULL|				\
+	MCI_TXFIFOEMPTY|MCI_RXDATAAVLBL)
+
+#define MCI_IRQENABLE	\
+	(MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK|	\
+	MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK|	\
+	MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK|		\
+	MCI_DATABLOCKENDMASK|MCI_TXFIFOHALFEMPTYMASK|			\
+	MCI_RXFIFOHALFFULLMASK)
+
+#define MCI_FIFOSIZE	16
+	
+#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
+
+struct mmci_host {
+	struct mmc_host		mmc;
+	void			*base;
+	int			irq;
+	unsigned int		mclk;
+	u32			pwr;
+
+	struct mmc_request	*req;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	unsigned int		data_xfered;
+
+	/* pio stuff */
+	void			*buffer;
+	unsigned int		size;
+
+	/* dma stuff */
+//	struct scatterlist	*sg_list;
+//	int			sg_len;
+//	int			sg_dir;
+};
+
+#define to_mmci_host(mmc)	container_of(mmc, struct mmci_host, mmc)
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/mmc.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,801 @@
+/*
+ *  linux/drivers/media/mmc/mmc.c
+ *
+ *  Copyright (C) 2003 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+
+#include "mmc.h"
+
+#ifdef CONFIG_MMC_DEBUG
+#define DBG(x...)	printk(KERN_DEBUG x)
+#else
+#define DBG(x...)	do { } while (0)
+#endif
+
+#define CMD_RETRIES	3
+
+/*
+ * OCR Bit positions to 10s of Vdd mV.
+ */
+static const unsigned short mmc_ocr_bit_to_vdd[] = {
+	150,	155,	160,	165,	170,	180,	190,	200,
+	210,	220,	230,	240,	250,	260,	270,	280,
+	290,	300,	310,	320,	330,	340,	350,	360
+};
+
+static const unsigned int tran_exp[] = {
+	10000,		100000,		1000000,	10000000,
+	0,		0,		0,		0
+};
+
+static const unsigned char tran_mant[] = {
+	0,	10,	12,	13,	15,	20,	25,	30,
+	35,	40,	45,	50,	55,	60,	70,	80,
+};
+
+static const unsigned int tacc_exp[] = {
+	1,	10,	100,	1000,	10000,	100000,	1000000, 10000000,
+};
+
+static const unsigned int tacc_mant[] = {
+	0,	10,	12,	13,	15,	20,	25,	30,
+	35,	40,	45,	50,	55,	60,	70,	80,
+};
+
+
+/**
+ *	mmc_request_done - finish processing an MMC command
+ *	@host: MMC host which completed command
+ *	@cmd: MMC command which completed
+ *	@err: MMC error code
+ *
+ *	MMC drivers should call this function when they have completed
+ *	their processing of a command.  This should be called before the
+ *	data part of the command has completed.
+ */
+void mmc_request_done(struct mmc_host *host, struct mmc_request *req)
+{
+	struct mmc_command *cmd = req->cmd;
+	int err = req->cmd->error;
+	DBG("MMC: req done (%02x): %d: %08x %08x %08x %08x\n", cmd->opcode,
+	    err, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
+
+	if (err && cmd->retries) {
+		cmd->retries--;
+		cmd->error = 0;
+		host->ops->request(host, req);
+	} else if (req->done) {
+		req->done(req);
+	}
+}
+
+EXPORT_SYMBOL(mmc_request_done);
+
+/**
+ *	mmc_start_request - start a command on a host
+ *	@host: MMC host to start command on
+ *	@cmd: MMC command to start
+ *
+ *	Queue a command on the specified host.  We expect the
+ *	caller to be holding the host lock with interrupts disabled.
+ */
+void
+mmc_start_request(struct mmc_host *host, struct mmc_request *req)
+{
+	DBG("MMC: starting cmd %02x arg %08x flags %08x\n",
+	    req->cmd->opcode, req->cmd->arg, req->cmd->flags);
+
+	req->cmd->error = 0;
+	req->cmd->req = req;
+	if (req->data) {
+		req->cmd->data = req->data;
+		req->data->error = 0;
+		req->data->req = req;
+		if (req->stop) {
+			req->data->stop = req->stop;
+			req->stop->error = 0;
+			req->stop->req = req;
+		}
+	}
+	host->ops->request(host, req);
+}
+
+EXPORT_SYMBOL(mmc_start_request);
+
+static void mmc_wait_done(struct mmc_request *req)
+{
+	complete(req->done_data);
+}
+
+int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *req)
+{
+	DECLARE_COMPLETION(complete);
+
+	req->done_data = &complete;
+	req->done = mmc_wait_done;
+
+	mmc_start_request(host, req);
+
+	wait_for_completion(&complete);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(mmc_wait_for_req);
+
+/**
+ *	mmc_wait_for_cmd - start a command and wait for completion
+ *	@host: MMC host to start command
+ *	@cmd: MMC command to start
+ *	@retries: maximum number of retries
+ *
+ *	Start a new MMC command for a host, and wait for the command
+ *	to complete.  Return any error that occurred while the command
+ *	was executing.  Do not attempt to parse the response.
+ */
+int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
+{
+	struct mmc_request req;
+
+	BUG_ON(host->card_busy == NULL);
+
+	memset(&req, 0, sizeof(struct mmc_request));
+
+	memset(cmd->resp, 0, sizeof(cmd->resp));
+	cmd->retries = retries;
+
+	req.cmd = cmd;
+	cmd->data = NULL;
+
+	mmc_wait_for_req(host, &req);
+
+	return cmd->error;
+}
+
+EXPORT_SYMBOL(mmc_wait_for_cmd);
+
+
+
+/**
+ *	__mmc_claim_host - exclusively claim a host
+ *	@host: mmc host to claim
+ *	@card: mmc card to claim host for
+ *
+ *	Claim a host for a set of operations.  If a valid card
+ *	is passed and this wasn't the last card selected, select
+ *	the card before returning.
+ *
+ *	Note: you should use mmc_card_claim_host or mmc_claim_host.
+ */
+int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int err = 0;
+
+	add_wait_queue(&host->wq, &wait);
+	spin_lock_irqsave(&host->lock, flags);
+	while (1) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		if (host->card_busy == NULL)
+			break;
+		spin_unlock_irqrestore(&host->lock, flags);
+		schedule();
+		spin_lock_irqsave(&host->lock, flags);
+	}
+	set_current_state(TASK_RUNNING);
+	host->card_busy = card;
+	spin_unlock_irqrestore(&host->lock, flags);
+	remove_wait_queue(&host->wq, &wait);
+
+	if (card != (void *)-1 && host->card_selected != card) {
+		struct mmc_command cmd;
+
+		host->card_selected = card;
+
+		cmd.opcode = MMC_SELECT_CARD;
+		cmd.arg = card->rca << 16;
+		cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+
+		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+	}
+
+	return err;
+}
+
+EXPORT_SYMBOL(__mmc_claim_host);
+
+/**
+ *	mmc_release_host - release a host
+ *	@host: mmc host to release
+ *
+ *	Release a MMC host, allowing others to claim the host
+ *	for their operations.
+ */
+void mmc_release_host(struct mmc_host *host)
+{
+	unsigned long flags;
+
+	BUG_ON(host->card_busy == NULL);
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->card_busy = NULL;
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	wake_up(&host->wq);
+}
+
+EXPORT_SYMBOL(mmc_release_host);
+
+static void mmc_deselect_cards(struct mmc_host *host)
+{
+	struct mmc_command cmd;
+
+	/*
+	 * Ensure that no card is selected.
+	 */
+	if (host->card_selected) {
+		host->card_selected = NULL;
+
+		cmd.opcode = MMC_SELECT_CARD;
+		cmd.arg = 0;
+		cmd.flags = MMC_RSP_NONE;
+
+		mmc_wait_for_cmd(host, &cmd, 0);
+	}
+}
+
+
+static inline void mmc_delay(unsigned int ms)
+{
+	if (ms < HZ / 1000) {
+		yield();
+		mdelay(ms);
+	} else {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(ms * HZ / 1000);
+	}
+}
+
+static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
+{
+	int bit;
+
+	/*
+	 * Mask off any voltages we don't support
+	 */
+	ocr &= host->ocr_avail;
+
+	/*
+	 * Select the lowest voltage
+	 */
+	bit = ffs(ocr);
+	if (bit) {
+		bit -= 1;
+
+		ocr = 1 << bit;
+
+		host->ios.vdd = mmc_ocr_bit_to_vdd[bit];
+		host->ops->set_ios(host, &host->ios);
+	} else {
+		ocr = 0;
+	}
+
+	return ocr;
+}
+
+static void mmc_decode_cid(struct mmc_cid *cid, u32 *resp)
+{
+	memset(cid, 0, sizeof(struct mmc_cid));
+
+	cid->manfid	  = resp[0] >> 8;
+	cid->prod_name[0] = resp[0];
+	cid->prod_name[1] = resp[1] >> 24;
+	cid->prod_name[2] = resp[1] >> 16;
+	cid->prod_name[3] = resp[1] >> 8;
+	cid->prod_name[4] = resp[1];
+	cid->prod_name[5] = resp[2] >> 24;
+	cid->prod_name[6] = resp[2] >> 16;
+	cid->prod_name[7] = '\0';
+	cid->hwrev	  = (resp[2] >> 12) & 15;
+	cid->fwrev	  = (resp[2] >> 8) & 15;
+	cid->serial	  = (resp[2] & 255) << 16 | (resp[3] >> 16);
+	cid->month	  = (resp[3] >> 12) & 15;
+	cid->year	  = (resp[3] >> 8) & 15;
+}
+
+static void mmc_decode_csd(struct mmc_csd *csd, u32 *resp)
+{
+	unsigned int e, m;
+
+	csd->mmc_prot	 = (resp[0] >> 26) & 15;
+	m = (resp[0] >> 19) & 15;
+	e = (resp[0] >> 16) & 7;
+	csd->tacc_ns	 = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
+	csd->tacc_clks	 = ((resp[0] >> 8) & 255) * 100;
+
+	m = (resp[0] >> 3) & 15;
+	e = resp[0] & 7;
+	csd->max_dtr	  = tran_exp[e] * tran_mant[m];
+	csd->cmdclass	  = (resp[1] >> 20) & 0xfff;
+
+	e = (resp[2] >> 15) & 7;
+	m = (resp[1] << 2 | resp[2] >> 30) & 0x3fff;
+	csd->capacity	  = (1 + m) << (e + 2);
+
+	csd->read_blkbits = (resp[1] >> 16) & 15;
+}
+
+/*
+ * Locate a MMC card on this MMC host given a CID.
+ */
+static struct mmc_card *
+mmc_find_card(struct mmc_host *host, struct mmc_cid *cid)
+{
+	struct mmc_card *card;
+
+	list_for_each_entry(card, &host->cards, node) {
+		if (memcmp(&card->cid, cid, sizeof(struct mmc_cid)) == 0)
+			return card;
+	}
+	return NULL;
+}
+
+/*
+ * Allocate a new MMC card, and assign a unique RCA.
+ */
+static struct mmc_card *
+mmc_alloc_card(struct mmc_host *host, struct mmc_cid *cid, unsigned int *frca)
+{
+	struct mmc_card *card, *c;
+	unsigned int rca = *frca;
+
+	card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
+	if (!card)
+		return ERR_PTR(-ENOMEM);
+
+	mmc_init_card(card, host);
+	memcpy(&card->cid, cid, sizeof(struct mmc_cid));
+
+ again:
+	list_for_each_entry(c, &host->cards, node)
+		if (c->rca == rca) {
+			rca++;
+			goto again;
+		}
+
+	card->rca = rca;
+
+	*frca = rca;
+
+	return card;
+}
+
+/*
+ * Apply power to the MMC stack.
+ */
+static void mmc_power_up(struct mmc_host *host)
+{
+	struct mmc_command cmd;
+	int bit = fls(host->ocr_avail) - 1;
+
+	host->ios.vdd = mmc_ocr_bit_to_vdd[bit];
+	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+	host->ios.power_mode = MMC_POWER_UP;
+	host->ops->set_ios(host, &host->ios);
+
+	mmc_delay(1);
+
+	host->ios.clock = host->f_min;
+	host->ios.power_mode = MMC_POWER_ON;
+	host->ops->set_ios(host, &host->ios);
+
+	mmc_delay(2);
+
+	cmd.opcode = MMC_GO_IDLE_STATE;
+	cmd.arg = 0;
+	cmd.flags = MMC_RSP_NONE;
+
+	mmc_wait_for_cmd(host, &cmd, 0);
+}
+
+static void mmc_power_off(struct mmc_host *host)
+{
+	host->ios.clock = 0;
+	host->ios.vdd = 0;
+	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+	host->ios.power_mode = MMC_POWER_OFF;
+	host->ops->set_ios(host, &host->ios);
+}
+
+static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+	struct mmc_command cmd;
+	int i, err = 0;
+
+	cmd.opcode = MMC_SEND_OP_COND;
+	cmd.arg = ocr;
+	cmd.flags = MMC_RSP_SHORT;
+
+	for (i = 100; i; i--) {
+		err = mmc_wait_for_cmd(host, &cmd, 0);
+		if (err != MMC_ERR_NONE)
+			break;
+
+		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+			break;
+
+		err = MMC_ERR_TIMEOUT;
+	}
+
+	if (rocr)
+		*rocr = cmd.resp[0];
+
+	return err;
+}
+
+/*
+ * Discover cards by requesting their CID.  If this command
+ * times out, it is not an error; there are no further cards
+ * to be discovered.  Add new cards to the list.
+ *
+ * Create a mmc_card entry for each discovered card, assigning
+ * it an RCA, and save the CID.
+ */
+static void mmc_discover_cards(struct mmc_host *host)
+{
+	struct mmc_card *card;
+	unsigned int first_rca = 1, err;
+
+	while (1) {
+		struct mmc_command cmd;
+		struct mmc_cid cid;
+
+		/*
+		 * Read CID
+		 */
+		cmd.opcode = MMC_ALL_SEND_CID;
+		cmd.arg = 0;
+		cmd.flags = MMC_RSP_LONG | MMC_RSP_CRC;
+
+		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+		if (err == MMC_ERR_TIMEOUT) {
+			err = MMC_ERR_NONE;
+			break;
+		}
+		if (err != MMC_ERR_NONE) {
+			printk(KERN_ERR "MMC: mmc%d error requesting CID: %d\n",
+				host->host_num, err);
+			break;
+		}
+
+		mmc_decode_cid(&cid, cmd.resp);
+
+		card = mmc_find_card(host, &cid);
+		if (!card) {
+			card = mmc_alloc_card(host, &cid, &first_rca);
+			if (IS_ERR(card)) {
+				err = PTR_ERR(card);
+				break;
+			}
+			list_add(&card->node, &host->cards);
+		}
+
+		card->state &= ~MMC_STATE_DEAD;
+
+		cmd.opcode = MMC_SET_RELATIVE_ADDR;
+		cmd.arg = card->rca << 16;
+		cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+
+		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+		if (err != MMC_ERR_NONE)
+			card->state |= MMC_STATE_DEAD;
+	}
+}
+
+static void mmc_read_csds(struct mmc_host *host)
+{
+	struct mmc_card *card;
+
+	list_for_each_entry(card, &host->cards, node) {
+		struct mmc_command cmd;
+		int err;
+
+		if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
+			continue;
+
+		cmd.opcode = MMC_SEND_CSD;
+		cmd.arg = card->rca << 16;
+		cmd.flags = MMC_RSP_LONG | MMC_RSP_CRC;
+
+		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+		if (err != MMC_ERR_NONE) {
+			card->state |= MMC_STATE_DEAD;
+			continue;
+		}
+
+		mmc_decode_csd(&card->csd, cmd.resp);
+	}
+}
+
+static unsigned int mmc_calculate_clock(struct mmc_host *host)
+{
+	struct mmc_card *card;
+	unsigned int max_dtr = host->f_max;
+
+	list_for_each_entry(card, &host->cards, node)
+		if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr)
+			max_dtr = card->csd.max_dtr;
+
+	DBG("MMC: selected %d.%03dMHz transfer rate\n",
+	    max_dtr / 1000000, (max_dtr / 1000) % 1000);
+
+	return max_dtr;
+}
+
+/*
+ * Check whether cards we already know about are still present.
+ * We do this by requesting status, and checking whether a card
+ * responds.
+ *
+ * A request for status does not cause a state change in data
+ * transfer mode.
+ */
+static void mmc_check_cards(struct mmc_host *host)
+{
+	struct list_head *l, *n;
+
+	mmc_deselect_cards(host);
+
+	list_for_each_safe(l, n, &host->cards) {
+		struct mmc_card *card = mmc_list_to_card(l);
+		struct mmc_command cmd;
+		int err;
+
+		cmd.opcode = MMC_SEND_STATUS;
+		cmd.arg = card->rca << 16;
+		cmd.flags = MMC_RSP_LONG | MMC_RSP_CRC;
+
+		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+		if (err == MMC_ERR_NONE)
+			continue;
+
+		/*
+		 * Ok, we believe this card has been removed.
+		 */
+		card->state |= MMC_STATE_DEAD;
+	}
+}
+
+static void mmc_setup(struct mmc_host *host)
+{
+	if (host->ios.power_mode != MMC_POWER_ON) {
+		int err;
+		u32 ocr;
+
+		mmc_power_up(host);
+
+		err = mmc_send_op_cond(host, 0, &ocr);
+		if (err != MMC_ERR_NONE)
+			return;
+
+		host->ocr = mmc_select_voltage(host, ocr);
+	} else {
+		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+		host->ios.clock = host->f_min;
+		host->ops->set_ios(host, &host->ios);
+
+		/*
+		 * We should remember the OCR mask from the existing
+		 * cards, and detect the new cards OCR mask, combine
+		 * the two and re-select the VDD.  However, if we do
+		 * change VDD, we should do an idle, and then do a
+		 * full re-initialisation.  We would need to notify
+		 * drivers so that they can re-setup the cards as
+		 * well, while keeping their queues at bay.
+		 *
+		 * For the moment, we take the easy way out - if the
+		 * new cards don't like our currently selected VDD,
+		 * they drop off the bus.
+		 */
+	}
+
+	if (host->ocr == 0)
+		return;
+
+	/*
+	 * Send the selected OCR multiple times... until the cards
+	 * all get the idea that they should be ready for CMD2.
+	 * (My SanDisk card seems to need this.)
+	 */
+	mmc_send_op_cond(host, host->ocr, NULL);
+
+	mmc_discover_cards(host);
+
+	/*
+	 * Ok, now switch to push-pull mode.
+	 */
+	host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+	host->ops->set_ios(host, &host->ios);
+
+	mmc_read_csds(host);
+}
+
+
+/**
+ *	mmc_detect_change - process change of state on a MMC socket
+ *	@host: host which changed state.
+ *
+ *	All we know is that card(s) have been inserted or removed
+ *	from the socket(s).  We don't know which socket or cards.
+ */
+void mmc_detect_change(struct mmc_host *host)
+{
+	struct list_head *l, *n;
+
+	mmc_claim_host(host);
+
+	if (host->ios.power_mode == MMC_POWER_ON)
+		mmc_check_cards(host);
+
+	mmc_setup(host);
+
+	if (!list_empty(&host->cards)) {
+		/*
+		 * (Re-)calculate the fastest clock rate which the
+		 * attached cards and the host support.
+		 */
+		host->ios.clock = mmc_calculate_clock(host);
+		host->ops->set_ios(host, &host->ios);
+	}
+
+	mmc_release_host(host);
+
+	list_for_each_safe(l, n, &host->cards) {
+		struct mmc_card *card = mmc_list_to_card(l);
+
+		/*
+		 * If this is a new and good card, register it.
+		 */
+		if (!mmc_card_present(card) && !mmc_card_dead(card)) {
+			if (mmc_register_card(card))
+				card->state |= MMC_STATE_DEAD;
+			else
+				card->state |= MMC_STATE_PRESENT;
+		}
+
+		/*
+		 * If this card is dead, destroy it.
+		 */
+		if (mmc_card_dead(card)) {
+			list_del(&card->node);
+			mmc_remove_card(card);
+		}
+	}
+
+	/*
+	 * If we discover that there are no cards on the
+	 * bus, turn off the clock and power down.
+	 */
+	if (list_empty(&host->cards))
+		mmc_power_off(host);
+}
+
+EXPORT_SYMBOL(mmc_detect_change);
+
+
+/**
+ *	mmc_init_host - initialise the per-host structure.
+ *	@host: mmc host
+ *
+ *	Initialise the per-host structure.
+ */
+int mmc_init_host(struct mmc_host *host)
+{
+	static unsigned int host_num;
+
+	memset(host, 0, sizeof(struct mmc_host));
+
+	host->host_num = host_num++;
+
+	spin_lock_init(&host->lock);
+	init_waitqueue_head(&host->wq);
+	INIT_LIST_HEAD(&host->cards);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(mmc_init_host);
+
+/**
+ *	mmc_add_host - initialise host hardware
+ *	@host: mmc host
+ */
+int mmc_add_host(struct mmc_host *host)
+{
+	mmc_power_off(host);
+	mmc_detect_change(host);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(mmc_add_host);
+
+/**
+ *	mmc_remove_host - remove host hardware
+ *	@host: mmc host
+ *
+ *	Unregister and remove all cards associated with this host,
+ *	and power down the MMC bus.
+ */
+void mmc_remove_host(struct mmc_host *host)
+{
+	struct list_head *l, *n;
+
+	list_for_each_safe(l, n, &host->cards) {
+		struct mmc_card *card = mmc_list_to_card(l);
+
+		mmc_remove_card(card);
+	}
+
+	mmc_power_off(host);
+}
+
+EXPORT_SYMBOL(mmc_remove_host);
+
+#ifdef CONFIG_PM
+
+/**
+ *	mmc_suspend_host - suspend a host
+ *	@host: mmc host
+ *	@state: suspend mode (PM_SUSPEND_xxx)
+ */
+int mmc_suspend_host(struct mmc_host *host, u32 state)
+{
+	mmc_claim_host(host);
+	mmc_deselect_cards(host);
+	mmc_power_off(host);
+	mmc_release_host(host);
+
+	return 0;
+}
+
+/**
+ *	mmc_resume_host - resume a previously suspended host
+ *	@host: mmc host
+ */
+int mmc_resume_host(struct mmc_host *host)
+{
+	mmc_detect_change(host);
+
+	return 0;
+}
+
+
+#else /* CONFIG_PM is not set */
+
+int mmc_suspend_host(struct mmc_host *host, u32 state) { return 0; }
+int mmc_resume_host(struct mmc_host *host) { return 0; }
+
+#endif
+
+EXPORT_SYMBOL(mmc_suspend_host);
+EXPORT_SYMBOL(mmc_resume_host);
+
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/media/mmc/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,21 @@
+#
+# Makefile for the kernel mmc device drivers.
+#
+
+#
+# Core
+#
+obj-$(CONFIG_MMC)		+= mmc_core.o
+
+#
+# Media drivers
+#
+obj-$(CONFIG_MMC_BLOCK)		+= mmc_block.o
+
+#
+# Host drivers
+#
+obj-$(CONFIG_MMC_ARMMMCI)	+= mmci.o
+obj-$(CONFIG_MMC_PXA)		+= pxamci.o
+
+mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
--- linux-2.6.5/drivers/media/Makefile~heh	2004-04-03 22:38:15.000000000 -0500
+++ linux-2.6.5/drivers/media/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -3,3 +3,6 @@
 #
 
 obj-y        := video/ radio/ dvb/ common/
+
+obj-$(CONFIG_MMC)	+= mmc/
+
--- linux-2.6.5/drivers/serial/Kconfig~heh	2004-04-03 22:38:15.000000000 -0500
+++ linux-2.6.5/drivers/serial/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -315,6 +315,29 @@
 	  your boot loader (lilo or loadlin) about how to pass options to the
 	  kernel at boot time.)
 
+config SERIAL_PXA
+	bool "PXA serial port support"
+	depends on ARM && ARCH_PXA
+	select SERIAL_CORE
+	help
+	  If you have a machine based on an Intel XScale PXA2xx CPU you
+	  can enable its onboard serial ports by enabling this option.
+
+config SERIAL_PXA_CONSOLE
+	bool "Console on PXA serial port"
+	depends on SERIAL_PXA
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have enabled the serial port on the Intel XScale PXA
+	  CPU you can make it the console by answering Y to this option.
+
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttySA0". (Try "man bootparam" or see the documentation of
+	  your boot loader (lilo or loadlin) about how to pass options to the
+	  kernel at boot time.)
+
 config SERIAL_SA1100
 	bool "SA1100 serial port support"
 	depends on ARM && ARCH_SA1100
--- linux-2.6.5/drivers/serial/pxa.c~heh	2004-04-03 22:38:16.000000000 -0500
+++ linux-2.6.5/drivers/serial/pxa.c	2004-04-30 20:57:36.000000000 -0400
@@ -294,7 +294,6 @@
 	unsigned char status;
 	unsigned int ret;
 
-return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
 	spin_lock_irqsave(&up->port.lock, flags);
 	status = serial_in(up, UART_MSR);
 	spin_unlock_irqrestore(&up->port.lock, flags);
@@ -800,6 +799,21 @@
 		.ops		= &serial_pxa_pops,
 		.line		= 2,
 	},
+  }, {  /* HWUART */
+	.name	= "HWUART",
+	.cken	= CKEN4_HWUART,
+	.port = {
+		.type		= PORT_PXA,
+		.iotype		= SERIAL_IO_MEM,
+		.membase	= (void *)&HWUART,
+		.mapbase	= __PREG(HWUART),
+		.irq		= IRQ_HWUART,
+		.uartclk	= 921600 * 16,
+		.fifosize	= 64,
+		.flags		= ASYNC_SKIP_TEST,
+		.ops		= &serial_pxa_pops,
+		.line		= 3,
+	},
   }
 };
 
--- linux-2.6.5/drivers/serial/sa1100.c~heh	2004-04-03 22:36:24.000000000 -0500
+++ linux-2.6.5/drivers/serial/sa1100.c	2004-04-30 20:57:36.000000000 -0400
@@ -448,6 +448,15 @@
 	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
 
 	/*
+	 * If we don't support modem control lines, don't allow
+	 * these to be set.
+	 */
+	if (0) {
+		termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
+		termios->c_cflag |= CLOCAL;
+	}
+
+	/*
 	 * We only support CS7 and CS8.
 	 */
 	while ((termios->c_cflag & CSIZE) != CS7 &&
@@ -894,6 +903,7 @@
 			if (sa1100_ports[i].port.mapbase != res->start)
 				continue;
 
+			sa1100_ports[i].port.dev = _dev;
 			uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port);
 			dev_set_drvdata(_dev, &sa1100_ports[i]);
 			break;
--- linux-2.6.5/drivers/mtd/mtd_blkdevs.c~heh	2004-04-03 22:36:14.000000000 -0500
+++ linux-2.6.5/drivers/mtd/mtd_blkdevs.c	2004-04-30 20:57:36.000000000 -0400
@@ -81,7 +81,7 @@
 	struct request_queue *rq = tr->blkcore_priv->rq;
 
 	/* we might get involved when memory gets low, so use PF_MEMALLOC */
-	current->flags |= PF_MEMALLOC;
+	current->flags |= PF_MEMALLOC|PF_IOTHREAD;
 
 	daemonize("%sd", tr->name);
 
--- linux-2.6.5/drivers/mtd/devices/doc1000.c~heh	2004-04-03 22:37:36.000000000 -0500
+++ linux-2.6.5/drivers/mtd/devices/doc1000.c	2004-04-30 20:57:36.000000000 -0400
@@ -1,6 +1,6 @@
 /*======================================================================
 
-  $Id: doc1000.c,v 1.15 2001/10/02 15:05:13 dwmw2 Exp $
+  $Id: doc1000.c,v 1.16 2001/12/28 22:45:17 dwmw2 Exp $
 
 ======================================================================*/
 
@@ -20,6 +20,7 @@
 #include <linux/ioctl.h>
 #include <asm/io.h>
 #include <asm/system.h>
+#include <asm/segment.h>
 #include <stdarg.h>
 #include <linux/delay.h>
 #include <linux/init.h>
@@ -482,7 +483,7 @@
 			else
 				priv->devstat[erase->dev] = erase->state = MTD_ERASE_PENDING;
 		}
-		else if (erase->time + erase_timeout < jiffies)
+		else if (time_after(jiffies, erase->time + erase_timeout))
 		{
 			printk("Flash erase timed out. The world is broken.\n");
 
--- linux-2.6.5/drivers/mtd/mtdconcat.c~heh	2004-04-03 22:37:37.000000000 -0500
+++ linux-2.6.5/drivers/mtd/mtdconcat.c	2004-04-30 20:57:36.000000000 -0400
@@ -7,7 +7,7 @@
  *
  * This code is GPL
  *
- * $Id: mtdconcat.c,v 1.4 2003/03/07 17:44:59 rkaiser Exp $
+ * $Id: mtdconcat.c,v 1.7 2003/06/29 21:26:34 dwmw2 Exp $
  */
 
 #include <linux/module.h>
@@ -26,7 +26,7 @@
  */
 struct mtd_concat {
 	struct mtd_info mtd;
-	int             num_subdev;
+	int num_subdev;
 	struct mtd_info **subdev;
 };
 
@@ -37,21 +37,20 @@
 #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev)	\
 	((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *)))
 
-
 /*
  * Given a pointer to the MTD object in the mtd_concat structure,
  * we can retrieve the pointer to that structure with this macro.
  */
 #define CONCAT(x)  ((struct mtd_concat *)(x))
 
-	
 /* 
  * MTD methods which look up the relevant subdevice, translate the
  * effective address and pass through to the subdevice.
  */
 
-static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, 
-			size_t *retlen, u_char *buf)
+static int
+concat_read(struct mtd_info *mtd, loff_t from, size_t len,
+	    size_t * retlen, u_char * buf)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int err = -EINVAL;
@@ -59,43 +58,43 @@
 
 	*retlen = 0;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size, retsize;
 
-		if (from >= subdev->size)
-		{   /* Not destined for this subdev */
-			size  = 0;
+		if (from >= subdev->size) {
+			/* Not destined for this subdev */
+			size = 0;
 			from -= subdev->size;
+			continue;
 		}
+		if (from + len > subdev->size)
+			/* First part goes into this subdev */
+			size = subdev->size - from;
 		else
-		{
-			if (from + len > subdev->size)
-				size = subdev->size - from; /* First part goes into this subdev */
-			else
-				size = len; /* Entire transaction goes into this subdev */
+			/* Entire transaction goes into this subdev */
+			size = len;
 
-			err = subdev->read(subdev, from, size, &retsize, buf);
+		err = subdev->read(subdev, from, size, &retsize, buf);
 
-			if(err)
-				break;
+		if (err)
+			break;
 
-			*retlen += retsize;
-			len -= size;
-			if(len == 0)
-				break;
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
 
-			err = -EINVAL;
-			buf += size;
-			from = 0;
-		}
+		err = -EINVAL;
+		buf += size;
+		from = 0;
 	}
 	return err;
 }
 
-static int concat_write (struct mtd_info *mtd, loff_t to, size_t len,
-			size_t *retlen, const u_char *buf)
+static int
+concat_write(struct mtd_info *mtd, loff_t to, size_t len,
+	     size_t * retlen, const u_char * buf)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int err = -EINVAL;
@@ -106,46 +105,44 @@
 
 	*retlen = 0;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size, retsize;
 
-		if (to >= subdev->size)
-		{
-			size  = 0;
+		if (to >= subdev->size) {
+			size = 0;
 			to -= subdev->size;
+			continue;
 		}
+		if (to + len > subdev->size)
+			size = subdev->size - to;
 		else
-		{
-			if (to + len > subdev->size)
-				size = subdev->size - to;
-			else
-				size = len;
+			size = len;
 
-			if (!(subdev->flags & MTD_WRITEABLE))
-				err = -EROFS;
-			else
-				err = subdev->write(subdev, to, size, &retsize, buf);
+		if (!(subdev->flags & MTD_WRITEABLE))
+			err = -EROFS;
+		else
+			err = subdev->write(subdev, to, size, &retsize, buf);
 
-			if(err)
-				break;
+		if (err)
+			break;
 
-			*retlen += retsize;
-			len -= size;
-			if(len == 0)
-				break;
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
 
-			err = -EINVAL;
-			buf += size;
-			to = 0;
-		}
+		err = -EINVAL;
+		buf += size;
+		to = 0;
 	}
 	return err;
 }
 
-static int concat_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
-            size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel)
+static int
+concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t * retlen, u_char * buf, u_char * eccbuf,
+		struct nand_oobinfo *oobsel)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int err = -EINVAL;
@@ -153,53 +150,56 @@
 
 	*retlen = 0;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size, retsize;
-        
-		if (from >= subdev->size)
-		{   /* Not destined for this subdev */
-			size  = 0;
+
+		if (from >= subdev->size) {
+			/* Not destined for this subdev */
+			size = 0;
 			from -= subdev->size;
+			continue;
 		}
+
+		if (from + len > subdev->size)
+			/* First part goes into this subdev */
+			size = subdev->size - from;
 		else
-		{
-			if (from + len > subdev->size)
-				size = subdev->size - from; /* First part goes into this subdev */
-			else
-				size = len; /* Entire transaction goes into this subdev */
-            
-            if (subdev->read_ecc)
-    			err = subdev->read_ecc(subdev, from, size, &retsize, buf, eccbuf, oobsel);
-            else
-                err = -EINVAL;
+			/* Entire transaction goes into this subdev */
+			size = len;
 
-			if(err)
-				break;
+		if (subdev->read_ecc)
+			err = subdev->read_ecc(subdev, from, size,
+					       &retsize, buf, eccbuf, oobsel);
+		else
+			err = -EINVAL;
 
-			*retlen += retsize;
-			len -= size;
-			if(len == 0)
-				break;
+		if (err)
+			break;
 
-			err = -EINVAL;
-			buf += size;
-            if (eccbuf)
-            {
-                eccbuf += subdev->oobsize;
-                /* in nand.c at least, eccbufs are tagged with 2 (int)eccstatus',
-                   we must account for these */
-                eccbuf += 2 * (sizeof(int)); 
-            }
-			from = 0;
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		if (eccbuf) {
+			eccbuf += subdev->oobsize;
+			/* in nand.c at least, eccbufs are
+			   tagged with 2 (int)eccstatus'; we
+			   must account for these */
+			eccbuf += 2 * (sizeof (int));
 		}
+		from = 0;
 	}
 	return err;
 }
 
-static int concat_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
-            size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel)
+static int
+concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
+		 size_t * retlen, const u_char * buf, u_char * eccbuf,
+		 struct nand_oobinfo *oobsel)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int err = -EINVAL;
@@ -210,50 +210,48 @@
 
 	*retlen = 0;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size, retsize;
-        
-		if (to >= subdev->size)
-		{
-			size  = 0;
+
+		if (to >= subdev->size) {
+			size = 0;
 			to -= subdev->size;
+			continue;
 		}
+		if (to + len > subdev->size)
+			size = subdev->size - to;
 		else
-		{
-			if (to + len > subdev->size)
-				size = subdev->size - to;
-			else
-				size = len;
+			size = len;
 
-			if (!(subdev->flags & MTD_WRITEABLE))
-				err = -EROFS;
-			else if (subdev->write_ecc)
-				err = subdev->write_ecc(subdev, to, size, &retsize, buf, eccbuf, oobsel);
-            else
-                err = -EINVAL;
+		if (!(subdev->flags & MTD_WRITEABLE))
+			err = -EROFS;
+		else if (subdev->write_ecc)
+			err = subdev->write_ecc(subdev, to, size,
+						&retsize, buf, eccbuf, oobsel);
+		else
+			err = -EINVAL;
 
-			if(err)
-				break;
+		if (err)
+			break;
 
-			*retlen += retsize;
-			len -= size;
-			if(len == 0)
-				break;
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
 
-			err = -EINVAL;
-			buf += size;
-            if (eccbuf)
-                eccbuf += subdev->oobsize;
-			to = 0;
-		}
+		err = -EINVAL;
+		buf += size;
+		if (eccbuf)
+			eccbuf += subdev->oobsize;
+		to = 0;
 	}
 	return err;
 }
 
-static int concat_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
-            size_t *retlen, u_char *buf)
+static int
+concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t * retlen, u_char * buf)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int err = -EINVAL;
@@ -261,46 +259,47 @@
 
 	*retlen = 0;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size, retsize;
-        
-		if (from >= subdev->size)
-		{   /* Not destined for this subdev */
-			size  = 0;
+
+		if (from >= subdev->size) {
+			/* Not destined for this subdev */
+			size = 0;
 			from -= subdev->size;
+			continue;
 		}
+		if (from + len > subdev->size)
+			/* First part goes into this subdev */
+			size = subdev->size - from;
 		else
-		{
-			if (from + len > subdev->size)
-				size = subdev->size - from; /* First part goes into this subdev */
-			else
-				size = len; /* Entire transaction goes into this subdev */
-            
-            if (subdev->read_oob)
-    			err = subdev->read_oob(subdev, from, size, &retsize, buf);
-            else
-                err = -EINVAL;
+			/* Entire transaction goes into this subdev */
+			size = len;
 
-			if(err)
-				break;
+		if (subdev->read_oob)
+			err = subdev->read_oob(subdev, from, size,
+					       &retsize, buf);
+		else
+			err = -EINVAL;
 
-			*retlen += retsize;
-			len -= size;
-			if(len == 0)
-				break;
+		if (err)
+			break;
 
-			err = -EINVAL;
-			buf += size;
-			from = 0;
-		}
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		from = 0;
 	}
 	return err;
 }
 
-static int concat_write_oob (struct mtd_info *mtd, loff_t to, size_t len, 
-            size_t *retlen, const u_char *buf)
+static int
+concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
+		 size_t * retlen, const u_char * buf)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int err = -EINVAL;
@@ -311,50 +310,46 @@
 
 	*retlen = 0;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size, retsize;
-        
-		if (to >= subdev->size)
-		{
-			size  = 0;
+
+		if (to >= subdev->size) {
+			size = 0;
 			to -= subdev->size;
+			continue;
 		}
+		if (to + len > subdev->size)
+			size = subdev->size - to;
 		else
-		{
-			if (to + len > subdev->size)
-				size = subdev->size - to;
-			else
-				size = len;
+			size = len;
 
-			if (!(subdev->flags & MTD_WRITEABLE))
-				err = -EROFS;
-			else if (subdev->write_oob)
-				err = subdev->write_oob(subdev, to, size, &retsize, buf);
-            else
-                err = -EINVAL;
+		if (!(subdev->flags & MTD_WRITEABLE))
+			err = -EROFS;
+		else if (subdev->write_oob)
+			err = subdev->write_oob(subdev, to, size, &retsize,
+						buf);
+		else
+			err = -EINVAL;
 
-			if(err)
-				break;
+		if (err)
+			break;
 
-			*retlen += retsize;
-			len -= size;
-			if(len == 0)
-				break;
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
 
-			err = -EINVAL;
-			buf += size;
-			to = 0;
-		}
+		err = -EINVAL;
+		buf += size;
+		to = 0;
 	}
 	return err;
 }
 
-
-static void concat_erase_callback (struct erase_info *instr)
+static void concat_erase_callback(struct erase_info *instr)
 {
-	wake_up((wait_queue_head_t *)instr->priv);
+	wake_up((wait_queue_head_t *) instr->priv);
 }
 
 static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
@@ -370,18 +365,18 @@
 
 	erase->mtd = mtd;
 	erase->callback = concat_erase_callback;
-	erase->priv = (unsigned long)&waitq;
-			
+	erase->priv = (unsigned long) &waitq;
+
 	/*
 	 * FIXME: Allow INTERRUPTIBLE. Which means
 	 * not having the wait_queue head on the stack.
 	 */
 	err = mtd->erase(mtd, erase);
-	if (!err)
-	{
+	if (!err) {
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		add_wait_queue(&waitq, &wait);
-		if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED)
+		if (erase->state != MTD_ERASE_DONE
+		    && erase->state != MTD_ERASE_FAILED)
 			schedule();
 		remove_wait_queue(&waitq, &wait);
 		set_current_state(TASK_RUNNING);
@@ -391,7 +386,7 @@
 	return err;
 }
 
-static int concat_erase (struct mtd_info *mtd, struct erase_info *instr)
+static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	struct mtd_info *subdev;
@@ -402,10 +397,10 @@
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
 
-	if(instr->addr > concat->mtd.size)
+	if (instr->addr > concat->mtd.size)
 		return -EINVAL;
 
-	if(instr->len + instr->addr > concat->mtd.size)
+	if (instr->len + instr->addr > concat->mtd.size)
 		return -EINVAL;
 
 	/*
@@ -414,23 +409,22 @@
 	 * region info rather than looking at each particular sub-device
 	 * in turn.
 	 */
-	if (!concat->mtd.numeraseregions)
-	{	/* the easy case: device has uniform erase block size */
-		if(instr->addr & (concat->mtd.erasesize - 1))
+	if (!concat->mtd.numeraseregions) {
+		/* the easy case: device has uniform erase block size */
+		if (instr->addr & (concat->mtd.erasesize - 1))
 			return -EINVAL;
-		if(instr->len & (concat->mtd.erasesize - 1))
+		if (instr->len & (concat->mtd.erasesize - 1))
 			return -EINVAL;
-	}
-	else
-	{	/* device has variable erase size */
-		struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions;
+	} else {
+		/* device has variable erase size */
+		struct mtd_erase_region_info *erase_regions =
+		    concat->mtd.eraseregions;
 
 		/*
 		 * Find the erase region where the to-be-erased area begins:
 		 */
-		for(i = 0; i < concat->mtd.numeraseregions && 
-		           instr->addr >= erase_regions[i].offset; i++)
-			;
+		for (i = 0; i < concat->mtd.numeraseregions &&
+		     instr->addr >= erase_regions[i].offset; i++) ;
 		--i;
 
 		/*
@@ -438,25 +432,26 @@
 		 * to-be-erased area begins. Verify that the starting
 		 * offset is aligned to this region's erase size:
 		 */
-		if (instr->addr & (erase_regions[i].erasesize-1))
+		if (instr->addr & (erase_regions[i].erasesize - 1))
 			return -EINVAL;
 
 		/*
 		 * now find the erase region where the to-be-erased area ends:
 		 */
-		for(; i < concat->mtd.numeraseregions && 
-		      (instr->addr + instr->len) >=  erase_regions[i].offset ; ++i)
-			;
+		for (; i < concat->mtd.numeraseregions &&
+		     (instr->addr + instr->len) >= erase_regions[i].offset;
+		     ++i) ;
 		--i;
 		/*
 		 * check if the ending offset is aligned to this region's erase size
 		 */
-		if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1))
+		if ((instr->addr + instr->len) & (erase_regions[i].erasesize -
+						  1))
 			return -EINVAL;
 	}
 
 	/* make a local copy of instr to avoid modifying the caller's struct */
-	erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL);
+	erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
 
 	if (!erase)
 		return -ENOMEM;
@@ -468,39 +463,40 @@
 	 * find the subdevice where the to-be-erased area begins, adjust
 	 * starting offset to be relative to the subdevice start
 	 */
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		subdev = concat->subdev[i];
-		if(subdev->size <= erase->addr)
+		if (subdev->size <= erase->addr)
 			erase->addr -= subdev->size;
 		else
 			break;
-    }
-	if(i >= concat->num_subdev)	/* must never happen since size */
-		BUG();					/* limit has been verified above */
+	}
+
+	/* must never happen since size limit has been verified above */
+	if (i >= concat->num_subdev)
+		BUG();
 
 	/* now do the erase: */
 	err = 0;
-	for(;length > 0; i++)	/* loop for all subevices affected by this request */
-	{
-		subdev = concat->subdev[i];		/* get current subdevice */
+	for (; length > 0; i++) {
+		/* loop for all subdevices affected by this request */
+		subdev = concat->subdev[i];	/* get current subdevice */
 
 		/* limit length to subdevice's size: */
-		if(erase->addr + length > subdev->size)
+		if (erase->addr + length > subdev->size)
 			erase->len = subdev->size - erase->addr;
 		else
 			erase->len = length;
 
-		if (!(subdev->flags & MTD_WRITEABLE))
-		{
+		if (!(subdev->flags & MTD_WRITEABLE)) {
 			err = -EROFS;
 			break;
 		}
 		length -= erase->len;
-		if ((err = concat_dev_erase(subdev, erase)))
-		{
-			if(err == -EINVAL)	/* sanity check: must never happen since */
-				BUG();			/* block alignment has been checked above */
+		if ((err = concat_dev_erase(subdev, erase))) {
+			/* sanity check: should never happen since
+			 * block alignment has been checked above */
+			if (err == -EINVAL)
+				BUG();
 			break;
 		}
 		/*
@@ -523,85 +519,79 @@
 	return 0;
 }
 
-static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
+static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int i, err = -EINVAL;
 
-	if ((len + ofs) > mtd->size) 
+	if ((len + ofs) > mtd->size)
 		return -EINVAL;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size;
 
-		if (ofs >= subdev->size)
-		{
-			size  = 0;
+		if (ofs >= subdev->size) {
+			size = 0;
 			ofs -= subdev->size;
+			continue;
 		}
+		if (ofs + len > subdev->size)
+			size = subdev->size - ofs;
 		else
-		{
-			if (ofs + len > subdev->size)
-				size = subdev->size - ofs;
-			else
-				size = len;
+			size = len;
 
-			err = subdev->lock(subdev, ofs, size);
+		err = subdev->lock(subdev, ofs, size);
 
-			if(err)
-				break;
+		if (err)
+			break;
 
-			len -= size;
-			if(len == 0)
-				break;
+		len -= size;
+		if (len == 0)
+			break;
 
-			err = -EINVAL;
-			ofs = 0;
-		}
+		err = -EINVAL;
+		ofs = 0;
 	}
+
 	return err;
 }
 
-static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
+static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int i, err = 0;
 
-	if ((len + ofs) > mtd->size) 
+	if ((len + ofs) > mtd->size)
 		return -EINVAL;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size;
 
-		if (ofs >= subdev->size)
-		{
-			size  = 0;
+		if (ofs >= subdev->size) {
+			size = 0;
 			ofs -= subdev->size;
+			continue;
 		}
+		if (ofs + len > subdev->size)
+			size = subdev->size - ofs;
 		else
-		{
-			if (ofs + len > subdev->size)
-				size = subdev->size - ofs;
-			else
-				size = len;
+			size = len;
 
-			err = subdev->unlock(subdev, ofs, size);
+		err = subdev->unlock(subdev, ofs, size);
 
-			if(err)
-				break;
+		if (err)
+			break;
 
-			len -= size;
-			if(len == 0)
-				break;
+		len -= size;
+		if (len == 0)
+			break;
 
-			err = -EINVAL;
-			ofs = 0;
-		}
+		err = -EINVAL;
+		ofs = 0;
 	}
+
 	return err;
 }
 
@@ -610,8 +600,7 @@
 	struct mtd_concat *concat = CONCAT(mtd);
 	int i;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		subdev->sync(subdev);
 	}
@@ -622,10 +611,9 @@
 	struct mtd_concat *concat = CONCAT(mtd);
 	int i, rc = 0;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
-		if((rc = subdev->suspend(subdev)) < 0)
+		if ((rc = subdev->suspend(subdev)) < 0)
 			return rc;
 	}
 	return rc;
@@ -636,8 +624,7 @@
 	struct mtd_concat *concat = CONCAT(mtd);
 	int i;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		subdev->resume(subdev);
 	}
@@ -649,11 +636,10 @@
  * stored to *new_dev upon success. This function does _not_
  * register any devices: this is the caller's responsibility.
  */
-struct mtd_info *mtd_concat_create(
-	struct mtd_info *subdev[],	/* subdevices to concatenate */
-	int num_devs,				/* number of subdevices      */
-	char *name)					/* name for the new device   */
-{
+struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to concatenate */
+				   int num_devs,	/* number of subdevices      */
+				   char *name)
+{				/* name for the new device   */
 	int i;
 	size_t size;
 	struct mtd_concat *concat;
@@ -661,94 +647,103 @@
 	int num_erase_region;
 
 	printk(KERN_NOTICE "Concatenating MTD devices:\n");
-	for(i = 0; i < num_devs; i++)
+	for (i = 0; i < num_devs; i++)
 		printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name);
 	printk(KERN_NOTICE "into device \"%s\"\n", name);
 
 	/* allocate the device structure */
 	size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
-	concat = kmalloc (size, GFP_KERNEL);
-	if(!concat)
-	{
-		printk ("memory allocation error while creating concatenated device \"%s\"\n",
-				name);
-			return NULL;
+	concat = kmalloc(size, GFP_KERNEL);
+	if (!concat) {
+		printk
+		    ("memory allocation error while creating concatenated device \"%s\"\n",
+		     name);
+		return NULL;
 	}
 	memset(concat, 0, size);
-	concat->subdev = (struct mtd_info **)(concat + 1);
+	concat->subdev = (struct mtd_info **) (concat + 1);
 
 	/*
 	 * Set up the new "super" device's MTD object structure, check for
 	 * incompatibilites between the subdevices.
 	 */
-	concat->mtd.type      = subdev[0]->type;
-	concat->mtd.flags     = subdev[0]->flags;
-	concat->mtd.size      = subdev[0]->size;
+	concat->mtd.type = subdev[0]->type;
+	concat->mtd.flags = subdev[0]->flags;
+	concat->mtd.size = subdev[0]->size;
 	concat->mtd.erasesize = subdev[0]->erasesize;
-	concat->mtd.oobblock  = subdev[0]->oobblock;
-	concat->mtd.oobsize   = subdev[0]->oobsize;
-	concat->mtd.ecctype   = subdev[0]->ecctype;
-	concat->mtd.eccsize   = subdev[0]->eccsize;
+	concat->mtd.oobblock = subdev[0]->oobblock;
+	concat->mtd.oobsize = subdev[0]->oobsize;
+	concat->mtd.ecctype = subdev[0]->ecctype;
+	concat->mtd.eccsize = subdev[0]->eccsize;
+	if (subdev[0]->read_ecc)
+		concat->mtd.read_ecc = concat_read_ecc;
+	if (subdev[0]->write_ecc)
+		concat->mtd.write_ecc = concat_write_ecc;
+	if (subdev[0]->read_oob)
+		concat->mtd.read_oob = concat_read_oob;
+	if (subdev[0]->write_oob)
+		concat->mtd.write_oob = concat_write_oob;
 
-	concat->subdev[0]   = subdev[0];
+	concat->subdev[0] = subdev[0];
 
-	for(i = 1; i < num_devs; i++)
-	{
-		if(concat->mtd.type != subdev[i]->type)
-		{
+	for (i = 1; i < num_devs; i++) {
+		if (concat->mtd.type != subdev[i]->type) {
 			kfree(concat);
-			printk ("Incompatible device type on \"%s\"\n", subdev[i]->name);
+			printk("Incompatible device type on \"%s\"\n",
+			       subdev[i]->name);
 			return NULL;
 		}
-		if(concat->mtd.flags != subdev[i]->flags)
-		{	/*
-			 * Expect all flags except MTD_WRITEABLE to be equal on
-			 * all subdevices.
+		if (concat->mtd.flags != subdev[i]->flags) {
+			/*
+			 * Expect all flags except MTD_WRITEABLE to be
+			 * equal on all subdevices.
 			 */
-			if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE)
-			{
+			if ((concat->mtd.flags ^ subdev[i]->
+			     flags) & ~MTD_WRITEABLE) {
 				kfree(concat);
-				printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name);
+				printk("Incompatible device flags on \"%s\"\n",
+				       subdev[i]->name);
 				return NULL;
-			}
-			else	/* if writeable attribute differs, make super device writeable */
-				concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE;
+			} else
+				/* if writeable attribute differs,
+				   make super device writeable */
+				concat->mtd.flags |=
+				    subdev[i]->flags & MTD_WRITEABLE;
 		}
 		concat->mtd.size += subdev[i]->size;
-		if(concat->mtd.oobblock != subdev[i]->oobblock ||
-		   concat->mtd.oobsize  != subdev[i]->oobsize  ||
-		   concat->mtd.ecctype  != subdev[i]->ecctype  ||
-		   concat->mtd.eccsize  != subdev[i]->eccsize)
-		{
+		if (concat->mtd.oobblock   !=  subdev[i]->oobblock ||
+		    concat->mtd.oobsize    !=  subdev[i]->oobsize ||
+		    concat->mtd.ecctype    !=  subdev[i]->ecctype ||
+		    concat->mtd.eccsize    !=  subdev[i]->eccsize ||
+		    !concat->mtd.read_ecc  != !subdev[i]->read_ecc ||
+		    !concat->mtd.write_ecc != !subdev[i]->write_ecc ||
+		    !concat->mtd.read_oob  != !subdev[i]->read_oob ||
+		    !concat->mtd.write_oob != !subdev[i]->write_oob) {
 			kfree(concat);
-			printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name);
+			printk("Incompatible OOB or ECC data on \"%s\"\n",
+			       subdev[i]->name);
 			return NULL;
 		}
 		concat->subdev[i] = subdev[i];
-		
+
 	}
 
-	concat->num_subdev  = num_devs;
-	concat->mtd.name    = name;
+	concat->num_subdev = num_devs;
+	concat->mtd.name = name;
 
 	/*
 	 * NOTE: for now, we do not provide any readv()/writev() methods
 	 *       because they are messy to implement and they are not
 	 *       used to a great extent anyway.
 	 */
-	concat->mtd.erase     = concat_erase;
-	concat->mtd.read      = concat_read;    
-	concat->mtd.write     = concat_write;
-	concat->mtd.read_ecc  = concat_read_ecc;    
-	concat->mtd.write_ecc = concat_write_ecc;
-	concat->mtd.read_oob  = concat_read_oob;    
-	concat->mtd.write_oob = concat_write_oob;
-	concat->mtd.sync      = concat_sync;
-	concat->mtd.lock      = concat_lock;
-	concat->mtd.unlock    = concat_unlock;
-	concat->mtd.suspend   = concat_suspend;
-	concat->mtd.resume    = concat_resume;
-
+	concat->mtd.erase = concat_erase;
+	concat->mtd.read = concat_read;
+	concat->mtd.write = concat_write;
+	concat->mtd.sync = concat_sync;
+	concat->mtd.lock = concat_lock;
+	concat->mtd.unlock = concat_unlock;
+	concat->mtd.suspend = concat_suspend;
+	concat->mtd.resume = concat_resume;
 
 	/*
 	 * Combine the erase block size info of the subdevices:
@@ -758,44 +753,44 @@
 	 */
 	max_erasesize = curr_erasesize = subdev[0]->erasesize;
 	num_erase_region = 1;
-	for(i = 0; i < num_devs; i++)
-	{
-		if(subdev[i]->numeraseregions == 0)
-		{	/* current subdevice has uniform erase size */
-			if(subdev[i]->erasesize != curr_erasesize)
-			{	/* if it differs from the last subdevice's erase size, count it */
+	for (i = 0; i < num_devs; i++) {
+		if (subdev[i]->numeraseregions == 0) {
+			/* current subdevice has uniform erase size */
+			if (subdev[i]->erasesize != curr_erasesize) {
+				/* if it differs from the last subdevice's erase size, count it */
 				++num_erase_region;
 				curr_erasesize = subdev[i]->erasesize;
-				if(curr_erasesize > max_erasesize)
+				if (curr_erasesize > max_erasesize)
 					max_erasesize = curr_erasesize;
 			}
-		}
-		else
-		{	/* current subdevice has variable erase size */
+		} else {
+			/* current subdevice has variable erase size */
 			int j;
-			for(j = 0; j < subdev[i]->numeraseregions; j++)
-			{	/* walk the list of erase regions, count any changes */
-				if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
-				{
+			for (j = 0; j < subdev[i]->numeraseregions; j++) {
+
+				/* walk the list of erase regions, count any changes */
+				if (subdev[i]->eraseregions[j].erasesize !=
+				    curr_erasesize) {
 					++num_erase_region;
-					curr_erasesize = subdev[i]->eraseregions[j].erasesize;
-					if(curr_erasesize > max_erasesize)
+					curr_erasesize =
+					    subdev[i]->eraseregions[j].
+					    erasesize;
+					if (curr_erasesize > max_erasesize)
 						max_erasesize = curr_erasesize;
 				}
 			}
 		}
 	}
 
-	if(num_erase_region == 1)
-	{	/*
+	if (num_erase_region == 1) {
+		/*
 		 * All subdevices have the same uniform erase size.
 		 * This is easy:
 		 */
 		concat->mtd.erasesize = curr_erasesize;
 		concat->mtd.numeraseregions = 0;
-	}
-	else
-	{	/*
+	} else {
+		/*
 		 * erase block size varies across the subdevices: allocate
 		 * space to store the data describing the variable erase regions
 		 */
@@ -804,13 +799,14 @@
 
 		concat->mtd.erasesize = max_erasesize;
 		concat->mtd.numeraseregions = num_erase_region;
-		concat->mtd.eraseregions = erase_region_p = kmalloc (
-		     num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
-		if(!erase_region_p)
-		{
+		concat->mtd.eraseregions = erase_region_p =
+		    kmalloc(num_erase_region *
+			    sizeof (struct mtd_erase_region_info), GFP_KERNEL);
+		if (!erase_region_p) {
 			kfree(concat);
-			printk ("memory allocation error while creating erase region list"
-			        " for device \"%s\"\n", name);
+			printk
+			    ("memory allocation error while creating erase region list"
+			     " for device \"%s\"\n", name);
 			return NULL;
 		}
 
@@ -820,46 +816,53 @@
 		 */
 		curr_erasesize = subdev[0]->erasesize;
 		begin = position = 0;
-		for(i = 0; i < num_devs; i++)
-		{
-			if(subdev[i]->numeraseregions == 0)
-			{	/* current subdevice has uniform erase size */
-				if(subdev[i]->erasesize != curr_erasesize)
-				{	/*
+		for (i = 0; i < num_devs; i++) {
+			if (subdev[i]->numeraseregions == 0) {
+				/* current subdevice has uniform erase size */
+				if (subdev[i]->erasesize != curr_erasesize) {
+					/*
 					 *  fill in an mtd_erase_region_info structure for the area
 					 *  we have walked so far:
 					 */
-					erase_region_p->offset    = begin;
-					erase_region_p->erasesize = curr_erasesize;
-					erase_region_p->numblocks = (position - begin) / curr_erasesize;
+					erase_region_p->offset = begin;
+					erase_region_p->erasesize =
+					    curr_erasesize;
+					erase_region_p->numblocks =
+					    (position - begin) / curr_erasesize;
 					begin = position;
 
 					curr_erasesize = subdev[i]->erasesize;
 					++erase_region_p;
 				}
 				position += subdev[i]->size;
-			}
-			else
-			{	/* current subdevice has variable erase size */
+			} else {
+				/* current subdevice has variable erase size */
 				int j;
-				for(j = 0; j < subdev[i]->numeraseregions; j++)
-				{	/* walk the list of erase regions, count any changes */
-					if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
-					{
-						erase_region_p->offset    = begin;
-						erase_region_p->erasesize = curr_erasesize;
-						erase_region_p->numblocks = (position - begin) / curr_erasesize;
+				for (j = 0; j < subdev[i]->numeraseregions; j++) {
+					/* walk the list of erase regions, count any changes */
+					if (subdev[i]->eraseregions[j].
+					    erasesize != curr_erasesize) {
+						erase_region_p->offset = begin;
+						erase_region_p->erasesize =
+						    curr_erasesize;
+						erase_region_p->numblocks =
+						    (position -
+						     begin) / curr_erasesize;
 						begin = position;
 
-						curr_erasesize = subdev[i]->eraseregions[j].erasesize;
+						curr_erasesize =
+						    subdev[i]->eraseregions[j].
+						    erasesize;
 						++erase_region_p;
 					}
-					position += subdev[i]->eraseregions[j].numblocks * curr_erasesize;
+					position +=
+					    subdev[i]->eraseregions[j].
+					    numblocks * curr_erasesize;
 				}
 			}
 		}
 		/* Now write the final entry */
-		erase_region_p->offset    = begin;
+		erase_region_p->offset = begin;
 		erase_region_p->erasesize = curr_erasesize;
 		erase_region_p->numblocks = (position - begin) / curr_erasesize;
 	}
@@ -874,16 +877,14 @@
 void mtd_concat_destroy(struct mtd_info *mtd)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
-	if(concat->mtd.numeraseregions)
+	if (concat->mtd.numeraseregions)
 		kfree(concat->mtd.eraseregions);
 	kfree(concat);
 }
 
-
 EXPORT_SYMBOL(mtd_concat_create);
 EXPORT_SYMBOL(mtd_concat_destroy);
 
-
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>");
 MODULE_DESCRIPTION("Generic support for concatenating of MTD devices");
--- linux-2.6.5/drivers/mtd/maps/Makefile~heh	2004-04-03 22:36:12.000000000 -0500
+++ linux-2.6.5/drivers/mtd/maps/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -17,12 +17,14 @@
 obj-$(CONFIG_MTD_ELAN_104NC)	+= elan-104nc.o
 obj-$(CONFIG_MTD_EPXA10DB)	+= epxa10db-flash.o
 obj-$(CONFIG_MTD_IQ80310)	+= iq80310.o
+obj-$(CONFIG_MTD_IQ80321)	+= iq80321.o
 obj-$(CONFIG_MTD_L440GX)	+= l440gx.o
 obj-$(CONFIG_MTD_AMD76XROM)	+= amd76xrom.o
 obj-$(CONFIG_MTD_ICH2ROM)	+= ich2rom.o
 obj-$(CONFIG_MTD_TSUNAMI)	+= tsunami_flash.o
 obj-$(CONFIG_MTD_LUBBOCK)	+= lubbock-flash.o
 obj-$(CONFIG_MTD_MBX860)	+= mbx860.o
+obj-$(CONFIG_MTD_NORA)		+= nora.o
 obj-$(CONFIG_MTD_CEIVA)		+= ceiva.o
 obj-$(CONFIG_MTD_OCTAGON)	+= octagon-5066.o
 obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o 
--- linux-2.6.5/drivers/mtd/maps/sa1100-flash.c~heh	2004-04-03 22:36:51.000000000 -0500
+++ linux-2.6.5/drivers/mtd/maps/sa1100-flash.c	2004-04-30 20:57:36.000000000 -0400
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
+#include <linux/device.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -887,6 +888,7 @@
 	int width;
 	void *vbase;
         void (*set_vpp)(struct map_info *, int);
+        char name[16];
 	struct map_info *map;
 	struct mtd_info *mtd;
 	struct resource *res;
@@ -925,6 +927,8 @@
 		}
 
 		sa[i].map = maps + i;
+		sa[i].map->name = sa[i].name;
+		sprintf(sa[i].name, "sa1100-%d", i);
 
 		sa[i].vbase = ioremap(sa[i].base, sa[i].size);
 		if (!sa[i].vbase) {
@@ -986,7 +990,7 @@
 			 */
 #ifdef CONFIG_MTD_CONCAT
 			*rmtd = mtd_concat_create(subdev, found,
-						  "sa1100 flash");
+						  "sa1100");
 			if (*rmtd == NULL)
 				ret = -ENXIO;
 #else
@@ -1044,13 +1048,9 @@
  *  - Is the MSC setup for flash (no -> failure)
  *  - Probe for flash
  */
-
-static struct map_info sa1100_probe_map __initdata = {
-	.name		= "SA1100-flash",
-};
-
-static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys)
+static void sa1100_probe_one_cs(unsigned int msc, unsigned long phys)
 {
+	struct map_info map;
 	struct mtd_info *mtd;
 
 	printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ",
@@ -1066,19 +1066,23 @@
 		return;
 	}
 
-	sa1100_probe_map.buswidth = msc & MSC_RBW ? 2 : 4;
-	sa1100_probe_map.size = SZ_1M;
-	sa1100_probe_map.phys = phys;
-	sa1100_probe_map.virt = (unsigned long)ioremap(phys, SZ_1M);
-	if (sa1100_probe_map.virt == 0)
+	memset(&map, 0, sizeof(map));
+
+	map.name = "Probe";
+	map.buswidth = msc & MSC_RBW ? 2 : 4;
+	map.size = SZ_1M;
+	map.phys = NO_XIP;
+	map.virt = (unsigned long)ioremap(phys, SZ_1M);
+	if (map.virt == 0)
 		goto fail;
-	simple_map_init(&sa1100_probe_map);
+
+	simple_map_init(&map);
 
 	/* Shame cfi_probe blurts out kernel messages... */
-	mtd = do_map_probe("cfi_probe", &sa1100_probe_map);
+	mtd = do_map_probe("cfi_probe", &map);
 	if (mtd)
 		map_destroy(mtd);
-	iounmap((void *)sa1100_probe_map.virt);
+	iounmap((void *)map.virt);
 
 	if (!mtd)
 		goto fail;
@@ -1090,7 +1094,7 @@
 	printk("failed\n");
 }
 
-static void __init sa1100_probe_flash(void)
+static void sa1100_probe_flash(void)
 {
 	printk(KERN_INFO "-- SA11xx Flash probe.  Please report results.\n");
 	sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS);
@@ -1321,10 +1325,9 @@
 		kfree(parsed_parts);
 }
 
-static struct mtd_info *mymtd;
-
-static int __init sa1100_mtd_init(void)
+static int __init sa1100_mtd_probe(struct device *dev)
 {
+	struct mtd_info *mtd;
 	int ret;
 	int nr;
 
@@ -1332,21 +1335,74 @@
 	if (nr < 0)
 		return nr;
 
-	ret = sa1100_setup_mtd(info, nr, &mymtd);
-	if (ret == 0)
-		sa1100_locate_partitions(mymtd);
+	ret = sa1100_setup_mtd(info, nr, &mtd);
+	if (ret == 0) {
+		sa1100_locate_partitions(mtd);
+		dev_set_drvdata(dev, mtd);
+	}
 
 	return ret;
 }
 
-static void __exit sa1100_mtd_cleanup(void)
+static int __exit sa1100_mtd_remove(struct device *dev)
 {
-	sa1100_destroy_mtd(info, mymtd);
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	sa1100_destroy_mtd(info, mtd);
 	sa1100_destroy_partitions();
+	return 0;
+}
+
+static int sa1100_mtd_suspend(struct device *dev, u32 level, u32 state)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	if (level == SUSPEND_SAVE_STATE)
+		mtd->suspend(mtd);
+	return 0;
+}
+
+static int sa1100_mtd_resume(struct device *dev, u32 level)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+	if (level == RESUME_RESTORE_STATE)
+		mtd->resume(mtd);
+	return 0;
+}
+
+static struct platform_device sa1100_mtd_device = {
+	.name		= "flash",
+	.id		= 0,
+};
+
+static struct device_driver sa1100_mtd_driver = {
+	.name		= "flash",
+	.bus		= &platform_bus_type,
+	.probe		= sa1100_mtd_probe,
+#ifdef MODULE
+	.remove		= sa1100_mtd_remove,
+#endif
+	.suspend	= sa1100_mtd_suspend,
+	.resume		= sa1100_mtd_resume,
+};
+
+static int __init sa1100_mtd_init(void)
+{
+	int ret = driver_register(&sa1100_mtd_driver);
+	if (ret == 0) {
+		ret = platform_device_register(&sa1100_mtd_device);
+		if (ret)
+			driver_unregister(&sa1100_mtd_driver);
+	}
+	return ret;
+}
+
+static void __exit sa1100_mtd_exit(void)
+{
+	platform_device_unregister(&sa1100_mtd_device);
+	driver_unregister(&sa1100_mtd_driver);
 }
 
 module_init(sa1100_mtd_init);
-module_exit(sa1100_mtd_cleanup);
+module_exit(sa1100_mtd_exit);
 
 MODULE_AUTHOR("Nicolas Pitre");
 MODULE_DESCRIPTION("SA1100 CFI map driver");
--- linux-2.6.5/drivers/mtd/maps/pcmciamtd.c~heh	2004-04-03 22:36:19.000000000 -0500
+++ linux-2.6.5/drivers/mtd/maps/pcmciamtd.c	2004-04-30 20:57:36.000000000 -0400
@@ -25,10 +25,9 @@
 #include <pcmcia/ds.h>
 
 #include <linux/mtd/map.h>
-#include <linux/mtd/mtd.h>
 
-#ifdef CONFIG_MTD_DEBUG
-static int debug = CONFIG_MTD_DEBUG_VERBOSE;
+#if 1 //def CONFIG_MTD_DEBUG
+static int debug = 5; //CONFIG_MTD_DEBUG_VERBOSE;
 MODULE_PARM(debug, "i");
 MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy");
 #undef DEBUG
@@ -88,7 +87,7 @@
 static int setvpp;
 
 /* Force card to be treated as FLASH, ROM or RAM */
-static int mem_type;
+static int mem_type = 1;
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
--- linux-2.6.5/drivers/mtd/maps/pci.c~heh	2004-04-03 22:36:56.000000000 -0500
+++ linux-2.6.5/drivers/mtd/maps/pci.c	2004-04-30 20:57:36.000000000 -0400
@@ -22,6 +22,8 @@
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
 
+#include <asm/io.h>
+
 struct map_pci_info;
 
 struct mtd_pci_info {
--- linux-2.6.5/drivers/input/Kconfig~heh	2004-04-03 22:36:18.000000000 -0500
+++ linux-2.6.5/drivers/input/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -80,7 +80,7 @@
 	  module will be called joydev.
 
 config INPUT_TSDEV
-	tristate "Touchscreen interface"
+	tristate "Compaq Touchscreen interface"
 	depends on INPUT
 	---help---
 	  Say Y here if you have an application that only can understand the
@@ -102,6 +102,10 @@
 	depends on INPUT_TSDEV
 	default "320"
 
+config INPUT_TSLIBDEV
+	tristate "TSLIB Touchscreen interface"
+	depends on INPUT
+
 config INPUT_EVDEV
 	tristate "Event interface"
 	depends on INPUT
--- linux-2.6.5/drivers/input/serio/Kconfig~heh	2004-04-03 22:37:07.000000000 -0500
+++ linux-2.6.5/drivers/input/serio/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -80,7 +80,7 @@
 
 config SERIO_RPCKBD
 	tristate "Acorn RiscPC keyboard controller"
-	depends on ARCH_ACORN && SERIO
+	depends on (ARCH_ACORN || ARCH_CLPS7500) && SERIO
 	default y
 	help
 	  Say Y here if you have the Acorn RiscPC and want to use an AT
@@ -89,6 +89,18 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called rpckbd.
 
+config SERIO_CLPS7500
+	tristate "CLPS7500 PS/2 mouse port controller"
+	depends on ARCH_CLPS7500 && SERIO
+	help
+	  Say Y here if you have CLPS7500 based hardware and want to use
+	  the mouse port.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module will be called clps7500ps2. If you want to compile it
+	  as a module, say M here and read <file:Documentation/modules.txt>.
+
 config SERIO_AMBAKMI
 	tristate "AMBA KMI keyboard controller"
 	depends on ARCH_INTEGRATOR && SERIO
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/input/serio/sa1100ir.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,90 @@
+/*
+ *  linux/drivers/input/serio/sa1100ir.c
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+
+
+
+struct sa1100_kbd {
+	struct serio	io;
+	void		*base;
+	int		irq;
+};
+
+static void sa1100ir_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct sa1100_kbd *kbd = dev_id;
+	unsigned int status;
+
+	do {
+		unsigned int flag, data;
+
+		status = readl(kbd->base + UTSR1);
+		if (!(status & UTSR1_RNE))
+			break;
+
+		flag = (status & UTSR1_FRE ? SERIO_FRAME : 0) |
+		       (status & UTSR1_PRE ? SERIO_PARITY : 0);
+
+		data = readl(kbd->base + UTDR);
+
+		serio_interrupt(&kbd->io, data, flag);
+	} while (1);
+
+	status = readl(kbd->base + UTSR0) & UTSR0_RID | UTSR0_RBB | UTSR0_REB;
+	if (status)
+		writel(status, kbd->base + UTSR0);
+}
+
+static int sa1100ir_kbd_open(struct serio *io)
+{
+	struct sa1100_kbd *kbd = io->driver;
+	int ret;
+
+	ret = request_irq(kbd->irq, sa1100ir_int, 0, kbd->io.phys, kbd);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void sa1100ir_kbd_close(struct serio *io)
+{
+	struct sa1100_kbd *kbd = io->driver;
+
+	free_irq(kbd->irq, kbd);
+}
+
+static struct sa1100_kbd sa1100_kbd = {
+	.io = {
+		.type	= 0,
+		.open	= sa1100ir_kbd_open,
+		.close	= sa1100ir_kbd_close,
+		.name	= "SA11x0 IR port",
+		.phys	= "sa11x0/ir",
+		.driver	= &sa1100_kbd,
+	},
+};
+
+static int __init sa1100_kbd_init(void)
+{
+	serio_register_port(&sa1100_kbd.io);
+}
+
+static void __exit sa1100_kbd_exit(void)
+{
+	serio_unregister_port(&sa1100_kbd.io);
+}
+
+module_init(sa1100_kbd_init);
+module_exit(sa1100_kbd_exit);
--- linux-2.6.5/drivers/input/serio/Makefile~heh	2004-04-03 22:36:16.000000000 -0500
+++ linux-2.6.5/drivers/input/serio/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -10,6 +10,7 @@
 obj-$(CONFIG_SERIO_SERPORT)	+= serport.o
 obj-$(CONFIG_SERIO_CT82C710)	+= ct82c710.o
 obj-$(CONFIG_SERIO_RPCKBD)	+= rpckbd.o
+obj-$(CONFIG_SERIO_CLPS7500)	+= clps7500ps2.o
 obj-$(CONFIG_SERIO_SA1111)	+= sa1111ps2.o
 obj-$(CONFIG_SERIO_AMBAKMI)	+= ambakmi.o
 obj-$(CONFIG_SERIO_Q40KBD)	+= q40kbd.o
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/input/tslibdev.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,358 @@
+/*
+ *  linux/drivers/input/tslibdev.c
+ *
+ *  Copyright (C) 2002 Russell King
+ *
+ * From tsdev.c:
+ *
+ *  Copyright (c) 2001 "Crazy" james Simmons 
+ *
+ *  Input driver to Touchscreen device driver module.
+ *
+ *  Sponsored by Transvirtual Technology
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <jsimmons@transvirtual.com>.
+ */
+
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/config.h>
+#include <linux/smp_lock.h>
+#include <linux/random.h>
+#include <linux/time.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+
+struct ucb1x00_dev {
+	int		exist;
+	char		name[16];
+	wait_queue_head_t wait;
+	struct list_head list;
+	struct input_handle handle;
+	int		x;
+	int		y;
+	int		pressure;
+};
+
+struct ts_event {
+	u16		pressure;
+	u16		x;
+	u16		y;
+	u16		pad;
+	struct timeval	stamp;
+};
+
+#define TSDEV_BUFFER_SIZE	64
+
+struct ucb1x00_list {
+	struct list_head	list;
+	struct fasync_struct	*fasync;
+	struct ucb1x00_dev	*tsdev;
+	unsigned int		head;
+	unsigned int		tail;
+	struct ts_event		event[TSDEV_BUFFER_SIZE];
+};
+
+static struct input_handler ucb1x00_handler;
+static struct ucb1x00_dev *ucb1x00_dev;
+
+static void ucb1x00_remove(struct ucb1x00_dev *tsdev);
+
+
+static int ucb1x00_fasync(int fd, struct file *file, int on)
+{
+	struct ucb1x00_list *list = file->private_data;
+	int retval;
+
+	retval = fasync_helper(fd, file, on, &list->fasync);
+	return retval < 0 ? retval : 0;
+}
+
+static int ucb1x00_open(struct inode *inode, struct file *file)
+{
+	struct ucb1x00_list *list;
+	int empty;
+
+	if (!ucb1x00_dev || !ucb1x00_dev->exist)
+		return -ENODEV;
+
+	printk(KERN_WARNING
+		"tslibdev: process %s (%d) uses obsolete tslib device\n",
+		current->comm, current->pid);
+
+	list = kmalloc(sizeof(struct ucb1x00_list), GFP_KERNEL);
+	if (!list)
+		return -ENOMEM;
+
+	memset(list, 0, sizeof(struct ucb1x00_list));
+
+	empty = list_empty(&ucb1x00_dev->list);
+
+	list->tsdev = ucb1x00_dev;
+	list_add(&list->list, &list->tsdev->list);
+
+	file->private_data = list;
+
+	if (empty && list->tsdev->exist)
+		input_open_device(&list->tsdev->handle);
+
+	return 0;
+}
+
+static int ucb1x00_release(struct inode *inode, struct file *file)
+{
+	struct ucb1x00_list *list = file->private_data;
+
+	ucb1x00_fasync(-1, file, 0);
+
+	list_del(&list->list);
+
+	ucb1x00_remove(list->tsdev);
+
+	kfree(list);
+
+	return 0;
+}
+
+static ssize_t
+ucb1x00_read(struct file *file, char *buffer, size_t count, loff_t * ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct ucb1x00_list *list = file->private_data;
+	int retval = 0;
+
+	if (list->head == list->tail) {
+		add_wait_queue(&list->tsdev->wait, &wait);
+
+		while (list->head == list->tail) {
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			if (!list->tsdev->exist) {
+				retval = -ENODEV;
+				break;
+			}
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				break;
+			}
+			if (signal_pending(current)) {
+				retval = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&list->tsdev->wait, &wait);
+	}
+
+	if (retval)
+		return retval;
+
+	while (list->head != list->tail && count >= sizeof(struct ts_event)) {
+		if (copy_to_user(buffer, list->event + list->tail,
+				 sizeof(struct ts_event)))
+			return retval ? retval : -EFAULT;
+		list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
+		retval += sizeof(struct ts_event);
+		buffer += sizeof(struct ts_event);
+		count -= sizeof(struct ts_event);
+	}
+	return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int ucb1x00_poll(struct file *file, poll_table * wait)
+{
+	struct ucb1x00_list *list = file->private_data;
+
+	poll_wait(file, &list->tsdev->wait, wait);
+	if (list->head != list->tail || !list->tsdev->exist)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int
+ucb1x00_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	    unsigned long arg)
+{
+	return -EINVAL;
+}
+
+struct file_operations ucb1x00_fops = {
+	.owner		= THIS_MODULE,
+	.open		= ucb1x00_open,
+	.release	= ucb1x00_release,
+	.read		= ucb1x00_read,
+	.poll		= ucb1x00_poll,
+	.fasync		= ucb1x00_fasync,
+	.ioctl		= ucb1x00_ioctl,
+};
+
+/*
+ * The official UCB1x00 touchscreen is a miscdevice:
+ *   10 char        Non-serial mice, misc features
+ *                   14 = /dev/touchscreen/ucb1x00  UCB 1x00 touchscreen
+ */
+static struct miscdevice ucb1x00_ts_dev = {
+	.minor		= 14,
+	.name		= "touchscreen/ucb1x00",
+	.fops		= &ucb1x00_fops,
+	.devfs_name	= "touchscreen/ucb1x00",
+};
+
+static void ucb1x00_remove(struct ucb1x00_dev *tsdev)
+{
+	if (list_empty(&tsdev->list)) {
+		if (tsdev->exist) {
+			input_close_device(&tsdev->handle);
+			wake_up_interruptible(&tsdev->wait);
+		} else {
+			misc_deregister(&ucb1x00_ts_dev);
+			ucb1x00_dev = NULL;
+			kfree(tsdev);
+		}
+	}
+}
+
+
+static void
+ucb1x00_event(struct input_handle *handle, unsigned int type, unsigned int code,
+	    int value)
+{
+	struct ucb1x00_dev *tsdev = handle->private;
+	struct list_head *l;
+
+	/* sorry, we only handle absolute stuff */
+	if (type == EV_ABS) {
+		switch (code) {
+		case ABS_X:
+			tsdev->x = value;
+			break;
+		case ABS_Y:
+			tsdev->y = value;
+			break;
+		case ABS_PRESSURE:
+			tsdev->pressure = value;
+			break;
+		}
+		return;
+	}
+
+	if (type != EV_SYN || code != SYN_REPORT)
+		return;
+
+	list_for_each(l, &tsdev->list) {
+		struct ucb1x00_list *list = list_entry(l, struct ucb1x00_list, list);
+		list->event[list->head].pressure = tsdev->pressure;
+		if (tsdev->pressure) {
+			list->event[list->head].x = tsdev->x;
+			list->event[list->head].y = tsdev->y;
+		} else {
+			list->event[list->head].x = 0;
+			list->event[list->head].y = 0;
+		}
+		do_gettimeofday(&list->event[list->head].stamp);
+		list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1);
+		kill_fasync(&list->fasync, SIGIO, POLL_IN);
+	}
+	wake_up_interruptible(&tsdev->wait);
+}
+
+static struct input_handle *
+ucb1x00_connect(struct input_handler *handler, struct input_dev *dev,
+	      struct input_device_id *id)
+{
+	struct ucb1x00_dev *tsdev;
+
+	if (ucb1x00_dev)
+		return NULL;
+
+	tsdev = kmalloc(sizeof(struct ucb1x00_dev), GFP_KERNEL);
+	if (!tsdev)
+		return NULL;
+
+	memset(tsdev, 0, sizeof(struct ucb1x00_dev));
+	init_waitqueue_head(&tsdev->wait);
+	INIT_LIST_HEAD(&tsdev->list);
+
+	ucb1x00_dev = tsdev;
+
+	strcpy(tsdev->name, ucb1x00_ts_dev.name);
+
+	tsdev->handle.dev     = dev;
+	tsdev->handle.name    = tsdev->name;
+	tsdev->handle.handler = handler;
+	tsdev->handle.private = tsdev;
+
+	misc_register(&ucb1x00_ts_dev);
+
+	tsdev->exist = 1;
+
+	return &tsdev->handle;
+}
+
+static void ucb1x00_disconnect(struct input_handle *handle)
+{
+	struct ucb1x00_dev *tsdev = handle->private;
+
+	tsdev->exist = 0;
+	ucb1x00_remove(tsdev);
+}
+
+static struct input_device_id ucb1x00_ids[] = {
+	{
+	      .flags	= INPUT_DEVICE_ID_MATCH_ABSBIT,
+	      .evbit	= { BIT(EV_ABS) },
+	      .absbit	= { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) },
+	},
+
+	{},/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, ucb1x00_ids);
+
+static struct input_handler ucb1x00_handler = {
+	.event		= ucb1x00_event,
+	.connect	= ucb1x00_connect,
+	.disconnect	= ucb1x00_disconnect,
+	.name		= "touchscreen/ucb1x00",
+	.id_table	= ucb1x00_ids,
+};
+
+static int __init ucb1x00_init(void)
+{
+	input_register_handler(&ucb1x00_handler);
+	printk(KERN_INFO "ts: UCB1x00 touchscreen protocol output\n");
+	return 0;
+}
+
+static void __exit ucb1x00_exit(void)
+{
+	input_unregister_handler(&ucb1x00_handler);
+}
+
+module_init(ucb1x00_init);
+module_exit(ucb1x00_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Input driver to UCB1x00 touchscreen converter");
--- linux-2.6.5/drivers/input/Makefile~heh	2004-04-03 22:38:17.000000000 -0500
+++ linux-2.6.5/drivers/input/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -8,6 +8,7 @@
 obj-$(CONFIG_INPUT_MOUSEDEV)	+= mousedev.o
 obj-$(CONFIG_INPUT_JOYDEV)	+= joydev.o
 obj-$(CONFIG_INPUT_EVDEV)	+= evdev.o
+obj-$(CONFIG_INPUT_TSLIBDEV)	+= tslibdev.o
 obj-$(CONFIG_INPUT_TSDEV)	+= tsdev.o
 obj-$(CONFIG_INPUT_POWER)	+= power.o
 obj-$(CONFIG_INPUT_EVBUG)	+= evbug.o
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/char/sa1100-rtc.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,416 @@
+/*
+ *	Real Time Clock interface for Linux on Intel SA11x0/PXA2xx
+ *
+ *	Copyright (c) 2000 Nils Faerber
+ *
+ *	Based on rtc.c by Paul Gortmaker
+ *	Date/time conversion routines taken from arch/arm/kernel/time.c
+ *			by Linus Torvalds and Russel King
+ *		and the GNU C Library
+ *	( ... I love the GPL ... just take what you need! ;)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *
+ *	1.00	2001-06-08	Nicolas Pitre <nico@cam.org>
+ *	- added periodic timer capability using OSMR1
+ *	- flag compatibility with other RTC chips
+ *	- permission checks for ioctls
+ *	- major cleanup, partial rewrite
+ *
+ *	0.03	2001-03-07	CIH <cih@coventive.com>
+ *	- Modify the bug setups RTC clock.
+ *
+ *	0.02	2001-02-27	Nils Faerber <nils@@kernelconcepts.de>
+ *	- removed mktime(), added alarm irq clear
+ *
+ *	0.01	2000-10-01	Nils Faerber <nils@@kernelconcepts.de>
+ *	- initial release
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+
+#include <asm/bitops.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/rtc.h>
+
+#define TIMER_FREQ		3686400
+
+#define RTC_DEF_DIVIDER		32768 - 1
+#define RTC_DEF_TRIM		0
+
+/* Those are the bits from a classic RTC we want to mimic */
+#define RTC_IRQF		0x80	/* any of the following 3 is active */
+#define RTC_PF			0x40
+#define RTC_AF			0x20
+#define RTC_UF			0x10
+
+static unsigned long rtc_freq = 1024;
+static struct rtc_time rtc_alarm = {
+	.tm_year	= 0,
+	.tm_mon		= 0,
+	.tm_mday	= 0,
+	.tm_hour	= 0,
+	.tm_mon		= 0,
+	.tm_sec		= 0,
+};
+
+extern spinlock_t rtc_lock;
+
+static int rtc_update_alarm(struct rtc_time *alrm)
+{
+	struct rtc_time alarm_tm, now_tm;
+	unsigned long now, time;
+	int ret;
+
+	do {
+		now = RCNR;
+		rtc_time_to_tm(now, &now_tm);
+		rtc_next_alarm_time(&alarm_tm, &now_tm, alrm);
+		ret = rtc_tm_to_time(&alarm_tm, &time);
+		if (ret != 0)
+			break;
+
+		RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL);
+		RTAR = time;
+	} while (now != RCNR);
+
+	return ret;
+}
+
+static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned int rtsr;
+	unsigned long events = 0;
+
+	spin_lock(&rtc_lock);
+
+	rtsr = RTSR;
+	/* clear interrupt sources */
+	RTSR = 0;
+	RTSR = (RTSR_AL|RTSR_HZ) & (rtsr >> 2);
+
+	/* clear alarm interrupt if it has occurred */
+	if (rtsr & RTSR_AL)
+		rtsr &= ~RTSR_ALE;
+	RTSR = rtsr & (RTSR_ALE|RTSR_HZE);
+
+	/* update irq data & counter */
+	if (rtsr & RTSR_AL)
+		events |= (RTC_AF|RTC_IRQF);
+	if (rtsr & RTSR_HZ)
+		events |= (RTC_UF|RTC_IRQF);
+
+	rtc_update(1, events);
+
+	if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm))
+		rtc_update_alarm(&rtc_alarm);
+
+	spin_unlock(&rtc_lock);
+
+	return IRQ_HANDLED;
+}
+
+#if 0
+static unsigned long rtc_irq_data;
+
+static irqreturn_t timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/*
+	 * If we match for the first time, the periodic interrupt flag won't
+	 * be set.  If it is, then we did wrap around (very unlikely but
+	 * still possible) and compute the amount of missed periods.
+	 * The match reg is updated only when the data is actually retrieved
+	 * to avoid unnecessary interrupts.
+	 */
+	OSSR = OSSR_M1;	/* clear match on timer1 */
+	if (rtc_irq_data & RTC_PF) {
+		rtc_irq_data += (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))) << 8;
+	} else {
+		rtc_update(1, RTC_PF | RTC_IRQF);
+	}
+
+	wake_up_interruptible(&rtc_wait);
+	kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);
+
+	return IRQ_HANDLED;
+}
+#endif
+
+static int sa1100_rtc_open(void)
+{
+	int ret;
+
+	ret = request_irq(IRQ_RTC1Hz, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL);
+	if (ret) {
+		printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTC1Hz);
+		goto fail_ui;
+	}
+	ret = request_irq(IRQ_RTCAlrm, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL);
+	if (ret) {
+		printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTCAlrm);
+		goto fail_ai;
+	}
+#if 0
+	ret = request_irq(IRQ_OST1, timer1_interrupt, SA_INTERRUPT, "rtc timer", NULL);
+	if (ret) {
+		printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_OST1);
+		goto fail_pi;
+	}
+	rtc_irq_data = 0;
+#endif
+	return 0;
+
+ fail_pi:
+	free_irq(IRQ_RTCAlrm, NULL);
+ fail_ai:
+	free_irq(IRQ_RTC1Hz, NULL);
+ fail_ui:
+	return ret;
+}
+
+static void sa1100_rtc_release(void)
+{
+	spin_lock_irq (&rtc_lock);
+	RTSR = 0;
+	OIER &= ~OIER_E1;
+	OSSR = OSSR_M1;
+	spin_unlock_irq (&rtc_lock);
+
+//	free_irq(IRQ_OST1, NULL);
+	free_irq(IRQ_RTCAlrm, NULL);
+	free_irq(IRQ_RTC1Hz, NULL);
+}
+
+#if 0
+ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long data;
+	ssize_t retval;
+
+	if (count < sizeof(unsigned long))
+		return -EINVAL;
+
+	add_wait_queue(&rtc_wait, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	for (;;) {
+		spin_lock_irq (&rtc_lock);
+		data = rtc_irq_data;
+		if (data != 0) {
+			rtc_irq_data = 0;
+			break;
+		}
+		spin_unlock_irq (&rtc_lock);
+
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			goto out;
+		}
+
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			goto out;
+		}
+
+		schedule();
+	}
+
+	if (data & RTC_PF) {
+		/* interpolate missed periods and set match for the next one */
+		unsigned long period = TIMER_FREQ/rtc_freq;
+		unsigned long oscr = OSCR;
+		unsigned long osmr1 = OSMR1;
+		unsigned long missed = (oscr - osmr1)/period;
+		data += missed << 8;
+		OSSR = OSSR_M1;	/* clear match on timer 1 */
+		OSMR1 = osmr1 + (missed + 1)*period;
+		/* ensure we didn't miss another match in the mean time */
+		while( (signed long)((osmr1 = OSMR1) - OSCR) <= 0 ) {
+			data += 0x100;
+			OSSR = OSSR_M1;	/* clear match on timer 1 */
+			OSMR1 = osmr1 + period;
+		}
+	}
+	spin_unlock_irq (&rtc_lock);
+
+	data -= 0x100;	/* the first IRQ wasn't actually missed */
+
+	retval = put_user(data, (unsigned long *)buf);
+	if (!retval)
+		retval = sizeof(unsigned long);
+
+out:
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&rtc_wait, &wait);
+	return retval;
+}
+#endif
+
+static int sa1100_rtc_ioctl(unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	case RTC_AIE_OFF:
+		spin_lock_irq(&rtc_lock);
+		RTSR &= ~RTSR_ALE;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_AIE_ON:
+		spin_lock_irq(&rtc_lock);
+		RTSR |= RTSR_ALE;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_UIE_OFF:
+		spin_lock_irq(&rtc_lock);
+		RTSR &= ~RTSR_HZE;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_UIE_ON:
+		spin_lock_irq(&rtc_lock);
+		RTSR |= RTSR_HZE;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+#if 0
+	case RTC_PIE_OFF:
+		spin_lock_irq(&rtc_lock);
+		OIER &= ~OIER_E1;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_PIE_ON:
+		if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE))
+			return -EACCES;
+		spin_lock_irq(&rtc_lock);
+		OSMR1 = TIMER_FREQ/rtc_freq + OSCR;
+		OIER |= OIER_E1;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_IRQP_READ:
+		return put_user(rtc_freq, (unsigned long *)arg);
+	case RTC_IRQP_SET:
+		if (arg < 1 || arg > TIMER_FREQ)
+			        return -EINVAL;
+		if ((arg > 64) && (!capable(CAP_SYS_RESOURCE)))
+			        return -EACCES;
+		rtc_freq = arg;
+		return 0;
+#endif
+	}
+	return -EINVAL;
+}
+
+static void sa1100_rtc_read_time(struct rtc_time *tm)
+{
+	rtc_time_to_tm(RCNR, tm);
+}
+
+static int sa1100_rtc_set_time(struct rtc_time *tm)
+{
+	unsigned long time;
+	int ret;
+
+	ret = rtc_tm_to_time(tm, &time);
+	if (ret == 0)
+		RCNR = time;
+	return ret;
+}
+
+static void sa1100_rtc_read_alarm(struct rtc_wkalrm *alrm)
+{
+	memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time));
+	alrm->pending = RTSR & RTSR_AL ? 1 : 0;
+}
+
+static int sa1100_rtc_set_alarm(struct rtc_wkalrm *alrm)
+{
+	int ret;
+
+	spin_lock_irq(&rtc_lock);
+	ret = rtc_update_alarm(&alrm->time);
+	if (ret == 0) {
+		memcpy(&rtc_alarm, &alrm->time, sizeof(struct rtc_time));
+
+		if (alrm->enabled)
+			enable_irq_wake(IRQ_RTCAlrm);
+		else
+			disable_irq_wake(IRQ_RTCAlrm);
+	}
+	spin_unlock_irq(&rtc_lock);
+
+	return ret;
+}
+
+static int sa1100_rtc_proc(char *buf)
+{
+	char *p = buf;
+
+	p += sprintf(p, "trim/divider\t: 0x%08x\n", RTTR);
+	p += sprintf(p, "alarm_IRQ\t: %s\n", (RTSR & RTSR_ALE) ? "yes" : "no" );
+	p += sprintf(p, "update_IRQ\t: %s\n", (RTSR & RTSR_HZE) ? "yes" : "no");
+	p += sprintf(p, "periodic_IRQ\t: %s\n", (OIER & OIER_E1) ? "yes" : "no");
+	p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq);
+
+	return p - buf;
+}
+
+static struct rtc_ops sa1100_rtc_ops = {
+	.owner		= THIS_MODULE,
+	.open		= sa1100_rtc_open,
+	.release	= sa1100_rtc_release,
+	.ioctl		= sa1100_rtc_ioctl,
+
+	.read_time	= sa1100_rtc_read_time,
+	.set_time	= sa1100_rtc_set_time,
+	.read_alarm	= sa1100_rtc_read_alarm,
+	.set_alarm	= sa1100_rtc_set_alarm,
+	.proc		= sa1100_rtc_proc,
+};
+
+static int __init rtc_init(void)
+{
+	/*
+	 * According to the manual we should be able to let RTTR be zero
+	 * and then a default diviser for a 32.768KHz clock is used.
+	 * Apparently this doesn't work, at least for my SA1110 rev 5.
+	 * If the clock divider is uninitialized then reset it to the
+	 * default value to get the 1Hz clock.
+	 */
+	if (RTTR == 0) {
+		RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16);
+		printk(KERN_WARNING "rtc: warning: initializing default clock divider/trim value\n");
+		/*  The current RTC value probably doesn't make sense either */
+		RCNR = 0;
+	}
+
+	register_rtc(&sa1100_rtc_ops);
+
+	return 0;
+}
+
+static void __exit rtc_exit(void)
+{
+	unregister_rtc(&sa1100_rtc_ops);
+}
+
+module_init(rtc_init);
+module_exit(rtc_exit);
+
+MODULE_AUTHOR("Nils Faerber <nils@@kernelconcepts.de>");
+MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");		/* so says the header */
--- linux-2.6.5/drivers/char/Kconfig~heh	2004-04-03 22:36:15.000000000 -0500
+++ linux-2.6.5/drivers/char/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -814,6 +814,10 @@
 
 	  If unsure, say N.
 
+config SA1100_RTC
+	tristate "SA1100 or PXA Real Time Clock"
+	depends on ARCH_SA1100 || ARCH_PXA
+
 config DTLK
 	tristate "Double Talk PC internal speech card support"
 	help
--- linux-2.6.5/drivers/char/tty_io.c~heh	2004-04-03 22:37:23.000000000 -0500
+++ linux-2.6.5/drivers/char/tty_io.c	2004-04-30 20:57:36.000000000 -0400
@@ -1535,10 +1535,17 @@
 	return 0;
 }
 
+/*
+ * In the case of pty's, "tty" is the master side
+ * and "real_tty" is the slave side.
+ */
 static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
 	struct winsize * arg)
 {
 	struct winsize tmp_ws;
+	struct task_struct *p;
+	struct list_head *l;
+	struct pid *pid;
 
 	if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
 		return -EFAULT;
@@ -1558,8 +1565,21 @@
 #endif
 	if (tty->pgrp > 0)
 		kill_pg(tty->pgrp, SIGWINCH, 1);
-	if ((real_tty->pgrp != tty->pgrp) && (real_tty->pgrp > 0))
-		kill_pg(real_tty->pgrp, SIGWINCH, 1);
+
+	/*
+	 * Send SIGWINCH to the whole session on the slave tty.
+	 * However, in the case of non-master pty's, be careful
+	 * not to send two SIGWINCH to the same procress group.
+	 */
+	if (real_tty->session > 0) {
+		read_lock(&tasklist_lock);
+		for_each_task_pid(real_tty->session, PIDTYPE_SID, p, l, pid) {
+			if (process_group(p) != tty->pgrp)
+				group_send_sig_info(SIGWINCH, (void *)1L, p);
+		}
+		read_unlock(&tasklist_lock);
+	}
+
 	tty->winsize = tmp_ws;
 	real_tty->winsize = tmp_ws;
 	return 0;
--- linux-2.6.5/drivers/Makefile~heh	2004-04-03 22:37:43.000000000 -0500
+++ linux-2.6.5/drivers/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -11,6 +11,8 @@
 # PnP must come after ACPI since it will eventually need to check if acpi
 # was used and do nothing if so
 obj-$(CONFIG_PNP)		+= pnp/
+obj-$(CONFIG_I2C)		+= i2c/
+obj-$(CONFIG_L3)		+= l3/
 
 # char/ comes before serial/ etc so that the VT console is the boot-time
 # default.
@@ -41,7 +43,6 @@
 obj-$(CONFIG_GAMEPORT)		+= input/gameport/
 obj-$(CONFIG_SERIO)		+= input/serio/
 obj-$(CONFIG_I2O)		+= message/
-obj-$(CONFIG_I2C)		+= i2c/
 obj-$(CONFIG_PHONE)		+= telephony/
 obj-$(CONFIG_MD)		+= md/
 obj-$(CONFIG_BT)		+= bluetooth/
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/l3/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,24 @@
+#
+# L3 bus configuration
+#
+
+menu "L3 serial bus support"
+
+config L3
+	tristate "L3 support"
+
+config L3_ALGOBIT
+	bool "L3 bit-banging interfaces"
+	depends on L3=y
+
+config L3_BIT_SA1100_GPIO
+	bool "SA11x0 GPIO adapter"
+	depends on L3_ALGOBIT && ARCH_SA1100
+
+# i2c must come before this
+config BIT_SA1100_GPIO
+	bool
+	depends on L3_BIT_SA1100_GPIO || I2C_BIT_SA1100_GPIO=y
+	default y
+
+endmenu
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/l3/l3-core.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,203 @@
+/*
+ *  linux/drivers/l3/l3-core.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ *  General structure taken from i2c-core.c by Simon G. Vogl
+ *
+ * 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.
+ *
+ *  See linux/Documentation/l3 for further documentation.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/l3/l3.h>
+
+static DECLARE_MUTEX(adapter_lock);
+static LIST_HEAD(adapter_list);
+
+static DECLARE_MUTEX(driver_lock);
+static LIST_HEAD(driver_list);
+
+/**
+ * l3_add_adapter - register a new L3 bus adapter
+ * @adap: l3_adapter structure for the registering adapter
+ *
+ * Make the adapter available for use by clients using name adap->name.
+ * The adap->adapters list is initialised by this function.
+ *
+ * Returns 0;
+ */
+int l3_add_adapter(struct l3_adapter *adap)
+{
+	down(&adapter_lock);
+	list_add(&adap->adapters, &adapter_list);
+	up(&adapter_lock);
+	return 0;	
+}
+
+/**
+ * l3_del_adapter - unregister a L3 bus adapter
+ * @adap: l3_adapter structure to unregister
+ *
+ * Remove an adapter from the list of available L3 Bus adapters.
+ *
+ * Returns 0;
+ */
+int l3_del_adapter(struct l3_adapter *adap)
+{
+	down(&adapter_lock);
+	list_del(&adap->adapters);
+	up(&adapter_lock);
+	return 0;
+}
+
+static struct l3_adapter *__l3_get_adapter(const char *name)
+{
+	struct list_head *l;
+
+	list_for_each(l, &adapter_list) {
+		struct l3_adapter *adap = list_entry(l, struct l3_adapter, adapters);
+
+		if (strcmp(adap->name, name) == 0)
+			return adap;
+	}
+
+	return NULL;
+}
+
+/**
+ * l3_get_adapter - get a reference to an adapter
+ * @name: driver name
+ *
+ * Obtain a l3_adapter structure for the specified adapter.  If the adapter
+ * is not currently load, then load it.  The adapter will be locked in core
+ * until all references are released via l3_put_adapter.
+ */
+struct l3_adapter *l3_get_adapter(const char *name)
+{
+	struct l3_adapter *adap;
+	int try;
+
+	for (try = 0; try < 2; try ++) {
+		down(&adapter_lock);
+		adap = __l3_get_adapter(name);
+		if (adap && !try_module_get(adap->owner))
+			adap = NULL;
+		up(&adapter_lock);
+
+		if (adap)
+			break;
+
+		if (try == 0)
+			request_module(name);
+	}
+
+	return adap;
+}
+
+/**
+ * l3_put_adapter - release a reference to an adapter
+ * @adap: driver to release reference
+ *
+ * Indicate to the L3 core that you no longer require the adapter reference.
+ * The adapter module may be unloaded when there are no references to its
+ * data structure.
+ *
+ * You must not use the reference after calling this function.
+ */
+void l3_put_adapter(struct l3_adapter *adap)
+{
+	if (adap && adap->owner)
+		module_put(adap->owner);
+}
+
+/**
+ * l3_transfer - transfer information on an L3 bus
+ * @adap: adapter structure to perform transfer on
+ * @msgs: array of l3_msg structures describing transfer
+ * @num: number of l3_msg structures
+ *
+ * Transfer the specified messages to/from a device on the L3 bus.
+ *
+ * Returns number of messages successfully transferred, otherwise negative
+ * error code.
+ */
+int l3_transfer(struct l3_adapter *adap, struct l3_msg msgs[], int num)
+{
+	int ret = -ENOSYS;
+
+	if (adap->algo->xfer) {
+		down(adap->lock);
+		ret = adap->algo->xfer(adap, msgs, num);
+		up(adap->lock);
+	}
+	return ret;
+}
+
+/**
+ * l3_write - send data to a device on an L3 bus
+ * @adap: L3 bus adapter
+ * @addr: L3 bus address
+ * @buf: buffer for bytes to send
+ * @len: number of bytes to send
+ *
+ * Send len bytes pointed to by buf to device address addr on the L3 bus
+ * described by client.
+ *
+ * Returns the number of bytes transferred, or negative error code.
+ */
+int l3_write(struct l3_adapter *adap, int addr, const char *buf, int len)
+{
+	struct l3_msg msg;
+	int ret;
+
+	msg.addr = addr;
+	msg.flags = 0;
+	msg.buf = (char *)buf;
+	msg.len = len;
+
+	ret = l3_transfer(adap, &msg, 1);
+	return ret == 1 ? len : ret;
+}
+
+/**
+ * l3_read - receive data from a device on an L3 bus
+ * @adap: L3 bus adapter
+ * @addr: L3 bus address
+ * @buf: buffer for bytes to receive
+ * @len: number of bytes to receive
+ *
+ * Receive len bytes from device address addr on the L3 bus described by
+ * client to a buffer pointed to by buf.
+ *
+ * Returns the number of bytes transferred, or negative error code.
+ */
+int l3_read(struct l3_adapter *adap, int addr, char *buf, int len)
+{
+	struct l3_msg msg;
+	int ret;
+
+	msg.addr = addr;
+	msg.flags = L3_M_RD;
+	msg.buf = buf;
+	msg.len = len;
+
+	ret = l3_transfer(adap, &msg, 1);
+	return ret == 1 ? len : ret;
+}
+
+EXPORT_SYMBOL(l3_add_adapter);
+EXPORT_SYMBOL(l3_del_adapter);
+EXPORT_SYMBOL(l3_get_adapter);
+EXPORT_SYMBOL(l3_put_adapter);
+EXPORT_SYMBOL(l3_transfer);
+EXPORT_SYMBOL(l3_write);
+EXPORT_SYMBOL(l3_read);
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/l3/l3-algo-bit.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,175 @@
+/*
+ * L3 bus algorithm module.
+ *
+ *  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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Note that L3 buses can share the same pins as I2C buses, so we must
+ *  _not_ generate an I2C start condition.  An I2C start condition is
+ *  defined as a high-to-low transition of the data line while the clock
+ *  is high.  Therefore, we must only change the data line while the
+ *  clock is low.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/l3/l3.h>
+#include <linux/l3/algo-bit.h>
+
+#define setdat(adap,val)	adap->setdat(adap->data, val)
+#define setclk(adap,val)	adap->setclk(adap->data, val)
+#define setmode(adap,val)	adap->setmode(adap->data, val)
+#define setdatin(adap)		adap->setdir(adap->data, 1)
+#define setdatout(adap)		adap->setdir(adap->data, 0)
+#define getdat(adap)		adap->getdat(adap->data)
+
+/*
+ * Send one byte of data to the chip.  Data is latched into the chip on
+ * the rising edge of the clock.
+ */
+static void sendbyte(struct l3_algo_bit_data *adap, unsigned int byte)
+{
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		setclk(adap, 0);
+		udelay(adap->data_hold);
+		setdat(adap, byte & 1);
+		udelay(adap->data_setup);
+		setclk(adap, 1);
+		udelay(adap->clock_high);
+		byte >>= 1;
+	}
+}
+
+/*
+ * Send a set of bytes to the chip.  We need to pulse the MODE line
+ * between each byte, but never at the start nor at the end of the
+ * transfer.
+ */
+static void sendbytes(struct l3_algo_bit_data *adap, const char *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (i) {
+			udelay(adap->mode_hold);
+			setmode(adap, 0);
+			udelay(adap->mode);
+		}
+		setmode(adap, 1);
+		udelay(adap->mode_setup);
+		sendbyte(adap, buf[i]);
+	}
+}
+
+/*
+ * Read one byte of data from the chip.  Data is latched into the chip on
+ * the rising edge of the clock.
+ */
+static unsigned int readbyte(struct l3_algo_bit_data *adap)
+{
+	unsigned int byte = 0;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		setclk(adap, 0);
+		udelay(adap->data_hold + adap->data_setup);
+		setclk(adap, 1);
+		if (getdat(adap))
+			byte |= 1 << i;
+		udelay(adap->clock_high);
+	}
+
+	return byte;
+}
+
+/*
+ * Read a set of bytes from the chip.  We need to pulse the MODE line
+ * between each byte, but never at the start nor at the end of the
+ * transfer.
+ */
+static void readbytes(struct l3_algo_bit_data *adap, char *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (i) {
+			udelay(adap->mode_hold);
+			setmode(adap, 0);
+		}
+		setmode(adap, 1);
+		udelay(adap->mode_setup);
+		buf[i] = readbyte(adap);
+	}
+}
+
+static int l3_xfer(struct l3_adapter *l3_adap, struct l3_msg msgs[], int num)
+{
+	struct l3_algo_bit_data *adap = l3_adap->algo_data;
+	int i;
+
+	/*
+	 * If we share an I2C bus, ensure that it is in STOP mode
+	 */
+	setclk(adap, 1);
+	setdat(adap, 1);
+	setmode(adap, 1);
+	setdatout(adap);
+	udelay(adap->mode);
+
+	for (i = 0; i < num; i++) {
+		struct l3_msg *pmsg = &msgs[i];
+
+		if (!(pmsg->flags & L3_M_NOADDR)) {
+			setmode(adap, 0);
+			udelay(adap->mode_setup);
+			sendbyte(adap, pmsg->addr);
+			udelay(adap->mode_hold);
+		}
+
+		if (pmsg->flags & L3_M_RD) {
+			setdatin(adap);
+			readbytes(adap, pmsg->buf, pmsg->len);
+		} else {
+			setdatout(adap);
+			sendbytes(adap, pmsg->buf, pmsg->len);
+		}
+	}
+
+	/*
+	 * Ensure that we leave the bus in I2C stop mode.
+	 */
+	setclk(adap, 1);
+	setdat(adap, 1);
+	setmode(adap, 0);
+	setdatin(adap);
+
+	return num;
+}
+
+static struct l3_algorithm l3_bit_algo = {
+	name:	"L3 bit-shift algorithm",
+	xfer:	l3_xfer,
+};
+
+int l3_bit_add_bus(struct l3_adapter *adap)
+{
+	adap->algo = &l3_bit_algo;
+	return l3_add_adapter(adap);
+}
+
+int l3_bit_del_bus(struct l3_adapter *adap)
+{
+	return l3_del_adapter(adap);
+}
+
+EXPORT_SYMBOL(l3_bit_add_bus);
+EXPORT_SYMBOL(l3_bit_del_bus);
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/l3/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,11 @@
+#
+# Makefile for the L3 bus driver.
+#
+
+# Link order:
+#  (core, adapters, algorithms, drivers) then clients
+
+l3-$(CONFIG_L3_ALGOBIT)		+= l3-algo-bit.o
+l3-$(CONFIG_BIT_SA1100_GPIO)	+= l3-bit-sa1100.o
+
+obj-$(CONFIG_L3)		+= l3-core.o $(l3-y) $(l3-drv-y)
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/l3/l3-bit-sa1100.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,271 @@
+/*
+ *  linux/drivers/l3/l3-bit-sa1100.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  This is a combined I2C and L3 bus driver.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/l3/algo-bit.h>
+
+#include <asm/system.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/arch/assabet.h>
+
+#define NAME "l3-bit-sa1100-gpio"
+
+struct bit_data {
+	unsigned int	sda;
+	unsigned int	scl;
+	unsigned int	l3_mode;
+};
+
+static int getsda(void *data)
+{
+	struct bit_data *bits = data;
+
+	return GPLR & bits->sda;
+}
+
+#ifdef CONFIG_I2C_BIT_SA1100_GPIO
+static void i2c_setsda(void *data, int state)
+{
+	struct bit_data *bits = data;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (state)
+		GPDR &= ~bits->sda;
+	else {
+		GPCR = bits->sda;
+		GPDR |= bits->sda;
+	}
+	local_irq_restore(flags);
+}
+
+static void i2c_setscl(void *data, int state)
+{
+	struct bit_data *bits = data;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (state)
+		GPDR &= ~bits->scl;
+	else {
+		GPCR = bits->scl;
+		GPDR |= bits->scl;
+	}
+	local_irq_restore(flags);
+}
+
+static int i2c_getscl(void *data)
+{
+	struct bit_data *bits = data;
+
+	return GPLR & bits->scl;
+}
+
+static struct i2c_algo_bit_data i2c_bit_data = {
+	.setsda		= i2c_setsda,
+	.setscl		= i2c_setscl,
+	.getsda		= getsda,
+	.getscl		= i2c_getscl,
+	.udelay		= 10,
+	.mdelay		= 10,
+	.timeout	= 100,
+};
+
+static struct i2c_adapter i2c_adapter = {
+	.algo_data	= &i2c_bit_data,
+};
+
+#define LOCK	&i2c_adapter.bus_lock
+
+static int __init i2c_init(struct bit_data *bits)
+{
+	i2c_bit_data.data = bits;
+	return i2c_bit_add_bus(&i2c_adapter);
+}
+
+static void i2c_exit(void)
+{
+	i2c_bit_del_bus(&i2c_adapter);
+}
+
+#else
+static DECLARE_MUTEX(l3_lock);
+#define LOCK		&l3_lock
+#define i2c_init(bits)	(0)
+#define i2c_exit()	do { } while (0)
+#endif
+
+#ifdef CONFIG_L3_BIT_SA1100_GPIO
+/*
+ * iPAQs need the clock line driven hard high and low.
+ */
+static void l3_setscl(void *data, int state)
+{
+	struct bit_data *bits = data;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (state)
+		GPSR = bits->scl;
+	else
+		GPCR = bits->scl;
+	GPDR |= bits->scl;
+	local_irq_restore(flags);
+}
+
+static void l3_setsda(void *data, int state)
+{
+	struct bit_data *bits = data;
+
+	if (state)
+		GPSR = bits->sda;
+	else
+		GPCR = bits->sda;
+}
+
+static void l3_setdir(void *data, int in)
+{
+	struct bit_data *bits = data;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (in)
+		GPDR &= ~bits->sda;
+	else
+		GPDR |= bits->sda;
+	local_irq_restore(flags);
+}
+
+static void l3_setmode(void *data, int state)
+{
+	struct bit_data *bits = data;
+
+	if (state)
+		GPSR = bits->l3_mode;
+	else
+		GPCR = bits->l3_mode;
+}
+
+static struct l3_algo_bit_data l3_bit_data = {
+	.data		= NULL,
+	.setdat		= l3_setsda,
+	.setclk		= l3_setscl,
+	.setmode	= l3_setmode,
+	.setdir		= l3_setdir,
+	.getdat		= getsda,
+	.data_hold	= 1,
+	.data_setup	= 1,
+	.clock_high	= 1,
+	.mode_hold	= 1,
+	.mode_setup	= 1,
+};
+
+static struct l3_adapter l3_adapter = {
+	.owner		= THIS_MODULE,
+	.name		= NAME,
+	.algo_data	= &l3_bit_data,
+	.lock		= LOCK,
+};
+
+static int __init l3_init(struct bit_data *bits)
+{
+	l3_bit_data.data = bits;
+	return l3_bit_add_bus(&l3_adapter);
+}
+
+static void __exit l3_exit(void)
+{
+	l3_bit_del_bus(&l3_adapter);
+}
+#else
+#define l3_init(bits)	(0)
+#define l3_exit()	do { } while (0)
+#endif
+
+static struct bit_data bit_data;
+
+static int __init bus_init(void)
+{
+	struct bit_data *bit = &bit_data;
+	unsigned long flags;
+	int ret;
+
+	if (machine_is_assabet() || machine_is_pangolin()) {
+		bit->sda     = GPIO_GPIO15;
+		bit->scl     = GPIO_GPIO18;
+		bit->l3_mode = GPIO_GPIO17;
+	}
+
+	if (machine_is_h3600() || machine_is_h3100()) {
+		bit->sda     = GPIO_GPIO14;
+		bit->scl     = GPIO_GPIO16;
+		bit->l3_mode = GPIO_GPIO15;
+	}
+
+	if (machine_is_stork()) {
+		bit->sda     = GPIO_GPIO15;
+		bit->scl     = GPIO_GPIO18;
+		bit->l3_mode = GPIO_GPIO17;
+	}
+
+	if (!bit->sda)
+		return -ENODEV;
+
+	/*
+	 * Default level for L3 mode is low.
+	 * We set SCL and SDA high (i2c idle state).
+	 */
+	local_irq_save(flags);
+	GPDR &= ~(bit->scl | bit->sda);
+	GPCR = bit->l3_mode | bit->scl | bit->sda;
+	GPDR |= bit->l3_mode;
+	local_irq_restore(flags);
+
+	if (machine_is_assabet()) {
+		/*
+		 * Release reset on UCB1300, ADI7171 and UDA1341.  We
+		 * need to do this here so that we can communicate on
+		 * the I2C/L3 buses.
+		 */
+		ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
+		mdelay(1);
+		ASSABET_BCR_clear(ASSABET_BCR_CODEC_RST);
+		mdelay(1);
+		ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
+	}
+
+	ret = i2c_init(bit);
+	if (ret == 0 && bit->l3_mode) {
+		ret = l3_init(bit);
+		if (ret)
+			i2c_exit();
+	}
+
+	return ret;
+}
+
+static void __exit bus_exit(void)
+{
+	l3_exit();
+	i2c_exit();
+}
+
+module_init(bus_init);
+module_exit(bus_exit);
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/switches.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,22 @@
+/*
+ *  linux/drivers/misc/switches.h
+ *
+ *  Copyright (C) 2001 John Dorsey
+ *
+ *  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.
+ *
+ *  19 December 2001 - created.
+ */
+
+#if !defined(_SWITCHES_H)
+# define _SWITCHES_H
+
+#include <linux/switches.h>
+
+#define SWITCHES_NAME "switches"
+
+extern int  switches_event(switches_mask_t *mask);
+
+#endif  /* !defined(_SWITCHES_H) */
--- linux-2.6.5/drivers/misc/Kconfig~heh	2004-04-03 22:36:26.000000000 -0500
+++ linux-2.6.5/drivers/misc/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -21,5 +21,62 @@
 
 	  If unsure, say N.
 
+menu "Multimedia Capabilities Port drivers"
+
+config MCP
+	tristate "Multimedia drivers"
+
+# Interface drivers
+config MCP_SA1100
+	tristate "Support SA1100 MCP interface"
+	depends on MCP && ARCH_SA1100
+
+# Chip drivers
+config MCP_UCB1200
+	tristate "Support for UCB1200 / UCB1300"
+	depends on MCP
+
+config MCP_UCB1200_AUDIO
+	tristate "Audio / Telephony interface support"
+	depends on MCP_UCB1200 && SOUND
+
+config MCP_UCB1200_TS
+	tristate "Touchscreen interface support"
+	depends on MCP_UCB1200 && INPUT
+
+endmenu
+
+
+menu "Console Switches"
+
+config SWITCHES
+	tristate "Console Switch Support"
+	help
+	  Say Y here to include support for simple console momentary switches.
+	  This driver implements a miscellaneous character device (named
+	  `switches' in /proc/misc) which can be read by userland programs
+	  to respond to switch press events. This mechanism is efficient for
+	  systems which may not implement a traditional heavyweight console
+	  server.
+
+	  It is also possible to say M to build this driver as a module (named
+	  `switches.o').
+
+config SWITCHES_SA1100
+	tristate "SA-1100 switches"
+	depends on SWITCHES && ARCH_SA1100
+	help
+	  Say Y here to include support for switches routed directly to
+	  interruptable signals on StrongARM SA-1100 systems.
+
+config SWITCHES_UCB1X00
+	tristate "UCB1x00 switches"
+	depends on SWITCHES && MCP_UCB1200
+	help
+	  Say Y here to include support for switches routed through a
+	  UCB1x00 modem/audio analog front-end device.
+
+endmenu
+
 endmenu
 
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/ucb1x00-assabet.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,73 @@
+/*
+ *  linux/drivers/misc/ucb1x00-assabet.c
+ *
+ *  Copyright (C) 2001-2003 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.
+ *
+ *  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 <asm/dma.h>
+
+#include "ucb1x00.h"
+
+#define UCB1X00_ATTR(name,input)\
+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", val);			\
+}								\
+static CLASS_DEVICE_ATTR(name,0444,name##_show,NULL)
+
+UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1);
+UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD0);
+UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2);
+
+static int ucb1x00_assabet_add(struct class_device *dev)
+{
+	class_device_create_file(dev, &class_device_attr_vbatt);
+	class_device_create_file(dev, &class_device_attr_vcharger);
+	class_device_create_file(dev, &class_device_attr_batt_temp);
+	return 0;
+}
+
+static void ucb1x00_assabet_remove(struct class_device *dev)
+{
+	class_device_remove_file(dev, &class_device_attr_batt_temp);
+	class_device_remove_file(dev, &class_device_attr_vcharger);
+	class_device_remove_file(dev, &class_device_attr_vbatt);
+}
+
+static struct class_interface ucb1x00_assabet_interface = {
+	.add	= ucb1x00_assabet_add,
+	.remove	= ucb1x00_assabet_remove,
+};
+
+static int __init ucb1x00_assabet_init(void)
+{
+	return ucb1x00_register_interface(&ucb1x00_assabet_interface);
+}
+
+static void __exit ucb1x00_assabet_exit(void)
+{
+	ucb1x00_unregister_interface(&ucb1x00_assabet_interface);
+}
+
+module_init(ucb1x00_assabet_init);
+module_exit(ucb1x00_assabet_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Assabet noddy testing only example ADC driver");
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/mcp-pxa.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,57 @@
+/*
+ *  linux/drivers/misc/mcp-pxa.c
+ *
+ *  2002-01-10 Jeff Sutherland <jeffs@accelent.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * NOTE: This is a quick hack to gain access to the aclink codec's
+ *       touch screen facility.  Its audio is handled by a separate
+ *       (non-mcp) driver at the present time.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/ac97_codec.h>
+
+#include "mcp.h"
+
+
+extern int pxa_ac97_get(struct ac97_codec **codec);
+extern void pxa_ac97_put(void);
+
+
+struct mcp *mcp_get(void)
+{
+	struct ac97_codec *codec;
+	if (pxa_ac97_get(&codec) < 0)
+		return NULL;
+	return (struct mcp *)codec;
+}
+
+void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
+{
+	struct ac97_codec *codec = (struct ac97_codec *)mcp;
+	codec->codec_write(codec, reg, val);
+}
+
+unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)
+{
+	struct ac97_codec *codec = (struct ac97_codec *)mcp;
+	return codec->codec_read(codec, reg);
+}
+
+void mcp_enable(struct mcp *mcp)
+{
+	/* 
+	 * Should we do something here to make sure the aclink
+	 * codec is alive???
+	 * A: not for now  --NP
+	*/
+}
+
+void mcp_disable(struct mcp *mcp)
+{
+}
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/mcp-core.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,235 @@
+/*
+ *  linux/drivers/misc/mcp-core.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * 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.
+ *
+ *  Generic MCP (Multimedia Communications Port) layer.  All MCP locking
+ *  is solely held within this file.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <linux/device.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+
+#include "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)
+
+static int mcp_bus_match(struct device *dev, struct device_driver *drv)
+{
+	return 1;
+}
+
+static int mcp_bus_probe(struct device *dev)
+{
+	struct mcp *mcp = to_mcp(dev);
+	struct mcp_driver *drv = to_mcp_driver(dev->driver);
+
+	return drv->probe(mcp);
+}
+
+static int mcp_bus_remove(struct device *dev)
+{
+	struct mcp *mcp = to_mcp(dev);
+	struct mcp_driver *drv = to_mcp_driver(dev->driver);
+
+	drv->remove(mcp);
+	return 0;
+}
+
+static int mcp_bus_suspend(struct device *dev, u32 state)
+{
+	struct mcp *mcp = to_mcp(dev);
+	int ret = 0;
+
+	if (dev->driver) {
+		struct mcp_driver *drv = to_mcp_driver(dev->driver);
+
+		ret = drv->suspend(mcp, state);
+	}
+	return ret;
+}
+
+static int mcp_bus_resume(struct device *dev)
+{
+	struct mcp *mcp = to_mcp(dev);
+	int ret = 0;
+
+	if (dev->driver) {
+		struct mcp_driver *drv = to_mcp_driver(dev->driver);
+
+		ret = drv->resume(mcp);
+	}
+	return ret;
+}
+
+static struct bus_type mcp_bus_type = {
+	.name		= "mcp",
+	.match		= mcp_bus_match,
+	.suspend	= mcp_bus_suspend,
+	.resume		= mcp_bus_resume,
+};
+
+/**
+ *	mcp_set_telecom_divisor - set the telecom divisor
+ *	@mcp: MCP interface structure
+ *	@div: SIB clock divisor
+ *
+ *	Set the telecom divisor on the MCP interface.  The resulting
+ *	sample rate is SIBCLOCK/div.
+ */
+void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div)
+{
+	spin_lock_irq(&mcp->lock);
+	mcp->set_telecom_divisor(mcp, div);
+	spin_unlock_irq(&mcp->lock);
+}
+
+/**
+ *	mcp_set_audio_divisor - set the audio divisor
+ *	@mcp: MCP interface structure
+ *	@div: SIB clock divisor
+ *
+ *	Set the audio divisor on the MCP interface.
+ */
+void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div)
+{
+	spin_lock_irq(&mcp->lock);
+	mcp->set_audio_divisor(mcp, div);
+	spin_unlock_irq(&mcp->lock);
+}
+
+/**
+ *	mcp_reg_write - write a device register
+ *	@mcp: MCP interface structure
+ *	@reg: 4-bit register index
+ *	@val: 16-bit data value
+ *
+ *	Write a device register.  The MCP interface must be enabled
+ *	to prevent this function hanging.
+ */
+void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mcp->lock, flags);
+	mcp->reg_write(mcp, reg, val);
+	spin_unlock_irqrestore(&mcp->lock, flags);
+}
+
+/**
+ *	mcp_reg_read - read a device register
+ *	@mcp: MCP interface structure
+ *	@reg: 4-bit register index
+ *
+ *	Read a device register and return its value.  The MCP interface
+ *	must be enabled to prevent this function hanging.
+ */
+unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&mcp->lock, flags);
+	val = mcp->reg_read(mcp, reg);
+	spin_unlock_irqrestore(&mcp->lock, flags);
+
+	return val;
+}
+
+/**
+ *	mcp_enable - enable the MCP interface
+ *	@mcp: MCP interface to enable
+ *
+ *	Enable the MCP interface.  Each call to mcp_enable will need
+ *	a corresponding call to mcp_disable to disable the interface.
+ */
+void mcp_enable(struct mcp *mcp)
+{
+	spin_lock_irq(&mcp->lock);
+	if (mcp->use_count++ == 0)
+		mcp->enable(mcp);
+	spin_unlock_irq(&mcp->lock);
+}
+
+/**
+ *	mcp_disable - disable the MCP interface
+ *	@mcp: MCP interface to disable
+ *
+ *	Disable the MCP interface.  The MCP interface will only be
+ *	disabled once the number of calls to mcp_enable matches the
+ *	number of calls to mcp_disable.
+ */
+void mcp_disable(struct mcp *mcp)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mcp->lock, flags);
+	if (--mcp->use_count == 0)
+		mcp->disable(mcp);
+	spin_unlock_irqrestore(&mcp->lock, flags);
+}
+
+int mcp_host_register(struct mcp *mcp, struct device *parent)
+{
+	mcp->attached_device.parent = parent;
+	mcp->attached_device.bus = &mcp_bus_type;
+	mcp->attached_device.dma_mask = parent->dma_mask;
+	strcpy(mcp->attached_device.bus_id, "mcp0");
+	return device_register(&mcp->attached_device);
+}
+
+void mcp_host_unregister(struct mcp *mcp)
+{
+	device_unregister_wait(&mcp->attached_device);
+}
+
+int mcp_driver_register(struct mcp_driver *mcpdrv)
+{
+	mcpdrv->drv.bus = &mcp_bus_type;
+	mcpdrv->drv.probe = mcp_bus_probe;
+	mcpdrv->drv.remove = mcp_bus_remove;
+	return driver_register(&mcpdrv->drv);
+}
+
+void mcp_driver_unregister(struct mcp_driver *mcpdrv)
+{
+	driver_unregister(&mcpdrv->drv);
+}
+
+static int __init mcp_init(void)
+{
+	return bus_register(&mcp_bus_type);
+}
+
+static void __exit mcp_exit(void)
+{
+	bus_unregister(&mcp_bus_type);
+}
+
+module_init(mcp_init);
+module_exit(mcp_exit);
+
+EXPORT_SYMBOL(mcp_set_telecom_divisor);
+EXPORT_SYMBOL(mcp_set_audio_divisor);
+EXPORT_SYMBOL(mcp_reg_write);
+EXPORT_SYMBOL(mcp_reg_read);
+EXPORT_SYMBOL(mcp_enable);
+EXPORT_SYMBOL(mcp_disable);
+EXPORT_SYMBOL(mcp_host_register);
+EXPORT_SYMBOL(mcp_host_unregister);
+EXPORT_SYMBOL(mcp_driver_register);
+EXPORT_SYMBOL(mcp_driver_unregister);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Core multimedia communications port driver");
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/ucb1x00-ts.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,465 @@
+/*
+ *  linux/drivers/misc/ucb1x00-ts.c
+ *
+ *  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 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/config.h>
+#include <linux/module.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/slab.h>
+
+#include <asm/dma.h>
+#include <asm/semaphore.h>
+
+#include "ucb1x00.h"
+
+
+struct ucb1x00_ts {
+	struct input_dev	idev;
+	struct ucb1x00		*ucb;
+
+	struct semaphore	irq_wait;
+	struct semaphore	sem;
+	struct completion	init_exit;
+	struct task_struct	*rtask;
+	int			use_count;
+	u16			x_res;
+	u16			y_res;
+
+	int			restart:1;
+	int			adcsync:1;
+};
+
+static int adcsync = UCB_NOSYNC;
+
+static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
+{
+	input_report_abs(&ts->idev, ABS_X, x);
+	input_report_abs(&ts->idev, ABS_Y, y);
+	input_report_abs(&ts->idev, ABS_PRESSURE, pressure);
+	input_sync(&ts->idev);
+}
+
+static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
+{
+	input_report_abs(&ts->idev, ABS_PRESSURE, 0);
+	input_sync(&ts->idev);
+}
+
+/*
+ * Switch to interrupt mode.
+ */
+static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
+{
+	if (ts->ucb->id == UCB_ID_1400_BUGGY)
+		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+				UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+				UCB_TS_CR_MODE_INT);
+	else
+		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.
+ */
+static inline unsigned int ucb1x00_ts_read_pressure(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_PRES | UCB_TS_CR_BIAS_ENA);
+
+	return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+}
+
+/*
+ * 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 unsigned int ucb1x00_ts_read_xpos(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);
+	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);
+	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);
+
+	udelay(55);
+
+	return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+}
+
+/*
+ * 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 unsigned int ucb1x00_ts_read_ypos(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);
+	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);
+	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);
+
+	udelay(55);
+
+	return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
+}
+
+/*
+ * 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, and in the UCB1400 case where
+ * any register access may sleep).
+ */
+static int ucb1x00_thread(void *_ts)
+{
+	struct ucb1x00_ts *ts = _ts;
+	struct task_struct *tsk = current;
+	int valid;
+
+	ts->rtask = tsk;
+
+	daemonize("ktsd");
+	/* only want to receive SIGKILL */
+	allow_signal(SIGKILL);
+
+	/*
+	 * We could run as a real-time thread.  However, thus far
+	 * this doesn't seem to be necessary.
+	 */
+//	tsk->policy = SCHED_FIFO;
+//	tsk->rt_priority = 1;
+
+	complete(&ts->init_exit);
+
+	valid = 0;
+
+	for (;;) {
+		unsigned int x, y, p, val;
+
+		ts->restart = 0;
+
+		ucb1x00_adc_enable(ts->ucb);
+
+		x = ucb1x00_ts_read_xpos(ts);
+		y = ucb1x00_ts_read_ypos(ts);
+		p = ucb1x00_ts_read_pressure(ts);
+
+		/*
+		 * Switch back to interrupt mode.
+		 */
+		ucb1x00_ts_mode_int(ts);
+		ucb1x00_adc_disable(ts->ucb);
+
+		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ / 100);
+		if (signal_pending(tsk))
+			break;
+
+		ucb1x00_enable(ts->ucb);
+		val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
+
+		if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) {
+			ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
+			ucb1x00_disable(ts->ucb);
+
+			/*
+			 * If we spat out a valid sample set last time,
+			 * spit out a "pen off" sample here.
+			 */
+			if (valid) {
+				ucb1x00_ts_event_release(ts);
+				valid = 0;
+			}
+
+			/*
+			 * Since ucb1x00_enable_irq() might sleep due
+			 * to the way the UCB1400 regs are accessed, we
+			 * can't use set_task_state() before that call,
+			 * and not changing state before enabling the
+			 * interrupt is racy.  A semaphore solves all
+			 * those issues quite nicely.
+			 */
+			down_interruptible(&ts->irq_wait);
+		} else {
+			ucb1x00_disable(ts->ucb);
+
+			/*
+			 * Filtering is policy.  Policy belongs in user
+			 * space.  We therefore leave it to user space
+			 * to do any filtering they please.
+			 */
+			if (!ts->restart) {
+				ucb1x00_ts_evt_add(ts, p, x, y);
+				valid = 1;
+			}
+
+			set_task_state(tsk, TASK_INTERRUPTIBLE);
+		}
+
+		schedule_timeout(HZ / 100);
+		if (signal_pending(tsk))
+			break;
+	}
+
+	ts->rtask = NULL;
+	complete_and_exit(&ts->init_exit, 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;
+	ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
+	up(&ts->irq_wait);
+}
+
+static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
+{
+	input_report_abs(&ts->idev, ABS_PRESSURE, 0);
+}
+
+static int ucb1x00_ts_open(struct input_dev *idev)
+{
+	struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev;
+	int ret = 0;
+
+	if (down_interruptible(&ts->sem))
+		return -EINTR;
+
+	if (ts->use_count++ != 0)
+		goto out;
+
+	if (ts->rtask)
+		panic("ucb1x00: rtask running?");
+
+	sema_init(&ts->irq_wait, 0);
+	ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
+	if (ret < 0)
+		goto out;
+
+	/*
+	 * 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);
+
+	init_completion(&ts->init_exit);
+	ret = kernel_thread(ucb1x00_thread, ts, CLONE_KERNEL);
+	if (ret >= 0) {
+		wait_for_completion(&ts->init_exit);
+		ret = 0;
+	} else {
+		ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+	}
+
+ out:
+	if (ret)
+		ts->use_count--;
+	up(&ts->sem);
+	return ret;
+}
+
+/*
+ * Release touchscreen resources.  Disable IRQs.
+ */
+static void ucb1x00_ts_close(struct input_dev *idev)
+{
+	struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev;
+
+	down(&ts->sem);
+	if (--ts->use_count == 0) {
+		if (ts->rtask) {
+			send_sig(SIGKILL, ts->rtask, 1);
+			wait_for_completion(&ts->init_exit);
+		}
+
+		ucb1x00_enable(ts->ucb);
+		ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+		ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
+		ucb1x00_disable(ts->ucb);
+	}
+	up(&ts->sem);
+}
+
+#if 0
+static int ucb1x00_ts_resume(struct device *_dev, u32 level)
+{
+	struct ucb1x00_device *dev = ucb1x00_dev(_dev);
+	struct ucb1x00_ts *ts = ucb1x00_get_drvdata(dev);
+
+	if (level == RESUME_ENABLE && ts->rtask != NULL) {
+		/*
+		 * Restart the TS thread to ensure the
+		 * TS interrupt mode is set up again
+		 * after sleep.
+		 */
+		ts->restart = 1;
+		up(&ts->irq_wait);
+	}
+	return 0;
+}
+#endif
+
+
+/*
+ * Initialisation.
+ */
+static int ucb1x00_ts_add(struct class_device *dev)
+{
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);
+	struct ucb1x00_ts *ts;
+
+	ts = kmalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	memset(ts, 0, sizeof(struct ucb1x00_ts));
+
+	ts->ucb = ucb;
+	ts->adcsync = adcsync;
+	init_MUTEX(&ts->sem);
+
+	ts->idev.name       = "Touchscreen panel";
+	ts->idev.id.product = ts->ucb->id;
+	ts->idev.open       = ucb1x00_ts_open;
+	ts->idev.close      = ucb1x00_ts_close;
+
+	__set_bit(EV_ABS, ts->idev.evbit);
+	__set_bit(ABS_X, ts->idev.absbit);
+	__set_bit(ABS_Y, ts->idev.absbit);
+	__set_bit(ABS_PRESSURE, ts->idev.absbit);
+
+	input_register_device(&ts->idev);
+
+	ucb->ts_data = ts;
+
+	return 0;
+}
+
+static void ucb1x00_ts_remove(struct class_device *dev)
+{
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);
+	struct ucb1x00_ts *ts = ucb->ts_data;
+
+	input_unregister_device(&ts->idev);
+	kfree(ts);
+}
+
+static struct class_interface ucb1x00_ts_interface = {
+	.add		= ucb1x00_ts_add,
+	.remove		= ucb1x00_ts_remove,
+};
+
+static int __init ucb1x00_ts_init(void)
+{
+	return ucb1x00_register_interface(&ucb1x00_ts_interface);
+}
+
+static void __exit ucb1x00_ts_exit(void)
+{
+	ucb1x00_unregister_interface(&ucb1x00_ts_interface);
+}
+
+#ifndef MODULE
+
+/*
+ * Parse kernel command-line options.
+ *
+ * syntax : ucbts=[sync|nosync],...
+ */
+static int __init ucb1x00_ts_setup(char *str)
+{
+	char *p;
+
+	while ((p = strsep(&str, ",")) != NULL) {
+		if (strcmp(p, "sync") == 0)
+			adcsync = UCB_SYNC;
+	}
+
+	return 1;
+}
+
+__setup("ucbts=", ucb1x00_ts_setup);
+
+#else
+
+MODULE_PARM(adcsync, "i");
+MODULE_PARM_DESC(adcsync, "Enable use of ADCSYNC signal");
+
+#endif
+
+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");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/ucb1x00-core.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,624 @@
+/*
+ *  linux/drivers/misc/ucb1x00-core.c
+ *
+ *  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.
+ *
+ *  The UCB1x00 core driver provides basic services for handling IO,
+ *  the ADC, interrupts, and accessing registers.  It is designed
+ *  such that everything goes through this layer, thereby providing
+ *  a consistent locking methodology, as well as allowing the drivers
+ *  to be used on other non-MCP-enabled hardware platforms.
+ *
+ *  Note that all locks are private to this file.  Nothing else may
+ *  touch them.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <asm/dma.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+
+#include "ucb1x00.h"
+
+/**
+ *	ucb1x00_io_set_dir - set IO direction
+ *	@ucb: UCB1x00 structure describing chip
+ *	@in:  bitfield of IO pins to be set as inputs
+ *	@out: bitfield of IO pins to be set as outputs
+ *
+ *	Set the IO direction of the ten general purpose IO pins on
+ *	the UCB1x00 chip.  The @in bitfield has priority over the
+ *	@out bitfield, in that if you specify a pin as both input
+ *	and output, it will end up as an input.
+ *
+ *	ucb1x00_enable must have been called to enable the comms
+ *	before using this function.
+ *
+ *	This function takes a spinlock, disabling interrupts.
+ */
+void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int in, unsigned int out)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ucb->io_lock, flags);
+	ucb->io_dir |= out;
+	ucb->io_dir &= ~in;
+
+	ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+	spin_unlock_irqrestore(&ucb->io_lock, flags);
+}
+
+/**
+ *	ucb1x00_io_write - set or clear IO outputs
+ *	@ucb:   UCB1x00 structure describing chip
+ *	@set:   bitfield of IO pins to set to logic '1'
+ *	@clear: bitfield of IO pins to set to logic '0'
+ *
+ *	Set the IO output state of the specified IO pins.  The value
+ *	is retained if the pins are subsequently configured as inputs.
+ *	The @clear bitfield has priority over the @set bitfield -
+ *	outputs will be cleared.
+ *
+ *	ucb1x00_enable must have been called to enable the comms
+ *	before using this function.
+ *
+ *	This function takes a spinlock, disabling interrupts.
+ */
+void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ucb->io_lock, flags);
+	ucb->io_out |= set;
+	ucb->io_out &= ~clear;
+
+	ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+	spin_unlock_irqrestore(&ucb->io_lock, flags);
+}
+
+/**
+ *	ucb1x00_io_read - read the current state of the IO pins
+ *	@ucb: UCB1x00 structure describing chip
+ *
+ *	Return a bitfield describing the logic state of the ten
+ *	general purpose IO pins.
+ *
+ *	ucb1x00_enable must have been called to enable the comms
+ *	before using this function.
+ *
+ *	This function does not take any semaphores or spinlocks.
+ */
+unsigned int ucb1x00_io_read(struct ucb1x00 *ucb)
+{
+	return ucb1x00_reg_read(ucb, UCB_IO_DATA);
+}
+
+/*
+ * UCB1300 data sheet says we must:
+ *  1. enable ADC	=> 5us (including reference startup time)
+ *  2. select input	=> 51*tsibclk  => 4.3us
+ *  3. start conversion	=> 102*tsibclk => 8.5us
+ * (tsibclk = 1/11981000)
+ * Period between SIB 128-bit frames = 10.7us
+ */
+
+/**
+ *	ucb1x00_adc_enable - enable the ADC converter
+ *	@ucb: UCB1x00 structure describing chip
+ *
+ *	Enable the ucb1x00 and ADC converter on the UCB1x00 for use.
+ *	Any code wishing to use the ADC converter must call this
+ *	function prior to using it.
+ *
+ *	This function takes the ADC semaphore to prevent two or more
+ *	concurrent uses, and therefore may sleep.  As a result, it
+ *	can only be called from process context, not interrupt
+ *	context.
+ *
+ *	You should release the ADC as soon as possible using
+ *	ucb1x00_adc_disable.
+ */
+void ucb1x00_adc_enable(struct ucb1x00 *ucb)
+{
+	down(&ucb->adc_sem);
+
+	ucb->adc_cr |= UCB_ADC_ENA;
+
+	ucb1x00_enable(ucb);
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr);
+}
+
+/**
+ *	ucb1x00_adc_read - read the specified ADC channel
+ *	@ucb: UCB1x00 structure describing chip
+ *	@adc_channel: ADC channel mask
+ *	@sync: wait for syncronisation pulse.
+ *
+ *	Start an ADC conversion and wait for the result.  Note that
+ *	synchronised ADC conversions (via the ADCSYNC pin) must wait
+ *	until the trigger is asserted and the conversion is finished.
+ *
+ *	This function currently spins waiting for the conversion to
+ *	complete (2 frames max without sync).
+ *
+ *	If called for a synchronised ADC conversion, it may sleep
+ *	with the ADC semaphore held.
+ *	
+ *	See ucb1x00.h for definition of the UCB_ADC_DAT macro.  It
+ *	addresses a bug in the ucb1200/1300 which, of course, Philips
+ *	decided to finally fix in the ucb1400 ;-) -jws
+ */
+unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
+{
+	unsigned int val;
+
+	if (sync)
+		adc_channel |= UCB_ADC_SYNC_ENA;
+
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel);
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START);
+
+	for (;;) {
+		val = ucb1x00_reg_read(ucb, UCB_ADC_DATA);
+		if (val & UCB_ADC_DAT_VAL)
+			break;
+		/* yield to other processes */
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+
+	return UCB_ADC_DAT(val);
+}
+
+/**
+ *	ucb1x00_adc_disable - disable the ADC converter
+ *	@ucb: UCB1x00 structure describing chip
+ *
+ *	Disable the ADC converter and release the ADC semaphore.
+ */
+void ucb1x00_adc_disable(struct ucb1x00 *ucb)
+{
+	ucb->adc_cr &= ~UCB_ADC_ENA;
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr);
+	ucb1x00_disable(ucb);
+
+	up(&ucb->adc_sem);
+}
+
+/*
+ * UCB1x00 Interrupt handling.
+ *
+ * The UCB1x00 can generate interrupts when the SIBCLK is stopped.
+ * Since we need to read an internal register, we must re-enable
+ * SIBCLK to talk to the chip.  We leave the clock running until
+ * we have finished processing all interrupts from the chip.
+ *
+ * A restriction with interrupts exists when using the ucb1400, as
+ * the codec read/write routines may sleep while waiting for codec
+ * access completion and uses semaphores for access control to the
+ * AC97 bus.  A complete codec read cycle could take  anywhere from
+ * 60 to 100uSec so we *definitely* don't want to spin inside the
+ * interrupt handler waiting for codec access.  So, we handle the
+ * interrupt by scheduling a RT kernel thread to run in process
+ * context instead of interrupt context.
+ */
+
+static int ucb1x00_thread(void *_ucb)
+{
+	struct task_struct *tsk = current;
+	DECLARE_WAITQUEUE(wait, tsk);
+	struct ucb1x00 *ucb = _ucb;
+	struct ucb1x00_irq *irq;
+	unsigned int isr, i;
+
+	ucb->rtask = tsk;
+
+	daemonize();
+	reparent_to_init();
+	tsk->tty = NULL;
+	tsk->policy = SCHED_FIFO;
+	tsk->rt_priority = 1;
+	strcpy(tsk->comm, "kUCB1x00d");
+
+	/* only want to receive SIGKILL */
+	spin_lock_irq(&tsk->sigmask_lock);
+	siginitsetinv(&tsk->blocked, sigmask(SIGKILL));
+	recalc_sigpending();
+	spin_unlock_irq(&tsk->sigmask_lock);
+
+	add_wait_queue(&ucb->irq_wait, &wait);
+	set_task_state(tsk, TASK_INTERRUPTIBLE);
+	complete(&ucb->complete);
+
+	for (;;) {
+		if (signal_pending(tsk))
+			break;
+		enable_irq(ucb->irq);
+		schedule();
+
+		ucb1x00_enable(ucb);
+		isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS);
+		ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr);
+		ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+		for (i = 0, irq = ucb->irq_handler;
+		     i < 16 && isr; 
+		     i++, isr >>= 1, irq++)
+			if (isr & 1 && irq->fn)
+				irq->fn(i, irq->devid);
+		ucb1x00_disable(ucb);
+
+		set_task_state(tsk, TASK_INTERRUPTIBLE);
+	}
+
+	remove_wait_queue(&ucb->irq_wait, &wait);
+	ucb->rtask = NULL;
+	complete_and_exit(&ucb->complete, 0);
+}
+
+static irqreturn_t ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs)
+{
+	struct ucb1x00 *ucb = devid;
+	disable_irq(irqnr);
+	wake_up(&ucb->irq_wait);
+	return IRQ_HANDLED;
+}
+
+/**
+ *	ucb1x00_hook_irq - hook a UCB1x00 interrupt
+ *	@ucb:   UCB1x00 structure describing chip
+ *	@idx:   interrupt index
+ *	@fn:    function to call when interrupt is triggered
+ *	@devid: device id to pass to interrupt handler
+ *
+ *	Hook the specified interrupt.  You can only register one handler
+ *	for each interrupt source.  The interrupt source is not enabled
+ *	by this function; use ucb1x00_enable_irq instead.
+ *
+ *	Interrupt handlers will be called with other interrupts enabled.
+ *
+ *	Returns zero on success, or one of the following errors:
+ *	 -EINVAL if the interrupt index is invalid
+ *	 -EBUSY if the interrupt has already been hooked
+ */
+int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid)
+{
+	struct ucb1x00_irq *irq;
+	int ret = -EINVAL;
+
+	if (idx < 16) {
+		irq = ucb->irq_handler + idx;
+		ret = -EBUSY;
+
+		spin_lock_irq(&ucb->lock);
+		if (irq->fn == NULL) {
+			irq->devid = devid;
+			irq->fn = fn;
+			ret = 0;
+		}
+		spin_unlock_irq(&ucb->lock);
+	}
+	return ret;
+}
+
+/**
+ *	ucb1x00_enable_irq - enable an UCB1x00 interrupt source
+ *	@ucb: UCB1x00 structure describing chip
+ *	@idx: interrupt index
+ *	@edges: interrupt edges to enable
+ *
+ *	Enable the specified interrupt to trigger on %UCB_RISING,
+ *	%UCB_FALLING or both edges.  The interrupt should have been
+ *	hooked by ucb1x00_hook_irq.
+ */
+void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges)
+{
+	unsigned long flags;
+
+	if (idx < 16) {
+		spin_lock_irqsave(&ucb->lock, flags);
+
+		ucb1x00_enable(ucb);
+
+		/* This prevents spurious interrupts on the UCB1400 */
+		ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 1 << idx);
+		ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+		if (edges & UCB_RISING) {
+			ucb->irq_ris_enbl |= 1 << idx;
+			ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+		}
+		if (edges & UCB_FALLING) {
+			ucb->irq_fal_enbl |= 1 << idx;
+			ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+		}
+		ucb1x00_disable(ucb);
+		spin_unlock_irqrestore(&ucb->lock, flags);
+	}
+}
+
+/**
+ *	ucb1x00_disable_irq - disable an UCB1x00 interrupt source
+ *	@ucb: UCB1x00 structure describing chip
+ *	@edges: interrupt edges to disable
+ *
+ *	Disable the specified interrupt triggering on the specified
+ *	(%UCB_RISING, %UCB_FALLING or both) edges.
+ */
+void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges)
+{
+	unsigned long flags;
+
+	if (idx < 16) {
+		spin_lock_irqsave(&ucb->lock, flags);
+
+		ucb1x00_enable(ucb);
+		if (edges & UCB_RISING) {
+			ucb->irq_ris_enbl &= ~(1 << idx);
+			ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+		}
+		if (edges & UCB_FALLING) {
+			ucb->irq_fal_enbl &= ~(1 << idx);
+			ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+		}
+		ucb1x00_disable(ucb);
+		spin_unlock_irqrestore(&ucb->lock, flags);
+	}
+}
+
+/**
+ *	ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt
+ *	@ucb: UCB1x00 structure describing chip
+ *	@idx: interrupt index
+ *	@devid: device id.
+ *
+ *	Disable the interrupt source and remove the handler.  devid must
+ *	match the devid passed when hooking the interrupt.
+ *
+ *	Returns zero on success, or one of the following errors:
+ *	 -EINVAL if the interrupt index is invalid
+ *	 -ENOENT if devid does not match
+ */
+int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid)
+{
+	struct ucb1x00_irq *irq;
+	int ret;
+
+	if (idx >= 16)
+		goto bad;
+
+	irq = ucb->irq_handler + idx;
+	ret = -ENOENT;
+
+	spin_lock_irq(&ucb->lock);
+	if (irq->devid == devid) {
+		ucb->irq_ris_enbl &= ~(1 << idx);
+		ucb->irq_fal_enbl &= ~(1 << idx);
+
+		ucb1x00_enable(ucb);
+		ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+		ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
+		ucb1x00_disable(ucb);
+
+		irq->fn = NULL;
+		irq->devid = NULL;
+		ret = 0;
+	}
+	spin_unlock_irq(&ucb->lock);
+	return ret;
+
+bad:
+	printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx);
+	return -EINVAL;
+}
+
+/*
+ * Try to probe our interrupt, rather than relying on lots of
+ * hard-coded machine dependencies.  For reference, the expected
+ * IRQ mappings are:
+ *
+ *  	Machine		Default IRQ
+ *	adsbitsy	IRQ_GPCIN4
+ *	cerf		IRQ_GPIO_UCB1200_IRQ
+ *	flexanet	IRQ_GPIO_GUI
+ *	freebird	IRQ_GPIO_FREEBIRD_UCB1300_IRQ
+ *	graphicsclient	ADS_EXT_IRQ(8)
+ *	graphicsmaster	ADS_EXT_IRQ(8)
+ *	lart		LART_IRQ_UCB1200
+ *	omnimeter	IRQ_GPIO23
+ *	pfs168		IRQ_GPIO_UCB1300_IRQ
+ *	simpad		IRQ_GPIO_UCB1300_IRQ
+ *	shannon		SHANNON_IRQ_GPIO_IRQ_CODEC
+ *	yopy		IRQ_GPIO_UCB1200_IRQ
+ */
+static int ucb1x00_detect_irq(struct ucb1x00 *ucb)
+{
+	unsigned long mask;
+
+	mask = probe_irq_on();
+	if (!mask)
+		return NO_IRQ;
+
+	/*
+	 * Enable the ADC interrupt.
+	 */
+	ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC);
+	ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC);
+	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
+	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+	/*
+	 * Cause an ADC interrupt.
+	 */
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
+
+	/*
+	 * Wait for the conversion to complete.
+	 */
+	while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0);
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, 0);
+
+	/*
+	 * Disable and clear interrupt.
+	 */
+	ucb1x00_reg_write(ucb, UCB_IE_RIS, 0);
+	ucb1x00_reg_write(ucb, UCB_IE_FAL, 0);
+	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
+	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+	/*
+	 * Read triggered interrupt.
+	 */
+	return probe_irq_off(mask);
+}
+
+static int ucb1x00_probe(struct mcp *mcp)
+{
+	struct ucb1x00 *ucb;
+	unsigned int id;
+	int ret = -ENODEV;
+
+	mcp_enable(mcp);
+	id = mcp_reg_read(mcp, UCB_ID);
+
+	if (id != UCB_ID_1200 && id != UCB_ID_1300) {
+		printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
+		goto err_disable;
+	}
+
+	ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL);
+	ret = -ENOMEM;
+	if (!ucb)
+		goto err_disable;
+
+	memset(ucb, 0, sizeof(struct ucb1x00));
+
+	ucb->cdev.class = &ucb1x00_class;
+	ucb->cdev.dev = &mcp->attached_device;
+	strlcpy(ucb->cdev.class_id, "ucb1x00", sizeof(ucb->cdev.class_id));
+
+	spin_lock_init(&ucb->lock);
+	spin_lock_init(&ucb->io_lock);
+	sema_init(&ucb->adc_sem, 1);
+
+	ucb->id  = id;
+	ucb->mcp = mcp;
+	ucb->irq = ucb1x00_detect_irq(ucb);
+	if (ucb->irq == NO_IRQ) {
+		printk(KERN_ERR "UCB1x00: IRQ probe failed\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	ret = request_irq(ucb->irq, ucb1x00_irq, 0, "UCB1x00", ucb);
+	if (ret) {
+		printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
+			ucb->irq, ret);
+		goto err_free;
+	}
+
+	set_irq_type(ucb->irq, IRQT_RISING);
+	mcp_set_drvdata(mcp, ucb);
+
+	ret = class_device_register(&ucb->cdev);
+	if (ret) {
+		free_irq(ucb->irq, ucb);
+ err_free:
+		kfree(ucb);
+	}
+ err_disable:
+	mcp_disable(mcp);
+	return ret;
+}
+
+static void ucb1x00_remove(struct mcp *mcp)
+{
+	struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
+
+	class_device_unregister(&ucb->cdev);
+	free_irq(ucb->irq, ucb);
+}
+
+static void ucb1x00_release(struct class_device *dev)
+{
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);
+	kfree(ucb);
+}
+
+static struct class ucb1x00_class = {
+	.name		= "ucb1x00",
+	.release	= ucb1x00_release,
+};
+
+int ucb1x00_register_interface(struct class_interface *intf)
+{
+	intf->class = &ucb1x00_class;
+	return class_interface_register(intf);
+}
+
+void ucb1x00_unregister_interface(struct class_interface *intf)
+{
+	class_interface_unregister(intf);
+}
+
+static struct mcp_driver ucb1x00_driver = {
+	.drv		= {
+		.name	= "ucb1x00",
+	},
+	.probe		= ucb1x00_probe,
+	.remove		= ucb1x00_remove,
+};
+
+static int __init ucb1x00_init(void)
+{
+	int ret = class_register(&ucb1x00_class);
+	if (ret == 0) {
+		ret = mcp_driver_register(&ucb1x00_driver);
+		if (ret)
+			class_unregister(&ucb1x00_class);
+	}
+	return ret;
+}
+
+static void __exit ucb1x00_exit(void)
+{
+	mcp_driver_unregister(&ucb1x00_driver);
+	class_unregister(&ucb1x00_class);
+}
+
+module_init(ucb1x00_init);
+module_exit(ucb1x00_exit);
+
+EXPORT_SYMBOL(ucb1x00_class);
+
+EXPORT_SYMBOL(ucb1x00_io_set_dir);
+EXPORT_SYMBOL(ucb1x00_io_write);
+EXPORT_SYMBOL(ucb1x00_io_read);
+
+EXPORT_SYMBOL(ucb1x00_adc_enable);
+EXPORT_SYMBOL(ucb1x00_adc_read);
+EXPORT_SYMBOL(ucb1x00_adc_disable);
+
+EXPORT_SYMBOL(ucb1x00_hook_irq);
+EXPORT_SYMBOL(ucb1x00_free_irq);
+EXPORT_SYMBOL(ucb1x00_enable_irq);
+EXPORT_SYMBOL(ucb1x00_disable_irq);
+
+EXPORT_SYMBOL(ucb1x00_register_interface);
+EXPORT_SYMBOL(ucb1x00_unregister_interface);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("UCB1x00 core driver");
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/switches-ucb1x00.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,214 @@
+/*
+ *  linux/drivers/misc/switches-ucb1x00.c
+ *
+ *  Copyright (C) 2001 John Dorsey
+ *
+ *  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.
+ *
+ *  19 December 2001 - created from sa1100_switches.c.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+
+#include <asm/dma.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#ifdef CONFIG_SA1100_ASSABET
+#include <asm/arch/assabet.h>
+#endif
+
+#include "switches.h"
+#include "ucb1x00.h"
+
+
+static void switches_ucb1x00_handler(int irq, void *devid);
+
+
+#ifdef CONFIG_SA1100_ASSABET
+
+/* Assabet
+ * ^^^^^^^
+ * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8.
+ * This code sets bits in the range [3, 8] in the mask that we
+ * return to userland. Note that we transpose signals SW7 and SW8;
+ * see assabet_switches_ucb1x00_handler().
+ */
+
+static int assabet_switches_ucb1x00_init(struct ucb1x00 *ucb)
+{
+	int i;
+
+	ucb1x00_enable(ucb);
+
+	ucb1x00_io_set_dir(ucb,
+			   UCB_IO_0 | UCB_IO_1 | UCB_IO_2 |
+			   UCB_IO_3 | UCB_IO_4 | UCB_IO_5, 0);
+
+	for (i = 0; i < 6; ++i) {
+		ucb1x00_enable_irq(ucb, i, UCB_RISING | UCB_FALLING);
+
+		if (ucb1x00_hook_irq(ucb, i,
+				     switches_ucb1x00_handler, ucb) < 0) {
+			printk(KERN_ERR "%s: unable to hook IRQ for "
+			       "UCB1300 IO_%d\n", SWITCHES_NAME, i);
+
+			/* FIXME: BUGGY ERROR HANDLING */
+			return -EBUSY;
+		}
+
+	}
+
+	ucb1x00_disable(ucb);
+
+	return 0;
+
+}
+
+static void assabet_switches_ucb1x00_shutdown(struct ucb1x00 *ucb)
+{
+	int i;
+
+	ucb1x00_enable(ucb);
+
+	for (i = 5; i >= 0; --i) {
+		ucb1x00_disable_irq(ucb, i, UCB_RISING | UCB_FALLING);
+
+		/* Only error conditions are ENOENT and EINVAL; silently
+		 * ignore:
+		 */
+		ucb1x00_free_irq(ucb, i, ucb);
+	}
+
+	ucb1x00_disable(ucb);
+}
+
+static void assabet_switches_ucb1x00_handler(struct ucb1x00 *ucb, int irq, switches_mask_t *mask)
+{
+	unsigned int last, this;
+	static unsigned int states = 0;
+
+	last = ((states & (1 << irq)) != 0);
+	this = ((ucb1x00_io_read(ucb) & (1 << irq)) != 0);
+
+	if (last == this) /* debounce */
+		return;
+
+	/* Intel StrongARM SA-1110 Development Board
+	 * Schematics Figure 5, Sheet 5 of 12
+	 *
+	 * See switches S8 and S7. Notice their
+	 * relationship to signals SW7 and SW8. Hmmm.
+	 */
+
+	switch (irq) {
+
+	case 4:
+
+		SWITCHES_SET(mask, 8, this);
+		break;
+
+	case 5:
+
+		SWITCHES_SET(mask, 7, this);
+		break;
+
+	default:
+
+		SWITCHES_SET(mask, irq + 3, this);
+
+	}
+
+	states = this ? (states | (1 << irq)) : (states & ~(1 << irq));
+
+}
+#endif  /* CONFIG_SA1100_ASSABET */
+
+
+/* switches_ucb1x00_handler()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * This routine is a generalized handler for UCB1x00 GPIO switches
+ * which calls a board-specific service routine and passes an event
+ * mask to the core event handler. This routine is appropriate for
+ * systems which use the ucb1x00 framework, and can be registered
+ * using ucb1x00_hook_irq().
+ */
+static void switches_ucb1x00_handler(int irq, void *devid)
+{
+	struct ucb1x00 *ucb = devid;
+	switches_mask_t mask;
+
+	SWITCHES_ZERO(&mask);
+
+	/* Porting note: call a board-specific UCB1x00 switch handler here.
+	 * The handler can assume that sufficient storage for `mask' has
+	 * been allocated, and that the corresponding switches_mask_t
+	 * structure has been zeroed.
+	 */
+
+#ifdef CONFIG_SA1100_ASSABET
+	if (machine_is_assabet()) {
+		assabet_switches_ucb1x00_handler(ucb, irq, &mask);
+	}
+#endif
+
+	switches_event(&mask);
+}
+
+static int switches_add(struct class_device *dev)
+{
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);
+	int ret = -ENODEV;
+
+#ifdef CONFIG_SA1100_ASSABET
+	if (machine_is_assabet()) {
+		ret = assabet_switches_ucb1x00_init(ucb);
+	}
+#endif
+	/* Porting note: call a board-specific init routine here. */
+
+	return ret;
+}
+
+static void switches_remove(struct class_device *dev)
+{
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);
+
+	/* Porting note: call a board-specific shutdown routine here. */
+
+#ifdef CONFIG_SA1100_ASSABET
+	if (machine_is_assabet()) {
+		assabet_switches_ucb1x00_shutdown(ucb);
+	}
+#endif
+}
+
+static struct class_interface ucb1x00_switches_interface = {
+	.add	= switches_add,
+	.remove	= switches_remove,
+};
+
+static int __init switches_ucb1x00_init(void)
+{
+	return ucb1x00_register_interface(&ucb1x00_switches_interface);
+}
+
+static void __exit switches_ucb1x00_exit(void)
+{
+	ucb1x00_unregister_interface(&ucb1x00_switches_interface);
+}
+
+module_init(switches_ucb1x00_init);
+module_exit(switches_ucb1x00_exit);
+
+MODULE_DESCRIPTION("ucb1x00 switches driver");
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/switches-core.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,200 @@
+/*
+ *  linux/drivers/misc/switches-core.c
+ *
+ *  Copyright (C) 2000-2001 John Dorsey
+ *
+ *  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.
+ *
+ *  5 October 2000 - created.
+ *
+ *  25 October 2000 - userland file interface added.
+ *
+ *  13 January 2001 - added support for Spot.
+ *
+ *  11 September 2001 - UCB1200 driver framework support added.
+ *
+ *  19 December 2001 - separated out SA-1100 and UCB1x00 code.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <asm/uaccess.h>
+
+#include "switches.h"
+
+
+MODULE_AUTHOR("John Dorsey");
+MODULE_DESCRIPTION("Console switch support");
+MODULE_LICENSE("GPL");
+
+
+struct switches_action {
+	struct list_head list;
+	switches_mask_t  mask;
+};
+
+
+static int switches_users = 0;
+
+static spinlock_t switches_lock = SPIN_LOCK_UNLOCKED;
+
+DECLARE_WAIT_QUEUE_HEAD(switches_wait);
+LIST_HEAD(switches_event_queue);
+
+
+static ssize_t switches_read(struct file *file, char *buffer,
+			     size_t count, loff_t *pos)
+{
+	unsigned long flags;
+	struct list_head *event;
+	struct switches_action *action;
+
+	if (count < sizeof(struct switches_mask_t))
+		return -EINVAL;
+
+	while (list_empty(&switches_event_queue)) {
+
+		if (file->f_flags & O_NDELAY)
+			return -EAGAIN;
+
+		interruptible_sleep_on(&switches_wait);
+
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+
+	}
+
+	if (verify_area(VERIFY_WRITE, buffer, sizeof(struct switches_mask_t)))
+		return -EFAULT;
+
+	spin_lock_irqsave(&switches_lock, flags);
+
+	event = switches_event_queue.next;
+	action = list_entry(event, struct switches_action, list);
+	copy_to_user(buffer, &(action->mask), sizeof(struct switches_mask_t));
+	list_del(event);
+	kfree(action);
+
+	spin_unlock_irqrestore(&switches_lock, flags);
+
+	return 0;
+
+}
+
+static ssize_t switches_write(struct file *file, const char *buffer,
+			      size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+static unsigned int switches_poll(struct file *file, poll_table *wait)
+{
+
+	poll_wait(file, &switches_wait, wait);
+
+	if (!list_empty(&switches_event_queue))
+		return POLLIN | POLLRDNORM;
+
+	return 0;
+
+}
+
+static int switches_open(struct inode *inode, struct file *file)
+{
+
+	if (switches_users > 0)
+		return -EBUSY;
+
+	++switches_users;
+	return 0;
+
+}
+
+static int switches_release(struct inode *inode, struct file *file)
+{
+
+	--switches_users;
+	return 0;
+
+}
+
+static struct file_operations switches_ops = {
+	.owner		= THIS_MODULE,
+	.read		= switches_read,
+	.write		= switches_write,
+	.poll		= switches_poll,
+	.open		= switches_open,
+	.release	= switches_release,
+};
+
+static struct miscdevice switches_misc = {
+	.minor		= MISC_DYNAMIC_MINOR,
+	.name		= SWITCHES_NAME,
+	.fops		= &switches_ops,
+};
+
+int switches_event(switches_mask_t *mask)
+{
+	struct switches_action *action;
+
+	if ((switches_users > 0) && (SWITCHES_COUNT(mask) > 0)) {
+
+		if ((action = (struct switches_action *)
+		     kmalloc(sizeof(struct switches_action),
+			     GFP_KERNEL)) == NULL) {
+			printk(KERN_ERR "%s: unable to allocate action "
+			       "descriptor\n", SWITCHES_NAME);
+			return -1;
+		}
+
+		action->mask = *mask;
+
+		spin_lock(&switches_lock);
+		list_add_tail(&action->list, &switches_event_queue);
+		spin_unlock(&switches_lock);
+
+		wake_up_interruptible(&switches_wait);
+
+	}
+
+	return 0;
+
+}
+
+EXPORT_SYMBOL(switches_event);
+
+
+static int __init switches_init(void)
+{
+	if (misc_register(&switches_misc) < 0) {
+		printk(KERN_ERR "%s: unable to register misc device\n",
+		       SWITCHES_NAME);
+		return -EIO;
+	}
+
+	printk(KERN_INFO "Console switches initialized\n");
+
+	return 0;
+}
+
+static void __exit switches_exit(void)
+{
+	if (misc_deregister(&switches_misc) < 0)
+		printk(KERN_ERR "%s: unable to deregister misc device\n",
+		       SWITCHES_NAME);
+}
+
+module_init(switches_init);
+module_exit(switches_exit);
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/ucb1x00-audio.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,439 @@
+/*
+ *  linux/drivers/misc/ucb1x00-audio.c
+ *
+ *  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 version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/list.h>
+#include <linux/device.h>
+
+#include <asm/dma.h>
+#include <asm/hardware.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#include "ucb1x00.h"
+
+#include "../sound/oss/sa1100-audio.h"
+
+#define MAGIC	0x41544154
+
+struct ucb1x00_audio {
+	struct file_operations	fops;
+	struct file_operations	mops;
+	struct ucb1x00		*ucb;
+	audio_stream_t		output_stream;
+	audio_stream_t		input_stream;
+	audio_state_t		state;
+	unsigned int		rate;
+	int			dev_id;
+	int			mix_id;
+	unsigned int		daa_oh_bit;
+	unsigned int		telecom;
+	unsigned int		magic;
+	unsigned int		ctrl_a;
+	unsigned int		ctrl_b;
+
+	/* mixer info */
+	unsigned int		mod_cnt;
+	unsigned short		output_level;
+	unsigned short		input_level;
+};
+
+#define REC_MASK	(SOUND_MASK_VOLUME | SOUND_MASK_MIC)
+#define DEV_MASK	REC_MASK
+
+static int
+ucb1x00_mixer_ioctl(struct inode *ino, struct file *filp, uint cmd, ulong arg)
+{
+	struct ucb1x00_audio *ucba;
+	unsigned int val, gain;
+	int ret = 0;
+
+	ucba = list_entry(filp->f_op, struct ucb1x00_audio, mops);
+
+	if (_IOC_TYPE(cmd) != 'M')
+		return -EINVAL;
+
+	if (cmd == SOUND_MIXER_INFO) {
+		struct mixer_info mi;
+
+		strncpy(mi.id, "UCB1x00", sizeof(mi.id));
+		strncpy(mi.name, "Philips UCB1x00", sizeof(mi.name));
+		mi.modify_counter = ucba->mod_cnt;
+		return copy_to_user((void *)arg, &mi, sizeof(mi)) ? -EFAULT : 0;
+	}
+
+	if (_IOC_DIR(cmd) & _IOC_WRITE) {
+		unsigned int left, right;
+
+		ret = get_user(val, (unsigned int *)arg);
+		if (ret)
+			goto out;
+
+		left  = val & 255;
+		right = val >> 8;
+
+		if (left > 100)
+			left = 100;
+		if (right > 100)
+			right = 100;
+
+		gain = (left + right) / 2;
+
+		ret = -EINVAL;
+		if (!ucba->telecom) {
+			switch(_IOC_NR(cmd)) {
+			case SOUND_MIXER_VOLUME:
+				ucba->output_level = gain | gain << 8;
+				ucba->mod_cnt++;
+				ucba->ctrl_b = (ucba->ctrl_b & 0xff00) |
+					       ((gain * 31) / 100);
+				ucb1x00_reg_write(ucba->ucb, UCB_AC_B,
+						  ucba->ctrl_b);
+				ret = 0;
+				break;
+
+			case SOUND_MIXER_MIC:
+				ucba->input_level = gain | gain << 8;
+				ucba->mod_cnt++;
+				ucba->ctrl_a = (ucba->ctrl_a & 0x7f) |
+					       (((gain * 31) / 100) << 7);
+				ucb1x00_reg_write(ucba->ucb, UCB_AC_A,
+						  ucba->ctrl_a);
+				ret = 0;
+				break;
+			}
+		}
+	}
+
+	if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_VOLUME:
+			val = ucba->output_level;
+			break;
+
+		case SOUND_MIXER_MIC:
+			val = ucba->input_level;
+			break;
+
+		case SOUND_MIXER_RECSRC:
+		case SOUND_MIXER_RECMASK:
+			val = ucba->telecom ? 0 : REC_MASK;
+			break;
+
+		case SOUND_MIXER_DEVMASK:
+			val = ucba->telecom ? 0 : DEV_MASK;
+			break;
+
+		case SOUND_MIXER_CAPS:
+		case SOUND_MIXER_STEREODEVS:
+			val = 0;
+			break;
+
+		default:
+			val = 0;
+			ret = -EINVAL;
+			break;
+		}
+
+		if (ret == 0)
+			ret = put_user(val, (int *)arg);
+	}
+ out:
+	return ret;
+}
+
+static int ucb1x00_audio_setrate(struct ucb1x00_audio *ucba, int rate)
+{
+	unsigned int div_rate = ucb1x00_clkrate(ucba->ucb) / 32;
+	unsigned int div;
+
+	div = (div_rate + (rate / 2)) / rate;
+	if (div < 6)
+		div = 6;
+	if (div > 127)
+		div = 127;
+
+	ucba->ctrl_a = (ucba->ctrl_a & ~0x7f) | div;
+
+	if (ucba->telecom) {
+		ucb1x00_reg_write(ucba->ucb, UCB_TC_B, 0);
+		ucb1x00_set_telecom_divisor(ucba->ucb, div * 32);
+		ucb1x00_reg_write(ucba->ucb, UCB_TC_A, ucba->ctrl_a);
+		ucb1x00_reg_write(ucba->ucb, UCB_TC_B, ucba->ctrl_b);
+	} else {
+		ucb1x00_reg_write(ucba->ucb, UCB_AC_B, 0);
+		ucb1x00_set_audio_divisor(ucba->ucb, div * 32);
+		ucb1x00_reg_write(ucba->ucb, UCB_AC_A, ucba->ctrl_a);
+		ucb1x00_reg_write(ucba->ucb, UCB_AC_B, ucba->ctrl_b);
+	}
+
+	ucba->rate = div_rate / div;
+
+	return ucba->rate;
+}
+
+static int ucb1x00_audio_getrate(struct ucb1x00_audio *ucba)
+{
+	return ucba->rate;
+}
+
+static void ucb1x00_audio_startup(void *data)
+{
+	struct ucb1x00_audio *ucba = data;
+
+	ucb1x00_enable(ucba->ucb);
+	ucb1x00_audio_setrate(ucba, ucba->rate);
+
+	ucb1x00_reg_write(ucba->ucb, UCB_MODE, UCB_MODE_DYN_VFLAG_ENA);
+
+	/*
+	 * Take off-hook
+	 */
+	if (ucba->daa_oh_bit)
+		ucb1x00_io_write(ucba->ucb, 0, ucba->daa_oh_bit);
+}
+
+static void ucb1x00_audio_shutdown(void *data)
+{
+	struct ucb1x00_audio *ucba = data;
+
+	/*
+	 * Place on-hook
+	 */
+	if (ucba->daa_oh_bit)
+		ucb1x00_io_write(ucba->ucb, ucba->daa_oh_bit, 0);
+
+	ucb1x00_reg_write(ucba->ucb, ucba->telecom ? UCB_TC_B : UCB_AC_B, 0);
+	ucb1x00_disable(ucba->ucb);
+}
+
+static int
+ucb1x00_audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+	struct ucb1x00_audio *ucba;
+	int val, ret = 0;
+
+	ucba = list_entry(file->f_op, struct ucb1x00_audio, fops);
+
+	/*
+	 * Make sure we have our magic number
+	 */
+	if (ucba->magic != MAGIC)
+		return -ENODEV;
+
+	switch (cmd) {
+	case SNDCTL_DSP_STEREO:
+		ret = get_user(val, (int *)arg);
+		if (ret)
+			return ret;
+		if (val != 0)
+			return -EINVAL;
+		val = 0;
+		break;
+
+	case SNDCTL_DSP_CHANNELS:
+	case SOUND_PCM_READ_CHANNELS:
+		val = 1;
+		break;
+
+	case SNDCTL_DSP_SPEED:
+		ret = get_user(val, (int *)arg);
+		if (ret)
+			return ret;
+		val = ucb1x00_audio_setrate(ucba, val);
+		break;
+
+	case SOUND_PCM_READ_RATE:
+		val = ucb1x00_audio_getrate(ucba);
+		break;
+
+	case SNDCTL_DSP_SETFMT:
+	case SNDCTL_DSP_GETFMTS:
+		val = AFMT_S16_LE;
+		break;
+
+	default:
+		return ucb1x00_mixer_ioctl(inode, file, cmd, arg);
+	}
+
+	return put_user(val, (int *)arg);
+}
+
+static int ucb1x00_audio_open(struct inode *inode, struct file *file)
+{
+	struct ucb1x00_audio *ucba;
+
+	ucba = list_entry(file->f_op, struct ucb1x00_audio, fops);
+
+	return sa1100_audio_attach(inode, file, &ucba->state);
+}
+
+static struct ucb1x00_audio *ucb1x00_audio_alloc(struct ucb1x00 *ucb)
+{
+	struct ucb1x00_audio *ucba;
+
+	ucba = kmalloc(sizeof(*ucba), GFP_KERNEL);
+	if (ucba) {
+		memset(ucba, 0, sizeof(*ucba));
+
+		ucba->magic = MAGIC;
+		ucba->ucb = ucb;
+		ucba->fops.owner = THIS_MODULE;
+		ucba->fops.open  = ucb1x00_audio_open;
+		ucba->mops.owner = THIS_MODULE;
+		ucba->mops.ioctl = ucb1x00_mixer_ioctl;
+		ucba->state.output_stream = &ucba->output_stream;
+		ucba->state.input_stream = &ucba->input_stream;
+		ucba->state.data = ucba;
+		ucba->state.hw_init = ucb1x00_audio_startup;
+		ucba->state.hw_shutdown = ucb1x00_audio_shutdown;
+		ucba->state.client_ioctl = ucb1x00_audio_ioctl;
+
+		/* There is a bug in the StrongARM causes corrupt MCP data to be sent to
+		 * the codec when the FIFOs are empty and writes are made to the OS timer
+		 * match register 0. To avoid this we must make sure that data is always
+		 * sent to the codec.
+		 */
+		ucba->state.need_tx_for_rx = 1;
+
+		init_MUTEX(&ucba->state.sem);
+		ucba->rate = 8000;
+	}
+	return ucba;
+}
+
+static struct ucb1x00_audio *ucb1x00_audio_add_one(struct ucb1x00 *ucb, int telecom)
+{
+	struct ucb1x00_audio *a;
+
+	a = ucb1x00_audio_alloc(ucb);
+	if (a) {
+		a->telecom = telecom;
+
+		a->input_stream.dev = ucb->cdev.dev;
+		a->output_stream.dev = ucb->cdev.dev;
+		a->ctrl_a = 0;
+
+		if (a->telecom) {
+			a->input_stream.dma_dev = ucb->mcp->dma_telco_rd;
+			a->input_stream.id = "UCB1x00 telco in";
+			a->output_stream.dma_dev = ucb->mcp->dma_telco_wr;
+			a->output_stream.id = "UCB1x00 telco out";
+			a->ctrl_b = UCB_TC_B_IN_ENA|UCB_TC_B_OUT_ENA;
+#if 0
+			a->daa_oh_bit = UCB_IO_8;
+
+			ucb1x00_enable(ucb);
+			ucb1x00_io_write(ucb, a->daa_oh_bit, 0);
+			ucb1x00_io_set_dir(ucb, UCB_IO_7 | UCB_IO_6, a->daa_oh_bit);
+			ucb1x00_disable(ucb);
+#endif
+		} else {
+			a->input_stream.dma_dev  = ucb->mcp->dma_audio_rd;
+			a->input_stream.id = "UCB1x00 audio in";
+			a->output_stream.dma_dev = ucb->mcp->dma_audio_wr;
+			a->output_stream.id = "UCB1x00 audio out";
+			a->ctrl_b = UCB_AC_B_IN_ENA|UCB_AC_B_OUT_ENA;
+		}
+
+		a->dev_id = register_sound_dsp(&a->fops, -1);
+		a->mix_id = register_sound_mixer(&a->mops, -1);
+
+		printk("Sound: UCB1x00 %s: dsp id %d mixer id %d\n",
+			a->telecom ? "telecom" : "audio",
+			a->dev_id, a->mix_id);
+	}
+
+	return a;
+}
+
+static void ucb1x00_audio_remove_one(struct ucb1x00_audio *a)
+{
+	unregister_sound_dsp(a->dev_id);
+	unregister_sound_mixer(a->mix_id);
+	kfree(a);
+}
+
+static int ucb1x00_audio_add(struct class_device *cdev)
+{
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(cdev);
+
+	if (ucb->cdev.dev == NULL || ucb->cdev.dev->dma_mask == NULL)
+		return -ENXIO;
+
+	ucb->audio_data = ucb1x00_audio_add_one(ucb, 0);
+	ucb->telecom_data = ucb1x00_audio_add_one(ucb, 1);
+
+	return 0;
+}
+
+static void ucb1x00_audio_remove(struct class_device *cdev)
+{
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(cdev);
+
+	ucb1x00_audio_remove_one(ucb->audio_data);
+	ucb1x00_audio_remove_one(ucb->telecom_data);
+}
+
+#if 0 //def CONFIG_PM
+static int ucb1x00_audio_suspend(struct ucb1x00 *ucb, u32 state)
+{
+	struct ucb1x00_audio *a;
+
+	a = ucb->audio_data;
+	sa1100_audio_suspend(&a->state, state);
+	a = ucb->telecom_data;
+	sa1100_audio_suspend(&a->state, state);
+
+	return 0;
+}
+
+static int ucb1x00_audio_resume(struct ucb1x00 *ucb)
+{
+	struct ucb1x00_audio *a;
+
+	a = ucb->audio_data;
+	sa1100_audio_resume(&a->state);
+	a = ucb->telecom_data;
+	sa1100_audio_resume(&a->state);
+
+	return 0;
+}
+#else
+#define ucb1x00_audio_suspend	NULL
+#define ucb1x00_audio_resume	NULL
+#endif
+
+static struct class_interface ucb1x00_audio_interface = {
+	.add		= ucb1x00_audio_add,
+	.remove		= ucb1x00_audio_remove,
+};
+
+static int __init ucb1x00_audio_init(void)
+{
+	return ucb1x00_register_interface(&ucb1x00_audio_interface);
+}
+
+static void __exit ucb1x00_audio_exit(void)
+{
+	ucb1x00_unregister_interface(&ucb1x00_audio_interface);
+}
+
+module_init(ucb1x00_audio_init);
+module_exit(ucb1x00_audio_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("UCB1x00 telecom/audio driver");
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/mcp-sa1100.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,275 @@
+/*
+ *  linux/drivers/misc/mcp-sa1100.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * 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.
+ *
+ *  SA1100 MCP (Multimedia Communications Port) driver.
+ *
+ *  MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include <asm/dma.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/system.h>
+
+#include <asm/arch/assabet.h>
+
+#include "mcp.h"
+
+static void
+mcp_sa1100_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
+{
+	unsigned int mccr0;
+
+	divisor /= 32;
+
+	mccr0 = Ser4MCCR0 & ~0x00007f00;
+	mccr0 |= divisor << 8;
+	Ser4MCCR0 = mccr0;
+}
+
+static void
+mcp_sa1100_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
+{
+	unsigned int mccr0;
+
+	divisor /= 32;
+
+	mccr0 = Ser4MCCR0 & ~0x0000007f;
+	mccr0 |= divisor;
+	Ser4MCCR0 = mccr0;
+}
+
+/*
+ * Write data to the device.  The bit should be set after 3 subframe
+ * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
+ * We really should try doing something more productive while we
+ * wait.
+ */
+static void
+mcp_sa1100_write(struct mcp *mcp, unsigned int reg, unsigned int val)
+{
+	int ret = -ETIME;
+	int i;
+
+	Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);
+
+	for (i = 0; i < 2; i++) {
+		udelay(mcp->rw_timeout);
+		if (Ser4MCSR & MCSR_CWC) {
+			ret = 0;
+			break;
+		}
+	}
+
+	if (ret < 0)
+		printk(KERN_WARNING "mcp: write timed out\n");
+}
+
+/*
+ * Read data from the device.  The bit should be set after 3 subframe
+ * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
+ * We really should try doing something more productive while we
+ * wait.
+ */
+static unsigned int
+mcp_sa1100_read(struct mcp *mcp, unsigned int reg)
+{
+	int ret = -ETIME;
+	int i;
+
+	Ser4MCDR2 = reg << 17 | MCDR2_Rd;
+
+	for (i = 0; i < 2; i++) {
+		udelay(mcp->rw_timeout);
+		if (Ser4MCSR & MCSR_CRC) {
+			ret = Ser4MCDR2 & 0xffff;
+			break;
+		}
+	}
+
+	if (ret < 0)
+		printk(KERN_WARNING "mcp: read timed out\n");
+
+	return ret;
+}
+
+static void mcp_sa1100_enable(struct mcp *mcp)
+{
+	Ser4MCSR = -1;
+	Ser4MCCR0 |= MCCR0_MCE;
+}
+
+static void mcp_sa1100_disable(struct mcp *mcp)
+{
+	Ser4MCCR0 &= ~MCCR0_MCE;
+}
+
+/*
+ * Our methods.
+ */
+static struct mcp mcp_sa1100 = {
+	.owner			= THIS_MODULE,
+	.lock			= SPIN_LOCK_UNLOCKED,
+	.sclk_rate		= 11981000,
+	.dma_audio_rd		= DMA_Ser4MCP0Rd,
+	.dma_audio_wr		= DMA_Ser4MCP0Wr,
+	.dma_telco_rd		= DMA_Ser4MCP1Rd,
+	.dma_telco_wr		= DMA_Ser4MCP1Wr,
+	.set_telecom_divisor	= mcp_sa1100_set_telecom_divisor,
+	.set_audio_divisor	= mcp_sa1100_set_audio_divisor,
+	.reg_write		= mcp_sa1100_write,
+	.reg_read		= mcp_sa1100_read,
+	.enable			= mcp_sa1100_enable,
+	.disable		= mcp_sa1100_disable,
+};
+
+static int mcp_sa1100_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mcp *mcp = &mcp_sa1100;
+	int ret;
+
+	if (!machine_is_adsbitsy()       && !machine_is_assabet()        &&
+	    !machine_is_cerf()           && !machine_is_flexanet()       &&
+	    !machine_is_freebird()       && !machine_is_graphicsclient() &&
+	    !machine_is_graphicsmaster() && !machine_is_lart()           &&
+	    !machine_is_omnimeter()      && !machine_is_pfs168()         &&
+	    !machine_is_shannon()        && !machine_is_simpad()         &&
+	    !machine_is_yopy())
+		return -ENODEV;
+
+	if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))
+		return -EBUSY;
+
+	mcp->me = dev;
+	dev_set_drvdata(dev, mcp);
+
+	if (machine_is_assabet()) {
+		ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
+	}
+
+	/*
+	 * Setup the PPC unit correctly.
+	 */
+	PPDR &= ~PPC_RXD4;
+	PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
+	PSDR |= PPC_RXD4;
+	PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+	PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
+	Ser4MCSR = -1;
+	Ser4MCCR1 = 0;
+	Ser4MCCR0 = 0x00007f7f | MCCR0_ADM;
+
+	/*
+	 * Calculate the read/write timeout (us) from the bit clock
+	 * rate.  This is the period for 3 64-bit frames.  Always
+	 * round this time up.
+	 */
+	mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
+			  mcp->sclk_rate;
+
+	ret = mcp_host_register(mcp, &pdev->dev);
+	if (ret != 0) {
+		release_mem_region(0x80060000, 0x60);
+		dev_set_drvdata(dev, NULL);
+	}
+
+	return ret;
+}
+
+static int mcp_sa1100_remove(struct device *dev)
+{
+	struct mcp *mcp = dev_get_drvdata(dev);
+
+	dev_set_drvdata(dev, NULL);
+
+	mcp_host_unregister(mcp);
+	release_mem_region(0x80060000, 0x60);
+
+	return 0;
+}
+
+struct mcp_sa1100_state {
+	u32	mccr0;
+	u32	mccr1;
+};
+
+static int mcp_sa1100_suspend(struct device *dev, u32 state, u32 level)
+{
+	struct mcp_sa1100_state *s = (struct mcp_sa1100_state *)dev->saved_state;
+
+	if (!s) {
+		s = kmalloc(sizeof(struct mcp_sa1100_state), GFP_KERNEL);
+		dev->saved_state = (unsigned char *)s;
+	}
+
+	if (s) {
+		s->mccr0 = Ser4MCCR0;
+		s->mccr1 = Ser4MCCR1;
+	}
+
+	if (level == SUSPEND_DISABLE)
+		Ser4MCCR0 &= ~MCCR0_MCE;
+	return 0;
+}
+
+static int mcp_sa1100_resume(struct device *dev, u32 level)
+{
+	struct mcp_sa1100_state *s = (struct mcp_sa1100_state *)dev->saved_state;
+
+	if (s && level == RESUME_RESTORE_STATE) {
+		Ser4MCCR1 = s->mccr1;
+		Ser4MCCR0 = s->mccr0;
+
+		dev->saved_state = NULL;
+		kfree(s);
+	}
+	return 0;
+}
+
+/*
+ * The driver for the SA11x0 MCP port.
+ */
+static struct device_driver mcp_sa1100_driver = {
+	.name		= "sa11x0-mcp",
+	.bus		= &platform_bus_type,
+	.probe		= mcp_sa1100_probe,
+	.remove		= mcp_sa1100_remove,
+	.suspend	= mcp_sa1100_suspend,
+	.resume		= mcp_sa1100_resume,
+};
+
+/*
+ * This needs re-working
+ */
+static int __init mcp_sa1100_init(void)
+{
+	return driver_register(&mcp_sa1100_driver);
+}
+
+static void __exit mcp_sa1100_exit(void)
+{
+	driver_unregister(&mcp_sa1100_driver);
+}
+
+module_init(mcp_sa1100_init);
+module_exit(mcp_sa1100_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
+MODULE_LICENSE("GPL");
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/mcp.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,58 @@
+/*
+ *  linux/drivers/misc/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 {
+	struct module	*owner;
+	struct device	*me;
+	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;
+	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 *);
+	struct device	attached_device;
+};
+
+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)
+
+int mcp_host_register(struct mcp *, struct device *);
+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 *, u32);
+	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)
+
+#endif
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/ucb1x00-input.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,233 @@
+/*
+ *  linux/drivers/misc/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_1400		0x4304
+#define UCB_ID_1400_BUGGY	0x4303	/* fake ID */
+
+#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;
+
+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];
+};
+
+/**
+ *	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);
+}
+
+struct ucb1x00 *ucb1x00_get(void);
+
+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
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/ucb1x00.h	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,271 @@
+/*
+ *  linux/drivers/misc/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
+
+#ifdef CONFIG_ARCH_PXA
+
+/* ucb1400 aclink register mappings: */
+
+#define UCB_IO_DATA	0x5a
+#define UCB_IO_DIR	0x5c
+#define UCB_IE_RIS	0x5e
+#define UCB_IE_FAL	0x60
+#define UCB_IE_STATUS	0x62
+#define UCB_IE_CLEAR	0x62
+#define UCB_TS_CR	0x64
+#define UCB_ADC_CR	0x66
+#define UCB_ADC_DATA	0x68
+#define UCB_ID		0x7e /* 7c is mfr id, 7e part id (from aclink spec) */
+
+#define UCB_ADC_DAT(x)		((x) & 0x3ff)
+
+#else
+
+/* ucb1x00 SIB register mappings: */
+
+#define UCB_IO_DATA	0x00
+#define UCB_IO_DIR	0x01
+#define UCB_IE_RIS	0x02
+#define UCB_IE_FAL	0x03
+#define UCB_IE_STATUS	0x04
+#define UCB_IE_CLEAR	0x04
+#define UCB_TC_A	0x05
+#define UCB_TC_B	0x06
+#define UCB_AC_A	0x07
+#define UCB_AC_B	0x08
+#define UCB_TS_CR	0x09
+#define UCB_ADC_CR	0x0a
+#define UCB_ADC_DATA	0x0b
+#define UCB_ID		0x0c
+#define UCB_MODE	0x0d
+
+#define UCB_ADC_DAT(x)		(((x) & 0x7fe0) >> 5)
+
+#endif
+
+
+#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_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_LOOP		(1 << 7)	/* UCB1200 */
+#define UCB_TC_A_AMPL		(1 << 7)	/* UCB1300 */
+
+#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_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_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_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_DAT_VAL		(1 << 15)
+
+#define UCB_ID_1200		0x1004
+#define UCB_ID_1300		0x1005
+#define UCB_ID_1400		0x4304
+#define UCB_ID_1400_BUGGY	0x4303	/* fake ID */
+
+#define UCB_MODE_DYN_VFLAG_ENA	(1 << 12)
+#define UCB_MODE_AUD_OFF_CAN	(1 << 13)
+
+#include <linux/completion.h>
+#include "mcp.h"
+
+struct ucb1x00_irq {
+	void *devid;
+	void (*fn)(int, void *);
+};
+
+extern struct class ucb1x00_class;
+
+struct ucb1x00 {
+	spinlock_t		lock;
+	struct mcp		*mcp;
+	unsigned int		irq;
+	struct semaphore	adc_sem;
+	spinlock_t		io_lock;
+	wait_queue_head_t	irq_wait;
+	struct completion	complete;
+	struct task_struct	*rtask;
+	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;
+	void			*audio_data;
+	void			*telecom_data;
+	void			*ts_data;
+};
+
+#define classdev_to_ucb1x00(cd)	container_of(cd, struct ucb1x00, cdev)
+
+int ucb1x00_register_interface(struct class_interface *intf);
+void ucb1x00_unregister_interface(struct class_interface *intf);
+
+/**
+ *	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);
+}
+
+#define ucb1x00_get()	NULL
+
+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
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/misc/switches-sa1100.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,323 @@
+/*
+ *  linux/drivers/misc/switches-sa1100.c
+ *
+ *  Copyright (C) 2001 John Dorsey
+ *
+ *  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.
+ *
+ *  19 December 2001 - created from sa1100_switches.c.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+
+#include <asm/arch/assabet.h>
+#include <asm/arch/neponset.h>
+#include <asm/arch/badge4.h>
+
+#include "switches.h"
+
+
+static irqreturn_t switches_sa1100_handler(int irq, void *dev_id,
+					   struct pt_regs *regs);
+
+
+#ifdef CONFIG_SA1100_ASSABET
+
+/* Assabet
+ * ^^^^^^^
+ * We have two general-purpose switches, S1 and S2, available via GPIO
+ * on Assabet. This code sets bits in the range [1, 2] in the mask that
+ * we return to userland.
+ */
+
+static int assabet_switches_sa1100_init(void)
+{
+
+	if (machine_has_neponset())
+		NCR_0 |= NCR_GP01_OFF;
+
+	if (request_irq(IRQ_GPIO0, switches_sa1100_handler, SA_INTERRUPT,
+			SWITCHES_NAME, NULL) < 0) {
+		printk(KERN_ERR "%s: unable to register IRQ for GPIO 0\n",
+		       SWITCHES_NAME);
+		return -EIO;
+	}
+
+	if (request_irq(IRQ_GPIO1, switches_sa1100_handler, SA_INTERRUPT,
+			SWITCHES_NAME, NULL) < 0) {
+		printk(KERN_ERR "%s: unable to register IRQ for GPIO 1\n",
+		       SWITCHES_NAME);
+		free_irq(IRQ_GPIO0, NULL);
+		return -EIO;
+	}
+
+	set_irq_type(IRQ_GPIO0, IRQT_BOTHEDGE);
+	set_irq_type(IRQ_GPIO1, IRQT_BOTHEDGE);
+
+	return 0;
+
+}
+
+static void assabet_switches_sa1100_shutdown(void)
+{
+
+	free_irq(IRQ_GPIO1, NULL);
+	free_irq(IRQ_GPIO0, NULL);
+
+}
+
+static irqreturn_t assabet_switches_sa1100_handler(int irq, switches_mask_t *mask)
+{
+	unsigned int s, last, this;
+	static unsigned int states = 0;
+
+	switch (irq) {
+
+	case IRQ_GPIO0:	s = 0; break;
+
+	case IRQ_GPIO1:	s = 1; break;
+
+	default:	return IRQ_NONE;
+
+	}
+
+	last = ((states & (1 << s)) != 0);
+	this = ((GPLR & GPIO_GPIO(s)) != 0);
+
+	if (last == this) /* debounce */
+		return IRQ_HANDLED;
+
+	SWITCHES_SET(mask, s + 1, this);
+
+	states = this ? (states | (1 << s)) : (states & ~(1 << s));
+
+	return IRQ_HANDLED;
+}
+#endif  /* CONFIG_SA1100_ASSABET */
+
+#ifdef CONFIG_SA1100_BADGE4
+
+/* BadgePAD 4
+ * ^^^^^^^^^^
+ *
+ * Here we use test point J6 (BADGE4_GPIO_TESTPT_J6 aka GPIO 23) as a
+ * general purpose switch input.   We map this to switch #0.
+ */
+
+#define BADGE4_SW0_GPIO GPIO_GPIO23	/* aka BADGE4_GPIO_TESTPT_J6  */
+#define BADGE4_SW0_IRQ  IRQ_GPIO23
+
+static int badge4_switches_sa1100_init(void)
+{
+	if (request_irq(BADGE4_SW0_IRQ, switches_sa1100_handler, SA_INTERRUPT,
+			SWITCHES_NAME, NULL) < 0) {
+		printk(KERN_ERR "%s: unable to register IRQ for SW0\n",
+		       SWITCHES_NAME);
+		return -EIO;
+	}
+
+	set_irq_type(BADGE4_SW0_IRQ, IRQT_BOTHEDGE);
+
+	return 0;
+}
+
+static void badge4_switches_sa1100_shutdown(void)
+{
+	free_irq(BADGE4_SW0_IRQ, NULL);
+}
+
+static irqreturn_t badge4_switches_sa1100_handler(int irq, switches_mask_t *mask)
+{
+	unsigned int swno, last, this, gpio;
+	static unsigned int states = 0;
+
+	switch (irq) {
+	case BADGE4_SW0_IRQ:
+		swno = 0;
+		gpio = BADGE4_SW0_GPIO;
+		break;
+	default:
+		return IRQ_NONE;
+	}
+
+	last = ((states & gpio) != 0);
+	this = ((GPLR & gpio) != 0);
+
+	if (last == this) /* debounce */
+		return IRQ_HANDLED;
+
+	SWITCHES_SET(mask, swno, this);
+
+	states = this ? (states | gpio) : (states & ~gpio);
+
+	return IRQ_HANDLED;
+}
+#endif /* CONFIG_SA1100_BADGE4 */
+
+
+#ifdef CONFIG_SA1100_SPOT
+
+/* Spot
+ * ^^^^
+ * Spot (R2, R3) has a single general-purpose switch (S1), which is
+ * also the power-on switch. We set bit [1] in the mask we return to
+ * userland.
+ */
+
+static int spot_switches_sa1100_init(void)
+{
+
+	set_GPIO_IRQ_edge(GPIO_SW1, GPIO_BOTH_EDGES);
+
+	if (request_irq(IRQ_GPIO_SW1, switches_sa1100_handler, SA_INTERRUPT,
+			SWITCHES_NAME, NULL) < 0) {
+		printk(KERN_ERR "%s: unable to register IRQ for SW1\n",
+		       SWITCHES_NAME);
+		return -EIO;
+	}
+
+	return 0;
+
+}
+
+static void spot_switches_sa1100_shutdown(void)
+{
+
+	free_irq(IRQ_GPIO_SW1, NULL);
+
+}
+
+static irqreturn_t spot_switches_sa1100_handler(int irq, switches_mask_t *mask)
+{
+	unsigned int s, last, this;
+	static unsigned int states = 0;
+
+	switch (irq) {
+
+	case IRQ_GPIO_SW1:	s = 0; break;
+
+	default:		return IRQ_NONE;
+
+	}
+
+	last = ((states & (1 << s)) != 0);
+	this = ((GPLR & GPIO_GPIO(s)) != 0);
+
+	if (last == this) /* debounce */
+		return IRQ_HANDLED;
+
+	SWITCHES_SET(mask, s + 1, this);
+
+	states = this ? (states | (1 << s)) : (states & ~(1 << s));
+
+	return IRQ_HANDLED;
+
+}
+#endif  /* CONFIG_SA1100_SPOT */
+
+
+/* switches_sa1100_handler()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * This routine is a generalized handler for SA-1100 switches
+ * which manages action descriptors and calls a board-specific
+ * service routine. This routine is appropriate for GPIO switches
+ * or other primary interrupt sources, and can be registered as a
+ * first-class IRQ handler using request_irq().
+ */
+static irqreturn_t switches_sa1100_handler(int irq, void *dev_id,
+					   struct pt_regs *regs)
+{
+	switches_mask_t mask;
+	irqreturn_t ret = IRQ_NONE;
+
+	SWITCHES_ZERO(&mask);
+
+	/* Porting note: call a board-specific switch interrupt handler
+	 * here. The handler can assume that sufficient storage for
+	 * `mask' has been allocated, and that the corresponding
+	 * switches_mask_t structure has been zeroed.
+	 */
+
+	if (machine_is_assabet()) {
+#ifdef CONFIG_SA1100_ASSABET
+		ret = assabet_switches_sa1100_handler(irq, &mask);
+#endif
+	} else if (machine_is_badge4()) {
+#ifdef CONFIG_SA1100_BADGE4
+		ret = badge4_switches_sa1100_handler(irq, &mask);
+#endif
+	} else if (machine_is_spot()) {
+#ifdef CONFIG_SA1100_SPOT
+		ret = spot_switches_sa1100_handler(irq, &mask);
+#endif
+	}
+
+	switches_event(&mask);
+
+	return ret;
+}
+
+int __init switches_sa1100_init(void)
+{
+
+	/* Porting note: call a board-specific init routine here. */
+
+	if (machine_is_assabet()) {
+#ifdef CONFIG_SA1100_ASSABET
+		if (assabet_switches_sa1100_init() < 0)
+			return -EIO;
+#endif
+	} else if (machine_is_badge4()) {
+#ifdef CONFIG_SA1100_BADGE4
+		if (badge4_switches_sa1100_init() < 0)
+			return -EIO;
+#endif
+	} else if (machine_is_spot()) {
+#ifdef CONFIG_SA1100_SPOT
+		if (spot_switches_sa1100_init() < 0)
+			return -EIO;
+#endif
+	}
+
+	return 0;
+
+}
+
+void __exit switches_sa1100_exit(void)
+{
+
+	/* Porting note: call a board-specific shutdown routine here. */
+
+	if (machine_is_assabet()) {
+#ifdef CONFIG_SA1100_ASSABET
+		assabet_switches_sa1100_shutdown();
+#endif
+	} else if (machine_is_badge4()) {
+#ifdef CONFIG_SA1100_BADGE4
+		badge4_switches_sa1100_shutdown();
+#endif
+	} else if (machine_is_spot()) {
+#ifdef CONFIG_SA1100_SPOT
+		spot_switches_sa1100_shutdown();
+#endif
+	}
+
+}
+
+module_init(switches_sa1100_init);
+module_exit(switches_sa1100_exit);
+
+MODULE_DESCRIPTION("SA-1100 switches driver");
+MODULE_LICENSE("GPL");
--- linux-2.6.5/drivers/misc/Makefile~heh	2004-04-03 22:38:17.000000000 -0500
+++ linux-2.6.5/drivers/misc/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -4,3 +4,21 @@
 obj- := misc.o	# Dummy rule to force built-in.o to be made
 
 obj-$(CONFIG_IBM_ASM)	+= ibmasm/
+
+obj-$(CONFIG_MCP)		+= mcp-core.o
+obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-core.o
+obj-$(CONFIG_MCP_UCB1200_AUDIO)	+= ucb1x00-audio.o
+obj-$(CONFIG_MCP_UCB1200_TS)	+= ucb1x00-ts.o
+
+ifeq ($(CONFIG_SA1100_ASSABET),y)
+obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-assabet.o
+endif
+
+obj-$(CONFIG_SWITCHES)		+= switches-core.o
+obj-$(CONFIG_SWITCHES_SA1100)	+= switches-sa1100.o
+obj-$(CONFIG_SWITCHES_UCB1X00)	+= switches-ucb1x00.o
+
+obj-$(CONFIG_MCP_SA1100)	+= mcp-sa1100.o
+
+obj-$(CONFIG_UCB1400_TS)	+= mcp-pxa.o ucb1x00-core.o ucb1x00-ts.o
+
--- linux-2.6.5/drivers/i2c/busses/Kconfig~heh	2004-04-03 22:38:10.000000000 -0500
+++ linux-2.6.5/drivers/i2c/busses/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -70,6 +70,16 @@
 	  This support is also available as a module.  If so, the module
 	  will be called i2c-hydra.
 
+config I2C_BIT_SA1100_GPIO
+	bool "SA1100 I2C GPIO adapter"
+	depends on ARCH_SA1100 && I2C_ALGOBIT
+	help
+	  This supports I2C on the SA11x0 processor GPIO pins.  This
+	  shares support with the L3 driver.
+
+	  This support is also available as a module.  If so, the module
+	  will be called l3-bit-sa1100.
+
 config I2C_I801
 	tristate "Intel 801"
 	depends on I2C && PCI && EXPERIMENTAL
@@ -241,6 +251,18 @@
 	  This support is also available as a module.  If so, the module 
 	  will be called i2c-prosavage.
 
+config I2C_PXA2XX
+	tristate "PXA I2C Interface"
+	depends on I2C && ARCH_PXA
+	select I2C_ALGOPXA
+	help
+	  This supports the use of the PXA I2C interface found on the Intel 
+	  PXA 25x and PXA 26x systems. Say Y if you have one of these. 
+
+          This support is also available as a module. If you want to compile
+          it as a module, say M here and read Documentation/modules.txt.
+          The module will be called i2c-adap-pxa.
+
 config I2C_RPXLITE
 	tristate "Embedded Planet RPX Lite/Classic support"
 	depends on (RPXLITE || RPXCLASSIC) && I2C
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/i2c/busses/pxa2xx_i2c.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,388 @@
+/*
+ *  i2c_adap_pxa.c
+ *
+ *  I2C adapter for the PXA I2C bus access.
+ *
+ *  Copyright (C) 2002 Intrinsyc Software Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  History:
+ *    Apr 2002: Initial version [CS]
+ *    Jun 2002: Properly seperated algo/adap [FB]
+ *    Jan 2003: Fixed several bugs concerning interrupt handling [Kai-Uwe Bloem]
+ *    Jan 2003: added limited signal handling [Kai-Uwe Bloem]
+ *    Jun 2003: updated for 2.5 [Dustin McIntire]
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/arch/irqs.h>              /* for IRQ_I2C */
+
+#include <linux/i2c-pxa.h>
+
+/*
+ * Set this to zero to remove all debug statements via dead code elimination.
+ */
+//#define DEBUG       1
+
+#if DEBUG
+static unsigned int i2c_debug = DEBUG;
+#else
+#define i2c_debug	0
+#endif
+
+static int irq = 0;
+static volatile int i2c_pending = 0;             /* interrupt pending when 1 */
+static volatile int bus_error = 0;
+static volatile int tx_finished = 0;
+static volatile int rx_finished = 0;
+
+static wait_queue_head_t i2c_wait;
+static void i2c_pxa_transfer( int lastbyte, int receive, int midbyte);
+
+/* place a byte in the transmit register */
+static void i2c_pxa_write_byte(u8 value)
+{
+	IDBR = value;
+}
+
+/* read byte in the receive register */
+static u8 i2c_pxa_read_byte(void)
+{
+	return (u8) (0xff & IDBR);
+}
+
+static void i2c_pxa_start(void)
+{
+	unsigned long icr = ICR;
+	icr |= ICR_START;
+	icr &= ~(ICR_STOP | ICR_ALDIE | ICR_ACKNAK);
+	ICR = icr;
+
+	bus_error=0;            /* clear any bus_error from previous txfers */
+	tx_finished=0;          /* clear rx and tx interrupts from previous txfers */
+	rx_finished=0;
+	i2c_pending = 0;
+}
+
+static void i2c_pxa_repeat_start(void)
+{
+	unsigned long icr = ICR;
+	icr |= ICR_START;
+	icr &= ~(ICR_STOP | ICR_ALDIE);
+	ICR = icr;
+
+	bus_error=0;            /* clear any bus_error from previous txfers */
+	tx_finished=0;          /* clear rx and tx interrupts from previous txfers */
+	rx_finished=0;
+	i2c_pending = 0;
+}
+
+static void i2c_pxa_stop(void)
+{
+	unsigned long icr = ICR;
+	icr |= ICR_STOP;
+	icr &= ~(ICR_START);
+	ICR = icr;
+}
+
+static void i2c_pxa_midbyte(void)
+{
+	unsigned long icr = ICR;
+	icr &= ~(ICR_START | ICR_STOP);
+	ICR = icr;
+}
+
+static void i2c_pxa_abort(void)
+{
+	unsigned long timeout = jiffies + HZ/4;
+
+#ifdef PXA_ABORT_MA
+	while ((long)(timeout - jiffies) > 0 && (ICR & ICR_TB)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+
+	ICR |= ICR_MA;
+	udelay(100);
+#else
+	while ((long)(timeout - jiffies) > 0 && (IBMR & 0x1) == 0) {
+		i2c_pxa_transfer( 1, I2C_RECEIVE, 1);
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+#endif
+	ICR &= ~(ICR_MA | ICR_START | ICR_STOP);
+}
+
+static int i2c_pxa_wait_bus_not_busy( void)
+{
+	int timeout = DEF_TIMEOUT;
+
+	while (timeout-- && (ISR & ISR_IBB)) {
+		udelay(100); /* wait for 100 us */
+	}
+
+	return (timeout<=0);
+}
+
+spinlock_t i2c_pxa_irqlock = SPIN_LOCK_UNLOCKED;
+
+static void i2c_pxa_wait_for_ite(void){
+	unsigned long flags;
+	if (irq > 0) {
+	    spin_lock_irqsave(&i2c_pxa_irqlock, flags);
+		if (i2c_pending == 0) {
+			interruptible_sleep_on_timeout(&i2c_wait, I2C_SLEEP_TIMEOUT );
+		}
+		i2c_pending = 0;
+		spin_unlock_irqrestore(&i2c_pxa_irqlock, flags);
+	} else {
+		udelay(100);
+	}
+}
+
+static int i2c_pxa_wait_for_int( int wait_type)
+{
+	int timeout = DEF_TIMEOUT;
+#ifdef DEBUG
+	if (bus_error)
+		printk(KERN_INFO"i2c_pxa_wait_for_int: Bus error on enter\n");
+	if (rx_finished)
+		printk(KERN_INFO"i2c_pxa_wait_for_int: Receive interrupt on enter\n");
+	if (tx_finished)
+		printk(KERN_INFO"i2c_pxa_wait_for_int: Transmit interrupt on enter\n");
+#endif
+
+	if (wait_type == I2C_RECEIVE){         /* wait on receive */
+
+		do {
+			i2c_pxa_wait_for_ite();
+		} while (!(rx_finished) && timeout-- && !signal_pending(current));
+
+#ifdef DEBUG
+		if (timeout<0){
+			if (tx_finished)
+				printk("Error: i2c-algo-pxa.o: received a tx"
+						" interrupt while waiting on a rx in wait_for_int");
+		}
+#endif
+	} else {                  /* wait on transmit */
+
+		do {
+			i2c_pxa_wait_for_ite();
+		} while (!(tx_finished) && timeout-- && !signal_pending(current));
+
+#ifdef DEBUG
+		if (timeout<0){
+			if (rx_finished)
+				printk("Error: i2c-algo-pxa.o: received a rx"
+					" interrupt while waiting on a tx in wait_for_int");
+		}
+#endif
+	}
+
+	udelay(ACK_DELAY);      /* this is needed for the bus error */
+
+	tx_finished=0;
+	rx_finished=0;
+
+	if (bus_error){
+		bus_error=0;
+		if( i2c_debug > 2)printk("wait_for_int: error - no ack.\n");
+		return BUS_ERROR;
+	}
+
+	if (signal_pending(current)) {
+		return (-ERESTARTSYS);
+	} else if (timeout < 0) {
+		if( i2c_debug > 2)printk("wait_for_int: timeout.\n");
+		return(-EIO);
+	} else
+		return(0);
+}
+
+static void i2c_pxa_transfer( int lastbyte, int receive, int midbyte)
+{
+	if( lastbyte)
+	{
+		if( receive==I2C_RECEIVE) ICR |= ICR_ACKNAK;
+		i2c_pxa_stop();
+	}
+	else if( midbyte)
+	{
+		i2c_pxa_midbyte();
+	}
+	ICR |= ICR_TB;
+}
+
+static void i2c_pxa_reset( void)
+{
+#ifdef DEBUG
+	printk("Resetting I2C Controller Unit\n");
+#endif
+
+	/* abort any transfer currently under way */
+	i2c_pxa_abort();
+
+	/* reset according to 9.8 */
+	ICR = ICR_UR;
+	ISR = I2C_ISR_INIT;
+	ICR &= ~ICR_UR;
+
+	/* set the global I2C clock on */
+	CKEN |= CKEN14_I2C;
+
+	/* set our slave address */
+	ISAR = I2C_PXA_SLAVE_ADDR;
+
+	/* set control register values */
+	ICR = I2C_ICR_INIT;
+
+	/* clear any leftover states from prior transmissions */
+	i2c_pending = rx_finished = tx_finished = bus_error = 0;
+
+	/* enable unit */
+	ICR |= ICR_IUE;
+	udelay(100);
+}
+
+static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id, struct pt_regs *regs)
+{
+    unsigned long flags;
+	int status, wakeup = 0;
+	status = (ISR);
+
+	if (status & ISR_BED){
+		(ISR) |= ISR_BED;
+		bus_error=ISR_BED;
+		wakeup = 1;
+	}
+	if (status & ISR_ITE){
+		(ISR) |= ISR_ITE;
+		tx_finished=ISR_ITE;
+		wakeup = 1;
+	}
+	if (status & ISR_IRF){
+		(ISR) |= ISR_IRF;
+		rx_finished=ISR_IRF;
+		wakeup = 1;
+	}
+	if (wakeup) {
+	    spin_lock_irqsave(&i2c_pxa_irqlock, flags);
+		i2c_pending = 1;
+		spin_unlock_irqrestore(&i2c_pxa_irqlock, flags);
+		wake_up_interruptible(&i2c_wait);
+	}
+	return IRQ_HANDLED;
+}
+
+static int i2c_pxa_resource_init( void)
+{
+	init_waitqueue_head(&i2c_wait);
+
+	if (request_irq(IRQ_I2C, &i2c_pxa_handler, SA_INTERRUPT, "I2C", 0) < 0) {
+		irq = 0;
+		if(i2c_debug)
+			printk(KERN_INFO "I2C: Failed to register I2C irq %i\n", IRQ_I2C);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void i2c_pxa_resource_release( void)
+{
+	if( irq > 0)
+	{
+		disable_irq(irq);
+		free_irq(irq,0);
+		irq=0;
+	}
+}
+
+static int i2c_pxa_client_register(struct i2c_client *client)
+{
+	return 0;
+}
+
+static int i2c_pxa_client_unregister(struct i2c_client *client)
+{
+	return 0;
+}
+
+static struct i2c_algo_pxa_data i2c_pxa_data = {
+	write_byte:		i2c_pxa_write_byte,
+	read_byte:		i2c_pxa_read_byte,
+
+	start:			i2c_pxa_start,
+	repeat_start:		i2c_pxa_repeat_start,
+	stop:			i2c_pxa_stop,
+	abort:			i2c_pxa_abort,
+
+	wait_bus_not_busy:	i2c_pxa_wait_bus_not_busy,
+	wait_for_interrupt:	i2c_pxa_wait_for_int,
+	transfer:		i2c_pxa_transfer,
+	reset:			i2c_pxa_reset,
+
+	udelay:			10,
+	timeout:		DEF_TIMEOUT,
+};
+
+static struct i2c_adapter i2c_pxa_ops = {
+	.owner      = THIS_MODULE,
+    .id         = I2C_ALGO_PXA,
+    .algo_data  = &i2c_pxa_data,
+    .dev        = {
+        .name   = "PXA I2C Adapter",
+    },
+	.client_register   = i2c_pxa_client_register,
+	.client_unregister = i2c_pxa_client_unregister,
+	.retries           = 2,
+};
+
+extern int i2c_pxa_add_bus(struct i2c_adapter *);
+extern int i2c_pxa_del_bus(struct i2c_adapter *);
+
+static int __init i2c_adap_pxa_init(void)
+{
+	if( i2c_pxa_resource_init() == 0) {
+
+		if (i2c_pxa_add_bus(&i2c_pxa_ops) < 0) {
+			i2c_pxa_resource_release();
+			printk(KERN_INFO "I2C: Failed to add bus\n");
+			return -ENODEV;
+		}
+	} else {
+		return -ENODEV;
+	}
+
+	printk(KERN_INFO "I2C: Successfully added bus\n");
+
+	return 0;
+}
+
+static void i2c_adap_pxa_exit(void)
+{
+	i2c_pxa_del_bus( &i2c_pxa_ops);
+	i2c_pxa_resource_release();
+
+	printk(KERN_INFO "I2C: Successfully removed bus\n");
+}
+
+module_init(i2c_adap_pxa_init);
+module_exit(i2c_adap_pxa_exit);
--- linux-2.6.5/drivers/i2c/busses/Makefile~heh	2004-04-03 22:36:17.000000000 -0500
+++ linux-2.6.5/drivers/i2c/busses/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -21,6 +21,7 @@
 obj-$(CONFIG_I2C_PARPORT_LIGHT)	+= i2c-parport-light.o
 obj-$(CONFIG_I2C_PIIX4)		+= i2c-piix4.o
 obj-$(CONFIG_I2C_PROSAVAGE)	+= i2c-prosavage.o
+obj-$(CONFIG_I2C_PXA)		+= pxa2xx_i2c.o
 obj-$(CONFIG_I2C_RPXLITE)	+= i2c-rpx.o
 obj-$(CONFIG_I2C_SAVAGE4)	+= i2c-savage4.o
 obj-$(CONFIG_I2C_SIS5595)	+= i2c-sis5595.o
--- linux-2.6.5/drivers/i2c/algos/Kconfig~heh	2004-04-03 22:37:59.000000000 -0500
+++ linux-2.6.5/drivers/i2c/algos/Kconfig	2004-04-30 20:57:36.000000000 -0400
@@ -38,6 +38,18 @@
 	  This support is also available as a module.  If so, the module 
 	  will be called i2c-algo-ite.
 
+config I2C_ALGOPXA
+	tristate "PXA I2C Algorithm"
+	depends on ARCH_PXA && I2C
+	help
+	  This supports the use of the PXA I2C interface found on the Intel
+	  PXA 25x and PXA 26x systems. Say Y if you have one of these. 
+	  You should also say Y for the PXA I2C peripheral driver support below.
+
+	  This support is also available as a module. If you want to compile
+	  it as a module, say M here and read Documentation/modules.txt.
+	  The module will be called i2c-algo-pxa.
+
 config I2C_ALGO8XX
 	tristate "MPC8xx CPM I2C interface"
 	depends on 8xx && I2C
--- /dev/null	2003-09-23 18:19:32.000000000 -0400
+++ linux-2.6.5/drivers/i2c/algos/i2c-algo-pxa.c	2004-04-30 20:57:36.000000000 -0400
@@ -0,0 +1,384 @@
+/*
+ *  i2c-algo-pxa.c
+ *
+ *  I2C algorithm for the PXA I2C bus access.
+ *  Byte driven algorithm similar to pcf.
+ *
+ *  Copyright (C) 2002 Intrinsyc Software Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  History:
+ *    Apr 2002: Initial version [CS]
+ *    Jun 2002: Properly seperated algo/adap [FB]
+ *    Jan 2003: added limited signal handling [Kai-Uwe Bloem]
+ *    Jan 2003: allow SMBUS_QUICK as valid msg [FB]
+ *    Jun 2003: updated for 2.5 [Dustin McIntire]
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>          /* struct i2c_msg and others */
+#include <linux/i2c-id.h>
+
+#include <linux/i2c-pxa.h>
+
+/*
+ * Set this to zero to remove all the debug statements via dead code elimination.
+ */
+//#define DEBUG		1
+
+#if DEBUG
+static unsigned int i2c_debug = DEBUG;
+#else
+#define i2c_debug	0
+#endif
+
+static int pxa_scan = 1;
+
+static int i2c_pxa_valid_messages( struct i2c_msg msgs[], int num)
+{
+	int i;
+	if (num < 1 || num > MAX_MESSAGES){
+		if( i2c_debug)
+			printk(KERN_INFO "Invalid number of messages (max=%d, num=%d)\n",
+				MAX_MESSAGES, num);
+		return -EINVAL;
+	}
+
+	/* check consistency of our messages */
+	for (i=0;i<num;i++){
+		if (&msgs[i]==NULL){
+			if( i2c_debug) printk(KERN_INFO "Msgs is NULL\n");
+			return -EINVAL;
+		} else {
+			if (msgs[i].buf == NULL){
+				if( i2c_debug)printk(KERN_INFO "Length is less than zero");
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 1;
+}
+
+static int i2c_pxa_readbytes(struct i2c_adapter *i2c_adap, char *buf,
+			int count, int last)
+{
+
+	int i, timeout=0;
+	struct i2c_algo_pxa_data *adap = i2c_adap->algo_data;
+
+	/* increment number of bytes to read by one -- read dummy byte */
+	for (i = 0; i <= count; i++) {
+		if (i!=0){
+			/* set ACK to NAK for last received byte ICR[ACKNAK] = 1
+			   only if not a repeated start */
+
+			if ((i == count) && last) {
+				adap->transfer( last, I2C_RECEIVE, 0);
+			}else{
+				adap->transfer( 0, I2C_RECEIVE, 1);
+			}
+
+			timeout = adap->wait_for_interrupt(I2C_RECEIVE);
+
+#ifdef DEBUG
+			if (timeout==BUS_ERROR){
+				printk(KERN_INFO "i2c_pxa_readbytes: bus error -> forcing reset\n");
+				adap->reset();
+				return I2C_RETRY;
+			} else
+#endif
+			if (timeout == -ERESTARTSYS) {
+				adap->abort();
+				return timeout;
+			} else
+			if (timeout){
+#ifdef DEBUG
+				printk(KERN_INFO "i2c_pxa_readbytes: timeout -> forcing reset\n");
+#endif
+				adap->reset();
+				return I2C_RETRY;
+			}
+
+		}
+
+		if (i) {
+			buf[i - 1] = adap->read_byte();
+		} else {
+			adap->read_byte(); /* dummy read */
+		}
+	}
+	return (i - 1);
+}
+
+static int i2c_pxa_sendbytes(struct i2c_adapter *i2c_adap, const char *buf,
+                         int count, int last)
+{
+
+	struct i2c_algo_pxa_data *adap = i2c_adap->algo_data;
+	int wrcount, timeout;
+
+	for (wrcount=0; wrcount<count; ++wrcount) {
+
+		adap->write_byte(buf[wrcount]);
+		if ((wrcount==(count-1)) && last) {
+			adap->transfer( last, I2C_TRANSMIT, 0);
+		}else{
+			adap->transfer( 0, I2C_TRANSMIT, 1);
+		}
+
+		timeout = adap->wait_for_interrupt(I2C_TRANSMIT);
+
+#ifdef DEBUG
+		if (timeout==BUS_ERROR) {
+			printk(KERN_INFO "i2c_pxa_sendbytes: bus error -> forcing reset.\n");
+			adap->reset();
+			return I2C_RETRY;
+		} else
+#endif
+		if (timeout == -ERESTARTSYS) {
+			adap->abort();
+			return timeout;
+		} else
+		if (timeout) {
+#ifdef DEBUG
+			printk(KERN_INFO "i2c_pxa_sendbytes: timeout -> forcing reset\n");
+#endif
+			adap->reset();
+			return I2C_RETRY;
+		}
+	}
+	return (wrcount);
+}
+
+
+static inline int i2c_pxa_set_ctrl_byte(struct i2c_algo_pxa_data * adap, struct i2c_msg *msg)
+{
+	u16 flags = msg->flags;
+	u8 addr;
+	addr = (u8) ( (0x7f & msg->addr) << 1 );
+	if (flags & I2C_M_RD )
+		addr |= 1;
+	if (flags & I2C_M_REV_DIR_ADDR )
+		addr ^= 1;
+	adap->write_byte(addr);
+	return 0;
+}
+
+static int i2c_pxa_do_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+	struct i2c_algo_pxa_data * adap;
+	struct i2c_msg *pmsg=NULL;
+	int i;
+	int ret=0, timeout;
+
+	adap = i2c_adap->algo_data;
+
+	timeout = adap->wait_bus_not_busy();
+
+	if (timeout) {
+		return I2C_RETRY;
+	}
+
+	for (i = 0;ret >= 0 && i < num; i++) {
+		int last = i + 1 == num;
+		pmsg = &msgs[i];
+
+		ret = i2c_pxa_set_ctrl_byte(adap,pmsg);
+
+		/* Send START */
+		if (i == 0) {
+			adap->start();
+		}else{
+			adap->repeat_start();
+		}
+
+		adap->transfer(0, I2C_TRANSMIT, 0);
+
+		/* Wait for ITE (transmit empty) */
+		timeout = adap->wait_for_interrupt(I2C_TRANSMIT);
+
+#ifdef DEBUG
+		/* Check for ACK (bus error) */
+		if (timeout==BUS_ERROR){
+			printk(KERN_INFO "i2c_pxa_do_xfer: bus error -> forcing reset\n");
+			adap->reset();
+			return I2C_RETRY;
+		} else
+#endif
+		if (timeout == -ERESTARTSYS) {
+			adap->abort();
+			return timeout;
+		} else
+		if (timeout) {
+#ifdef DEBUG
+			printk(KERN_INFO "i2c_pxa_do_xfer: timeout -> forcing reset\n");
+#endif
+			adap->reset();
+			return I2C_RETRY;
+		}
+/* FIXME: handle arbitration... */
+#if 0
+		/* Check for bus arbitration loss */
+		if (adap->arbitration_loss()){
+			printk("Arbitration loss detected \n");
+			adap->reset();
+			return I2C_RETRY;
+		}
+#endif
+
+		/* Read */
+		if (pmsg->flags & I2C_M_RD) {
+			/* read bytes into buffer*/
+			ret = i2c_pxa_readbytes(i2c_adap, pmsg->buf, pmsg->len, last);
+#if DEBUG > 2
+			if (ret != pmsg->len) {
+				printk(KERN_INFO"i2c_pxa_do_xfer: read %d/%d bytes.\n",
+					ret, pmsg->len);
+			} else {
+				printk(KERN_INFO"i2c_pxa_do_xfer: read %d bytes.\n",ret);
+			}
+#endif
+		} else { /* Write */
+			ret = i2c_pxa_sendbytes(i2c_adap, pmsg->buf, pmsg->len, last);
+#if DEBUG > 2
+			if (ret != pmsg->len) {
+				printk(KERN_INFO"i2c_pxa_do_xfer: wrote %d/%d bytes.\n",
+					ret, pmsg->len);
+			} else {
+				printk(KERN_INFO"i2c_pxa_do_xfer: wrote %d bytes.\n",ret);
+			}
+#endif
+		}
+	}
+
+	if (ret<0){
+		return ret;
+	}else{
+		return i;
+	}
+}
+
+static int i2c_pxa_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+	int retval = i2c_pxa_valid_messages( msgs, num);
+	if( retval > 0)
+	{
+		int i;
+		for (i=i2c_adap->retries; i>=0; i--){
+			int retval = i2c_pxa_do_xfer(i2c_adap,msgs,num);
+			if (retval!=I2C_RETRY){
+				return retval;
+			}
+			if( i2c_debug)printk(KERN_INFO"Retrying transmission \n");
+			udelay(100);
+		}
+		if( i2c_debug)printk(KERN_INFO"Retried %i times\n",i2c_adap->retries);
+		return -EREMOTEIO;
+
+	}
+	return retval;
+}
+
+static u32 i2c_pxa_functionality(struct i2c_adapter * adapter)
+{
+    /* Emulate the SMBUS functions */
+	return I2C_FUNC_SMBUS_EMUL;
+}
+
+struct i2c_algorithm i2c_pxa_algorithm  = {
+	name:                   "PXA I2C Algorithm",
+	id:                     I2C_ALGO_PXA,
+	master_xfer:            i2c_pxa_xfer,
+	smbus_xfer:             NULL,
+	slave_send:             NULL,
+	slave_recv:             NULL,
+	algo_control:           NULL,
+    functionality:          i2c_pxa_functionality,
+};
+
+/*
+ * registering functions to load algorithms at runtime
+ */
+int i2c_pxa_add_bus(struct i2c_adapter *i2c_adap)
+{
+	struct i2c_algo_pxa_data *adap = i2c_adap->algo_data;
+
+	printk(KERN_INFO"I2C: Adding %s.\n", i2c_adap->dev.name);
+
+	i2c_adap->algo = &i2c_pxa_algorithm;
+
+	MOD_INC_USE_COUNT;
+
+	/* register new adapter to i2c module... */
+	i2c_add_adapter(i2c_adap);
+
+	adap->reset();
+
+	/* scan bus */
+	if (pxa_scan) {
+		int i;
+		printk(KERN_INFO "I2C: Scanning bus ");
+		for (i = 0x02; i < 0xff; i+=2) {
+			if( i==(I2C_PXA_SLAVE_ADDR<<1)) continue;
+
+			if (adap->wait_bus_not_busy()) {
+				printk(KERN_INFO "I2C: scanning bus %s - TIMEOUT.\n",
+						i2c_adap->dev.name);
+				return -EIO;
+			}
+			adap->write_byte(i);
+			adap->start();
+			adap->transfer(0, I2C_TRANSMIT, 0);
+
+			if ((adap->wait_for_interrupt(I2C_TRANSMIT) != BUS_ERROR)) {
+				printk("(%02x)",i>>1);
+				adap->abort();
+			} else {
+//				printk(".");
+				adap->stop();
+			}
+			udelay(adap->udelay);
+		}
+		printk("\n");
+	}
+	return 0;
+}
+
+int i2c_pxa_del_bus(struct i2c_adapter *i2c_adap)
+{
+	int res;
+	if ((res = i2c_del_adapter(i2c_adap)) < 0)
+		return res;
+
+	MOD_DEC_USE_COUNT;
+
+	printk(KERN_INFO "I2C: Removing %s.\n", i2c_adap->dev.name);
+
+	return 0;
+}
+
+static int __init i2c_algo_pxa_init (void)
+{
+	printk(KERN_INFO "I2C: PXA algorithm module loaded.\n");
+	return 0;
+}
+
+EXPORT_SYMBOL(i2c_pxa_add_bus);
+EXPORT_SYMBOL(i2c_pxa_del_bus);
+
+MODULE_PARM(pxa_scan, "i");
+MODULE_PARM_DESC(pxa_scan, "Scan for active chips on the bus");
+
+MODULE_AUTHOR("Intrinsyc Software Inc.");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_algo_pxa_init);
--- linux-2.6.5/drivers/i2c/algos/Makefile~heh	2004-04-03 22:37:37.000000000 -0500
+++ linux-2.6.5/drivers/i2c/algos/Makefile	2004-04-30 20:57:36.000000000 -0400
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_I2C_ALGOBIT)	+= i2c-algo-bit.o
 obj-$(CONFIG_I2C_ALGOPCF)	+= i2c-algo-pcf.o
+obj-$(CONFIG_I2C_ALGOPXA)	+= i2c-algo-pxa.o
 obj-$(CONFIG_I2C_ALGOITE)	+= i2c-algo-ite.o
 
 ifeq ($(CONFIG_I2C_DEBUG_ALGO),y)
--- linux-2.6.5/Makefile~heh	2004-04-03 22:37:36.000000000 -0500
+++ linux-2.6.5/Makefile	2004-04-30 20:57:58.000000000 -0400
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 5
-EXTRAVERSION =
+EXTRAVERSION = -gnalm1
 NAME=Zonked Quokka
 
 # *DOCUMENTATION*