Automatic merge of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git
diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c
index e23f534..8d986b8 100644
--- a/arch/arm/mach-s3c2410/clock.c
+++ b/arch/arm/mach-s3c2410/clock.c
@@ -478,7 +478,7 @@
 {
 	unsigned long upllcon = __raw_readl(S3C2410_UPLLCON);
 
-	s3c2440_clk_upll.rate = s3c2410_get_pll(upllcon, clk_xtal.rate) * 2;
+	s3c2440_clk_upll.rate = s3c2410_get_pll(upllcon, clk_xtal.rate);
 
 	printk("S3C2440: Clock Support, UPLL %ld.%03ld MHz\n",
 	       print_mhz(s3c2440_clk_upll.rate));
diff --git a/arch/arm/mach-s3c2410/s3c2440.c b/arch/arm/mach-s3c2410/s3c2440.c
index 9a8cc5a..d4c8281 100644
--- a/arch/arm/mach-s3c2410/s3c2440.c
+++ b/arch/arm/mach-s3c2410/s3c2440.c
@@ -192,9 +192,11 @@
 
 	iotable_init(s3c2440_iodesc, ARRAY_SIZE(s3c2440_iodesc));
 	iotable_init(mach_desc, size);
+
 	/* rename any peripherals used differing from the s3c2410 */
 
-	s3c_device_i2c.name = "s3c2440-i2c";
+	s3c_device_i2c.name  = "s3c2440-i2c";
+	s3c_device_nand.name = "s3c2440-nand";
 
 	/* change irq for watchdog */
 
@@ -225,7 +227,7 @@
 		break;
 
 	case S3C2440_CLKDIVN_HDIVN_2:
-		hdiv = 1;
+		hdiv = 2;
 		break;
 
 	case S3C2440_CLKDIVN_HDIVN_4_8:
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index c4fc6be..48bac7d 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -412,21 +412,20 @@
 
 config TLS_REG_EMUL
 	bool
-	default y if (SMP || CPU_32v6) && (CPU_32v5 || CPU_32v4 || CPU_32v3)
+	default y if SMP && (CPU_32v5 || CPU_32v4 || CPU_32v3)
 	help
-	  We might be running on an ARMv6+ processor which should have the TLS
-	  register but for some reason we can't use it, or maybe an SMP system
-	  using a pre-ARMv6 processor (there are apparently a few prototypes
-	  like that in existence) and therefore access to that register must
-	  be emulated.
+	  An SMP system using a pre-ARMv6 processor (there are apparently
+	  a few prototypes like that in existence) and therefore access to
+	  that required register must be emulated.
 
 config HAS_TLS_REG
 	bool
-	depends on CPU_32v6
-	default y if !TLS_REG_EMUL
+	depends on !TLS_REG_EMUL
+	default y if SMP || CPU_32v7
 	help
 	  This selects support for the CP15 thread register.
-	  It is defined to be available on ARMv6 or later.  If a particular
-	  ARMv6 or later CPU doesn't support it then it must omc;ide "select
-	  TLS_REG_EMUL" along with its other caracteristics.
+	  It is defined to be available on some ARMv6 processors (including
+	  all SMP capable ARMv6's) or later processors.  User space may
+	  assume directly accessing that register and always obtain the
+	  expected value only on ARMv7 and above.
 
diff --git a/arch/arm/mm/copypage-v4mc.S b/arch/arm/mm/copypage-v4mc.S
deleted file mode 100644
index 305af3d..0000000
--- a/arch/arm/mm/copypage-v4mc.S
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- *  linux/arch/arm/lib/copy_page-armv4mc.S
- *
- *  Copyright (C) 1995-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.
- *
- *  ASM optimised string functions
- */
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <asm/constants.h>
-
-	.text
-	.align	5
-/*
- * ARMv4 mini-dcache optimised copy_user_page
- *
- * We flush the destination cache lines just before we write the data into the
- * corresponding address.  Since the Dcache is read-allocate, this removes the
- * Dcache aliasing issue.  The writes will be forwarded to the write buffer,
- * and merged as appropriate.
- *
- * Note: We rely on all ARMv4 processors implementing the "invalidate D line"
- * instruction.  If your processor does not supply this, you have to write your
- * own copy_user_page that does the right thing.
- */
-ENTRY(v4_mc_copy_user_page)
-	stmfd	sp!, {r4, lr}			@ 2
-	mov	r4, r0
-	mov	r0, r1
-	bl	map_page_minicache
-	mov	r1, #PAGE_SZ/64			@ 1
-	ldmia	r0!, {r2, r3, ip, lr}		@ 4
-1:	mcr	p15, 0, r4, c7, c6, 1		@ 1   invalidate D line
-	stmia	r4!, {r2, r3, ip, lr}		@ 4
-	ldmia	r0!, {r2, r3, ip, lr}		@ 4+1
-	stmia	r4!, {r2, r3, ip, lr}		@ 4
-	ldmia	r0!, {r2, r3, ip, lr}		@ 4
-	mcr	p15, 0, r4, c7, c6, 1		@ 1   invalidate D line
-	stmia	r4!, {r2, r3, ip, lr}		@ 4
-	ldmia	r0!, {r2, r3, ip, lr}		@ 4
-	subs	r1, r1, #1			@ 1
-	stmia	r4!, {r2, r3, ip, lr}		@ 4
-	ldmneia	r0!, {r2, r3, ip, lr}		@ 4
-	bne	1b				@ 1
-	ldmfd	sp!, {r4, pc}			@ 3
-
-	.align	5
-/*
- * ARMv4 optimised clear_user_page
- *
- * Same story as above.
- */
-ENTRY(v4_mc_clear_user_page)
-	str	lr, [sp, #-4]!
-	mov	r1, #PAGE_SZ/64			@ 1
-	mov	r2, #0				@ 1
-	mov	r3, #0				@ 1
-	mov	ip, #0				@ 1
-	mov	lr, #0				@ 1
-1:	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line
-	stmia	r0!, {r2, r3, ip, lr}		@ 4
-	stmia	r0!, {r2, r3, ip, lr}		@ 4
-	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line
-	stmia	r0!, {r2, r3, ip, lr}		@ 4
-	stmia	r0!, {r2, r3, ip, lr}		@ 4
-	subs	r1, r1, #1			@ 1
-	bne	1b				@ 1
-	ldr	pc, [sp], #4
-
-	__INITDATA
-
-	.type	v4_mc_user_fns, #object
-ENTRY(v4_mc_user_fns)
-	.long	v4_mc_clear_user_page
-	.long	v4_mc_copy_user_page
-	.size	v4_mc_user_fns, . - v4_mc_user_fns
diff --git a/arch/arm/mm/copypage-v4mc.c b/arch/arm/mm/copypage-v4mc.c
new file mode 100644
index 0000000..fc69dcc
--- /dev/null
+++ b/arch/arm/mm/copypage-v4mc.c
@@ -0,0 +1,111 @@
+/*
+ *  linux/arch/arm/lib/copypage-armv4mc.S
+ *
+ *  Copyright (C) 1995-2005 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 handles the mini data cache, as found on SA11x0 and XScale
+ * processors.  When we copy a user page page, we map it in such a way
+ * that accesses to this page will not touch the main data cache, but
+ * will be cached in the mini data cache.  This prevents us thrashing
+ * the main data cache on page faults.
+ */
+#include <linux/init.h>
+#include <linux/mm.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
+
+/*
+ * 0xffff8000 to 0xffffffff is reserved for any ARM architecture
+ * specific hacks for copying pages efficiently.
+ */
+#define minicache_pgprot __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | \
+				  L_PTE_CACHEABLE)
+
+#define TOP_PTE(x)	pte_offset_kernel(top_pmd, x)
+
+static DEFINE_SPINLOCK(minicache_lock);
+
+/*
+ * ARMv4 mini-dcache optimised copy_user_page
+ *
+ * We flush the destination cache lines just before we write the data into the
+ * corresponding address.  Since the Dcache is read-allocate, this removes the
+ * Dcache aliasing issue.  The writes will be forwarded to the write buffer,
+ * and merged as appropriate.
+ *
+ * Note: We rely on all ARMv4 processors implementing the "invalidate D line"
+ * instruction.  If your processor does not supply this, you have to write your
+ * own copy_user_page that does the right thing.
+ */
+static void __attribute__((naked))
+mc_copy_user_page(void *from, void *to)
+{
+	asm volatile(
+	"stmfd	sp!, {r4, lr}			@ 2\n\
+	mov	r4, %2				@ 1\n\
+	ldmia	%0!, {r2, r3, ip, lr}		@ 4\n\
+1:	mcr	p15, 0, %1, c7, c6, 1		@ 1   invalidate D line\n\
+	stmia	%1!, {r2, r3, ip, lr}		@ 4\n\
+	ldmia	%0!, {r2, r3, ip, lr}		@ 4+1\n\
+	stmia	%1!, {r2, r3, ip, lr}		@ 4\n\
+	ldmia	%0!, {r2, r3, ip, lr}		@ 4\n\
+	mcr	p15, 0, %1, c7, c6, 1		@ 1   invalidate D line\n\
+	stmia	%1!, {r2, r3, ip, lr}		@ 4\n\
+	ldmia	%0!, {r2, r3, ip, lr}		@ 4\n\
+	subs	r4, r4, #1			@ 1\n\
+	stmia	%1!, {r2, r3, ip, lr}		@ 4\n\
+	ldmneia	%0!, {r2, r3, ip, lr}		@ 4\n\
+	bne	1b				@ 1\n\
+	ldmfd	sp!, {r4, pc}			@ 3"
+	:
+	: "r" (from), "r" (to), "I" (PAGE_SIZE / 64));
+}
+
+void v4_mc_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr)
+{
+	spin_lock(&minicache_lock);
+
+	set_pte(TOP_PTE(0xffff8000), pfn_pte(__pa(kfrom) >> PAGE_SHIFT, minicache_pgprot));
+	flush_tlb_kernel_page(0xffff8000);
+
+	mc_copy_user_page((void *)0xffff8000, kto);
+
+	spin_unlock(&minicache_lock);
+}
+
+/*
+ * ARMv4 optimised clear_user_page
+ */
+void __attribute__((naked))
+v4_mc_clear_user_page(void *kaddr, unsigned long vaddr)
+{
+	asm volatile(
+	"str	lr, [sp, #-4]!\n\
+	mov	r1, %0				@ 1\n\
+	mov	r2, #0				@ 1\n\
+	mov	r3, #0				@ 1\n\
+	mov	ip, #0				@ 1\n\
+	mov	lr, #0				@ 1\n\
+1:	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line\n\
+	stmia	r0!, {r2, r3, ip, lr}		@ 4\n\
+	stmia	r0!, {r2, r3, ip, lr}		@ 4\n\
+	mcr	p15, 0, r0, c7, c6, 1		@ 1   invalidate D line\n\
+	stmia	r0!, {r2, r3, ip, lr}		@ 4\n\
+	stmia	r0!, {r2, r3, ip, lr}		@ 4\n\
+	subs	r1, r1, #1			@ 1\n\
+	bne	1b				@ 1\n\
+	ldr	pc, [sp], #4"
+	:
+	: "I" (PAGE_SIZE / 64));
+}
+
+struct cpu_user_fns v4_mc_user_fns __initdata = {
+	.cpu_clear_user_page	= v4_mc_clear_user_page, 
+	.cpu_copy_user_page	= v4_mc_copy_user_page,
+};
diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c
index 694ac82..a8c0023 100644
--- a/arch/arm/mm/copypage-v6.c
+++ b/arch/arm/mm/copypage-v6.c
@@ -26,8 +26,8 @@
 #define to_address	(0xffffc000)
 #define to_pgprot	PAGE_KERNEL
 
-static pte_t *from_pte;
-static pte_t *to_pte;
+#define TOP_PTE(x)	pte_offset_kernel(top_pmd, x)
+
 static DEFINE_SPINLOCK(v6_lock);
 
 #define DCACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT)
@@ -74,8 +74,8 @@
 	 */
 	spin_lock(&v6_lock);
 
-	set_pte(from_pte + offset, pfn_pte(__pa(kfrom) >> PAGE_SHIFT, from_pgprot));
-	set_pte(to_pte + offset, pfn_pte(__pa(kto) >> PAGE_SHIFT, to_pgprot));
+	set_pte(TOP_PTE(from_address) + offset, pfn_pte(__pa(kfrom) >> PAGE_SHIFT, from_pgprot));
+	set_pte(TOP_PTE(to_address) + offset, pfn_pte(__pa(kto) >> PAGE_SHIFT, to_pgprot));
 
 	from = from_address + (offset << PAGE_SHIFT);
 	to   = to_address + (offset << PAGE_SHIFT);
@@ -114,7 +114,7 @@
 	 */
 	spin_lock(&v6_lock);
 
-	set_pte(to_pte + offset, pfn_pte(__pa(kaddr) >> PAGE_SHIFT, to_pgprot));
+	set_pte(TOP_PTE(to_address) + offset, pfn_pte(__pa(kaddr) >> PAGE_SHIFT, to_pgprot));
 	flush_tlb_kernel_page(to);
 	clear_page((void *)to);
 
@@ -129,21 +129,6 @@
 static int __init v6_userpage_init(void)
 {
 	if (cache_is_vipt_aliasing()) {
-		pgd_t *pgd;
-		pmd_t *pmd;
-
-		pgd = pgd_offset_k(from_address);
-		pmd = pmd_alloc(&init_mm, pgd, from_address);
-		if (!pmd)
-			BUG();
-		from_pte = pte_alloc_kernel(&init_mm, pmd, from_address);
-		if (!from_pte)
-			BUG();
-
-		to_pte = pte_alloc_kernel(&init_mm, pmd, to_address);
-		if (!to_pte)
-			BUG();
-
 		cpu_user.cpu_clear_user_page = v6_clear_user_page_aliasing;
 		cpu_user.cpu_copy_user_page = v6_copy_user_page_aliasing;
 	}
@@ -151,5 +136,4 @@
 	return 0;
 }
 
-__initcall(v6_userpage_init);
-
+core_initcall(v6_userpage_init);
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index c6de48d..4085ed9 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -13,6 +13,29 @@
 
 #include <asm/cacheflush.h>
 #include <asm/system.h>
+#include <asm/tlbflush.h>
+
+#ifdef CONFIG_CPU_CACHE_VIPT
+#define ALIAS_FLUSH_START	0xffff4000
+
+#define TOP_PTE(x)	pte_offset_kernel(top_pmd, x)
+
+static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr)
+{
+	unsigned long to = ALIAS_FLUSH_START + (CACHE_COLOUR(vaddr) << PAGE_SHIFT);
+
+	set_pte(TOP_PTE(to), pfn_pte(pfn, PAGE_KERNEL));
+	flush_tlb_kernel_page(to);
+
+	asm(	"mcrr	p15, 0, %1, %0, c14\n"
+	"	mcrr	p15, 0, %1, %0, c5\n"
+	    :
+	    : "r" (to), "r" (to + PAGE_SIZE - L1_CACHE_BYTES)
+	    : "cc");
+}
+#else
+#define flush_pfn_alias(pfn,vaddr)	do { } while (0)
+#endif
 
 static void __flush_dcache_page(struct address_space *mapping, struct page *page)
 {
@@ -37,6 +60,18 @@
 		return;
 
 	/*
+	 * This is a page cache page.  If we have a VIPT cache, we
+	 * only need to do one flush - which would be at the relevant
+	 * userspace colour, which is congruent with page->index.
+	 */
+	if (cache_is_vipt()) {
+		if (cache_is_vipt_aliasing())
+			flush_pfn_alias(page_to_pfn(page),
+					page->index << PAGE_CACHE_SHIFT);
+		return;
+	}
+
+	/*
 	 * There are possible user space mappings of this page:
 	 * - VIVT cache: we need to also write back and invalidate all user
 	 *   data in the current VM view associated with this page.
@@ -57,8 +92,6 @@
 			continue;
 		offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
 		flush_cache_page(mpnt, mpnt->vm_start + offset, page_to_pfn(page));
-		if (cache_is_vipt())
-			break;
 	}
 	flush_dcache_mmap_unlock(mapping);
 }
diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c
index 585dfb8..2c2b93d 100644
--- a/arch/arm/mm/mm-armv.c
+++ b/arch/arm/mm/mm-armv.c
@@ -37,6 +37,8 @@
 
 EXPORT_SYMBOL(pgprot_kernel);
 
+pmd_t *top_pmd;
+
 struct cachepolicy {
 	const char	policy[16];
 	unsigned int	cr_mask;
@@ -142,6 +144,16 @@
 
 #define FIRST_KERNEL_PGD_NR	(FIRST_USER_PGD_NR + USER_PTRS_PER_PGD)
 
+static inline pmd_t *pmd_off(pgd_t *pgd, unsigned long virt)
+{
+	return pmd_offset(pgd, virt);
+}
+
+static inline pmd_t *pmd_off_k(unsigned long virt)
+{
+	return pmd_off(pgd_offset_k(virt), virt);
+}
+
 /*
  * need to get a 16k page for level 1
  */
@@ -220,7 +232,7 @@
 		return;
 
 	/* pgd is always present and good */
-	pmd = (pmd_t *)pgd;
+	pmd = pmd_off(pgd, 0);
 	if (pmd_none(*pmd))
 		goto free;
 	if (pmd_bad(*pmd)) {
@@ -246,9 +258,8 @@
 static inline void
 alloc_init_section(unsigned long virt, unsigned long phys, int prot)
 {
-	pmd_t *pmdp;
+	pmd_t *pmdp = pmd_off_k(virt);
 
-	pmdp = pmd_offset(pgd_offset_k(virt), virt);
 	if (virt & (1 << 20))
 		pmdp++;
 
@@ -283,11 +294,9 @@
 static inline void
 alloc_init_page(unsigned long virt, unsigned long phys, unsigned int prot_l1, pgprot_t prot)
 {
-	pmd_t *pmdp;
+	pmd_t *pmdp = pmd_off_k(virt);
 	pte_t *ptep;
 
-	pmdp = pmd_offset(pgd_offset_k(virt), virt);
-
 	if (pmd_none(*pmdp)) {
 		unsigned long pmdval;
 		ptep = alloc_bootmem_low_pages(2 * PTRS_PER_PTE *
@@ -310,7 +319,7 @@
  */
 static inline void clear_mapping(unsigned long virt)
 {
-	pmd_clear(pmd_offset(pgd_offset_k(virt), virt));
+	pmd_clear(pmd_off_k(virt));
 }
 
 struct mem_types {
@@ -578,7 +587,7 @@
 			 PMD_TYPE_SECT;
 		if (cpu_arch <= CPU_ARCH_ARMv5)
 			pmdval |= PMD_BIT4;
-		pmd = pmd_offset(pgd + i, i << PGDIR_SHIFT);
+		pmd = pmd_off(pgd, i << PGDIR_SHIFT);
 		pmd[0] = __pmd(pmdval);
 		pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
 		flush_pmd_entry(pmd);
@@ -675,6 +684,8 @@
 
 	flush_cache_all();
 	flush_tlb_all();
+
+	top_pmd = pmd_off_k(0xffff0000);
 }
 
 /*
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 2e70d74..4991bbd 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -51,7 +51,7 @@
 
 config MMC_WBSD
 	tristate "Winbond W83L51xD SD/MMC Card Interface support"
-	depends on MMC && ISA && ISA_DMA_API
+	depends on MMC && ISA_DMA_API
 	help
 	  This selects the Winbond(R) W83L51xD Secure digital and
           Multimedia card Interface.
diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c
index 3974752..b7fbd30 100644
--- a/drivers/mmc/wbsd.c
+++ b/drivers/mmc/wbsd.c
@@ -28,7 +28,9 @@
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
 #include <linux/delay.h>
+#include <linux/pnp.h>
 #include <linux/highmem.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/protocol.h>
@@ -40,7 +42,7 @@
 #include "wbsd.h"
 
 #define DRIVER_NAME "wbsd"
-#define DRIVER_VERSION "1.1"
+#define DRIVER_VERSION "1.2"
 
 #ifdef CONFIG_MMC_DEBUG
 #define DBG(x...) \
@@ -52,10 +54,6 @@
 #define DBGF(x...)	do { } while (0)
 #endif
 
-static unsigned int io = 0x248;
-static unsigned int irq = 6;
-static int dma = 2;
-
 #ifdef CONFIG_MMC_DEBUG
 void DBG_REG(int reg, u8 value)
 {
@@ -79,28 +77,61 @@
 #endif
 
 /*
+ * Device resources
+ */
+
+#ifdef CONFIG_PNP
+
+static const struct pnp_device_id pnp_dev_table[] = {
+	{ "WEC0517", 0 },
+	{ "WEC0518", 0 },
+	{ "", 0 },
+};
+
+MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
+
+#endif /* CONFIG_PNP */
+
+#ifdef CONFIG_PNP
+static unsigned int nopnp = 0;
+#else
+static const unsigned int nopnp = 1;
+#endif
+static unsigned int io = 0x248;
+static unsigned int irq = 6;
+static int dma = 2;
+
+/*
  * Basic functions
  */
 
 static inline void wbsd_unlock_config(struct wbsd_host* host)
 {
+	BUG_ON(host->config == 0);
+	
 	outb(host->unlock_code, host->config);
 	outb(host->unlock_code, host->config);
 }
 
 static inline void wbsd_lock_config(struct wbsd_host* host)
 {
+	BUG_ON(host->config == 0);
+	
 	outb(LOCK_CODE, host->config);
 }
 
 static inline void wbsd_write_config(struct wbsd_host* host, u8 reg, u8 value)
 {
+	BUG_ON(host->config == 0);
+	
 	outb(reg, host->config);
 	outb(value, host->config + 1);
 }
 
 static inline u8 wbsd_read_config(struct wbsd_host* host, u8 reg)
 {
+	BUG_ON(host->config == 0);
+	
 	outb(reg, host->config);
 	return inb(host->config + 1);
 }
@@ -133,6 +164,13 @@
 	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
 	
 	/*
+	 * Set DAT3 to input
+	 */
+	setup &= ~WBSD_DAT3_H;
+	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+	host->flags &= ~WBSD_FIGNORE_DETECT;
+	
+	/*
 	 * Read back default clock.
 	 */
 	host->clk = wbsd_read_index(host, WBSD_IDX_CLK);
@@ -148,6 +186,14 @@
 	wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F);
 	
 	/*
+	 * Test for card presence
+	 */
+	if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT)
+		host->flags |= WBSD_FCARD_PRESENT;
+	else
+		host->flags &= ~WBSD_FCARD_PRESENT;
+	
+	/*
 	 * Enable interesting interrupts.
 	 */
 	ier = 0;
@@ -407,8 +453,6 @@
 	}
 }
 
-static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs);
-
 static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
 {
 	int i;
@@ -646,6 +690,13 @@
 	}
 	
 	wbsd_kunmap_sg(host);
+	
+	/*
+	 * The controller stops sending interrupts for
+	 * 'FIFO empty' under certain conditions. So we
+	 * need to be a bit more pro-active.
+	 */
+	tasklet_schedule(&host->fifo_tasklet);
 }
 
 static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data)
@@ -850,9 +901,11 @@
 	wbsd_request_end(host, host->mrq);
 }
 
-/*
- * MMC Callbacks
- */
+/*****************************************************************************\
+ *                                                                           *
+ * MMC layer callbacks                                                       *
+ *                                                                           *
+\*****************************************************************************/
 
 static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
 {
@@ -874,7 +927,7 @@
 	 * If there is no card in the slot then
 	 * timeout immediatly.
 	 */
-	if (!(inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT))
+	if (!(host->flags & WBSD_FCARD_PRESENT))
 	{
 		cmd->error = MMC_ERR_TIMEOUT;
 		goto done;
@@ -953,33 +1006,50 @@
 		host->clk = clk;
 	}
 
+	/*
+	 * Power up card.
+	 */
 	if (ios->power_mode != MMC_POWER_OFF)
 	{
-		/*
-		 * Power up card.
-		 */
 		pwr = inb(host->base + WBSD_CSR);
 		pwr &= ~WBSD_POWER_N;
 		outb(pwr, host->base + WBSD_CSR);
-
-		/*
-		 * This behaviour is stolen from the
-		 * Windows driver. Don't know why, but
-		 * it is needed.
-		 */
-		setup = wbsd_read_index(host, WBSD_IDX_SETUP);
-		if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
-			setup |= WBSD_DAT3_H;
-		else
-			setup &= ~WBSD_DAT3_H;
-		wbsd_write_index(host, WBSD_IDX_SETUP, setup);
-
-		mdelay(1);
 	}
 
+	/*
+	 * MMC cards need to have pin 1 high during init.
+	 * Init time corresponds rather nicely with the bus mode.
+	 * It wreaks havoc with the card detection though so
+	 * that needs to be disabed.
+	 */
+	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+	if ((ios->power_mode == MMC_POWER_ON) &&
+		(ios->bus_mode == MMC_BUSMODE_OPENDRAIN))
+	{
+		setup |= WBSD_DAT3_H;
+		host->flags |= WBSD_FIGNORE_DETECT;
+	}
+	else
+	{
+		setup &= ~WBSD_DAT3_H;
+		host->flags &= ~WBSD_FIGNORE_DETECT;
+	}
+	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+	
 	spin_unlock_bh(&host->lock);
 }
 
+static struct mmc_host_ops wbsd_ops = {
+	.request	= wbsd_request,
+	.set_ios	= wbsd_set_ios,
+};
+
+/*****************************************************************************\
+ *                                                                           *
+ * Interrupt handling                                                        *
+ *                                                                           *
+\*****************************************************************************/
+
 /*
  * Tasklets
  */
@@ -1005,17 +1075,33 @@
 {
 	struct wbsd_host* host = (struct wbsd_host*)param;
 	u8 csr;
+	int change = 0;
 	
 	spin_lock(&host->lock);
 	
+	if (host->flags & WBSD_FIGNORE_DETECT)
+	{
+		spin_unlock(&host->lock);
+		return;
+	}
+	
 	csr = inb(host->base + WBSD_CSR);
 	WARN_ON(csr == 0xff);
 	
 	if (csr & WBSD_CARDPRESENT)
-		DBG("Card inserted\n");
-	else
+	{
+		if (!(host->flags & WBSD_FCARD_PRESENT))
+		{
+			DBG("Card inserted\n");
+			host->flags |= WBSD_FCARD_PRESENT;
+			change = 1;
+		}
+	}
+	else if (host->flags & WBSD_FCARD_PRESENT)
 	{
 		DBG("Card removed\n");
+		host->flags &= ~WBSD_FCARD_PRESENT;
+		change = 1;
 		
 		if (host->mrq)
 		{
@@ -1033,7 +1119,8 @@
 	 */
 	spin_unlock(&host->lock);
 
-	mmc_detect_change(host->mmc);
+	if (change)
+		mmc_detect_change(host->mmc);
 }
 
 static void wbsd_tasklet_fifo(unsigned long param)
@@ -1200,11 +1287,85 @@
 	return IRQ_HANDLED;
 }
 
+/*****************************************************************************\
+ *                                                                           *
+ * Device initialisation and shutdown                                        *
+ *                                                                           *
+\*****************************************************************************/
+
 /*
- * Support functions for probe
+ * Allocate/free MMC structure.
  */
 
-static int wbsd_scan(struct wbsd_host* host)
+static int __devinit wbsd_alloc_mmc(struct device* dev)
+{
+	struct mmc_host* mmc;
+	struct wbsd_host* host;
+	
+	/*
+	 * Allocate MMC structure.
+	 */
+	mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
+	if (!mmc)
+		return -ENOMEM;
+	
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+
+	host->dma = -1;
+
+	/*
+	 * Set host parameters.
+	 */
+	mmc->ops = &wbsd_ops;
+	mmc->f_min = 375000;
+	mmc->f_max = 24000000;
+	mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+	
+	spin_lock_init(&host->lock);
+	
+	/*
+	 * Maximum number of segments. Worst case is one sector per segment
+	 * so this will be 64kB/512.
+	 */
+	mmc->max_hw_segs = 128;
+	mmc->max_phys_segs = 128;
+	
+	/*
+	 * Maximum number of sectors in one transfer. Also limited by 64kB
+	 * buffer.
+	 */
+	mmc->max_sectors = 128;
+	
+	/*
+	 * Maximum segment size. Could be one segment with the maximum number
+	 * of segments.
+	 */
+	mmc->max_seg_size = mmc->max_sectors * 512;
+	
+	dev_set_drvdata(dev, mmc);
+	
+	return 0;
+}
+
+static void __devexit wbsd_free_mmc(struct device* dev)
+{
+	struct mmc_host* mmc;
+	
+	mmc = dev_get_drvdata(dev);
+	if (!mmc)
+		return;
+	
+	mmc_free_host(mmc);
+	
+	dev_set_drvdata(dev, NULL);
+}
+
+/*
+ * Scan for known chip id:s
+ */
+
+static int __devinit wbsd_scan(struct wbsd_host* host)
 {
 	int i, j, k;
 	int id;
@@ -1258,12 +1419,16 @@
 	return -ENODEV;
 }
 
-static int wbsd_request_regions(struct wbsd_host* host)
+/*
+ * Allocate/free io port ranges
+ */
+
+static int __devinit wbsd_request_region(struct wbsd_host* host, int base)
 {
 	if (io & 0x7)
 		return -EINVAL;
 	
-	if (!request_region(io, 8, DRIVER_NAME))
+	if (!request_region(base, 8, DRIVER_NAME))
 		return -EIO;
 	
 	host->base = io;
@@ -1271,19 +1436,25 @@
 	return 0;
 }
 
-static void wbsd_release_regions(struct wbsd_host* host)
+static void __devexit wbsd_release_regions(struct wbsd_host* host)
 {
 	if (host->base)
 		release_region(host->base, 8);
+	
+	host->base = 0;
 
 	if (host->config)
 		release_region(host->config, 2);
+	
+	host->config = 0;
 }
 
-static void wbsd_init_dma(struct wbsd_host* host)
+/*
+ * Allocate/free DMA port and buffer
+ */
+
+static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma)
 {
-	host->dma = -1;
-	
 	if (dma < 0)
 		return;
 	
@@ -1294,7 +1465,7 @@
 	 * We need to allocate a special buffer in
 	 * order for ISA to be able to DMA to it.
 	 */
-	host->dma_buffer = kmalloc(65536,
+	host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
 		GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
 	if (!host->dma_buffer)
 		goto free;
@@ -1302,7 +1473,8 @@
 	/*
 	 * Translate the address to a physical address.
 	 */
-	host->dma_addr = isa_virt_to_bus(host->dma_buffer);
+	host->dma_addr = dma_map_single(host->mmc->dev, host->dma_buffer,
+		WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
 			
 	/*
 	 * ISA DMA must be aligned on a 64k basis.
@@ -1325,6 +1497,10 @@
 	 */
 	BUG_ON(1);
 	
+	dma_unmap_single(host->mmc->dev, host->dma_addr, WBSD_DMA_SIZE,
+		DMA_BIDIRECTIONAL);
+	host->dma_addr = (dma_addr_t)NULL;
+	
 	kfree(host->dma_buffer);
 	host->dma_buffer = NULL;
 
@@ -1336,86 +1512,39 @@
 		"Falling back on FIFO.\n", dma);
 }
 
-static struct mmc_host_ops wbsd_ops = {
-	.request	= wbsd_request,
-	.set_ios	= wbsd_set_ios,
-};
+static void __devexit wbsd_release_dma(struct wbsd_host* host)
+{
+	if (host->dma_addr)
+		dma_unmap_single(host->mmc->dev, host->dma_addr, WBSD_DMA_SIZE,
+			DMA_BIDIRECTIONAL);
+	if (host->dma_buffer)
+		kfree(host->dma_buffer);
+	if (host->dma >= 0)
+		free_dma(host->dma);
+	
+	host->dma = -1;
+	host->dma_buffer = NULL;
+	host->dma_addr = (dma_addr_t)NULL;
+}
 
 /*
- * Device probe
+ * Allocate/free IRQ.
  */
 
-static int wbsd_probe(struct device* dev)
+static int __devinit wbsd_request_irq(struct wbsd_host* host, int irq)
 {
-	struct wbsd_host* host = NULL;
-	struct mmc_host* mmc = NULL;
 	int ret;
 	
 	/*
-	 * Allocate MMC structure.
-	 */
-	mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
-	if (!mmc)
-		return -ENOMEM;
-	
-	host = mmc_priv(mmc);
-	host->mmc = mmc;
-	
-	/*
-	 * Scan for hardware.
-	 */
-	ret = wbsd_scan(host);
-	if (ret)
-		goto freemmc;
-
-	/*
-	 * Reset the chip.
-	 */	
-	wbsd_write_config(host, WBSD_CONF_SWRST, 1);
-	wbsd_write_config(host, WBSD_CONF_SWRST, 0);
-
-	/*
-	 * Allocate I/O ports.
-	 */
-	ret = wbsd_request_regions(host);
-	if (ret)
-		goto release;
-
-	/*
-	 * Set host parameters.
-	 */
-	mmc->ops = &wbsd_ops;
-	mmc->f_min = 375000;
-	mmc->f_max = 24000000;
-	mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
-	
-	spin_lock_init(&host->lock);
-
-	/*
-	 * Select SD/MMC function.
-	 */
-	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
-	
-	/*
-	 * Set up card detection.
-	 */
-	wbsd_write_config(host, WBSD_CONF_PINS, 0x02);
-	
-	/*
-	 * Configure I/O port.
-	 */
-	wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
-	wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
-
-	/*
 	 * Allocate interrupt.
 	 */
+
 	ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);
 	if (ret)
-		goto release;
+		return ret;
 	
 	host->irq = irq;
-	
+
 	/*
 	 * Set up tasklets.
 	 */
@@ -1426,138 +1555,323 @@
 	tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, (unsigned long)host);
 	tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host);
 	
-	/*
-	 * Configure interrupt.
-	 */
-	wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
-	
-	/*
-	 * Allocate DMA.
-	 */
-	wbsd_init_dma(host);
-	
-	/*
-	 * If all went well, then configure DMA.
-	 */
-	if (host->dma >= 0)
-		wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
-	
-	/*
-	 * Maximum number of segments. Worst case is one sector per segment
-	 * so this will be 64kB/512.
-	 */
-	mmc->max_hw_segs = 128;
-	mmc->max_phys_segs = 128;
-	
-	/*
-	 * Maximum number of sectors in one transfer. Also limited by 64kB
-	 * buffer.
-	 */
-	mmc->max_sectors = 128;
-	
-	/*
-	 * Maximum segment size. Could be one segment with the maximum number
-	 * of segments.
-	 */
-	mmc->max_seg_size = mmc->max_sectors * 512;
-	
-	/*
-	 * Enable chip.
-	 */
-	wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
-	
-	/*
-	 * Power up chip.
-	 */
-	wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
-	
-	/*
-	 * Power Management stuff. No idea how this works.
-	 * Not tested.
-	 */
-#ifdef CONFIG_PM
-	wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
-#endif
-
-	/*
-	 * Reset the chip into a known state.
-	 */
-	wbsd_init_device(host);
-	
-	dev_set_drvdata(dev, mmc);
-	
-	/*
-	 * Add host to MMC layer.
-	 */
-	mmc_add_host(mmc);
-
-	printk(KERN_INFO "%s: W83L51xD id %x at 0x%x irq %d dma %d\n",
-		mmc->host_name, (int)host->chip_id, (int)host->base,
-		(int)host->irq, (int)host->dma);
-
 	return 0;
-
-release:
-	wbsd_release_regions(host);
-
-freemmc:
-	mmc_free_host(mmc);
-
-	return ret;
 }
 
-/*
- * Device remove
- */
-
-static int wbsd_remove(struct device* dev)
+static void __devexit wbsd_release_irq(struct wbsd_host* host)
 {
-	struct mmc_host* mmc = dev_get_drvdata(dev);
-	struct wbsd_host* host;
-	
-	if (!mmc)
-		return 0;
-
-	host = mmc_priv(mmc);
-	
-	/*
-	 * Unregister host with MMC layer.
-	 */
-	mmc_remove_host(mmc);
-
-	/*
-	 * Power down the SD/MMC function.
-	 */
-	wbsd_unlock_config(host);
-	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
-	wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
-	wbsd_lock_config(host);
-	
-	/*
-	 * Free resources.
-	 */
-	if (host->dma_buffer)
-		kfree(host->dma_buffer);
-	
-	if (host->dma >= 0)
-		free_dma(host->dma);
+	if (!host->irq)
+		return;
 
 	free_irq(host->irq, host);
 	
+	host->irq = 0;
+		
 	tasklet_kill(&host->card_tasklet);
 	tasklet_kill(&host->fifo_tasklet);
 	tasklet_kill(&host->crc_tasklet);
 	tasklet_kill(&host->timeout_tasklet);
 	tasklet_kill(&host->finish_tasklet);
 	tasklet_kill(&host->block_tasklet);
+}
+
+/*
+ * Allocate all resources for the host.
+ */
+
+static int __devinit wbsd_request_resources(struct wbsd_host* host,
+	int base, int irq, int dma)
+{
+	int ret;
 	
+	/*
+	 * Allocate I/O ports.
+	 */
+	ret = wbsd_request_region(host, base);
+	if (ret)
+		return ret;
+
+	/*
+	 * Allocate interrupt.
+	 */
+	ret = wbsd_request_irq(host, irq);
+	if (ret)
+		return ret;
+
+	/*
+	 * Allocate DMA.
+	 */
+	wbsd_request_dma(host, dma);
+	
+	return 0;
+}
+
+/*
+ * Release all resources for the host.
+ */
+
+static void __devexit wbsd_release_resources(struct wbsd_host* host)
+{
+	wbsd_release_dma(host);
+	wbsd_release_irq(host);
 	wbsd_release_regions(host);
+}
+
+/*
+ * Configure the resources the chip should use.
+ */
+
+static void __devinit wbsd_chip_config(struct wbsd_host* host)
+{
+	/*
+	 * Reset the chip.
+	 */	
+	wbsd_write_config(host, WBSD_CONF_SWRST, 1);
+	wbsd_write_config(host, WBSD_CONF_SWRST, 0);
+
+	/*
+	 * Select SD/MMC function.
+	 */
+	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
 	
-	mmc_free_host(mmc);
+	/*
+	 * Set up card detection.
+	 */
+	wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
+	
+	/*
+	 * Configure chip
+	 */
+	wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
+	wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
+	
+	wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
+	
+	if (host->dma >= 0)
+		wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
+	
+	/*
+	 * Enable and power up chip.
+	 */
+	wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
+	wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
+}
+
+/*
+ * Check that configured resources are correct.
+ */
+ 
+static int __devinit wbsd_chip_validate(struct wbsd_host* host)
+{
+	int base, irq, dma;
+	
+	/*
+	 * Select SD/MMC function.
+	 */
+	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+	
+	/*
+	 * Read configuration.
+	 */
+	base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
+	base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
+	
+	irq = wbsd_read_config(host, WBSD_CONF_IRQ);
+	
+	dma = wbsd_read_config(host, WBSD_CONF_DRQ);
+	
+	/*
+	 * Validate against given configuration.
+	 */
+	if (base != host->base)
+		return 0;
+	if (irq != host->irq)
+		return 0;
+	if ((dma != host->dma) && (host->dma != -1))
+		return 0;
+	
+	return 1;
+}
+
+/*****************************************************************************\
+ *                                                                           *
+ * Devices setup and shutdown                                                *
+ *                                                                           *
+\*****************************************************************************/
+
+static int __devinit wbsd_init(struct device* dev, int base, int irq, int dma,
+	int pnp)
+{
+	struct wbsd_host* host = NULL;
+	struct mmc_host* mmc = NULL;
+	int ret;
+	
+	ret = wbsd_alloc_mmc(dev);
+	if (ret)
+		return ret;
+	
+	mmc = dev_get_drvdata(dev);
+	host = mmc_priv(mmc);
+	
+	/*
+	 * Scan for hardware.
+	 */
+	ret = wbsd_scan(host);
+	if (ret)
+	{
+		if (pnp && (ret == -ENODEV))
+		{
+			printk(KERN_WARNING DRIVER_NAME
+				": Unable to confirm device presence. You may "
+				"experience lock-ups.\n");
+		}
+		else
+		{
+			wbsd_free_mmc(dev);
+			return ret;
+		}
+	}
+	
+	/*
+	 * Request resources.
+	 */
+	ret = wbsd_request_resources(host, io, irq, dma);
+	if (ret)
+	{
+		wbsd_release_resources(host);
+		wbsd_free_mmc(dev);
+		return ret;
+	}
+	
+	/*
+	 * See if chip needs to be configured.
+	 */
+	if (pnp && (host->config != 0))
+	{
+		if (!wbsd_chip_validate(host))
+		{
+			printk(KERN_WARNING DRIVER_NAME
+				": PnP active but chip not configured! "
+				"You probably have a buggy BIOS. "
+				"Configuring chip manually.\n");
+			wbsd_chip_config(host);
+		}
+	}
+	else
+		wbsd_chip_config(host);
+	
+	/*
+	 * Power Management stuff. No idea how this works.
+	 * Not tested.
+	 */
+#ifdef CONFIG_PM
+	if (host->config)
+		wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
+#endif
+	/*
+	 * Allow device to initialise itself properly.
+	 */
+	mdelay(5);
+
+	/*
+	 * Reset the chip into a known state.
+	 */
+	wbsd_init_device(host);
+	
+	mmc_add_host(mmc);
+
+	printk(KERN_INFO "%s: W83L51xD", mmc->host_name);
+	if (host->chip_id != 0)
+		printk(" id %x", (int)host->chip_id);
+	printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
+	if (host->dma >= 0)
+		printk(" dma %d", (int)host->dma);
+	else
+		printk(" FIFO");
+	if (pnp)
+		printk(" PnP");
+	printk("\n");
 
 	return 0;
 }
 
+static void __devexit wbsd_shutdown(struct device* dev, int pnp)
+{
+	struct mmc_host* mmc = dev_get_drvdata(dev);
+	struct wbsd_host* host;
+	
+	if (!mmc)
+		return;
+
+	host = mmc_priv(mmc);
+	
+	mmc_remove_host(mmc);
+
+	if (!pnp)
+	{
+		/*
+		 * Power down the SD/MMC function.
+		 */
+		wbsd_unlock_config(host);
+		wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+		wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
+		wbsd_lock_config(host);
+	}
+	
+	wbsd_release_resources(host);
+	
+	wbsd_free_mmc(dev);
+}
+
+/*
+ * Non-PnP
+ */
+
+static int __devinit wbsd_probe(struct device* dev)
+{
+	return wbsd_init(dev, io, irq, dma, 0);
+}
+
+static int __devexit wbsd_remove(struct device* dev)
+{
+	wbsd_shutdown(dev, 0);
+
+	return 0;
+}
+
+/*
+ * PnP
+ */
+
+#ifdef CONFIG_PNP
+
+static int __devinit
+wbsd_pnp_probe(struct pnp_dev * pnpdev, const struct pnp_device_id *dev_id)
+{
+	int io, irq, dma;
+	
+	/*
+	 * Get resources from PnP layer.
+	 */
+	io = pnp_port_start(pnpdev, 0);
+	irq = pnp_irq(pnpdev, 0);
+	if (pnp_dma_valid(pnpdev, 0))
+		dma = pnp_dma(pnpdev, 0);
+	else
+		dma = -1;
+	
+	DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
+	
+	return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
+}
+
+static void __devexit wbsd_pnp_remove(struct pnp_dev * dev)
+{
+	wbsd_shutdown(&dev->dev, 1);
+}
+
+#endif /* CONFIG_PNP */
+
 /*
  * Power management
  */
@@ -1581,17 +1895,7 @@
 #define wbsd_resume NULL
 #endif
 
-static void wbsd_release(struct device *dev)
-{
-}
-
-static struct platform_device wbsd_device = {
-	.name		= DRIVER_NAME,
-	.id			= -1,
-	.dev		= {
-		.release = wbsd_release,
-	},
-};
+static struct platform_device *wbsd_device;
 
 static struct device_driver wbsd_driver = {
 	.name		= DRIVER_NAME,
@@ -1603,6 +1907,17 @@
 	.resume		= wbsd_resume,
 };
 
+#ifdef CONFIG_PNP
+
+static struct pnp_driver wbsd_pnp_driver = {
+	.name		= DRIVER_NAME,
+	.id_table	= pnp_dev_table,
+	.probe		= wbsd_pnp_probe,
+	.remove		= wbsd_pnp_remove,
+};
+
+#endif /* CONFIG_PNP */
+
 /*
  * Module loading/unloading
  */
@@ -1615,29 +1930,57 @@
 		": Winbond W83L51xD SD/MMC card interface driver, "
 		DRIVER_VERSION "\n");
 	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
-	
-	result = driver_register(&wbsd_driver);
-	if (result < 0)
-		return result;
 
-	result = platform_device_register(&wbsd_device);
-	if (result < 0)
-		return result;
+#ifdef CONFIG_PNP
+
+	if (!nopnp)
+	{
+		result = pnp_register_driver(&wbsd_pnp_driver);
+		if (result < 0)
+			return result;
+	}
+
+#endif /* CONFIG_PNP */	
+	
+	if (nopnp)
+	{
+		result = driver_register(&wbsd_driver);
+		if (result < 0)
+			return result;
+
+		wbsd_device = platform_device_register_simple(DRIVER_NAME, -1,
+			NULL, 0);
+		if (IS_ERR(wbsd_device))
+			return PTR_ERR(wbsd_device);
+	}
 
 	return 0;
 }
 
 static void __exit wbsd_drv_exit(void)
 {
-	platform_device_unregister(&wbsd_device);
+#ifdef CONFIG_PNP
+
+	if (!nopnp)
+		pnp_unregister_driver(&wbsd_pnp_driver);
 	
-	driver_unregister(&wbsd_driver);
+#endif /* CONFIG_PNP */	
+
+	if (nopnp)
+	{
+		platform_device_unregister(wbsd_device);
+	
+		driver_unregister(&wbsd_driver);
+	}
 
 	DBG("unloaded\n");
 }
 
 module_init(wbsd_drv_init);
 module_exit(wbsd_drv_exit);
+#ifdef CONFIG_PNP
+module_param(nopnp, uint, 0444);
+#endif
 module_param(io, uint, 0444);
 module_param(irq, uint, 0444);
 module_param(dma, int, 0444);
@@ -1646,6 +1989,9 @@
 MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
 MODULE_VERSION(DRIVER_VERSION);
 
+#ifdef CONFIG_PNP
+MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)");
+#endif
 MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");
 MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");
 MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");
diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h
index fdc03b5..864f3082 100644
--- a/drivers/mmc/wbsd.h
+++ b/drivers/mmc/wbsd.h
@@ -35,6 +35,12 @@
 
 #define DEVICE_SD		0x03
 
+#define WBSD_PINS_DAT3_HI	0x20
+#define WBSD_PINS_DAT3_OUT	0x10
+#define WBSD_PINS_GP11_HI	0x04
+#define WBSD_PINS_DETECT_GP11	0x02
+#define WBSD_PINS_DETECT_DAT3	0x01
+
 #define WBSD_CMDR		0x00
 #define WBSD_DFR		0x01
 #define WBSD_EIR		0x02
@@ -133,6 +139,7 @@
 #define WBSD_CRC_OK		0x05 /* S010E (00101) */
 #define WBSD_CRC_FAIL		0x0B /* S101E (01011) */
 
+#define WBSD_DMA_SIZE		65536
 
 struct wbsd_host
 {
@@ -140,6 +147,11 @@
 	
 	spinlock_t		lock;		/* Mutex */
 
+	int			flags;		/* Driver states */
+
+#define WBSD_FCARD_PRESENT	(1<<0)		/* Card is present */
+#define WBSD_FIGNORE_DETECT	(1<<1)		/* Ignore card detection */
+	
 	struct mmc_request*	mrq;		/* Current request */
 	
 	u8			isr;		/* Accumulated ISR */
diff --git a/drivers/serial/21285.c b/drivers/serial/21285.c
index 33fbda7..0b10169 100644
--- a/drivers/serial/21285.c
+++ b/drivers/serial/21285.c
@@ -126,18 +126,8 @@
 				flag = TTY_FRAME;
 		}
 
-		if ((rxs & port->ignore_status_mask) == 0) {
-			tty_insert_flip_char(tty, ch, flag);
-		}
-		if ((rxs & RXSTAT_OVERRUN) &&
-		    tty->flip.count < TTY_FLIPBUF_SIZE) {
-			/*
-			 * Overrun is special, since it's reported
-			 * immediately, and doesn't affect the current
-			 * character.
-			 */
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-		}
+		uart_insert_char(port, rxs, RXSTAT_OVERRUN, ch, flag);
+
 		status = *CSR_UARTFLG;
 	}
 	tty_flip_buffer_push(tty);
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 0d93586..3bbf0cc 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -1122,18 +1122,9 @@
 		}
 		if (uart_handle_sysrq_char(&up->port, ch, regs))
 			goto ignore_char;
-		if ((lsr & up->port.ignore_status_mask) == 0) {
-			tty_insert_flip_char(tty, ch, flag);
-		}
-		if ((lsr & UART_LSR_OE) &&
-		    tty->flip.count < TTY_FLIPBUF_SIZE) {
-			/*
-			 * Overrun is special, since it's reported
-			 * immediately, and doesn't affect the current
-			 * character.
-			 */
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-		}
+
+		uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
+
 	ignore_char:
 		lsr = serial_inp(up, UART_LSR);
 	} while ((lsr & UART_LSR_DR) && (max_count-- > 0));
diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c
index f2a5e29..2884b31 100644
--- a/drivers/serial/amba-pl010.c
+++ b/drivers/serial/amba-pl010.c
@@ -198,18 +198,8 @@
 		if (uart_handle_sysrq_char(port, ch, regs))
 			goto ignore_char;
 
-		if ((rsr & port->ignore_status_mask) == 0) {
-			tty_insert_flip_char(tty, ch, flag);
-		}
-		if ((rsr & UART01x_RSR_OE) &&
-		    tty->flip.count < TTY_FLIPBUF_SIZE) {
-			/*
-			 * Overrun is special, since it's reported
-			 * immediately, and doesn't affect the current
-			 * character
-			 */
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-		}
+		uart_insert_char(port, rsr, UART01x_RSR_OE, ch, flag);
+
 	ignore_char:
 		status = UART_GET_FR(port);
 	}
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c
index d5cbef3..7db88ee 100644
--- a/drivers/serial/amba-pl011.c
+++ b/drivers/serial/amba-pl011.c
@@ -163,18 +163,8 @@
 		if (uart_handle_sysrq_char(&uap->port, ch, regs))
 			goto ignore_char;
 
-		if ((rsr & uap->port.ignore_status_mask) == 0) {
-			tty_insert_flip_char(tty, ch, flag);
-		}
-		if ((rsr & UART01x_RSR_OE) &&
-		    tty->flip.count < TTY_FLIPBUF_SIZE) {
-			/*
-			 * Overrun is special, since it's reported
-			 * immediately, and doesn't affect the current
-			 * character
-			 */
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-		}
+		uart_insert_char(&uap->port, rsr, UART01x_RSR_OE, ch, flag);
+
 	ignore_char:
 		status = readw(uap->port.membase + UART01x_FR);
 	}
diff --git a/drivers/serial/clps711x.c b/drivers/serial/clps711x.c
index 6242f30..e92522b 100644
--- a/drivers/serial/clps711x.c
+++ b/drivers/serial/clps711x.c
@@ -143,10 +143,7 @@
 		 * CHECK: does overrun affect the current character?
 		 * ASSUMPTION: it does not.
 		 */
-		if ((ch & port->ignore_status_mask & ~RXSTAT_OVERRUN) == 0)
-			tty_insert_flip_char(tty, ch, flg);
-		if ((ch & ~port->ignore_status_mask & RXSTAT_OVERRUN) == 0)
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		uart_insert_char(port, ch, UARTDR_OVERR, ch, flg);
 
 	ignore_char:
 		status = clps_readl(SYSFLG(port));
diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c
index 51d8a49..9dc151d 100644
--- a/drivers/serial/pxa.c
+++ b/drivers/serial/pxa.c
@@ -161,20 +161,12 @@
 			else if (*status & UART_LSR_FE)
 				flag = TTY_FRAME;
 		}
+
 		if (uart_handle_sysrq_char(&up->port, ch, regs))
 			goto ignore_char;
-		if ((*status & up->port.ignore_status_mask) == 0) {
-			tty_insert_flip_char(tty, ch, flag);
-		}
-		if ((*status & UART_LSR_OE) &&
-		    tty->flip.count < TTY_FLIPBUF_SIZE) {
-			/*
-			 * Overrun is special, since it's reported
-			 * immediately, and doesn't affect the current
-			 * character.
-			 */
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-		}
+
+		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
+
 	ignore_char:
 		*status = serial_in(up, UART_LSR);
 	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c
index 435750d..2a9f7ad 100644
--- a/drivers/serial/s3c2410.c
+++ b/drivers/serial/s3c2410.c
@@ -394,20 +394,7 @@
 		if (uart_handle_sysrq_char(port, ch, regs))
 			goto ignore_char;
 
-		if ((uerstat & port->ignore_status_mask) == 0) {
-			tty_insert_flip_char(tty, ch, flag);
-		}
-
-		if ((uerstat & S3C2410_UERSTAT_OVERRUN) &&
-		    tty->flip.count < TTY_FLIPBUF_SIZE) {
-			/*
-			 * Overrun is special, since it's reported
-			 * immediately, and doesn't affect the current
-			 * character.
-			 */
-
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-		}
+		uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);
 
 	ignore_char:
 		continue;
diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c
index 157218b..22565a6 100644
--- a/drivers/serial/sa1100.c
+++ b/drivers/serial/sa1100.c
@@ -237,10 +237,7 @@
 		if (uart_handle_sysrq_char(&sport->port, ch, regs))
 			goto ignore_char;
 
-		if ((status & port->ignore_status_mask & ~UTSR1_TO_SM(UTSR1_ROR)) == 0)
-			tty_insert_flip_char(tty, ch, flg);
-		if (status & ~port->ignore_status_mask & UTSR1_TO_SM(UTSR1_ROR))
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		uart_insert_char(&sport->port, status, UTSR1_TO_SM(UTSR1_ROR), ch, flg);
 
 	ignore_char:
 		status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
diff --git a/drivers/serial/serial_lh7a40x.c b/drivers/serial/serial_lh7a40x.c
index 85cfa08..56f269b 100644
--- a/drivers/serial/serial_lh7a40x.c
+++ b/drivers/serial/serial_lh7a40x.c
@@ -190,18 +190,7 @@
 		if (uart_handle_sysrq_char (port, (unsigned char) data, regs))
 			continue;
 
-		if ((data & port->ignore_status_mask) == 0) {
-			tty_insert_flip_char(tty, data, flag);
-		}
-		if ((data & RxOverrunError)
-		    && tty->flip.count < TTY_FLIPBUF_SIZE) {
-			/*
-			 * Overrun is special, since it's reported
-			 * immediately, and doesn't affect the current
-			 * character
-			 */
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-		}
+		uart_insert_char(port, data, RxOverrunError, data, flag);
 	}
 	tty_flip_buffer_push (tty);
 	return;
diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c
index 37b2ef2..3f1051a 100644
--- a/drivers/serial/serial_txx9.c
+++ b/drivers/serial/serial_txx9.c
@@ -350,18 +350,9 @@
 		}
 		if (uart_handle_sysrq_char(&up->port, ch, regs))
 			goto ignore_char;
-		if ((disr & up->port.ignore_status_mask) == 0) {
-			tty_insert_flip_char(tty, ch, flag);
-		}
-		if ((disr & TXX9_SIDISR_UOER) &&
-		    tty->flip.count < TTY_FLIPBUF_SIZE) {
-			/*
-			 * Overrun is special, since it's reported
-			 * immediately, and doesn't affect the current
-			 * character.
-			 */
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-		}
+
+		uart_insert_char(&up->port, disr, TXX9_SIDISR_UOER, ch, flag);
+
 	ignore_char:
 		disr = sio_in(up, TXX9_SIDISR);
 	} while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0));
diff --git a/drivers/serial/vr41xx_siu.c b/drivers/serial/vr41xx_siu.c
index 3078861..5d2ceb6 100644
--- a/drivers/serial/vr41xx_siu.c
+++ b/drivers/serial/vr41xx_siu.c
@@ -412,10 +412,8 @@
 
 		if (uart_handle_sysrq_char(port, ch, regs))
 			goto ignore_char;
-		if ((lsr & port->ignore_status_mask) == 0)
-			tty_insert_flip_char(tty, ch, flag);
-		if ((lsr & UART_LSR_OE) && (tty->flip.count < TTY_FLIPBUF_SIZE))
-			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+
+		uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);
 
 	ignore_char:
 		lsr = siu_read(port, UART_LSR);
diff --git a/include/asm-arm/arch-imx/imx-regs.h b/include/asm-arm/arch-imx/imx-regs.h
index f32c203..93b840e 100644
--- a/include/asm-arm/arch-imx/imx-regs.h
+++ b/include/asm-arm/arch-imx/imx-regs.h
@@ -228,6 +228,30 @@
 #define PD31_BIN_SPI2_TXD    ( GPIO_PORTD | GPIO_BIN | 31 )
 
 /*
+ * PWM controller
+ */
+#define PWMC	__REG(IMX_PWM_BASE + 0x00)	/* PWM Control Register		*/
+#define PWMS	__REG(IMX_PWM_BASE + 0x04)	/* PWM Sample Register		*/
+#define PWMP	__REG(IMX_PWM_BASE + 0x08)	/* PWM Period Register		*/
+#define PWMCNT	__REG(IMX_PWM_BASE + 0x0C)	/* PWM Counter Register		*/
+
+#define PWMC_HCTR		(0x01<<18)		/* Halfword FIFO Data Swapping	*/
+#define PWMC_BCTR		(0x01<<17)		/* Byte FIFO Data Swapping	*/
+#define PWMC_SWR		(0x01<<16)		/* Software Reset		*/
+#define PWMC_CLKSRC		(0x01<<15)		/* Clock Source			*/
+#define PWMC_PRESCALER(x)	(((x-1) & 0x7F) << 8)	/* PRESCALER			*/
+#define PWMC_IRQ		(0x01<< 7)		/* Interrupt Request		*/
+#define PWMC_IRQEN		(0x01<< 6)		/* Interrupt Request Enable	*/
+#define PWMC_FIFOAV		(0x01<< 5)		/* FIFO Available		*/
+#define PWMC_EN			(0x01<< 4)		/* Enables/Disables the PWM	*/
+#define PWMC_REPEAT(x)		(((x) & 0x03) << 2)	/* Sample Repeats		*/
+#define PWMC_CLKSEL(x)		(((x) & 0x03) << 0)	/* Clock Selection		*/
+
+#define PWMS_SAMPLE(x)		((x) & 0xFFFF)		/* Contains a two-sample word	*/
+#define PWMP_PERIOD(x)		((x) & 0xFFFF)		/* Represents the PWM's period	*/
+#define PWMC_COUNTER(x)		((x) & 0xFFFF)		/* Represents the current count value	*/
+
+/*
  *  DMA Controller
  */
 #define DCR     __REG(IMX_DMAC_BASE +0x00)	/* DMA Control Register */
diff --git a/include/asm-arm/arch-s3c2410/regs-nand.h b/include/asm-arm/arch-s3c2410/regs-nand.h
index c443ac8..7cff235 100644
--- a/include/asm-arm/arch-s3c2410/regs-nand.h
+++ b/include/asm-arm/arch-s3c2410/regs-nand.h
@@ -1,16 +1,17 @@
 /* linux/include/asm-arm/arch-s3c2410/regs-nand.h
  *
- * Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk>
+ * Copyright (c) 2004,2005 Simtec Electronics <linux@simtec.co.uk>
  *		      http://www.simtec.co.uk/products/SWLINUX/
  *
  * 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.
  *
- * S3C2410 clock register definitions
+ * S3C2410 NAND register definitions
  *
  *  Changelog:
  *    18-Aug-2004    BJD     Copied file from 2.4 and updated
+ *    01-May-2005    BJD     Added definitions for s3c2440 controller
 */
 
 #ifndef __ASM_ARM_REGS_NAND
@@ -26,6 +27,22 @@
 #define S3C2410_NFSTAT  S3C2410_NFREG(0x10)
 #define S3C2410_NFECC   S3C2410_NFREG(0x14)
 
+#define S3C2440_NFCONT   S3C2410_NFREG(0x04)
+#define S3C2440_NFCMD    S3C2410_NFREG(0x08)
+#define S3C2440_NFADDR   S3C2410_NFREG(0x0C)
+#define S3C2440_NFDATA   S3C2410_NFREG(0x10)
+#define S3C2440_NFECCD0  S3C2410_NFREG(0x14)
+#define S3C2440_NFECCD1  S3C2410_NFREG(0x18)
+#define S3C2440_NFECCD   S3C2410_NFREG(0x1C)
+#define S3C2440_NFSTAT   S3C2410_NFREG(0x20)
+#define S3C2440_NFESTAT0 S3C2410_NFREG(0x24)
+#define S3C2440_NFESTAT1 S3C2410_NFREG(0x28)
+#define S3C2440_NFMECC0  S3C2410_NFREG(0x2C)
+#define S3C2440_NFMECC1  S3C2410_NFREG(0x30)
+#define S3C2440_NFSECC   S3C2410_NFREG(0x34)
+#define S3C2440_NFSBLK   S3C2410_NFREG(0x38)
+#define S3C2440_NFEBLK   S3C2410_NFREG(0x3C)
+
 #define S3C2410_NFCONF_EN          (1<<15)
 #define S3C2410_NFCONF_512BYTE     (1<<14)
 #define S3C2410_NFCONF_4STEP       (1<<13)
@@ -37,7 +54,28 @@
 
 #define S3C2410_NFSTAT_BUSY        (1<<0)
 
-/* think ECC can only be 8bit read? */
+#define S3C2440_NFCONF_BUSWIDTH_8	(0<<0)
+#define S3C2440_NFCONF_BUSWIDTH_16	(1<<0)
+#define S3C2440_NFCONF_ADVFLASH		(1<<3)
+#define S3C2440_NFCONF_TACLS(x)		((x)<<12)
+#define S3C2440_NFCONF_TWRPH0(x)	((x)<<8)
+#define S3C2440_NFCONF_TWRPH1(x)	((x)<<4)
+
+#define S3C2440_NFCONT_LOCKTIGHT	(1<<13)
+#define S3C2440_NFCONT_SOFTLOCK		(1<<12)
+#define S3C2440_NFCONT_ILLEGALACC_EN	(1<<10)
+#define S3C2440_NFCONT_RNBINT_EN	(1<<9)
+#define S3C2440_NFCONT_RN_FALLING	(1<<8)
+#define S3C2440_NFCONT_SPARE_ECCLOCK	(1<<6)
+#define S3C2440_NFCONT_MAIN_ECCLOCK	(1<<5)
+#define S3C2440_NFCONT_INITECC		(1<<4)
+#define S3C2440_NFCONT_nFCE		(1<<1)
+#define S3C2440_NFCONT_ENABLE		(1<<0)
+
+#define S3C2440_NFSTAT_READY		(1<<0)
+#define S3C2440_NFSTAT_nCE		(1<<1)
+#define S3C2440_NFSTAT_RnB_CHANGE	(1<<2)
+#define S3C2440_NFSTAT_ILLEGAL_ACCESS	(1<<3)
 
 #endif /* __ASM_ARM_REGS_NAND */
 
diff --git a/include/asm-arm/page.h b/include/asm-arm/page.h
index 4ca3a8e..019c45d 100644
--- a/include/asm-arm/page.h
+++ b/include/asm-arm/page.h
@@ -114,19 +114,8 @@
 				 unsigned long user);
 #endif
 
-#define clear_user_page(addr,vaddr,pg)			\
-	do {						\
-		preempt_disable();			\
-		__cpu_clear_user_page(addr, vaddr);	\
-		preempt_enable();			\
-	} while (0)
-
-#define copy_user_page(to,from,vaddr,pg)		\
-	do {						\
-		preempt_disable();			\
-		__cpu_copy_user_page(to, from, vaddr);	\
-		preempt_enable();			\
-	} while (0)
+#define clear_user_page(addr,vaddr,pg)	 __cpu_clear_user_page(addr, vaddr)
+#define copy_user_page(to,from,vaddr,pg) __cpu_copy_user_page(to, from, vaddr)
 
 #define clear_page(page)	memzero((void *)(page), PAGE_SIZE)
 extern void copy_page(void *to, const void *from);
@@ -171,6 +160,9 @@
 
 #endif /* STRICT_MM_TYPECHECKS */
 
+/* the upper-most page table pointer */
+extern pmd_t *top_pmd;
+
 /* Pure 2^n version of get_order */
 static inline int get_order(unsigned long size)
 {
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index c3fb598..d6025af 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -479,6 +479,25 @@
 	}
 }
 
+#include <linux/tty_flip.h>
+
+static inline void
+uart_insert_char(struct uart_port *port, unsigned int status,
+		 unsigned int overrun, unsigned int ch, unsigned int flag)
+{
+	struct tty_struct *tty = port->info->tty;
+
+	if ((status & port->ignore_status_mask & ~overrun) == 0)
+		tty_insert_flip_char(tty, ch, flag);
+
+	/*
+	 * Overrun is special.  Since it's reported immediately,
+	 * it doesn't affect the current character.
+	 */
+	if (status & ~port->ignore_status_mask & overrun)
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+}
+
 /*
  *	UART_ENABLE_MS - determine if port should enable modem status irqs
  */