r3628 - in developers/sameo/patches: . ar6k-ng

sameo at sita.openmoko.org sameo at sita.openmoko.org
Mon Dec 10 23:31:57 CET 2007


Author: sameo
Date: 2007-12-10 23:31:54 +0100 (Mon, 10 Dec 2007)
New Revision: 3628

Added:
   developers/sameo/patches/ar6k-ng/
   developers/sameo/patches/ar6k-ng/100-sdio_merge.patch
   developers/sameo/patches/ar6k-ng/110-s3c24xx_sdio_merge.patch
   developers/sameo/patches/ar6k-ng/120-sdio_ext_ops.patch
   developers/sameo/patches/ar6k-ng/130-gta02v2.patch
   developers/sameo/patches/ar6k-ng/140-s3c24xx_mci_platform.patch
   developers/sameo/patches/ar6k-ng/150-mmc_powerup_delay.patch
   developers/sameo/patches/ar6k-ng/s3c2440_wlan_gpio_cfg.patch
   developers/sameo/patches/ar6k-ng/series
Log:
ar6k-ng: First iteration, the ar6k chip gets detected and initialized.


Added: developers/sameo/patches/ar6k-ng/100-sdio_merge.patch
===================================================================
--- developers/sameo/patches/ar6k-ng/100-sdio_merge.patch	2007-12-10 17:51:36 UTC (rev 3627)
+++ developers/sameo/patches/ar6k-ng/100-sdio_merge.patch	2007-12-10 22:31:54 UTC (rev 3628)
@@ -0,0 +1,8835 @@
+---
+ drivers/mmc/card/Kconfig        |   25 
+ drivers/mmc/card/Makefile       |    2 
+ drivers/mmc/card/block.c        |   14 
+ drivers/mmc/card/queue.c        |  214 ++++++-
+ drivers/mmc/card/queue.h        |    7 
+ drivers/mmc/card/sdio_uart.c    | 1158 ++++++++++++++++++++++++++++++++++++++++
+ drivers/mmc/core/Makefile       |    5 
+ drivers/mmc/core/bus.c          |  288 +++++++++
+ drivers/mmc/core/bus.h          |   22 
+ drivers/mmc/core/core.c         |  392 +++++++------
+ drivers/mmc/core/core.h         |   31 -
+ drivers/mmc/core/host.c         |  162 +++++
+ drivers/mmc/core/host.h         |   18 
+ drivers/mmc/core/mmc.c          |  152 +++--
+ drivers/mmc/core/mmc_ops.c      |   36 -
+ drivers/mmc/core/mmc_ops.h      |    2 
+ drivers/mmc/core/sd.c           |  179 ++++--
+ drivers/mmc/core/sd_ops.c       |  102 +--
+ drivers/mmc/core/sd_ops.h       |    3 
+ drivers/mmc/core/sdio.c         |  382 +++++++++++++
+ drivers/mmc/core/sdio_bus.c     |  271 +++++++++
+ drivers/mmc/core/sdio_bus.h     |   22 
+ drivers/mmc/core/sdio_cis.c     |  404 +++++++++++++
+ drivers/mmc/core/sdio_cis.h     |   23 
+ drivers/mmc/core/sdio_io.c      |  562 +++++++++++++++++++
+ drivers/mmc/core/sdio_irq.c     |  245 ++++++++
+ drivers/mmc/core/sdio_ops.c     |  148 +++++
+ drivers/mmc/core/sdio_ops.h     |   22 
+ drivers/mmc/core/sysfs.c        |  347 -----------
+ drivers/mmc/core/sysfs.h        |   19 
+ drivers/mmc/host/at91_mci.c     |  252 ++++----
+ drivers/mmc/host/au1xmmc.c      |   34 -
+ drivers/mmc/host/imxmmc.c       |   16 
+ drivers/mmc/host/mmci.c         |   14 
+ drivers/mmc/host/mmci.h         |    2 
+ drivers/mmc/host/omap.c         |   14 
+ drivers/mmc/host/pxamci.c       |   14 
+ drivers/mmc/host/pxamci.h       |   22 
+ drivers/mmc/host/sdhci.c        |  201 ++++--
+ drivers/mmc/host/sdhci.h        |    4 
+ drivers/mmc/host/tifm_sd.c      |   20 
+ drivers/mmc/host/wbsd.c         |   66 --
+ drivers/mmc/host/wbsd.h         |    2 
+ include/linux/mmc/card.h        |   30 +
+ include/linux/mmc/core.h        |   39 -
+ include/linux/mmc/host.h        |   12 
+ include/linux/mmc/sdio.h        |  159 +++++
+ include/linux/mmc/sdio_func.h   |  150 +++++
+ include/linux/mmc/sdio_ids.h    |   23 
+ include/linux/mod_devicetable.h |   11 
+ scripts/mod/file2alias.c        |   20 
+ 51 files changed, 5313 insertions(+), 1049 deletions(-)
+
+Index: linux-2.6.22-atheros-ng/drivers/mmc/card/block.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/card/block.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/card/block.c	2007-11-13 11:35:23.000000000 +0100
+@@ -154,7 +154,7 @@
+ 	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ 
+ 	err = mmc_wait_for_cmd(card->host, &cmd, 0);
+-	if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD))
++	if (err || !(cmd.resp[0] & R1_APP_CMD))
+ 		return (u32)-1;
+ 
+ 	memset(&cmd, 0, sizeof(struct mmc_command));
+@@ -192,7 +192,7 @@
+ 
+ 	mmc_wait_for_req(card->host, &mrq);
+ 
+-	if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE)
++	if (cmd.error || data.error)
+ 		return (u32)-1;
+ 
+ 	blocks = ntohl(blocks);
+@@ -262,7 +262,9 @@
+ 		}
+ 
+ 		brq.data.sg = mq->sg;
+-		brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg);
++		brq.data.sg_len = mmc_queue_map_sg(mq);
++
++		mmc_queue_bounce_pre(mq);
+ 
+ 		if (brq.data.blocks !=
+ 		    (req->nr_sectors >> (md->block_bits - 9))) {
+@@ -279,6 +281,9 @@
+ 		}
+ 
+ 		mmc_wait_for_req(card->host, &brq.mrq);
++
++		mmc_queue_bounce_post(mq);
++
+ 		if (brq.cmd.error) {
+ 			printk(KERN_ERR "%s: error %d sending read/write command\n",
+ 			       req->rq_disk->disk_name, brq.cmd.error);
+@@ -409,13 +414,12 @@
+ 		return ERR_PTR(-ENOSPC);
+ 	__set_bit(devidx, dev_use);
+ 
+-	md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
++	md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
+ 	if (!md) {
+ 		ret = -ENOMEM;
+ 		goto out;
+ 	}
+ 
+-	memset(md, 0, sizeof(struct mmc_blk_data));
+ 
+ 	/*
+ 	 * Set the read-only status based on the supported commands
+Index: linux-2.6.22-atheros-ng/drivers/mmc/card/Kconfig
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/card/Kconfig	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/card/Kconfig	2007-11-13 11:35:23.000000000 +0100
+@@ -14,3 +14,28 @@
+ 	  mount the filesystem. Almost everyone wishing MMC support
+ 	  should say Y or M here.
+ 
++config MMC_BLOCK_BOUNCE
++	bool "Use bounce buffer for simple hosts"
++	depends on MMC_BLOCK
++	default y
++	help
++	  SD/MMC is a high latency protocol where it is crucial to
++	  send large requests in order to get high performance. Many
++	  controllers, however, are restricted to continuous memory
++	  (i.e. they can't do scatter-gather), something the kernel
++	  rarely can provide.
++
++	  Say Y here to help these restricted hosts by bouncing
++	  requests back and forth from a large buffer. You will get
++	  a big performance gain at the cost of up to 64 KiB of
++	  physical memory.
++
++	  If unsure, say Y here.
++
++config SDIO_UART
++	tristate "SDIO UART/GPS class support"
++	depends on MMC
++	help
++	  SDIO function driver for SDIO cards that implements the UART
++	  class, as well as the GPS class which appears like a UART.
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/card/Makefile
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/card/Makefile	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/card/Makefile	2007-11-13 11:35:23.000000000 +0100
+@@ -9,3 +9,5 @@
+ obj-$(CONFIG_MMC_BLOCK)		+= mmc_block.o
+ mmc_block-objs			:= block.o queue.o
+ 
++obj-$(CONFIG_SDIO_UART)		+= sdio_uart.o
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/card/queue.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/card/queue.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/card/queue.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/queue.c
++ *  linux/drivers/mmc/card/queue.c
+  *
+  *  Copyright (C) 2003 Russell King, All Rights Reserved.
+  *  Copyright 2006-2007 Pierre Ossman
+@@ -11,12 +11,15 @@
+  */
+ #include <linux/module.h>
+ #include <linux/blkdev.h>
++#include <linux/freezer.h>
+ #include <linux/kthread.h>
+ 
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/host.h>
+ #include "queue.h"
+ 
++#define MMC_QUEUE_BOUNCESZ	65536
++
+ #define MMC_QUEUE_SUSPENDED	(1 << 0)
+ 
+ /*
+@@ -42,11 +45,7 @@
+ 	struct mmc_queue *mq = d;
+ 	struct request_queue *q = mq->queue;
+ 
+-	/*
+-	 * Set iothread to ensure that we aren't put to sleep by
+-	 * the process freezing.  We handle suspension ourselves.
+-	 */
+-	current->flags |= PF_MEMALLOC|PF_NOFREEZE;
++	current->flags |= PF_MEMALLOC;
+ 
+ 	down(&mq->thread_sem);
+ 	do {
+@@ -84,7 +83,7 @@
+  * 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)
++static void mmc_request(struct request_queue *q)
+ {
+ 	struct mmc_queue *mq = q->queuedata;
+ 	struct request *req;
+@@ -127,21 +126,63 @@
+ 	if (!mq->queue)
+ 		return -ENOMEM;
+ 
+-	blk_queue_prep_rq(mq->queue, mmc_prep_request);
+-	blk_queue_bounce_limit(mq->queue, limit);
+-	blk_queue_max_sectors(mq->queue, host->max_req_size / 512);
+-	blk_queue_max_phys_segments(mq->queue, host->max_phys_segs);
+-	blk_queue_max_hw_segments(mq->queue, host->max_hw_segs);
+-	blk_queue_max_segment_size(mq->queue, host->max_seg_size);
+-
+ 	mq->queue->queuedata = mq;
+ 	mq->req = NULL;
+ 
+-	mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs,
+-			 GFP_KERNEL);
+-	if (!mq->sg) {
+-		ret = -ENOMEM;
+-		goto cleanup_queue;
++	blk_queue_prep_rq(mq->queue, mmc_prep_request);
++
++#ifdef CONFIG_MMC_BLOCK_BOUNCE
++	if (host->max_hw_segs == 1) {
++		unsigned int bouncesz;
++
++		bouncesz = MMC_QUEUE_BOUNCESZ;
++
++		if (bouncesz > host->max_req_size)
++			bouncesz = host->max_req_size;
++		if (bouncesz > host->max_seg_size)
++			bouncesz = host->max_seg_size;
++
++		mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
++		if (!mq->bounce_buf) {
++			printk(KERN_WARNING "%s: unable to allocate "
++				"bounce buffer\n", mmc_card_name(card));
++		} else {
++			blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_HIGH);
++			blk_queue_max_sectors(mq->queue, bouncesz / 512);
++			blk_queue_max_phys_segments(mq->queue, bouncesz / 512);
++			blk_queue_max_hw_segments(mq->queue, bouncesz / 512);
++			blk_queue_max_segment_size(mq->queue, bouncesz);
++
++			mq->sg = kmalloc(sizeof(struct scatterlist),
++				GFP_KERNEL);
++			if (!mq->sg) {
++				ret = -ENOMEM;
++				goto cleanup_queue;
++			}
++
++			mq->bounce_sg = kmalloc(sizeof(struct scatterlist) *
++				bouncesz / 512, GFP_KERNEL);
++			if (!mq->bounce_sg) {
++				ret = -ENOMEM;
++				goto cleanup_queue;
++			}
++		}
++	}
++#endif
++
++	if (!mq->bounce_buf) {
++		blk_queue_bounce_limit(mq->queue, limit);
++		blk_queue_max_sectors(mq->queue, host->max_req_size / 512);
++		blk_queue_max_phys_segments(mq->queue, host->max_phys_segs);
++		blk_queue_max_hw_segments(mq->queue, host->max_hw_segs);
++		blk_queue_max_segment_size(mq->queue, host->max_seg_size);
++
++		mq->sg = kmalloc(sizeof(struct scatterlist) *
++			host->max_phys_segs, GFP_KERNEL);
++		if (!mq->sg) {
++			ret = -ENOMEM;
++			goto cleanup_queue;
++		}
+ 	}
+ 
+ 	init_MUTEX(&mq->thread_sem);
+@@ -149,22 +190,28 @@
+ 	mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd");
+ 	if (IS_ERR(mq->thread)) {
+ 		ret = PTR_ERR(mq->thread);
+-		goto free_sg;
++		goto free_bounce_sg;
+ 	}
+ 
+ 	return 0;
+-
+- free_sg:
+-	kfree(mq->sg);
+-	mq->sg = NULL;
++ free_bounce_sg:
++ 	if (mq->bounce_sg)
++ 		kfree(mq->bounce_sg);
++ 	mq->bounce_sg = NULL;
+  cleanup_queue:
++ 	if (mq->sg)
++		kfree(mq->sg);
++	mq->sg = NULL;
++	if (mq->bounce_buf)
++		kfree(mq->bounce_buf);
++	mq->bounce_buf = NULL;
+ 	blk_cleanup_queue(mq->queue);
+ 	return ret;
+ }
+ 
+ void mmc_cleanup_queue(struct mmc_queue *mq)
+ {
+-	request_queue_t *q = mq->queue;
++	struct request_queue *q = mq->queue;
+ 	unsigned long flags;
+ 
+ 	/* Mark that we should start throwing out stragglers */
+@@ -178,9 +225,17 @@
+ 	/* Then terminate our worker thread */
+ 	kthread_stop(mq->thread);
+ 
++ 	if (mq->bounce_sg)
++ 		kfree(mq->bounce_sg);
++ 	mq->bounce_sg = NULL;
++
+ 	kfree(mq->sg);
+ 	mq->sg = NULL;
+ 
++	if (mq->bounce_buf)
++		kfree(mq->bounce_buf);
++	mq->bounce_buf = NULL;
++
+ 	blk_cleanup_queue(mq->queue);
+ 
+ 	mq->card = NULL;
+@@ -197,7 +252,7 @@
+  */
+ void mmc_queue_suspend(struct mmc_queue *mq)
+ {
+-	request_queue_t *q = mq->queue;
++	struct request_queue *q = mq->queue;
+ 	unsigned long flags;
+ 
+ 	if (!(mq->flags & MMC_QUEUE_SUSPENDED)) {
+@@ -217,7 +272,7 @@
+  */
+ void mmc_queue_resume(struct mmc_queue *mq)
+ {
+-	request_queue_t *q = mq->queue;
++	struct request_queue *q = mq->queue;
+ 	unsigned long flags;
+ 
+ 	if (mq->flags & MMC_QUEUE_SUSPENDED) {
+@@ -231,3 +286,108 @@
+ 	}
+ }
+ 
++static void copy_sg(struct scatterlist *dst, unsigned int dst_len,
++	struct scatterlist *src, unsigned int src_len)
++{
++	unsigned int chunk;
++	char *dst_buf, *src_buf;
++	unsigned int dst_size, src_size;
++
++	dst_buf = NULL;
++	src_buf = NULL;
++	dst_size = 0;
++	src_size = 0;
++
++	while (src_len) {
++		BUG_ON(dst_len == 0);
++
++		if (dst_size == 0) {
++			dst_buf = page_address(dst->page) + dst->offset;
++			dst_size = dst->length;
++		}
++
++		if (src_size == 0) {
++			src_buf = page_address(src->page) + src->offset;
++			src_size = src->length;
++		}
++
++		chunk = min(dst_size, src_size);
++
++		memcpy(dst_buf, src_buf, chunk);
++
++		dst_buf += chunk;
++		src_buf += chunk;
++		dst_size -= chunk;
++		src_size -= chunk;
++
++		if (dst_size == 0) {
++			dst++;
++			dst_len--;
++		}
++
++		if (src_size == 0) {
++			src++;
++			src_len--;
++		}
++	}
++}
++
++unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
++{
++	unsigned int sg_len;
++
++	if (!mq->bounce_buf)
++		return blk_rq_map_sg(mq->queue, mq->req, mq->sg);
++
++	BUG_ON(!mq->bounce_sg);
++
++	sg_len = blk_rq_map_sg(mq->queue, mq->req, mq->bounce_sg);
++
++	mq->bounce_sg_len = sg_len;
++
++	/*
++	 * Shortcut in the event we only get a single entry.
++	 */
++	if (sg_len == 1) {
++		memcpy(mq->sg, mq->bounce_sg, sizeof(struct scatterlist));
++		return 1;
++	}
++
++	mq->sg[0].page = virt_to_page(mq->bounce_buf);
++	mq->sg[0].offset = offset_in_page(mq->bounce_buf);
++	mq->sg[0].length = 0;
++
++	while (sg_len) {
++		mq->sg[0].length += mq->bounce_sg[sg_len - 1].length;
++		sg_len--;
++	}
++
++	return 1;
++}
++
++void mmc_queue_bounce_pre(struct mmc_queue *mq)
++{
++	if (!mq->bounce_buf)
++		return;
++
++	if (mq->bounce_sg_len == 1)
++		return;
++	if (rq_data_dir(mq->req) != WRITE)
++		return;
++
++	copy_sg(mq->sg, 1, mq->bounce_sg, mq->bounce_sg_len);
++}
++
++void mmc_queue_bounce_post(struct mmc_queue *mq)
++{
++	if (!mq->bounce_buf)
++		return;
++
++	if (mq->bounce_sg_len == 1)
++		return;
++	if (rq_data_dir(mq->req) != READ)
++		return;
++
++	copy_sg(mq->bounce_sg, mq->bounce_sg_len, mq->sg, 1);
++}
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/card/queue.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/card/queue.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/card/queue.h	2007-11-13 11:35:23.000000000 +0100
+@@ -14,6 +14,9 @@
+ 	void			*data;
+ 	struct request_queue	*queue;
+ 	struct scatterlist	*sg;
++	char			*bounce_buf;
++	struct scatterlist	*bounce_sg;
++	unsigned int		bounce_sg_len;
+ };
+ 
+ extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *);
+@@ -21,4 +24,8 @@
+ extern void mmc_queue_suspend(struct mmc_queue *);
+ extern void mmc_queue_resume(struct mmc_queue *);
+ 
++extern unsigned int mmc_queue_map_sg(struct mmc_queue *);
++extern void mmc_queue_bounce_pre(struct mmc_queue *);
++extern void mmc_queue_bounce_post(struct mmc_queue *);
++
+ #endif
+Index: linux-2.6.22-atheros-ng/drivers/mmc/card/sdio_uart.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/card/sdio_uart.c	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,1158 @@
++/*
++ * linux/drivers/mmc/card/sdio_uart.c - SDIO UART/GPS driver
++ *
++ * Based on drivers/serial/8250.c and drivers/serial/serial_core.c
++ * by Russell King.
++ *
++ * Author:	Nicolas Pitre
++ * Created:	June 15, 2007
++ * Copyright:	MontaVista Software, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++/*
++ * Note: Although this driver assumes a 16550A-like UART implementation,
++ * it is not possible to leverage the common 8250/16550 driver, nor the
++ * core UART infrastructure, as they assumes direct access to the hardware
++ * registers, often under a spinlock.  This is not possible in the SDIO
++ * context as SDIO access functions must be able to sleep.
++ *
++ * Because we need to lock the SDIO host to ensure an exclusive access to
++ * the card, we simply rely on that lock to also prevent and serialize
++ * concurrent access to the same port.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/mutex.h>
++#include <linux/serial_reg.h>
++#include <linux/circ_buf.h>
++#include <linux/gfp.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++
++#include <linux/mmc/core.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio_func.h>
++#include <linux/mmc/sdio_ids.h>
++
++
++#define UART_NR		8	/* Number of UARTs this driver can handle */
++
++
++#define UART_XMIT_SIZE	PAGE_SIZE
++#define WAKEUP_CHARS	256
++
++#define circ_empty(circ)	((circ)->head == (circ)->tail)
++#define circ_clear(circ)	((circ)->head = (circ)->tail = 0)
++
++#define circ_chars_pending(circ) \
++		(CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
++
++#define circ_chars_free(circ) \
++		(CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
++
++
++struct uart_icount {
++	__u32	cts;
++	__u32	dsr;
++	__u32	rng;
++	__u32	dcd;
++	__u32	rx;
++	__u32	tx;
++	__u32	frame;
++	__u32	overrun;
++	__u32	parity;
++	__u32	brk;
++};
++
++struct sdio_uart_port {
++	struct kref		kref;
++	struct tty_struct	*tty;
++	unsigned int		index;
++	unsigned int		opened;
++	struct mutex		open_lock;
++	struct sdio_func	*func;
++	struct mutex		func_lock;
++	struct task_struct	*in_sdio_uart_irq;
++	unsigned int		regs_offset;
++	struct circ_buf		xmit;
++	spinlock_t		write_lock;
++	struct uart_icount	icount;
++	unsigned int		uartclk;
++	unsigned int		mctrl;
++	unsigned int		read_status_mask;
++	unsigned int		ignore_status_mask;
++	unsigned char		x_char;
++	unsigned char           ier;
++	unsigned char           lcr;
++};
++
++static struct sdio_uart_port *sdio_uart_table[UART_NR];
++static DEFINE_SPINLOCK(sdio_uart_table_lock);
++
++static int sdio_uart_add_port(struct sdio_uart_port *port)
++{
++	int index, ret = -EBUSY;
++
++	kref_init(&port->kref);
++	mutex_init(&port->open_lock);
++	mutex_init(&port->func_lock);
++	spin_lock_init(&port->write_lock);
++
++	spin_lock(&sdio_uart_table_lock);
++	for (index = 0; index < UART_NR; index++) {
++		if (!sdio_uart_table[index]) {
++			port->index = index;
++			sdio_uart_table[index] = port;
++			ret = 0;
++			break;
++		}
++	}
++	spin_unlock(&sdio_uart_table_lock);
++
++	return ret;
++}
++
++static struct sdio_uart_port *sdio_uart_port_get(unsigned index)
++{
++	struct sdio_uart_port *port;
++
++	if (index >= UART_NR)
++		return NULL;
++
++	spin_lock(&sdio_uart_table_lock);
++	port = sdio_uart_table[index];
++	if (port)
++		kref_get(&port->kref);
++	spin_unlock(&sdio_uart_table_lock);
++
++	return port;
++}
++
++static void sdio_uart_port_destroy(struct kref *kref)
++{
++	struct sdio_uart_port *port =
++		container_of(kref, struct sdio_uart_port, kref);
++	kfree(port);
++}
++
++static void sdio_uart_port_put(struct sdio_uart_port *port)
++{
++	kref_put(&port->kref, sdio_uart_port_destroy);
++}
++
++static void sdio_uart_port_remove(struct sdio_uart_port *port)
++{
++	struct sdio_func *func;
++
++	BUG_ON(sdio_uart_table[port->index] != port);
++
++	spin_lock(&sdio_uart_table_lock);
++	sdio_uart_table[port->index] = NULL;
++	spin_unlock(&sdio_uart_table_lock);
++
++	/*
++	 * We're killing a port that potentially still is in use by
++	 * the tty layer. Be careful to prevent any further access
++	 * to the SDIO function and arrange for the tty layer to
++	 * give up on that port ASAP.
++	 * Beware: the lock ordering is critical.
++	 */
++	mutex_lock(&port->open_lock);
++	mutex_lock(&port->func_lock);
++	func = port->func;
++	sdio_claim_host(func);
++	port->func = NULL;
++	mutex_unlock(&port->func_lock);
++	if (port->opened)
++		tty_hangup(port->tty);
++	mutex_unlock(&port->open_lock);
++	sdio_release_irq(func);
++	sdio_disable_func(func);
++	sdio_release_host(func);
++
++	sdio_uart_port_put(port);
++}
++
++static int sdio_uart_claim_func(struct sdio_uart_port *port)
++{
++	mutex_lock(&port->func_lock);
++	if (unlikely(!port->func)) {
++		mutex_unlock(&port->func_lock);
++		return -ENODEV;
++	}
++	if (likely(port->in_sdio_uart_irq != current))
++		sdio_claim_host(port->func);
++	mutex_unlock(&port->func_lock);
++	return 0;
++}
++
++static inline void sdio_uart_release_func(struct sdio_uart_port *port)
++{
++	if (likely(port->in_sdio_uart_irq != current))
++		sdio_release_host(port->func);
++}
++
++static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset)
++{
++	unsigned char c;
++	c = sdio_readb(port->func, port->regs_offset + offset, NULL);
++	return c;
++}
++
++static inline void sdio_out(struct sdio_uart_port *port, int offset, int value)
++{
++	sdio_writeb(port->func, value, port->regs_offset + offset, NULL);
++}
++
++static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port)
++{
++	unsigned char status;
++	unsigned int ret;
++
++	status = sdio_in(port, UART_MSR);
++
++	ret = 0;
++	if (status & UART_MSR_DCD)
++		ret |= TIOCM_CAR;
++	if (status & UART_MSR_RI)
++		ret |= TIOCM_RNG;
++	if (status & UART_MSR_DSR)
++		ret |= TIOCM_DSR;
++	if (status & UART_MSR_CTS)
++		ret |= TIOCM_CTS;
++	return ret;
++}
++
++static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl)
++{
++	unsigned char mcr = 0;
++
++	if (mctrl & TIOCM_RTS)
++		mcr |= UART_MCR_RTS;
++	if (mctrl & TIOCM_DTR)
++		mcr |= UART_MCR_DTR;
++	if (mctrl & TIOCM_OUT1)
++		mcr |= UART_MCR_OUT1;
++	if (mctrl & TIOCM_OUT2)
++		mcr |= UART_MCR_OUT2;
++	if (mctrl & TIOCM_LOOP)
++		mcr |= UART_MCR_LOOP;
++
++	sdio_out(port, UART_MCR, mcr);
++}
++
++static inline void sdio_uart_update_mctrl(struct sdio_uart_port *port,
++					  unsigned int set, unsigned int clear)
++{
++	unsigned int old;
++
++	old = port->mctrl;
++	port->mctrl = (old & ~clear) | set;
++	if (old != port->mctrl)
++		sdio_uart_write_mctrl(port, port->mctrl);
++}
++
++#define sdio_uart_set_mctrl(port, x)	sdio_uart_update_mctrl(port, x, 0)
++#define sdio_uart_clear_mctrl(port, x)	sdio_uart_update_mctrl(port, 0, x)
++
++static void sdio_uart_change_speed(struct sdio_uart_port *port,
++				   struct ktermios *termios,
++				   struct ktermios *old)
++{
++	unsigned char cval, fcr = 0;
++	unsigned int baud, quot;
++
++	switch (termios->c_cflag & CSIZE) {
++	case CS5:
++		cval = UART_LCR_WLEN5;
++		break;
++	case CS6:
++		cval = UART_LCR_WLEN6;
++		break;
++	case CS7:
++		cval = UART_LCR_WLEN7;
++		break;
++	default:
++	case CS8:
++		cval = UART_LCR_WLEN8;
++		break;
++	}
++
++	if (termios->c_cflag & CSTOPB)
++		cval |= UART_LCR_STOP;
++	if (termios->c_cflag & PARENB)
++		cval |= UART_LCR_PARITY;
++	if (!(termios->c_cflag & PARODD))
++		cval |= UART_LCR_EPAR;
++
++	for (;;) {
++		baud = tty_termios_baud_rate(termios);
++		if (baud == 0)
++			baud = 9600;  /* Special case: B0 rate. */
++		if (baud <= port->uartclk)
++			break;
++		/*
++		 * Oops, the quotient was zero.  Try again with the old
++		 * baud rate if possible, otherwise default to 9600.
++		 */
++		termios->c_cflag &= ~CBAUD;
++		if (old) {
++			termios->c_cflag |= old->c_cflag & CBAUD;
++			old = NULL;
++		} else
++			termios->c_cflag |= B9600;
++	}
++	quot = (2 * port->uartclk + baud) / (2 * baud);
++
++	if (baud < 2400)
++		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
++	else
++		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10;
++
++	port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
++	if (termios->c_iflag & INPCK)
++		port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
++	if (termios->c_iflag & (BRKINT | PARMRK))
++		port->read_status_mask |= UART_LSR_BI;
++
++	/*
++	 * Characters to ignore
++	 */
++	port->ignore_status_mask = 0;
++	if (termios->c_iflag & IGNPAR)
++		port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
++	if (termios->c_iflag & IGNBRK) {
++		port->ignore_status_mask |= UART_LSR_BI;
++		/*
++		 * If we're ignoring parity and break indicators,
++		 * ignore overruns too (for real raw support).
++		 */
++		if (termios->c_iflag & IGNPAR)
++			port->ignore_status_mask |= UART_LSR_OE;
++	}
++
++	/*
++	 * ignore all characters if CREAD is not set
++	 */
++	if ((termios->c_cflag & CREAD) == 0)
++		port->ignore_status_mask |= UART_LSR_DR;
++
++	/*
++	 * CTS flow control flag and modem status interrupts
++	 */
++	port->ier &= ~UART_IER_MSI;
++	if ((termios->c_cflag & CRTSCTS) || !(termios->c_cflag & CLOCAL))
++		port->ier |= UART_IER_MSI;
++
++	port->lcr = cval;
++
++	sdio_out(port, UART_IER, port->ier);
++	sdio_out(port, UART_LCR, cval | UART_LCR_DLAB);
++	sdio_out(port, UART_DLL, quot & 0xff);
++	sdio_out(port, UART_DLM, quot >> 8);
++	sdio_out(port, UART_LCR, cval);
++	sdio_out(port, UART_FCR, fcr);
++
++	sdio_uart_write_mctrl(port, port->mctrl);
++}
++
++static void sdio_uart_start_tx(struct sdio_uart_port *port)
++{
++	if (!(port->ier & UART_IER_THRI)) {
++		port->ier |= UART_IER_THRI;
++		sdio_out(port, UART_IER, port->ier);
++	}
++}
++
++static void sdio_uart_stop_tx(struct sdio_uart_port *port)
++{
++	if (port->ier & UART_IER_THRI) {
++		port->ier &= ~UART_IER_THRI;
++		sdio_out(port, UART_IER, port->ier);
++	}
++}
++
++static void sdio_uart_stop_rx(struct sdio_uart_port *port)
++{
++	port->ier &= ~UART_IER_RLSI;
++	port->read_status_mask &= ~UART_LSR_DR;
++	sdio_out(port, UART_IER, port->ier);
++}
++
++static void sdio_uart_receive_chars(struct sdio_uart_port *port, int *status)
++{
++	struct tty_struct *tty = port->tty;
++	unsigned int ch, flag;
++	int max_count = 256;
++
++	do {
++		ch = sdio_in(port, UART_RX);
++		flag = TTY_NORMAL;
++		port->icount.rx++;
++
++		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
++				        UART_LSR_FE | UART_LSR_OE))) {
++			/*
++			 * For statistics only
++			 */
++			if (*status & UART_LSR_BI) {
++				*status &= ~(UART_LSR_FE | UART_LSR_PE);
++				port->icount.brk++;
++			} else if (*status & UART_LSR_PE)
++				port->icount.parity++;
++			else if (*status & UART_LSR_FE)
++				port->icount.frame++;
++			if (*status & UART_LSR_OE)
++				port->icount.overrun++;
++
++			/*
++			 * Mask off conditions which should be ignored.
++			 */
++			*status &= port->read_status_mask;
++			if (*status & UART_LSR_BI) {
++				flag = TTY_BREAK;
++			} else if (*status & UART_LSR_PE)
++				flag = TTY_PARITY;
++			else if (*status & UART_LSR_FE)
++				flag = TTY_FRAME;
++		}
++
++		if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 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 & UART_LSR_OE)
++			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
++
++		*status = sdio_in(port, UART_LSR);
++	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
++	tty_flip_buffer_push(tty);
++}
++
++static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
++{
++	struct circ_buf *xmit = &port->xmit;
++	int count;
++
++	if (port->x_char) {
++		sdio_out(port, UART_TX, port->x_char);
++		port->icount.tx++;
++		port->x_char = 0;
++		return;
++	}
++	if (circ_empty(xmit) || port->tty->stopped || port->tty->hw_stopped) {
++		sdio_uart_stop_tx(port);
++		return;
++	}
++
++	count = 16;
++	do {
++		sdio_out(port, UART_TX, xmit->buf[xmit->tail]);
++		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
++		port->icount.tx++;
++		if (circ_empty(xmit))
++			break;
++	} while (--count > 0);
++
++	if (circ_chars_pending(xmit) < WAKEUP_CHARS)
++		tty_wakeup(port->tty);
++
++	if (circ_empty(xmit))
++		sdio_uart_stop_tx(port);
++}
++
++static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
++{
++	int status;
++
++	status = sdio_in(port, UART_MSR);
++
++	if ((status & UART_MSR_ANY_DELTA) == 0)
++		return;
++
++	if (status & UART_MSR_TERI)
++		port->icount.rng++;
++	if (status & UART_MSR_DDSR)
++		port->icount.dsr++;
++	if (status & UART_MSR_DDCD)
++		port->icount.dcd++;
++	if (status & UART_MSR_DCTS) {
++		port->icount.cts++;
++		if (port->tty->termios->c_cflag & CRTSCTS) {
++			int cts = (status & UART_MSR_CTS);
++			if (port->tty->hw_stopped) {
++				if (cts) {
++					port->tty->hw_stopped = 0;
++					sdio_uart_start_tx(port);
++					tty_wakeup(port->tty);
++				}
++			} else {
++				if (!cts) {
++					port->tty->hw_stopped = 1;
++					sdio_uart_stop_tx(port);
++				}
++			}
++		}
++	}
++}
++
++/*
++ * This handles the interrupt from one port.
++ */
++static void sdio_uart_irq(struct sdio_func *func)
++{
++	struct sdio_uart_port *port = sdio_get_drvdata(func);
++	unsigned int iir, lsr;
++
++	/*
++	 * In a few places sdio_uart_irq() is called directly instead of
++	 * waiting for the actual interrupt to be raised and the SDIO IRQ
++	 * thread scheduled in order to reduce latency.  However, some
++	 * interaction with the tty core may end up calling us back
++	 * (serial echo, flow control, etc.) through those same places
++	 * causing undesirable effects.  Let's stop the recursion here.
++	 */
++	if (unlikely(port->in_sdio_uart_irq == current))
++		return;
++
++	iir = sdio_in(port, UART_IIR);
++	if (iir & UART_IIR_NO_INT)
++		return;
++
++	port->in_sdio_uart_irq = current;
++	lsr = sdio_in(port, UART_LSR);
++	if (lsr & UART_LSR_DR)
++		sdio_uart_receive_chars(port, &lsr);
++	sdio_uart_check_modem_status(port);
++	if (lsr & UART_LSR_THRE)
++		sdio_uart_transmit_chars(port);
++	port->in_sdio_uart_irq = NULL;
++}
++
++static int sdio_uart_startup(struct sdio_uart_port *port)
++{
++	unsigned long page;
++	int ret;
++
++	/*
++	 * Set the TTY IO error marker - we will only clear this
++	 * once we have successfully opened the port.
++	 */
++	set_bit(TTY_IO_ERROR, &port->tty->flags);
++
++	/* Initialise and allocate the transmit buffer. */
++	page = __get_free_page(GFP_KERNEL);
++	if (!page)
++		return -ENOMEM;
++	port->xmit.buf = (unsigned char *)page;
++	circ_clear(&port->xmit);
++
++	ret = sdio_uart_claim_func(port);
++	if (ret)
++		goto err1;
++	ret = sdio_enable_func(port->func);
++	if (ret)
++		goto err2;
++	ret = sdio_claim_irq(port->func, sdio_uart_irq);
++	if (ret)
++		goto err3;
++
++	/*
++	 * Clear the FIFO buffers and disable them.
++	 * (they will be reenabled in sdio_change_speed())
++	 */
++	sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
++	sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
++			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
++	sdio_out(port, UART_FCR, 0);
++
++	/*
++	 * Clear the interrupt registers.
++	 */
++	(void) sdio_in(port, UART_LSR);
++	(void) sdio_in(port, UART_RX);
++	(void) sdio_in(port, UART_IIR);
++	(void) sdio_in(port, UART_MSR);
++
++	/*
++	 * Now, initialize the UART
++	 */
++	sdio_out(port, UART_LCR, UART_LCR_WLEN8);
++
++	port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
++	port->mctrl = TIOCM_OUT2;
++
++	sdio_uart_change_speed(port, port->tty->termios, NULL);
++
++	if (port->tty->termios->c_cflag & CBAUD)
++		sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
++
++	if (port->tty->termios->c_cflag & CRTSCTS)
++		if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))
++			port->tty->hw_stopped = 1;
++
++	clear_bit(TTY_IO_ERROR, &port->tty->flags);
++
++	/* Kick the IRQ handler once while we're still holding the host lock */
++	sdio_uart_irq(port->func);
++
++	sdio_uart_release_func(port);
++	return 0;
++
++err3:
++	sdio_disable_func(port->func);
++err2:
++	sdio_uart_release_func(port);
++err1:
++	free_page((unsigned long)port->xmit.buf);
++	return ret;
++}
++
++static void sdio_uart_shutdown(struct sdio_uart_port *port)
++{
++	int ret;
++
++	ret = sdio_uart_claim_func(port);
++	if (ret)
++		goto skip;
++
++	sdio_uart_stop_rx(port);
++
++	/* TODO: wait here for TX FIFO to drain */
++
++	/* Turn off DTR and RTS early. */
++	if (port->tty->termios->c_cflag & HUPCL)
++		sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
++
++	 /* Disable interrupts from this port */
++	sdio_release_irq(port->func);
++	port->ier = 0;
++	sdio_out(port, UART_IER, 0);
++
++	sdio_uart_clear_mctrl(port, TIOCM_OUT2);
++
++	/* Disable break condition and FIFOs. */
++	port->lcr &= ~UART_LCR_SBC;
++	sdio_out(port, UART_LCR, port->lcr);
++	sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
++				 UART_FCR_CLEAR_RCVR |
++				 UART_FCR_CLEAR_XMIT);
++	sdio_out(port, UART_FCR, 0);
++
++	sdio_disable_func(port->func);
++
++	sdio_uart_release_func(port);
++
++skip:
++	/* Free the transmit buffer page. */
++	free_page((unsigned long)port->xmit.buf);
++}
++
++static int sdio_uart_open (struct tty_struct *tty, struct file * filp)
++{
++	struct sdio_uart_port *port;
++	int ret;
++
++	port = sdio_uart_port_get(tty->index);
++	if (!port)
++		return -ENODEV;
++
++	mutex_lock(&port->open_lock);
++
++	/*
++	 * Make sure not to mess up with a dead port
++	 * which has not been closed yet.
++	 */
++	if (tty->driver_data && tty->driver_data != port) {
++		mutex_unlock(&port->open_lock);
++		sdio_uart_port_put(port);
++		return -EBUSY;
++	}
++
++	if (!port->opened) {
++		tty->driver_data = port;
++		port->tty = tty;
++		ret = sdio_uart_startup(port);
++		if (ret) {
++			tty->driver_data = NULL;
++			port->tty = NULL;
++			mutex_unlock(&port->open_lock);
++			sdio_uart_port_put(port);
++			return ret;
++		}
++	}
++	port->opened++;
++	mutex_unlock(&port->open_lock);
++	return 0;
++}
++
++static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++
++	if (!port)
++		return;
++
++	mutex_lock(&port->open_lock);
++	BUG_ON(!port->opened);
++
++	/*
++	 * This is messy.  The tty layer calls us even when open()
++	 * returned an error.  Ignore this close request if tty->count
++	 * is larger than port->count.
++	 */
++	if (tty->count > port->opened) {
++		mutex_unlock(&port->open_lock);
++		return;
++	}
++
++	if (--port->opened == 0) {
++		tty->closing = 1;
++		sdio_uart_shutdown(port);
++		tty_ldisc_flush(tty);
++		port->tty = NULL;
++		tty->driver_data = NULL;
++		tty->closing = 0;
++	}
++	mutex_unlock(&port->open_lock);
++	sdio_uart_port_put(port);
++}
++
++static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
++			   int count)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++	struct circ_buf *circ = &port->xmit;
++	int c, ret = 0;
++
++	if (!port->func)
++		return -ENODEV;
++
++	spin_lock(&port->write_lock);
++	while (1) {
++		c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
++		if (count < c)
++			c = count;
++		if (c <= 0)
++			break;
++		memcpy(circ->buf + circ->head, buf, c);
++		circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
++		buf += c;
++		count -= c;
++		ret += c;
++	}
++	spin_unlock(&port->write_lock);
++
++	if ( !(port->ier & UART_IER_THRI)) {
++		int err = sdio_uart_claim_func(port);
++		if (!err) {
++			sdio_uart_start_tx(port);
++			sdio_uart_irq(port->func);
++			sdio_uart_release_func(port);
++		} else
++			ret = err;
++	}
++
++	return ret;
++}
++
++static int sdio_uart_write_room(struct tty_struct *tty)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++	return port ? circ_chars_free(&port->xmit) : 0;
++}
++
++static int sdio_uart_chars_in_buffer(struct tty_struct *tty)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++	return port ? circ_chars_pending(&port->xmit) : 0;
++}
++
++static void sdio_uart_send_xchar(struct tty_struct *tty, char ch)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++
++	port->x_char = ch;
++	if (ch && !(port->ier & UART_IER_THRI)) {
++		if (sdio_uart_claim_func(port) != 0)
++			return;
++		sdio_uart_start_tx(port);
++		sdio_uart_irq(port->func);
++		sdio_uart_release_func(port);
++	}
++}
++
++static void sdio_uart_throttle(struct tty_struct *tty)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++
++	if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
++		return;
++
++	if (sdio_uart_claim_func(port) != 0)
++		return;
++
++	if (I_IXOFF(tty)) {
++		port->x_char = STOP_CHAR(tty);
++		sdio_uart_start_tx(port);
++	}
++
++	if (tty->termios->c_cflag & CRTSCTS)
++		sdio_uart_clear_mctrl(port, TIOCM_RTS);
++
++	sdio_uart_irq(port->func);
++	sdio_uart_release_func(port);
++}
++
++static void sdio_uart_unthrottle(struct tty_struct *tty)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++
++	if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
++		return;
++
++	if (sdio_uart_claim_func(port) != 0)
++		return;
++
++	if (I_IXOFF(tty)) {
++		if (port->x_char) {
++			port->x_char = 0;
++		} else {
++			port->x_char = START_CHAR(tty);
++			sdio_uart_start_tx(port);
++		}
++	}
++
++	if (tty->termios->c_cflag & CRTSCTS)
++		sdio_uart_set_mctrl(port, TIOCM_RTS);
++
++	sdio_uart_irq(port->func);
++	sdio_uart_release_func(port);
++}
++
++static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++	unsigned int cflag = tty->termios->c_cflag;
++
++#define RELEVANT_IFLAG(iflag)   ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
++
++	if ((cflag ^ old_termios->c_cflag) == 0 &&
++	    RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
++		return;
++
++	if (sdio_uart_claim_func(port) != 0)
++		return;
++
++	sdio_uart_change_speed(port, tty->termios, old_termios);
++
++	/* Handle transition to B0 status */
++	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
++		sdio_uart_clear_mctrl(port, TIOCM_RTS | TIOCM_DTR);
++
++	/* Handle transition away from B0 status */
++	if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
++		unsigned int mask = TIOCM_DTR;
++		if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags))
++			mask |= TIOCM_RTS;
++		sdio_uart_set_mctrl(port, mask);
++	}
++
++	/* Handle turning off CRTSCTS */
++	if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
++		tty->hw_stopped = 0;
++		sdio_uart_start_tx(port);
++	}
++
++	/* Handle turning on CRTSCTS */
++	if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
++		if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) {
++			tty->hw_stopped = 1;
++			sdio_uart_stop_tx(port);
++		}
++	}
++
++	sdio_uart_release_func(port);
++}
++
++static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++
++	if (sdio_uart_claim_func(port) != 0)
++		return;
++
++	if (break_state == -1)
++		port->lcr |= UART_LCR_SBC;
++	else
++		port->lcr &= ~UART_LCR_SBC;
++	sdio_out(port, UART_LCR, port->lcr);
++
++	sdio_uart_release_func(port);
++}
++
++static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++	int result;
++
++	result = sdio_uart_claim_func(port);
++	if (!result) {
++		result = port->mctrl | sdio_uart_get_mctrl(port);
++		sdio_uart_release_func(port);
++	}
++
++	return result;
++}
++
++static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file,
++			      unsigned int set, unsigned int clear)
++{
++	struct sdio_uart_port *port = tty->driver_data;
++	int result;
++
++	result =sdio_uart_claim_func(port);
++	if(!result) {
++		sdio_uart_update_mctrl(port, set, clear);
++		sdio_uart_release_func(port);
++	}
++
++	return result;
++}
++
++static int sdio_uart_read_proc(char *page, char **start, off_t off,
++			       int count, int *eof, void *data)
++{
++	int i, len = 0;
++	off_t begin = 0;
++
++	len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n",
++		       "", "", "");
++	for (i = 0; i < UART_NR && len < PAGE_SIZE - 96; i++) {
++		struct sdio_uart_port *port = sdio_uart_port_get(i);
++		if (port) {
++			len += sprintf(page+len, "%d: uart:SDIO", i);
++			if(capable(CAP_SYS_ADMIN)) {
++				len += sprintf(page + len, " tx:%d rx:%d",
++					       port->icount.tx, port->icount.rx);
++				if (port->icount.frame)
++					len += sprintf(page + len, " fe:%d",
++						       port->icount.frame);
++				if (port->icount.parity)
++					len += sprintf(page + len, " pe:%d",
++						       port->icount.parity);
++				if (port->icount.brk)
++					len += sprintf(page + len, " brk:%d",
++						       port->icount.brk);
++				if (port->icount.overrun)
++					len += sprintf(page + len, " oe:%d",
++						       port->icount.overrun);
++				if (port->icount.cts)
++					len += sprintf(page + len, " cts:%d",
++						       port->icount.cts);
++				if (port->icount.dsr)
++					len += sprintf(page + len, " dsr:%d",
++						       port->icount.dsr);
++				if (port->icount.rng)
++					len += sprintf(page + len, " rng:%d",
++						       port->icount.rng);
++				if (port->icount.dcd)
++					len += sprintf(page + len, " dcd:%d",
++						       port->icount.dcd);
++			}
++			strcat(page, "\n");
++			len++;
++			sdio_uart_port_put(port);
++		}
++
++		if (len + begin > off + count)
++			goto done;
++		if (len + begin < off) {
++			begin += len;
++			len = 0;
++		}
++	}
++	*eof = 1;
++
++done:
++	if (off >= len + begin)
++		return 0;
++	*start = page + (off - begin);
++	return (count < begin + len - off) ? count : (begin + len - off);
++}
++
++static const struct tty_operations sdio_uart_ops = {
++	.open			= sdio_uart_open,
++	.close			= sdio_uart_close,
++	.write			= sdio_uart_write,
++	.write_room		= sdio_uart_write_room,
++	.chars_in_buffer	= sdio_uart_chars_in_buffer,
++	.send_xchar		= sdio_uart_send_xchar,
++	.throttle		= sdio_uart_throttle,
++	.unthrottle		= sdio_uart_unthrottle,
++	.set_termios		= sdio_uart_set_termios,
++	.break_ctl		= sdio_uart_break_ctl,
++	.tiocmget		= sdio_uart_tiocmget,
++	.tiocmset		= sdio_uart_tiocmset,
++	.read_proc		= sdio_uart_read_proc,
++};
++
++static struct tty_driver *sdio_uart_tty_driver;
++
++static int sdio_uart_probe(struct sdio_func *func,
++			   const struct sdio_device_id *id)
++{
++	struct sdio_uart_port *port;
++	int ret;
++
++	port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL);
++	if (!port)
++		return -ENOMEM;
++
++	if (func->class == SDIO_CLASS_UART) {
++		printk(KERN_WARNING "%s: need info on UART class basic setup\n",
++		       sdio_func_id(func));
++		kfree(port);
++		return -ENOSYS;
++	} else if (func->class == SDIO_CLASS_GPS) {
++		/*
++		 * We need tuple 0x91.  It contains SUBTPL_SIOREG
++		 * and SUBTPL_RCVCAPS.
++		 */
++		struct sdio_func_tuple *tpl;
++		for (tpl = func->tuples; tpl; tpl = tpl->next) {
++			if (tpl->code != 0x91)
++				continue;
++			if (tpl->size < 10)
++				continue;
++			if (tpl->data[1] == 0)  /* SUBTPL_SIOREG */
++				break;
++		}
++		if (!tpl) {
++			printk(KERN_WARNING
++			       "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
++			       sdio_func_id(func));
++			kfree(port);
++			return -EINVAL;
++		}
++		printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
++		       sdio_func_id(func), tpl->data[2], tpl->data[3]);
++		port->regs_offset = (tpl->data[4] << 0) |
++				    (tpl->data[5] << 8) |
++				    (tpl->data[6] << 16);
++		printk(KERN_DEBUG "%s: regs offset = 0x%x\n",
++		       sdio_func_id(func), port->regs_offset);
++		port->uartclk = tpl->data[7] * 115200;
++		if (port->uartclk == 0)
++			port->uartclk = 115200;
++		printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n",
++		       sdio_func_id(func), port->uartclk,
++		       tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));
++	} else {
++		kfree(port);
++		return -EINVAL;
++	}
++
++	port->func = func;
++	sdio_set_drvdata(func, port);
++
++	ret = sdio_uart_add_port(port);
++	if (ret) {
++		kfree(port);
++	} else {
++		struct device *dev;
++		dev = tty_register_device(sdio_uart_tty_driver, port->index, &func->dev);
++		if (IS_ERR(dev)) {
++			sdio_uart_port_remove(port);
++			ret = PTR_ERR(dev);
++		}
++	}
++
++	return ret;
++}
++
++static void sdio_uart_remove(struct sdio_func *func)
++{
++	struct sdio_uart_port *port = sdio_get_drvdata(func);
++
++	tty_unregister_device(sdio_uart_tty_driver, port->index);
++	sdio_uart_port_remove(port);
++}
++
++static const struct sdio_device_id sdio_uart_ids[] = {
++	{ SDIO_DEVICE_CLASS(SDIO_CLASS_UART)		},
++	{ SDIO_DEVICE_CLASS(SDIO_CLASS_GPS)		},
++	{ /* end: all zeroes */				},
++};
++
++MODULE_DEVICE_TABLE(sdio, sdio_uart_ids);
++
++static struct sdio_driver sdio_uart_driver = {
++	.probe		= sdio_uart_probe,
++	.remove		= sdio_uart_remove,
++	.name		= "sdio_uart",
++	.id_table	= sdio_uart_ids,
++};
++
++static int __init sdio_uart_init(void)
++{
++	int ret;
++	struct tty_driver *tty_drv;
++
++	sdio_uart_tty_driver = tty_drv = alloc_tty_driver(UART_NR);
++	if (!tty_drv)
++		return -ENOMEM;
++
++	tty_drv->owner = THIS_MODULE;
++	tty_drv->driver_name = "sdio_uart";
++	tty_drv->name =   "ttySDIO";
++	tty_drv->major = 0;  /* dynamically allocated */
++	tty_drv->minor_start = 0;
++	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
++	tty_drv->subtype = SERIAL_TYPE_NORMAL;
++	tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
++	tty_drv->init_termios = tty_std_termios;
++	tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL;
++	tty_drv->init_termios.c_ispeed = 4800;
++	tty_drv->init_termios.c_ospeed = 4800;
++	tty_set_operations(tty_drv, &sdio_uart_ops);
++
++	ret = tty_register_driver(tty_drv);
++	if (ret)
++		goto err1;
++
++	ret = sdio_register_driver(&sdio_uart_driver);
++	if (ret)
++		goto err2;
++
++	return 0;
++
++err2:
++	tty_unregister_driver(tty_drv);
++err1:
++	put_tty_driver(tty_drv);
++	return ret;
++}
++
++static void __exit sdio_uart_exit(void)
++{
++	sdio_unregister_driver(&sdio_uart_driver);
++	tty_unregister_driver(sdio_uart_tty_driver);
++	put_tty_driver(sdio_uart_tty_driver);
++}
++
++module_init(sdio_uart_init);
++module_exit(sdio_uart_exit);
++
++MODULE_AUTHOR("Nicolas Pitre");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/bus.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/bus.c	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,288 @@
++/*
++ *  linux/drivers/mmc/core/bus.c
++ *
++ *  Copyright (C) 2003 Russell King, All Rights Reserved.
++ *  Copyright (C) 2007 Pierre Ossman
++ *
++ * 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 card bus driver model
++ */
++
++#include <linux/device.h>
++#include <linux/err.h>
++
++#include <linux/mmc/card.h>
++#include <linux/mmc/host.h>
++
++#include "sysfs.h"
++#include "core.h"
++#include "sdio_cis.h"
++#include "bus.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 ssize_t mmc_type_show(struct device *dev,
++	struct device_attribute *attr, char *buf)
++{
++	struct mmc_card *card = dev_to_mmc_card(dev);
++
++	switch (card->type) {
++	case MMC_TYPE_MMC:
++		return sprintf(buf, "MMC\n");
++	case MMC_TYPE_SD:
++		return sprintf(buf, "SD\n");
++	case MMC_TYPE_SDIO:
++		return sprintf(buf, "SDIO\n");
++	default:
++		return -EFAULT;
++	}
++}
++
++static struct device_attribute mmc_dev_attrs[] = {
++	MMC_ATTR_RO(type),
++	__ATTR_NULL,
++};
++
++/*
++ * 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_uevent(struct device *dev, char **envp, int num_envp, char *buf,
++		int buf_size)
++{
++	struct mmc_card *card = dev_to_mmc_card(dev);
++	const char *type;
++	int i = 0, length = 0;
++
++	switch (card->type) {
++	case MMC_TYPE_MMC:
++		type = "MMC";
++		break;
++	case MMC_TYPE_SD:
++		type = "SD";
++		break;
++	case MMC_TYPE_SDIO:
++		type = "SDIO";
++		break;
++	default:
++		type = NULL;
++	}
++
++	if (type) {
++		if (add_uevent_var(envp, num_envp, &i,
++				buf, buf_size, &length,
++				"MMC_TYPE=%s", type))
++			return -ENOMEM;
++	}
++
++	if (add_uevent_var(envp, num_envp, &i,
++			buf, buf_size, &length,
++			"MMC_NAME=%s", mmc_card_name(card)))
++		return -ENOMEM;
++
++	envp[i] = NULL;
++
++	return 0;
++}
++
++static int mmc_bus_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_bus_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;
++}
++
++static int mmc_bus_suspend(struct device *dev, pm_message_t 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",
++	.dev_attrs	= mmc_dev_attrs,
++	.match		= mmc_bus_match,
++	.uevent		= mmc_bus_uevent,
++	.probe		= mmc_bus_probe,
++	.remove		= mmc_bus_remove,
++	.suspend	= mmc_bus_suspend,
++	.resume		= mmc_bus_resume,
++};
++
++int mmc_register_bus(void)
++{
++	return bus_register(&mmc_bus_type);
++}
++
++void mmc_unregister_bus(void)
++{
++	bus_unregister(&mmc_bus_type);
++}
++
++/**
++ *	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;
++	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);
++
++static void mmc_release_card(struct device *dev)
++{
++	struct mmc_card *card = dev_to_mmc_card(dev);
++
++	sdio_free_common_cis(card);
++
++	kfree(card);
++}
++
++/*
++ * Allocate and initialise a new MMC card structure.
++ */
++struct mmc_card *mmc_alloc_card(struct mmc_host *host)
++{
++	struct mmc_card *card;
++
++	card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);
++	if (!card)
++		return ERR_PTR(-ENOMEM);
++
++	card->host = host;
++
++	device_initialize(&card->dev);
++
++	card->dev.parent = mmc_classdev(host);
++	card->dev.bus = &mmc_bus_type;
++	card->dev.release = mmc_release_card;
++
++	return card;
++}
++
++/*
++ * Register a new MMC card with the driver model.
++ */
++int mmc_add_card(struct mmc_card *card)
++{
++	int ret;
++	const char *type;
++
++	snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
++		 "%s:%04x", mmc_hostname(card->host), card->rca);
++
++	switch (card->type) {
++	case MMC_TYPE_MMC:
++		type = "MMC";
++		break;
++	case MMC_TYPE_SD:
++		type = "SD";
++		if (mmc_card_blockaddr(card))
++			type = "SDHC";
++		break;
++	case MMC_TYPE_SDIO:
++		type = "SDIO";
++		break;
++	default:
++		type = "?";
++		break;
++	}
++
++	printk(KERN_INFO "%s: new %s%s card at address %04x\n",
++		mmc_hostname(card->host),
++		mmc_card_highspeed(card) ? "high speed " : "",
++		type, card->rca);
++
++	card->dev.uevent_suppress = 1;
++
++	ret = device_add(&card->dev);
++	if (ret)
++		return ret;
++
++	if (card->host->bus_ops->sysfs_add) {
++		ret = card->host->bus_ops->sysfs_add(card->host, card);
++		if (ret) {
++			device_del(&card->dev);
++			return ret;
++		 }
++	}
++
++	card->dev.uevent_suppress = 0;
++
++	kobject_uevent(&card->dev.kobj, KOBJ_ADD);
++
++	mmc_card_set_present(card);
++
++	return 0;
++}
++
++/*
++ * 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)) {
++		printk(KERN_INFO "%s: card %04x removed\n",
++			mmc_hostname(card->host), card->rca);
++
++		if (card->host->bus_ops->sysfs_remove)
++			card->host->bus_ops->sysfs_remove(card->host, card);
++		device_del(&card->dev);
++	}
++
++	put_device(&card->dev);
++}
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/bus.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/bus.h	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,22 @@
++/*
++ *  linux/drivers/mmc/core/bus.h
++ *
++ *  Copyright (C) 2003 Russell King, All Rights Reserved.
++ *  Copyright 2007 Pierre Ossman
++ *
++ * 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_CORE_BUS_H
++#define _MMC_CORE_BUS_H
++
++struct mmc_card *mmc_alloc_card(struct mmc_host *host);
++int mmc_add_card(struct mmc_card *card);
++void mmc_remove_card(struct mmc_card *card);
++
++int mmc_register_bus(void);
++void mmc_unregister_bus(void);
++
++#endif
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/core.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/core.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/core.c	2007-11-13 11:35:23.000000000 +0100
+@@ -27,13 +27,36 @@
+ #include <linux/mmc/sd.h>
+ 
+ #include "core.h"
+-#include "sysfs.h"
++#include "bus.h"
++#include "host.h"
++#include "sdio_bus.h"
+ 
+ #include "mmc_ops.h"
+ #include "sd_ops.h"
++#include "sdio_ops.h"
+ 
+ extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
+ extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
++extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
++
++static struct workqueue_struct *workqueue;
++
++/*
++ * Internal function. Schedule delayed work in the MMC work queue.
++ */
++static int mmc_schedule_delayed_work(struct delayed_work *work,
++				     unsigned long delay)
++{
++	return queue_delayed_work(workqueue, work, delay);
++}
++
++/*
++ * Internal function. Flush all scheduled work from the MMC work queue.
++ */
++static void mmc_flush_scheduled_work(void)
++{
++	flush_workqueue(workqueue);
++}
+ 
+ /**
+  *	mmc_request_done - finish processing an MMC request
+@@ -48,32 +71,41 @@
+ 	struct mmc_command *cmd = mrq->cmd;
+ 	int err = cmd->error;
+ 
+-	pr_debug("%s: req done (CMD%u): %d/%d/%d: %08x %08x %08x %08x\n",
+-		 mmc_hostname(host), cmd->opcode, err,
+-		 mrq->data ? mrq->data->error : 0,
+-		 mrq->stop ? mrq->stop->error : 0,
+-		 cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
+-
+ 	if (err && cmd->retries) {
++		pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
++			mmc_hostname(host), cmd->opcode, err);
++
+ 		cmd->retries--;
+ 		cmd->error = 0;
+ 		host->ops->request(host, mrq);
+-	} else if (mrq->done) {
+-		mrq->done(mrq);
++	} else {
++		pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
++			mmc_hostname(host), cmd->opcode, err,
++			cmd->resp[0], cmd->resp[1],
++			cmd->resp[2], cmd->resp[3]);
++
++		if (mrq->data) {
++			pr_debug("%s:     %d bytes transferred: %d\n",
++				mmc_hostname(host),
++				mrq->data->bytes_xfered, mrq->data->error);
++		}
++
++		if (mrq->stop) {
++			pr_debug("%s:     (CMD%u): %d: %08x %08x %08x %08x\n",
++				mmc_hostname(host), mrq->stop->opcode,
++				mrq->stop->error,
++				mrq->stop->resp[0], mrq->stop->resp[1],
++				mrq->stop->resp[2], mrq->stop->resp[3]);
++		}
++
++		if (mrq->done)
++			mrq->done(mrq);
+ 	}
+ }
+ 
+ EXPORT_SYMBOL(mmc_request_done);
+ 
+-/**
+- *	mmc_start_request - start a command on a host
+- *	@host: MMC host to start command on
+- *	@mrq: MMC request to start
+- *
+- *	Queue a command on the specified host.  We expect the
+- *	caller to be holding the host lock with interrupts disabled.
+- */
+-void
++static void
+ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
+ {
+ #ifdef CONFIG_MMC_DEBUG
+@@ -84,6 +116,21 @@
+ 		 mmc_hostname(host), mrq->cmd->opcode,
+ 		 mrq->cmd->arg, mrq->cmd->flags);
+ 
++	if (mrq->data) {
++		pr_debug("%s:     blksz %d blocks %d flags %08x "
++			"tsac %d ms nsac %d\n",
++			mmc_hostname(host), mrq->data->blksz,
++			mrq->data->blocks, mrq->data->flags,
++			mrq->data->timeout_ns / 1000000,
++			mrq->data->timeout_clks);
++	}
++
++	if (mrq->stop) {
++		pr_debug("%s:     CMD%u arg %08x flags %08x\n",
++			 mmc_hostname(host), mrq->stop->opcode,
++			 mrq->stop->arg, mrq->stop->flags);
++	}
++
+ 	WARN_ON(!host->claimed);
+ 
+ 	mrq->cmd->error = 0;
+@@ -113,14 +160,21 @@
+ 	host->ops->request(host, mrq);
+ }
+ 
+-EXPORT_SYMBOL(mmc_start_request);
+-
+ static void mmc_wait_done(struct mmc_request *mrq)
+ {
+ 	complete(mrq->done_data);
+ }
+ 
+-int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
++/**
++ *	mmc_wait_for_req - start a request and wait for completion
++ *	@host: MMC host to start command
++ *	@mrq: MMC request to start
++ *
++ *	Start a new MMC custom command request for a host, and wait
++ *	for the command to complete. Does not attempt to parse the
++ *	response.
++ */
++void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
+ {
+ 	DECLARE_COMPLETION_ONSTACK(complete);
+ 
+@@ -130,8 +184,6 @@
+ 	mmc_start_request(host, mrq);
+ 
+ 	wait_for_completion(&complete);
+-
+-	return 0;
+ }
+ 
+ EXPORT_SYMBOL(mmc_wait_for_req);
+@@ -150,7 +202,7 @@
+ {
+ 	struct mmc_request mrq;
+ 
+-	BUG_ON(!host->claimed);
++	WARN_ON(!host->claimed);
+ 
+ 	memset(&mrq, 0, sizeof(struct mmc_request));
+ 
+@@ -172,6 +224,9 @@
+  *	@data: data phase for command
+  *	@card: the MMC card associated with the data transfer
+  *	@write: flag to differentiate reads from writes
++ *
++ *	Computes the data timeout parameters according to the
++ *	correct algorithm given the card type.
+  */
+ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
+ 			  int write)
+@@ -179,6 +234,15 @@
+ 	unsigned int mult;
+ 
+ 	/*
++	 * SDIO cards only define an upper 1 s limit on access.
++	 */
++	if (mmc_card_sdio(card)) {
++		data->timeout_ns = 1000000000;
++		data->timeout_clks = 0;
++		return;
++	}
++
++	/*
+ 	 * SD cards use a 100 multiplier rather than 10
+ 	 */
+ 	mult = mmc_card_sd(card) ? 100 : 10;
+@@ -222,36 +286,43 @@
+ /**
+  *	__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.
++ *	@abort: whether or not the operation should be aborted
+  *
+- *	Note: you should use mmc_card_claim_host or mmc_claim_host.
++ *	Claim a host for a set of operations.  If @abort is non null and
++ *	dereference a non-zero value then this will return prematurely with
++ *	that non-zero value without acquiring the lock.  Returns zero
++ *	with the lock held otherwise.
+  */
+-void mmc_claim_host(struct mmc_host *host)
++int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
+ {
+ 	DECLARE_WAITQUEUE(wait, current);
+ 	unsigned long flags;
++	int stop;
++
++	might_sleep();
+ 
+ 	add_wait_queue(&host->wq, &wait);
+ 	spin_lock_irqsave(&host->lock, flags);
+ 	while (1) {
+ 		set_current_state(TASK_UNINTERRUPTIBLE);
+-		if (!host->claimed)
++		stop = abort ? atomic_read(abort) : 0;
++		if (stop || !host->claimed)
+ 			break;
+ 		spin_unlock_irqrestore(&host->lock, flags);
+ 		schedule();
+ 		spin_lock_irqsave(&host->lock, flags);
+ 	}
+ 	set_current_state(TASK_RUNNING);
+-	host->claimed = 1;
++	if (!stop)
++		host->claimed = 1;
++	else
++		wake_up(&host->wq);
+ 	spin_unlock_irqrestore(&host->lock, flags);
+ 	remove_wait_queue(&host->wq, &wait);
++	return stop;
+ }
+ 
+-EXPORT_SYMBOL(mmc_claim_host);
++EXPORT_SYMBOL(__mmc_claim_host);
+ 
+ /**
+  *	mmc_release_host - release a host
+@@ -264,7 +335,7 @@
+ {
+ 	unsigned long flags;
+ 
+-	BUG_ON(!host->claimed);
++	WARN_ON(!host->claimed);
+ 
+ 	spin_lock_irqsave(&host->lock, flags);
+ 	host->claimed = 0;
+@@ -369,22 +440,6 @@
+ }
+ 
+ /*
+- * Allocate a new MMC card
+- */
+-struct mmc_card *mmc_alloc_card(struct mmc_host *host)
+-{
+-	struct mmc_card *card;
+-
+-	card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
+-	if (!card)
+-		return ERR_PTR(-ENOMEM);
+-
+-	mmc_init_card(card, host);
+-
+-	return card;
+-}
+-
+-/*
+  * Apply power to the MMC stack.  This is a two-stage process.
+  * First, we enable power to the card without the clock running.
+  * We then wait a bit for the power to stabilise.  Finally,
+@@ -429,6 +484,45 @@
+ }
+ 
+ /*
++ * Cleanup when the last reference to the bus operator is dropped.
++ */
++void __mmc_release_bus(struct mmc_host *host)
++{
++	BUG_ON(!host);
++	BUG_ON(host->bus_refs);
++	BUG_ON(!host->bus_dead);
++
++	host->bus_ops = NULL;
++}
++
++/*
++ * Increase reference count of bus operator
++ */
++static inline void mmc_bus_get(struct mmc_host *host)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&host->lock, flags);
++	host->bus_refs++;
++	spin_unlock_irqrestore(&host->lock, flags);
++}
++
++/*
++ * Decrease reference count of bus operator and free it if
++ * it is the last reference.
++ */
++static inline void mmc_bus_put(struct mmc_host *host)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&host->lock, flags);
++	host->bus_refs--;
++	if ((host->bus_refs == 0) && host->bus_ops)
++		__mmc_release_bus(host);
++	spin_unlock_irqrestore(&host->lock, flags);
++}
++
++/*
+  * Assign a mmc bus handler to a host. Only one bus handler may control a
+  * host at any given time.
+  */
+@@ -439,7 +533,7 @@
+ 	BUG_ON(!host);
+ 	BUG_ON(!ops);
+ 
+-	BUG_ON(!host->claimed);
++	WARN_ON(!host->claimed);
+ 
+ 	spin_lock_irqsave(&host->lock, flags);
+ 
+@@ -463,8 +557,8 @@
+ 
+ 	BUG_ON(!host);
+ 
+-	BUG_ON(!host->claimed);
+-	BUG_ON(!host->bus_ops);
++	WARN_ON(!host->claimed);
++	WARN_ON(!host->bus_ops);
+ 
+ 	spin_lock_irqsave(&host->lock, flags);
+ 
+@@ -477,42 +571,25 @@
+ 	mmc_bus_put(host);
+ }
+ 
+-/*
+- * Cleanup when the last reference to the bus operator is dropped.
+- */
+-void __mmc_release_bus(struct mmc_host *host)
+-{
+-	BUG_ON(!host);
+-	BUG_ON(host->bus_refs);
+-	BUG_ON(!host->bus_dead);
+-
+-	host->bus_ops = NULL;
+-}
+-
+ /**
+  *	mmc_detect_change - process change of state on a MMC socket
+  *	@host: host which changed state.
+  *	@delay: optional delay to wait before detection (jiffies)
+  *
+- *	All we know is that card(s) have been inserted or removed
+- *	from the socket(s).  We don't know which socket or cards.
++ *	MMC drivers should call this when they detect a card has been
++ *	inserted or removed. The MMC layer will confirm that any
++ *	present card is still functional, and initialize any newly
++ *	inserted.
+  */
+ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
+ {
+-#ifdef CONFIG_MMC_DEBUG
+-	unsigned long flags;
+-	spin_lock_irqsave(&host->lock, flags);
+-	BUG_ON(host->removed);
+-	spin_unlock_irqrestore(&host->lock, flags);
+-#endif
+-
+ 	mmc_schedule_delayed_work(&host->detect, delay);
+ }
+ 
+ EXPORT_SYMBOL(mmc_detect_change);
+ 
+ 
+-static void mmc_rescan(struct work_struct *work)
++void mmc_rescan(struct work_struct *work)
+ {
+ 	struct mmc_host *host =
+ 		container_of(work, struct mmc_host, detect.work);
+@@ -535,24 +612,38 @@
+ 
+ 		mmc_send_if_cond(host, host->ocr_avail);
+ 
++		/*
++		 * First we search for SDIO...
++		 */
++		err = mmc_send_io_op_cond(host, 0, &ocr);
++		if (!err) {
++			if (mmc_attach_sdio(host, ocr))
++				mmc_power_off(host);
++			return;
++		}
++
++		/*
++		 * ...then normal SD...
++		 */
+ 		err = mmc_send_app_op_cond(host, 0, &ocr);
+-		if (err == MMC_ERR_NONE) {
++		if (!err) {
+ 			if (mmc_attach_sd(host, ocr))
+ 				mmc_power_off(host);
+-		} else {
+-			/*
+-			 * If we fail to detect any SD cards then try
+-			 * searching for MMC cards.
+-			 */
+-			err = mmc_send_op_cond(host, 0, &ocr);
+-			if (err == MMC_ERR_NONE) {
+-				if (mmc_attach_mmc(host, ocr))
+-					mmc_power_off(host);
+-			} else {
++			return;
++		}
++
++		/*
++		 * ...and finally MMC.
++		 */
++		err = mmc_send_op_cond(host, 0, &ocr);
++		if (!err) {
++			if (mmc_attach_mmc(host, ocr))
+ 				mmc_power_off(host);
+-				mmc_release_host(host);
+-			}
++			return;
+ 		}
++
++		mmc_release_host(host);
++		mmc_power_off(host);
+ 	} else {
+ 		if (host->bus_ops->detect && !host->bus_dead)
+ 			host->bus_ops->detect(host);
+@@ -561,69 +652,13 @@
+ 	}
+ }
+ 
+-
+-/**
+- *	mmc_alloc_host - initialise the per-host structure.
+- *	@extra: sizeof private data structure
+- *	@dev: pointer to host device model structure
+- *
+- *	Initialise the per-host structure.
+- */
+-struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
++void mmc_start_host(struct mmc_host *host)
+ {
+-	struct mmc_host *host;
+-
+-	host = mmc_alloc_host_sysfs(extra, dev);
+-	if (host) {
+-		spin_lock_init(&host->lock);
+-		init_waitqueue_head(&host->wq);
+-		INIT_DELAYED_WORK(&host->detect, mmc_rescan);
+-
+-		/*
+-		 * By default, hosts do not support SGIO or large requests.
+-		 * They have to set these according to their abilities.
+-		 */
+-		host->max_hw_segs = 1;
+-		host->max_phys_segs = 1;
+-		host->max_seg_size = PAGE_CACHE_SIZE;
+-
+-		host->max_req_size = PAGE_CACHE_SIZE;
+-		host->max_blk_size = 512;
+-		host->max_blk_count = PAGE_CACHE_SIZE / 512;
+-	}
+-
+-	return host;
+-}
+-
+-EXPORT_SYMBOL(mmc_alloc_host);
+-
+-/**
+- *	mmc_add_host - initialise host hardware
+- *	@host: mmc host
+- */
+-int mmc_add_host(struct mmc_host *host)
+-{
+-	int ret;
+-
+-	ret = mmc_add_host_sysfs(host);
+-	if (ret == 0) {
+-		mmc_power_off(host);
+-		mmc_detect_change(host, 0);
+-	}
+-
+-	return ret;
++	mmc_power_off(host);
++	mmc_detect_change(host, 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)
++void mmc_stop_host(struct mmc_host *host)
+ {
+ #ifdef CONFIG_MMC_DEBUG
+ 	unsigned long flags;
+@@ -648,24 +683,8 @@
+ 	BUG_ON(host->card);
+ 
+ 	mmc_power_off(host);
+-	mmc_remove_host_sysfs(host);
+-}
+-
+-EXPORT_SYMBOL(mmc_remove_host);
+-
+-/**
+- *	mmc_free_host - free the host structure
+- *	@host: mmc host
+- *
+- *	Free the host once all references to it have been dropped.
+- */
+-void mmc_free_host(struct mmc_host *host)
+-{
+-	mmc_free_host_sysfs(host);
+ }
+ 
+-EXPORT_SYMBOL(mmc_free_host);
+-
+ #ifdef CONFIG_PM
+ 
+ /**
+@@ -726,4 +745,47 @@
+ 
+ #endif
+ 
++static int __init mmc_init(void)
++{
++	int ret;
++
++	workqueue = create_singlethread_workqueue("kmmcd");
++	if (!workqueue)
++		return -ENOMEM;
++
++	ret = mmc_register_bus();
++	if (ret)
++		goto destroy_workqueue;
++
++	ret = mmc_register_host_class();
++	if (ret)
++		goto unregister_bus;
++
++	ret = sdio_register_bus();
++	if (ret)
++		goto unregister_host_class;
++
++	return 0;
++
++unregister_host_class:
++	mmc_unregister_host_class();
++unregister_bus:
++	mmc_unregister_bus();
++destroy_workqueue:
++	destroy_workqueue(workqueue);
++
++	return ret;
++}
++
++static void __exit mmc_exit(void)
++{
++	sdio_unregister_bus();
++	mmc_unregister_host_class();
++	mmc_unregister_bus();
++	destroy_workqueue(workqueue);
++}
++
++subsys_initcall(mmc_init);
++module_exit(mmc_exit);
++
+ MODULE_LICENSE("GPL");
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/core.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/core.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/core.h	2007-11-13 11:35:23.000000000 +0100
+@@ -18,6 +18,8 @@
+ struct mmc_bus_ops {
+ 	void (*remove)(struct mmc_host *);
+ 	void (*detect)(struct mmc_host *);
++	int (*sysfs_add)(struct mmc_host *, struct mmc_card *card);
++	void (*sysfs_remove)(struct mmc_host *, struct mmc_card *card);
+ 	void (*suspend)(struct mmc_host *);
+ 	void (*resume)(struct mmc_host *);
+ };
+@@ -25,28 +27,6 @@
+ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
+ void mmc_detach_bus(struct mmc_host *host);
+ 
+-void __mmc_release_bus(struct mmc_host *host);
+-
+-static inline void mmc_bus_get(struct mmc_host *host)
+-{
+-	unsigned long flags;
+-
+-	spin_lock_irqsave(&host->lock, flags);
+-	host->bus_refs++;
+-	spin_unlock_irqrestore(&host->lock, flags);
+-}
+-
+-static inline void mmc_bus_put(struct mmc_host *host)
+-{
+-	unsigned long flags;
+-
+-	spin_lock_irqsave(&host->lock, flags);
+-	host->bus_refs--;
+-	if ((host->bus_refs == 0) && host->bus_ops)
+-		__mmc_release_bus(host);
+-	spin_unlock_irqrestore(&host->lock, flags);
+-}
+-
+ void mmc_set_chip_select(struct mmc_host *host, int mode);
+ void mmc_set_clock(struct mmc_host *host, unsigned int hz);
+ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
+@@ -54,8 +34,6 @@
+ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
+ void mmc_set_timing(struct mmc_host *host, unsigned int timing);
+ 
+-struct mmc_card *mmc_alloc_card(struct mmc_host *host);
+-
+ static inline void mmc_delay(unsigned int ms)
+ {
+ 	if (ms < 1000 / HZ) {
+@@ -66,5 +44,8 @@
+ 	}
+ }
+ 
+-#endif
++void mmc_rescan(struct work_struct *work);
++void mmc_start_host(struct mmc_host *host);
++void mmc_stop_host(struct mmc_host *host);
+ 
++#endif
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/host.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/host.c	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,162 @@
++/*
++ *  linux/drivers/mmc/core/host.c
++ *
++ *  Copyright (C) 2003 Russell King, All Rights Reserved.
++ *  Copyright (C) 2007 Pierre Ossman
++ *
++ * 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 host class device management
++ */
++
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/idr.h>
++#include <linux/pagemap.h>
++
++#include <linux/mmc/host.h>
++
++#include "core.h"
++#include "host.h"
++
++#define cls_dev_to_mmc_host(d)	container_of(d, struct mmc_host, class_dev)
++
++static void mmc_host_classdev_release(struct device *dev)
++{
++	struct mmc_host *host = cls_dev_to_mmc_host(dev);
++	kfree(host);
++}
++
++static struct class mmc_host_class = {
++	.name		= "mmc_host",
++	.dev_release	= mmc_host_classdev_release,
++};
++
++int mmc_register_host_class(void)
++{
++	return class_register(&mmc_host_class);
++}
++
++void mmc_unregister_host_class(void)
++{
++	class_unregister(&mmc_host_class);
++}
++
++static DEFINE_IDR(mmc_host_idr);
++static DEFINE_SPINLOCK(mmc_host_lock);
++
++/**
++ *	mmc_alloc_host - initialise the per-host structure.
++ *	@extra: sizeof private data structure
++ *	@dev: pointer to host device model structure
++ *
++ *	Initialise the per-host structure.
++ */
++struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
++{
++	struct mmc_host *host;
++
++	host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
++	if (!host)
++		return NULL;
++
++	host->parent = dev;
++	host->class_dev.parent = dev;
++	host->class_dev.class = &mmc_host_class;
++	device_initialize(&host->class_dev);
++
++	spin_lock_init(&host->lock);
++	init_waitqueue_head(&host->wq);
++	INIT_DELAYED_WORK(&host->detect, mmc_rescan);
++
++	/*
++	 * By default, hosts do not support SGIO or large requests.
++	 * They have to set these according to their abilities.
++	 */
++	host->max_hw_segs = 1;
++	host->max_phys_segs = 1;
++	host->max_seg_size = PAGE_CACHE_SIZE;
++
++	host->max_req_size = PAGE_CACHE_SIZE;
++	host->max_blk_size = 512;
++	host->max_blk_count = PAGE_CACHE_SIZE / 512;
++
++	return host;
++}
++
++EXPORT_SYMBOL(mmc_alloc_host);
++
++/**
++ *	mmc_add_host - initialise host hardware
++ *	@host: mmc host
++ *
++ *	Register the host with the driver model. The host must be
++ *	prepared to start servicing requests before this function
++ *	completes.
++ */
++int mmc_add_host(struct mmc_host *host)
++{
++	int err;
++
++	WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
++		!host->ops->enable_sdio_irq);
++
++	if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
++		return -ENOMEM;
++
++	spin_lock(&mmc_host_lock);
++	err = idr_get_new(&mmc_host_idr, host, &host->index);
++	spin_unlock(&mmc_host_lock);
++	if (err)
++		return err;
++
++	snprintf(host->class_dev.bus_id, BUS_ID_SIZE,
++		 "mmc%d", host->index);
++
++	err = device_add(&host->class_dev);
++	if (err)
++		return err;
++
++	mmc_start_host(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. No new requests will be issued
++ *	after this function has returned.
++ */
++void mmc_remove_host(struct mmc_host *host)
++{
++	mmc_stop_host(host);
++
++	device_del(&host->class_dev);
++
++	spin_lock(&mmc_host_lock);
++	idr_remove(&mmc_host_idr, host->index);
++	spin_unlock(&mmc_host_lock);
++}
++
++EXPORT_SYMBOL(mmc_remove_host);
++
++/**
++ *	mmc_free_host - free the host structure
++ *	@host: mmc host
++ *
++ *	Free the host once all references to it have been dropped.
++ */
++void mmc_free_host(struct mmc_host *host)
++{
++	put_device(&host->class_dev);
++}
++
++EXPORT_SYMBOL(mmc_free_host);
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/host.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/host.h	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,18 @@
++/*
++ *  linux/drivers/mmc/core/host.h
++ *
++ *  Copyright (C) 2003 Russell King, All Rights Reserved.
++ *  Copyright 2007 Pierre Ossman
++ *
++ * 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_CORE_HOST_H
++#define _MMC_CORE_HOST_H
++
++int mmc_register_host_class(void);
++void mmc_unregister_host_class(void);
++
++#endif
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/Makefile
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/Makefile	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/Makefile	2007-11-13 11:35:23.000000000 +0100
+@@ -7,5 +7,8 @@
+ endif
+ 
+ obj-$(CONFIG_MMC)		+= mmc_core.o
+-mmc_core-y			:= core.o sysfs.o mmc.o mmc_ops.o sd.o sd_ops.o
++mmc_core-y			:= core.o sysfs.o bus.o host.o \
++				   mmc.o mmc_ops.o sd.o sd_ops.o \
++				   sdio.o sdio_ops.o sdio_bus.o \
++				   sdio_cis.o sdio_io.o sdio_irq.o
+ 
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/mmc.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/mmc.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/mmc.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/mmc.c
++ *  linux/drivers/mmc/core/mmc.c
+  *
+  *  Copyright (C) 2003-2004 Russell King, All Rights Reserved.
+  *  Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
+@@ -18,6 +18,7 @@
+ 
+ #include "core.h"
+ #include "sysfs.h"
++#include "bus.h"
+ #include "mmc_ops.h"
+ 
+ static const unsigned int tran_exp[] = {
+@@ -99,7 +100,7 @@
+ 		break;
+ 
+ 	default:
+-		printk("%s: card has unknown MMCA version %d\n",
++		printk(KERN_ERR "%s: card has unknown MMCA version %d\n",
+ 			mmc_hostname(card->host), card->csd.mmca_vsn);
+ 		return -EINVAL;
+ 	}
+@@ -122,7 +123,7 @@
+ 	 */
+ 	csd_struct = UNSTUFF_BITS(resp, 126, 2);
+ 	if (csd_struct != 1 && csd_struct != 2) {
+-		printk("%s: unrecognised CSD structure version %d\n",
++		printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
+ 			mmc_hostname(card->host), csd_struct);
+ 		return -EINVAL;
+ 	}
+@@ -163,10 +164,10 @@
+ 
+ 	BUG_ON(!card);
+ 
+-	err = MMC_ERR_FAILED;
++	err = -EIO;
+ 
+ 	if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
+-		return MMC_ERR_NONE;
++		return 0;
+ 
+ 	/*
+ 	 * As the ext_csd is so large and mostly unused, we don't store the
+@@ -175,13 +176,19 @@
+ 	ext_csd = kmalloc(512, GFP_KERNEL);
+ 	if (!ext_csd) {
+ 		printk(KERN_ERR "%s: could not allocate a buffer to "
+-			"receive the ext_csd. mmc v4 cards will be "
+-			"treated as v3.\n", mmc_hostname(card->host));
+-		return MMC_ERR_FAILED;
++			"receive the ext_csd.\n", mmc_hostname(card->host));
++		return -ENOMEM;
+ 	}
+ 
+ 	err = mmc_send_ext_csd(card, ext_csd);
+-	if (err != MMC_ERR_NONE) {
++	if (err) {
++		/*
++		 * We all hosts that cannot perform the command
++		 * to fail more gracefully
++		 */
++		if (err != -EINVAL)
++			goto out;
++
+ 		/*
+ 		 * High capacity cards should have this "magic" size
+ 		 * stored in their CSD.
+@@ -196,8 +203,9 @@
+ 				"EXT_CSD, performance might "
+ 				"suffer.\n",
+ 				mmc_hostname(card->host));
+-			err = MMC_ERR_NONE;
++			err = 0;
+ 		}
++
+ 		goto out;
+ 	}
+ 
+@@ -236,7 +244,7 @@
+  * In the case of a resume, "curcard" will contain the card
+  * we're trying to reinitialise.
+  */
+-static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
++static int mmc_init_card(struct mmc_host *host, u32 ocr,
+ 	struct mmc_card *oldcard)
+ {
+ 	struct mmc_card *card;
+@@ -245,7 +253,7 @@
+ 	unsigned int max_dtr;
+ 
+ 	BUG_ON(!host);
+-	BUG_ON(!host->claimed);
++	WARN_ON(!host->claimed);
+ 
+ 	/*
+ 	 * Since we're changing the OCR value, we seem to
+@@ -257,19 +265,21 @@
+ 
+ 	/* The extra bit indicates that we support high capacity */
+ 	err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto err;
+ 
+ 	/*
+ 	 * Fetch CID from card.
+ 	 */
+ 	err = mmc_all_send_cid(host, cid);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto err;
+ 
+ 	if (oldcard) {
+-		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
++		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
++			err = -ENOENT;
+ 			goto err;
++		}
+ 
+ 		card = oldcard;
+ 	} else {
+@@ -277,8 +287,10 @@
+ 		 * Allocate card structure.
+ 		 */
+ 		card = mmc_alloc_card(host);
+-		if (IS_ERR(card))
++		if (IS_ERR(card)) {
++			err = PTR_ERR(card);
+ 			goto err;
++		}
+ 
+ 		card->type = MMC_TYPE_MMC;
+ 		card->rca = 1;
+@@ -289,7 +301,7 @@
+ 	 * Set card RCA.
+ 	 */
+ 	err = mmc_set_relative_addr(card);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto free_card;
+ 
+ 	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+@@ -299,14 +311,14 @@
+ 		 * Fetch CSD from card.
+ 		 */
+ 		err = mmc_send_csd(card, card->raw_csd);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			goto free_card;
+ 
+ 		err = mmc_decode_csd(card);
+-		if (err < 0)
++		if (err)
+ 			goto free_card;
+ 		err = mmc_decode_cid(card);
+-		if (err < 0)
++		if (err)
+ 			goto free_card;
+ 	}
+ 
+@@ -314,7 +326,7 @@
+ 	 * Select card, as all following commands rely on that.
+ 	 */
+ 	err = mmc_select_card(card);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto free_card;
+ 
+ 	if (!oldcard) {
+@@ -322,7 +334,7 @@
+ 		 * Fetch and process extened CSD.
+ 		 */
+ 		err = mmc_read_ext_csd(card);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			goto free_card;
+ 	}
+ 
+@@ -333,7 +345,7 @@
+ 		(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
+ 		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ 			EXT_CSD_HS_TIMING, 1);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			goto free_card;
+ 
+ 		mmc_card_set_highspeed(card);
+@@ -362,7 +374,7 @@
+ 		(host->caps & MMC_CAP_4_BIT_DATA)) {
+ 		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ 			EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			goto free_card;
+ 
+ 		mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+@@ -371,14 +383,14 @@
+ 	if (!oldcard)
+ 		host->card = card;
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ 
+ free_card:
+ 	if (!oldcard)
+ 		mmc_remove_card(card);
+ err:
+ 
+-	return MMC_ERR_FAILED;
++	return err;
+ }
+ 
+ /*
+@@ -412,9 +424,8 @@
+ 
+ 	mmc_release_host(host);
+ 
+-	if (err != MMC_ERR_NONE) {
+-		mmc_remove_card(host->card);
+-		host->card = NULL;
++	if (err) {
++		mmc_remove(host);
+ 
+ 		mmc_claim_host(host);
+ 		mmc_detach_bus(host);
+@@ -422,6 +433,53 @@
+ 	}
+ }
+ 
++MMC_ATTR_FN(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
++	card->raw_cid[2], card->raw_cid[3]);
++MMC_ATTR_FN(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
++	card->raw_csd[2], card->raw_csd[3]);
++MMC_ATTR_FN(date, "%02d/%04d\n", card->cid.month, card->cid.year);
++MMC_ATTR_FN(fwrev, "0x%x\n", card->cid.fwrev);
++MMC_ATTR_FN(hwrev, "0x%x\n", card->cid.hwrev);
++MMC_ATTR_FN(manfid, "0x%06x\n", card->cid.manfid);
++MMC_ATTR_FN(name, "%s\n", card->cid.prod_name);
++MMC_ATTR_FN(oemid, "0x%04x\n", card->cid.oemid);
++MMC_ATTR_FN(serial, "0x%08x\n", card->cid.serial);
++
++static struct device_attribute mmc_dev_attrs[] = {
++	MMC_ATTR_RO(cid),
++	MMC_ATTR_RO(csd),
++	MMC_ATTR_RO(date),
++	MMC_ATTR_RO(fwrev),
++	MMC_ATTR_RO(hwrev),
++	MMC_ATTR_RO(manfid),
++	MMC_ATTR_RO(name),
++	MMC_ATTR_RO(oemid),
++	MMC_ATTR_RO(serial),
++	__ATTR_NULL,
++};
++
++/*
++ * Adds sysfs entries as relevant.
++ */
++static int mmc_sysfs_add(struct mmc_host *host, struct mmc_card *card)
++{
++	int ret;
++
++	ret = mmc_add_attrs(card, mmc_dev_attrs);
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++/*
++ * Removes the sysfs entries added by mmc_sysfs_add().
++ */
++static void mmc_sysfs_remove(struct mmc_host *host, struct mmc_card *card)
++{
++	mmc_remove_attrs(card, mmc_dev_attrs);
++}
++
+ #ifdef CONFIG_MMC_UNSAFE_RESUME
+ 
+ /*
+@@ -452,16 +510,17 @@
+ 	BUG_ON(!host->card);
+ 
+ 	mmc_claim_host(host);
++	err = mmc_init_card(host, host->ocr, host->card);
++	mmc_release_host(host);
+ 
+-	err = mmc_sd_init_card(host, host->ocr, host->card);
+-	if (err != MMC_ERR_NONE) {
+-		mmc_remove_card(host->card);
+-		host->card = NULL;
++	if (err) {
++		mmc_remove(host);
+ 
++		mmc_claim_host(host);
+ 		mmc_detach_bus(host);
++		mmc_release_host(host);
+ 	}
+ 
+-	mmc_release_host(host);
+ }
+ 
+ #else
+@@ -474,6 +533,8 @@
+ static const struct mmc_bus_ops mmc_ops = {
+ 	.remove = mmc_remove,
+ 	.detect = mmc_detect,
++	.sysfs_add = mmc_sysfs_add,
++	.sysfs_remove = mmc_sysfs_remove,
+ 	.suspend = mmc_suspend,
+ 	.resume = mmc_resume,
+ };
+@@ -486,7 +547,7 @@
+ 	int err;
+ 
+ 	BUG_ON(!host);
+-	BUG_ON(!host->claimed);
++	WARN_ON(!host->claimed);
+ 
+ 	mmc_attach_bus(host, &mmc_ops);
+ 
+@@ -506,32 +567,37 @@
+ 	/*
+ 	 * Can we support the voltage of the card?
+ 	 */
+-	if (!host->ocr)
++	if (!host->ocr) {
++		err = -EINVAL;
+ 		goto err;
++	}
+ 
+ 	/*
+ 	 * Detect and init the card.
+ 	 */
+-	err = mmc_sd_init_card(host, host->ocr, NULL);
+-	if (err != MMC_ERR_NONE)
++	err = mmc_init_card(host, host->ocr, NULL);
++	if (err)
+ 		goto err;
+ 
+ 	mmc_release_host(host);
+ 
+-	err = mmc_register_card(host->card);
++	err = mmc_add_card(host->card);
+ 	if (err)
+-		goto reclaim_host;
++		goto remove_card;
+ 
+ 	return 0;
+ 
+-reclaim_host:
+-	mmc_claim_host(host);
++remove_card:
+ 	mmc_remove_card(host->card);
+ 	host->card = NULL;
++	mmc_claim_host(host);
+ err:
+ 	mmc_detach_bus(host);
+ 	mmc_release_host(host);
+ 
+-	return 0;
++	printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
++		mmc_hostname(host), err);
++
++	return err;
+ }
+ 
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/mmc_ops.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/mmc_ops.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/mmc_ops.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/mmc_ops.h
++ *  linux/drivers/mmc/core/mmc_ops.h
+  *
+  *  Copyright 2006-2007 Pierre Ossman
+  *
+@@ -40,10 +40,10 @@
+ 	}
+ 
+ 	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		return err;
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ int mmc_select_card(struct mmc_card *card)
+@@ -99,13 +99,13 @@
+ 
+ 	for (i = 100; i; i--) {
+ 		err = mmc_wait_for_cmd(host, &cmd, 0);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			break;
+ 
+ 		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+ 			break;
+ 
+-		err = MMC_ERR_TIMEOUT;
++		err = -ETIMEDOUT;
+ 
+ 		mmc_delay(10);
+ 	}
+@@ -131,12 +131,12 @@
+ 	cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
+ 
+ 	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		return err;
+ 
+ 	memcpy(cid, cmd.resp, sizeof(u32) * 4);
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ int mmc_set_relative_addr(struct mmc_card *card)
+@@ -154,10 +154,10 @@
+ 	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ 
+ 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		return err;
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ int mmc_send_csd(struct mmc_card *card, u32 *csd)
+@@ -176,12 +176,12 @@
+ 	cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
+ 
+ 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		return err;
+ 
+ 	memcpy(csd, cmd.resp, sizeof(u32) * 4);
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+@@ -218,12 +218,12 @@
+ 
+ 	mmc_wait_for_req(card->host, &mrq);
+ 
+-	if (cmd.error != MMC_ERR_NONE)
++	if (cmd.error)
+ 		return cmd.error;
+-	if (data.error != MMC_ERR_NONE)
++	if (data.error)
+ 		return data.error;
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
+@@ -244,10 +244,10 @@
+ 	cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ 
+ 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		return err;
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ int mmc_send_status(struct mmc_card *card, u32 *status)
+@@ -265,12 +265,12 @@
+ 	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ 
+ 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		return err;
+ 
+ 	if (status)
+ 		*status = cmd.resp[0];
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/mmc_ops.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/mmc_ops.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/mmc_ops.h	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/mmc_ops.h
++ *  linux/drivers/mmc/core/mmc_ops.h
+  *
+  *  Copyright 2006-2007 Pierre Ossman
+  *
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sd.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/sd.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sd.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/sd.c
++ *  linux/drivers/mmc/core/sd.c
+  *
+  *  Copyright (C) 2003-2004 Russell King, All Rights Reserved.
+  *  SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
+@@ -19,11 +19,10 @@
+ 
+ #include "core.h"
+ #include "sysfs.h"
++#include "bus.h"
+ #include "mmc_ops.h"
+ #include "sd_ops.h"
+ 
+-#include "core.h"
+-
+ static const unsigned int tran_exp[] = {
+ 	10000,		100000,		1000000,	10000000,
+ 	0,		0,		0,		0
+@@ -150,7 +149,7 @@
+ 		csd->write_partial = 0;
+ 		break;
+ 	default:
+-		printk("%s: unrecognised CSD structure version %d\n",
++		printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
+ 			mmc_hostname(card->host), csd_struct);
+ 		return -EINVAL;
+ 	}
+@@ -167,14 +166,12 @@
+ 	unsigned int scr_struct;
+ 	u32 resp[4];
+ 
+-	BUG_ON(!mmc_card_sd(card));
+-
+ 	resp[3] = card->raw_scr[1];
+ 	resp[2] = card->raw_scr[0];
+ 
+ 	scr_struct = UNSTUFF_BITS(resp, 60, 4);
+ 	if (scr_struct != 0) {
+-		printk("%s: unrecognised SCR structure version %d\n",
++		printk(KERN_ERR "%s: unrecognised SCR structure version %d\n",
+ 			mmc_hostname(card->host), scr_struct);
+ 		return -EINVAL;
+ 	}
+@@ -194,31 +191,38 @@
+ 	u8 *status;
+ 
+ 	if (card->scr.sda_vsn < SCR_SPEC_VER_1)
+-		return MMC_ERR_NONE;
++		return 0;
+ 
+ 	if (!(card->csd.cmdclass & CCC_SWITCH)) {
+ 		printk(KERN_WARNING "%s: card lacks mandatory switch "
+ 			"function, performance might suffer.\n",
+ 			mmc_hostname(card->host));
+-		return MMC_ERR_NONE;
++		return 0;
+ 	}
+ 
+-	err = MMC_ERR_FAILED;
++	err = -EIO;
+ 
+ 	status = kmalloc(64, GFP_KERNEL);
+ 	if (!status) {
+-		printk("%s: could not allocate a buffer for switch "
+-		       "capabilities.\n",
+-			mmc_hostname(card->host));
+-		return err;
++		printk(KERN_ERR "%s: could not allocate a buffer for "
++			"switch capabilities.\n", mmc_hostname(card->host));
++		return -ENOMEM;
+ 	}
+ 
+ 	err = mmc_sd_switch(card, 0, 0, 1, status);
+-	if (err != MMC_ERR_NONE) {
++	if (err) {
++		/*
++		 * We all hosts that cannot perform the command
++		 * to fail more gracefully
++		 */
++		if (err != -EINVAL)
++			goto out;
++
+ 		printk(KERN_WARNING "%s: problem reading switch "
+ 			"capabilities, performance might suffer.\n",
+ 			mmc_hostname(card->host));
+-		err = MMC_ERR_NONE;
++		err = 0;
++
+ 		goto out;
+ 	}
+ 
+@@ -240,29 +244,28 @@
+ 	u8 *status;
+ 
+ 	if (card->scr.sda_vsn < SCR_SPEC_VER_1)
+-		return MMC_ERR_NONE;
++		return 0;
+ 
+ 	if (!(card->csd.cmdclass & CCC_SWITCH))
+-		return MMC_ERR_NONE;
++		return 0;
+ 
+ 	if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
+-		return MMC_ERR_NONE;
++		return 0;
+ 
+ 	if (card->sw_caps.hs_max_dtr == 0)
+-		return MMC_ERR_NONE;
++		return 0;
+ 
+-	err = MMC_ERR_FAILED;
++	err = -EIO;
+ 
+ 	status = kmalloc(64, GFP_KERNEL);
+ 	if (!status) {
+-		printk("%s: could not allocate a buffer for switch "
+-		       "capabilities.\n",
+-			mmc_hostname(card->host));
+-		return err;
++		printk(KERN_ERR "%s: could not allocate a buffer for "
++			"switch capabilities.\n", mmc_hostname(card->host));
++		return -ENOMEM;
+ 	}
+ 
+ 	err = mmc_sd_switch(card, 1, 0, 1, status);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto out;
+ 
+ 	if ((status[16] & 0xF) != 1) {
+@@ -295,7 +298,7 @@
+ 	unsigned int max_dtr;
+ 
+ 	BUG_ON(!host);
+-	BUG_ON(!host->claimed);
++	WARN_ON(!host->claimed);
+ 
+ 	/*
+ 	 * Since we're changing the OCR value, we seem to
+@@ -312,23 +315,25 @@
+ 	 * block-addressed SDHC cards.
+ 	 */
+ 	err = mmc_send_if_cond(host, ocr);
+-	if (err == MMC_ERR_NONE)
++	if (!err)
+ 		ocr |= 1 << 30;
+ 
+ 	err = mmc_send_app_op_cond(host, ocr, NULL);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto err;
+ 
+ 	/*
+ 	 * Fetch CID from card.
+ 	 */
+ 	err = mmc_all_send_cid(host, cid);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto err;
+ 
+ 	if (oldcard) {
+-		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
++		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
++			err = -ENOENT;
+ 			goto err;
++		}
+ 
+ 		card = oldcard;
+ 	} else {
+@@ -336,8 +341,10 @@
+ 		 * Allocate card structure.
+ 		 */
+ 		card = mmc_alloc_card(host);
+-		if (IS_ERR(card))
++		if (IS_ERR(card)) {
++			err = PTR_ERR(card);
+ 			goto err;
++		}
+ 
+ 		card->type = MMC_TYPE_SD;
+ 		memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+@@ -347,7 +354,7 @@
+ 	 * Set card RCA.
+ 	 */
+ 	err = mmc_send_relative_addr(host, &card->rca);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto free_card;
+ 
+ 	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+@@ -357,11 +364,11 @@
+ 		 * Fetch CSD from card.
+ 		 */
+ 		err = mmc_send_csd(card, card->raw_csd);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			goto free_card;
+ 
+ 		err = mmc_decode_csd(card);
+-		if (err < 0)
++		if (err)
+ 			goto free_card;
+ 
+ 		mmc_decode_cid(card);
+@@ -371,7 +378,7 @@
+ 	 * Select card, as all following commands rely on that.
+ 	 */
+ 	err = mmc_select_card(card);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto free_card;
+ 
+ 	if (!oldcard) {
+@@ -379,7 +386,7 @@
+ 		 * Fetch SCR from card.
+ 		 */
+ 		err = mmc_app_send_scr(card, card->raw_scr);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			goto free_card;
+ 
+ 		err = mmc_decode_scr(card);
+@@ -390,7 +397,7 @@
+ 		 * Fetch switch information from card.
+ 		 */
+ 		err = mmc_read_switch(card);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			goto free_card;
+ 	}
+ 
+@@ -398,7 +405,7 @@
+ 	 * Attempt to change to high-speed (if supported)
+ 	 */
+ 	err = mmc_switch_hs(card);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto free_card;
+ 
+ 	/*
+@@ -421,7 +428,7 @@
+ 	if ((host->caps & MMC_CAP_4_BIT_DATA) &&
+ 		(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+ 		err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			goto free_card;
+ 
+ 		mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+@@ -445,14 +452,14 @@
+ 	if (!oldcard)
+ 		host->card = card;
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ 
+ free_card:
+ 	if (!oldcard)
+ 		mmc_remove_card(card);
+ err:
+ 
+-	return MMC_ERR_FAILED;
++	return err;
+ }
+ 
+ /*
+@@ -486,9 +493,8 @@
+ 
+ 	mmc_release_host(host);
+ 
+-	if (err != MMC_ERR_NONE) {
+-		mmc_remove_card(host->card);
+-		host->card = NULL;
++	if (err) {
++		mmc_sd_remove(host);
+ 
+ 		mmc_claim_host(host);
+ 		mmc_detach_bus(host);
+@@ -496,6 +502,55 @@
+ 	}
+ }
+ 
++MMC_ATTR_FN(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
++	card->raw_cid[2], card->raw_cid[3]);
++MMC_ATTR_FN(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
++	card->raw_csd[2], card->raw_csd[3]);
++MMC_ATTR_FN(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]);
++MMC_ATTR_FN(date, "%02d/%04d\n", card->cid.month, card->cid.year);
++MMC_ATTR_FN(fwrev, "0x%x\n", card->cid.fwrev);
++MMC_ATTR_FN(hwrev, "0x%x\n", card->cid.hwrev);
++MMC_ATTR_FN(manfid, "0x%06x\n", card->cid.manfid);
++MMC_ATTR_FN(name, "%s\n", card->cid.prod_name);
++MMC_ATTR_FN(oemid, "0x%04x\n", card->cid.oemid);
++MMC_ATTR_FN(serial, "0x%08x\n", card->cid.serial);
++
++static struct device_attribute mmc_sd_dev_attrs[] = {
++	MMC_ATTR_RO(cid),
++	MMC_ATTR_RO(csd),
++	MMC_ATTR_RO(scr),
++	MMC_ATTR_RO(date),
++	MMC_ATTR_RO(fwrev),
++	MMC_ATTR_RO(hwrev),
++	MMC_ATTR_RO(manfid),
++	MMC_ATTR_RO(name),
++	MMC_ATTR_RO(oemid),
++	MMC_ATTR_RO(serial),
++	__ATTR_NULL,
++};
++
++/*
++ * Adds sysfs entries as relevant.
++ */
++static int mmc_sd_sysfs_add(struct mmc_host *host, struct mmc_card *card)
++{
++	int ret;
++
++	ret = mmc_add_attrs(card, mmc_sd_dev_attrs);
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++/*
++ * Removes the sysfs entries added by mmc_sysfs_add().
++ */
++static void mmc_sd_sysfs_remove(struct mmc_host *host, struct mmc_card *card)
++{
++	mmc_remove_attrs(card, mmc_sd_dev_attrs);
++}
++
+ #ifdef CONFIG_MMC_UNSAFE_RESUME
+ 
+ /*
+@@ -526,16 +581,17 @@
+ 	BUG_ON(!host->card);
+ 
+ 	mmc_claim_host(host);
+-
+ 	err = mmc_sd_init_card(host, host->ocr, host->card);
+-	if (err != MMC_ERR_NONE) {
+-		mmc_remove_card(host->card);
+-		host->card = NULL;
++	mmc_release_host(host);
++
++	if (err) {
++		mmc_sd_remove(host);
+ 
++		mmc_claim_host(host);
+ 		mmc_detach_bus(host);
++		mmc_release_host(host);
+ 	}
+ 
+-	mmc_release_host(host);
+ }
+ 
+ #else
+@@ -548,6 +604,8 @@
+ static const struct mmc_bus_ops mmc_sd_ops = {
+ 	.remove = mmc_sd_remove,
+ 	.detect = mmc_sd_detect,
++	.sysfs_add = mmc_sd_sysfs_add,
++	.sysfs_remove = mmc_sd_sysfs_remove,
+ 	.suspend = mmc_sd_suspend,
+ 	.resume = mmc_sd_resume,
+ };
+@@ -560,7 +618,7 @@
+ 	int err;
+ 
+ 	BUG_ON(!host);
+-	BUG_ON(!host->claimed);
++	WARN_ON(!host->claimed);
+ 
+ 	mmc_attach_bus(host, &mmc_sd_ops);
+ 
+@@ -587,32 +645,37 @@
+ 	/*
+ 	 * Can we support the voltage(s) of the card(s)?
+ 	 */
+-	if (!host->ocr)
++	if (!host->ocr) {
++		err = -EINVAL;
+ 		goto err;
++	}
+ 
+ 	/*
+ 	 * Detect and init the card.
+ 	 */
+ 	err = mmc_sd_init_card(host, host->ocr, NULL);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		goto err;
+ 
+ 	mmc_release_host(host);
+ 
+-	err = mmc_register_card(host->card);
++	err = mmc_add_card(host->card);
+ 	if (err)
+-		goto reclaim_host;
++		goto remove_card;
+ 
+ 	return 0;
+ 
+-reclaim_host:
+-	mmc_claim_host(host);
++remove_card:
+ 	mmc_remove_card(host->card);
+ 	host->card = NULL;
++	mmc_claim_host(host);
+ err:
+ 	mmc_detach_bus(host);
+ 	mmc_release_host(host);
+ 
+-	return 0;
++	printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
++		mmc_hostname(host), err);
++
++	return err;
+ }
+ 
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_bus.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_bus.c	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,271 @@
++/*
++ *  linux/drivers/mmc/core/sdio_bus.c
++ *
++ *  Copyright 2007 Pierre Ossman
++ *
++ * 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.
++ *
++ * SDIO function driver model
++ */
++
++#include <linux/device.h>
++#include <linux/err.h>
++
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "sdio_cis.h"
++#include "sdio_bus.h"
++
++#define dev_to_sdio_func(d)	container_of(d, struct sdio_func, dev)
++#define to_sdio_driver(d)	container_of(d, struct sdio_driver, drv)
++
++/* show configuration fields */
++#define sdio_config_attr(field, format_string)				\
++static ssize_t								\
++field##_show(struct device *dev, struct device_attribute *attr, char *buf)				\
++{									\
++	struct sdio_func *func;						\
++									\
++	func = dev_to_sdio_func (dev);					\
++	return sprintf (buf, format_string, func->field);		\
++}
++
++sdio_config_attr(class, "0x%02x\n");
++sdio_config_attr(vendor, "0x%04x\n");
++sdio_config_attr(device, "0x%04x\n");
++
++static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
++	struct sdio_func *func = dev_to_sdio_func (dev);
++
++	return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n",
++			func->class, func->vendor, func->device);
++}
++
++static struct device_attribute sdio_dev_attrs[] = {
++	__ATTR_RO(class),
++	__ATTR_RO(vendor),
++	__ATTR_RO(device),
++	__ATTR_RO(modalias),
++	__ATTR_NULL,
++};
++
++static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
++	const struct sdio_device_id *id)
++{
++	if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
++		return NULL;
++	if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
++		return NULL;
++	if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
++		return NULL;
++	return id;
++}
++
++static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
++	struct sdio_driver *sdrv)
++{
++	const struct sdio_device_id *ids;
++
++	ids = sdrv->id_table;
++
++	if (ids) {
++		while (ids->class || ids->vendor || ids->device) {
++			if (sdio_match_one(func, ids))
++				return ids;
++			ids++;
++		}
++	}
++
++	return NULL;
++}
++
++static int sdio_bus_match(struct device *dev, struct device_driver *drv)
++{
++	struct sdio_func *func = dev_to_sdio_func(dev);
++	struct sdio_driver *sdrv = to_sdio_driver(drv);
++
++	if (sdio_match_device(func, sdrv))
++		return 1;
++
++	return 0;
++}
++
++static int
++sdio_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
++		int buf_size)
++{
++	struct sdio_func *func = dev_to_sdio_func(dev);
++	int i = 0, length = 0;
++
++	if (add_uevent_var(envp, num_envp, &i,
++			buf, buf_size, &length,
++			"SDIO_CLASS=%02X", func->class))
++		return -ENOMEM;
++
++	if (add_uevent_var(envp, num_envp, &i,
++			buf, buf_size, &length,
++			"SDIO_ID=%04X:%04X", func->vendor, func->device))
++		return -ENOMEM;
++
++	if (add_uevent_var(envp, num_envp, &i,
++			buf, buf_size, &length,
++			"MODALIAS=sdio:c%02Xv%04Xd%04X",
++			func->class, func->vendor, func->device))
++		return -ENOMEM;
++
++	envp[i] = NULL;
++
++	return 0;
++}
++
++static int sdio_bus_probe(struct device *dev)
++{
++	struct sdio_driver *drv = to_sdio_driver(dev->driver);
++	struct sdio_func *func = dev_to_sdio_func(dev);
++	const struct sdio_device_id *id;
++	int ret;
++
++	id = sdio_match_device(func, drv);
++	if (!id)
++		return -ENODEV;
++
++	/* Set the default block size so the driver is sure it's something
++	 * sensible. */
++	sdio_claim_host(func);
++	ret = sdio_set_block_size(func, 0);
++	sdio_release_host(func);
++	if (ret)
++		return ret;
++
++	return drv->probe(func, id);
++}
++
++static int sdio_bus_remove(struct device *dev)
++{
++	struct sdio_driver *drv = to_sdio_driver(dev->driver);
++	struct sdio_func *func = dev_to_sdio_func(dev);
++
++	drv->remove(func);
++
++	if (func->irq_handler) {
++		printk(KERN_WARNING "WARNING: driver %s did not remove "
++			"its interrupt handler!\n", drv->name);
++		sdio_claim_host(func);
++		sdio_release_irq(func);
++		sdio_release_host(func);
++	}
++
++	return 0;
++}
++
++static struct bus_type sdio_bus_type = {
++	.name		= "sdio",
++	.dev_attrs	= sdio_dev_attrs,
++	.match		= sdio_bus_match,
++	.uevent		= sdio_bus_uevent,
++	.probe		= sdio_bus_probe,
++	.remove		= sdio_bus_remove,
++};
++
++int sdio_register_bus(void)
++{
++	return bus_register(&sdio_bus_type);
++}
++
++void sdio_unregister_bus(void)
++{
++	bus_unregister(&sdio_bus_type);
++}
++
++/**
++ *	sdio_register_driver - register a function driver
++ *	@drv: SDIO function driver
++ */
++int sdio_register_driver(struct sdio_driver *drv)
++{
++	drv->drv.name = drv->name;
++	drv->drv.bus = &sdio_bus_type;
++	return driver_register(&drv->drv);
++}
++
++EXPORT_SYMBOL_GPL(sdio_register_driver);
++
++/**
++ *	sdio_unregister_driver - unregister a function driver
++ *	@drv: SDIO function driver
++ */
++void sdio_unregister_driver(struct sdio_driver *drv)
++{
++	drv->drv.bus = &sdio_bus_type;
++	driver_unregister(&drv->drv);
++}
++
++EXPORT_SYMBOL_GPL(sdio_unregister_driver);
++
++static void sdio_release_func(struct device *dev)
++{
++	struct sdio_func *func = dev_to_sdio_func(dev);
++
++	sdio_free_func_cis(func);
++
++	kfree(func);
++}
++
++/*
++ * Allocate and initialise a new SDIO function structure.
++ */
++struct sdio_func *sdio_alloc_func(struct mmc_card *card)
++{
++	struct sdio_func *func;
++
++	func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);
++	if (!func)
++		return ERR_PTR(-ENOMEM);
++
++	func->card = card;
++
++	device_initialize(&func->dev);
++
++	func->dev.parent = &card->dev;
++	func->dev.bus = &sdio_bus_type;
++	func->dev.release = sdio_release_func;
++
++	return func;
++}
++
++/*
++ * Register a new SDIO function with the driver model.
++ */
++int sdio_add_func(struct sdio_func *func)
++{
++	int ret;
++
++	snprintf(func->dev.bus_id, sizeof(func->dev.bus_id),
++		 "%s:%d", mmc_card_id(func->card), func->num);
++
++	printk(KERN_INFO "New sdio device [%s]\n", func->dev.bus_id);
++
++	ret = device_add(&func->dev);
++	if (ret == 0)
++		sdio_func_set_present(func);
++
++	return ret;
++}
++
++/*
++ * Unregister a SDIO function with the driver model, and
++ * (eventually) free it.
++ */
++void sdio_remove_func(struct sdio_func *func)
++{
++	if (sdio_func_present(func))
++		device_del(&func->dev);
++
++	put_device(&func->dev);
++}
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_bus.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_bus.h	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,22 @@
++/*
++ *  linux/drivers/mmc/core/sdio_bus.h
++ *
++ *  Copyright 2007 Pierre Ossman
++ *
++ * 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.
++ */
++#ifndef _MMC_CORE_SDIO_BUS_H
++#define _MMC_CORE_SDIO_BUS_H
++
++struct sdio_func *sdio_alloc_func(struct mmc_card *card);
++int sdio_add_func(struct sdio_func *func);
++void sdio_remove_func(struct sdio_func *func);
++
++int sdio_register_bus(void);
++void sdio_unregister_bus(void);
++
++#endif
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sdio.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sdio.c	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,382 @@
++/*
++ *  linux/drivers/mmc/sdio.c
++ *
++ *  Copyright 2006-2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <linux/err.h>
++
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "core.h"
++#include "bus.h"
++#include "sdio_bus.h"
++#include "mmc_ops.h"
++#include "sd_ops.h"
++#include "sdio_ops.h"
++#include "sdio_cis.h"
++
++static int sdio_read_fbr(struct sdio_func *func)
++{
++	int ret;
++	unsigned char data;
++
++	ret = mmc_io_rw_direct(func->card, 0, 0,
++		SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data);
++	if (ret)
++		goto out;
++
++	data &= 0x0f;
++
++	if (data == 0x0f) {
++		ret = mmc_io_rw_direct(func->card, 0, 0,
++			SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF_EXT, 0, &data);
++		if (ret)
++			goto out;
++	}
++
++	func->class = data;
++
++out:
++	return ret;
++}
++
++static int sdio_init_func(struct mmc_card *card, unsigned int fn)
++{
++	int ret;
++	struct sdio_func *func;
++
++	BUG_ON(fn > SDIO_MAX_FUNCS);
++
++	func = sdio_alloc_func(card);
++	if (IS_ERR(func))
++		return PTR_ERR(func);
++
++	func->num = fn;
++
++	ret = sdio_read_fbr(func);
++	if (ret)
++		goto fail;
++
++	ret = sdio_read_func_cis(func);
++	if (ret)
++		goto fail;
++
++	card->sdio_func[fn - 1] = func;
++
++	return 0;
++
++fail:
++	/*
++	 * It is okay to remove the function here even though we hold
++	 * the host lock as we haven't registered the device yet.
++	 */
++	sdio_remove_func(func);
++	return ret;
++}
++
++static int sdio_read_cccr(struct mmc_card *card)
++{
++	int ret;
++	int cccr_vsn;
++	unsigned char data;
++
++	memset(&card->cccr, 0, sizeof(struct sdio_cccr));
++
++	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
++	if (ret)
++		goto out;
++
++	cccr_vsn = data & 0x0f;
++
++	if (cccr_vsn > SDIO_CCCR_REV_1_20) {
++		printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n",
++			mmc_hostname(card->host), cccr_vsn);
++		return -EINVAL;
++	}
++
++	card->cccr.sdio_vsn = (data & 0xf0) >> 4;
++
++	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data);
++	if (ret)
++		goto out;
++
++	if (data & SDIO_CCCR_CAP_SMB)
++		card->cccr.multi_block = 1;
++	if (data & SDIO_CCCR_CAP_LSC)
++		card->cccr.low_speed = 1;
++	if (data & SDIO_CCCR_CAP_4BLS)
++		card->cccr.wide_bus = 1;
++
++	if (cccr_vsn >= SDIO_CCCR_REV_1_10) {
++		ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data);
++		if (ret)
++			goto out;
++
++		if (data & SDIO_POWER_SMPC)
++			card->cccr.high_power = 1;
++	}
++
++	if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
++		ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data);
++		if (ret)
++			goto out;
++
++		if (data & SDIO_SPEED_SHS)
++			card->cccr.high_speed = 1;
++	}
++
++out:
++	return ret;
++}
++
++static int sdio_enable_wide(struct mmc_card *card)
++{
++	int ret;
++	u8 ctrl;
++
++	if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
++		return 0;
++
++	if (card->cccr.low_speed && !card->cccr.wide_bus)
++		return 0;
++
++	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
++	if (ret)
++		return ret;
++
++	ctrl |= SDIO_BUS_WIDTH_4BIT;
++
++	ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
++	if (ret)
++		return ret;
++
++	mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
++
++	return 0;
++}
++
++/*
++ * Host is being removed. Free up the current card.
++ */
++static void mmc_sdio_remove(struct mmc_host *host)
++{
++	int i;
++
++	BUG_ON(!host);
++	BUG_ON(!host->card);
++
++	for (i = 0;i < host->card->sdio_funcs;i++) {
++		if (host->card->sdio_func[i]) {
++			sdio_remove_func(host->card->sdio_func[i]);
++			host->card->sdio_func[i] = NULL;
++		}
++	}
++
++	mmc_remove_card(host->card);
++	host->card = NULL;
++}
++
++/*
++ * Card detection callback from host.
++ */
++static void mmc_sdio_detect(struct mmc_host *host)
++{
++	int err;
++
++	BUG_ON(!host);
++	BUG_ON(!host->card);
++
++	mmc_claim_host(host);
++
++	/*
++	 * Just check if our card has been removed.
++	 */
++	err = mmc_select_card(host->card);
++
++	mmc_release_host(host);
++
++	if (err) {
++		mmc_sdio_remove(host);
++
++		mmc_claim_host(host);
++		mmc_detach_bus(host);
++		mmc_release_host(host);
++	}
++}
++
++
++static const struct mmc_bus_ops mmc_sdio_ops = {
++	.remove = mmc_sdio_remove,
++	.detect = mmc_sdio_detect,
++};
++
++
++/*
++ * Starting point for SDIO card init.
++ */
++int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
++{
++	int err;
++	int i, funcs;
++	struct mmc_card *card;
++
++	BUG_ON(!host);
++	WARN_ON(!host->claimed);
++
++	mmc_attach_bus(host, &mmc_sdio_ops);
++
++	/*
++	 * Sanity check the voltages that the card claims to
++	 * support.
++	 */
++	if (ocr & 0x7F) {
++		printk(KERN_WARNING "%s: card claims to support voltages "
++		       "below the defined range. These will be ignored.\n",
++		       mmc_hostname(host));
++		ocr &= ~0x7F;
++	}
++
++	if (ocr & MMC_VDD_165_195) {
++		printk(KERN_WARNING "%s: SDIO card claims to support the "
++		       "incompletely defined 'low voltage range'. This "
++		       "will be ignored.\n", mmc_hostname(host));
++		ocr &= ~MMC_VDD_165_195;
++	}
++
++	host->ocr = mmc_select_voltage(host, ocr);
++
++	/*
++	 * Can we support the voltage(s) of the card(s)?
++	 */
++	if (!host->ocr) {
++		err = -EINVAL;
++		goto err;
++	}
++
++	/*
++	 * Inform the card of the voltage
++	 */
++	err = mmc_send_io_op_cond(host, host->ocr, &ocr);
++	if (err)
++		goto err;
++
++	/*
++	 * The number of functions on the card is encoded inside
++	 * the ocr.
++	 */
++	funcs = (ocr & 0x70000000) >> 28;
++
++	/*
++	 * Allocate card structure.
++	 */
++	card = mmc_alloc_card(host);
++	if (IS_ERR(card)) {
++		err = PTR_ERR(card);
++		goto err;
++	}
++
++	card->type = MMC_TYPE_SDIO;
++	card->sdio_funcs = funcs;
++
++	host->card = card;
++
++	/*
++	 * Set card RCA.
++	 */
++	err = mmc_send_relative_addr(host, &card->rca);
++	if (err)
++		goto remove;
++
++	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
++
++	/*
++	 * Select card, as all following commands rely on that.
++	 */
++	err = mmc_select_card(card);
++	if (err)
++		goto remove;
++
++	/*
++	 * Read the common registers.
++	 */
++	err = sdio_read_cccr(card);
++	if (err)
++		goto remove;
++
++	/*
++	 * Read the common CIS tuples.
++	 */
++	err = sdio_read_common_cis(card);
++	if (err)
++		goto remove;
++
++	/*
++	 * No support for high-speed yet, so just set
++	 * the card's maximum speed.
++	 */
++	mmc_set_clock(host, card->cis.max_dtr);
++
++	/*
++	 * Switch to wider bus (if supported).
++	 */
++	err = sdio_enable_wide(card);
++	if (err)
++		goto remove;
++
++	/*
++	 * Initialize (but don't add) all present functions.
++	 */
++	for (i = 0;i < funcs;i++) {
++		err = sdio_init_func(host->card, i + 1);
++		if (err)
++			goto remove;
++	}
++
++	mmc_release_host(host);
++
++	/*
++	 * First add the card to the driver model...
++	 */
++	err = mmc_add_card(host->card);
++	if (err)
++		goto remove_added;
++
++	/*
++	 * ...then the SDIO functions.
++	 */
++	for (i = 0;i < funcs;i++) {
++		err = sdio_add_func(host->card->sdio_func[i]);
++		if (err)
++			goto remove_added;
++	}
++
++	return 0;
++
++
++remove_added:
++	/* Remove without lock if the device has been added. */
++	mmc_sdio_remove(host);
++	mmc_claim_host(host);
++remove:
++	/* And with lock if it hasn't been added. */
++	if (host->card)
++		mmc_sdio_remove(host);
++err:
++	mmc_detach_bus(host);
++	mmc_release_host(host);
++
++	printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
++		mmc_hostname(host), err);
++
++	return err;
++}
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_cis.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_cis.c	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,404 @@
++/*
++ * linux/drivers/mmc/core/sdio_cis.c
++ *
++ * Author:	Nicolas Pitre
++ * Created:	June 11, 2007
++ * Copyright:	MontaVista Software Inc.
++ *
++ * Copyright 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <linux/kernel.h>
++
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "sdio_cis.h"
++#include "sdio_ops.h"
++
++#define sdio_card_func_id(card, func) \
++	(func ? sdio_func_id(func) : mmc_card_id(card))
++
++static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,
++			 const unsigned char *buf, unsigned size)
++{
++	unsigned i, nr_strings = 0;
++
++	printk(KERN_DEBUG "%s: CISTPL_VERS_1 = %u.%u\n",
++	       sdio_card_func_id(card, func), buf[0], buf[1]);
++	buf += 2;
++
++	for (i = 0; i < size - 2; i++) {
++		if (buf[i] == 0xff)
++			break;
++		if (buf[i] == 0)
++			nr_strings++;
++	}
++	printk(KERN_INFO "%s:", sdio_card_func_id(card, func));
++	for (i = 0; i < nr_strings; i++) {
++		printk(" \"%s\"", buf);
++		while (*buf++);
++	}
++	printk("\n");
++
++	return 0;
++}
++
++static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func,
++			 const unsigned char *buf, unsigned size)
++{
++	unsigned int vendor, device;
++
++	/* TPLMID_MANF */
++	vendor = buf[0] | (buf[1] << 8);
++	printk(KERN_DEBUG "%s: TPLMID_MANF = 0x%04x\n",
++	       sdio_card_func_id(card, func), vendor);
++
++	/* TPLMID_CARD */
++	device = buf[2] | (buf[3] << 8);
++	printk(KERN_DEBUG "%s: TPLMID_CARD = 0x%04x\n",
++	       sdio_card_func_id(card, func), device);
++
++	if (func) {
++		func->vendor = vendor;
++		func->device = device;
++	} else {
++		card->cis.vendor = vendor;
++		card->cis.device = device;
++	}
++
++	return 0;
++}
++
++static int cistpl_funcid(struct mmc_card *card, struct sdio_func *func,
++			 const unsigned char *buf, unsigned size)
++{
++	/* TPLFID_FUNCTION */
++	printk(KERN_DEBUG "%s: TPLFID_FUNCTION = 0x%02x\n",
++	       sdio_card_func_id(card, func), buf[0]);
++
++	return 0;
++}
++
++static const unsigned char speed_val[16] =
++	{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
++static const unsigned int speed_unit[8] =
++	{ 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
++
++static int cistpl_funce_common(struct mmc_card *card,
++			       const unsigned char *buf, unsigned size)
++{
++	if (size < 0x04 || buf[0] != 0)
++		return -EINVAL;
++
++	/* TPLFE_FN0_BLK_SIZE */
++	card->cis.blksize = buf[1] | (buf[2] << 8);
++	printk(KERN_DEBUG "%s: TPLFE_FN0_BLK_SIZE = %u\n",
++	       mmc_card_id(card), (unsigned)card->cis.blksize);
++	/* TPLFE_MAX_TRAN_SPEED */
++	card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] *
++			    speed_unit[buf[3] & 7];
++	printk(KERN_DEBUG "%s: max speed = %u kbps\n",
++	       mmc_card_id(card), (unsigned)card->cis.max_dtr/1000);
++
++	return 0;
++}
++
++static int cistpl_funce_func(struct sdio_func *func,
++			     const unsigned char *buf, unsigned size)
++{
++	unsigned val;
++	unsigned vsn;
++	unsigned min_size;
++
++	vsn = func->card->cccr.sdio_vsn;
++	min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
++
++	if (size < min_size || buf[0] != 1)
++		return -EINVAL;
++
++	/* TPLFE_FUNCTION_INFO */
++	val = buf[1];
++	printk(KERN_DEBUG "%s: TPLFE_FUNCTION_INFO = 0x%02x\n",
++	       sdio_func_id(func), val);
++	/* TPLFE_STD_IO_REV */
++	val = buf[2];
++	printk(KERN_DEBUG "%s: TPLFE_STD_IO_REV = 0x%02x\n",
++	       sdio_func_id(func), val);
++	/* TPLFE_CARD_PSN */
++	val = buf[3] | (buf[4] << 8) | (buf[5] << 16) | (buf[6] << 24);
++	printk(KERN_DEBUG "%s: TPLFE_CARD_PSN = 0x%08x\n",
++	       sdio_func_id(func), val);
++	/* TPLFE_CSA_SIZE */
++	val = buf[7] | (buf[8] << 8) | (buf[9] << 16) | (buf[10] << 24);
++	printk(KERN_DEBUG "%s: TPLFE_CSA_SIZE = 0x%08x\n",
++	       sdio_func_id(func), val);
++	/* TPLFE_CSA_PROPERTY */
++	val = buf[11];
++	printk(KERN_DEBUG "%s: TPLFE_CSA_PROPERTY = 0x%02x\n",
++	       sdio_func_id(func), val);
++	/* TPLFE_MAX_BLK_SIZE */
++	func->max_blksize = buf[12] | (buf[13] << 8);
++	printk(KERN_DEBUG "%s: TPLFE_MAX_BLK_SIZE = %u\n",
++	       sdio_func_id(func), (unsigned)func->max_blksize);
++	/* TPLFE_OCR */
++	val = buf[14] | (buf[15] << 8) | (buf[16] << 16) | (buf[17] << 24);
++	printk(KERN_DEBUG "%s: TPLFE_OCR = 0x%08x\n",
++	       sdio_func_id(func), val);
++	/* TPLFE_OP_MIN_PWR, TPLFE_OP_AVG_PWR, TPLFE_OP_MAX_PWR */
++	printk(KERN_DEBUG "%s: operating current (min/avg/max) = %u/%u/%u mA\n",
++	       sdio_func_id(func), buf[18], buf[19], buf[20]);
++	/* TPLFE_SB_MIN_PWR, TPLFE_SB_AVG_PWR, TPLFE_SB_MAX_PWR */
++	printk(KERN_DEBUG "%s: standby current (min/avg/max) = %u/%u/%u mA\n",
++	       sdio_func_id(func), buf[21], buf[22], buf[23]);
++	/* TPLFE_MIN_BW */
++	val = buf[24] | (buf[25] << 8);
++	printk(KERN_DEBUG "%s: minimum data bandwidth = %u KB/sec\n",
++	       sdio_func_id(func), val);
++	/* TPLFE_OPT_BW */
++	val = buf[26] | (buf[27] << 8);
++	printk(KERN_DEBUG "%s: optimum data bandwidth = %u KB/sec\n",
++	       sdio_func_id(func), val);
++	if (vsn > SDIO_SDIO_REV_1_00) {
++		/* TPLFE_ENABLE_TIMEOUT_VAL */
++		val = buf[28] | (buf[29] << 8);
++		printk(KERN_DEBUG "%s: TPLFE_ENABLE_TIMEOUT_VAL = %u\n",
++		       sdio_func_id(func), val);
++		/* TPLFE_SP_AVG_PWR_3.3V */
++		val = buf[30] | (buf[31] << 8);
++		/* TPLFE_SP_MAX_PWR_3.3V */
++		val = buf[32] | (buf[33] << 8);
++		/* TPLFE_HP_AVG_PWR_3.3V */
++		val = buf[34] | (buf[35] << 8);
++		/* TPLFE_HP_MAX_PWR_3.3V */
++		val = buf[36] | (buf[37] << 8);
++		/* TPLFE_LP_AVG_PWR_3.3V */
++		val = buf[38] | (buf[39] << 8);
++		/* TPLFE_LP_MAX_PWR_3.3V */
++		val = buf[40] | (buf[41] << 8);
++	}
++
++	return 0;
++}
++
++static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
++			const unsigned char *buf, unsigned size)
++{
++	int ret;
++
++	/*
++	 * There should be two versions of the CISTPL_FUNCE tuple,
++	 * one for the common CIS (function 0) and a version used by
++	 * the individual function's CIS (1-7). Yet, the later has a
++	 * different length depending on the SDIO spec version.
++	 */
++	if (func)
++		ret = cistpl_funce_func(func, buf, size);
++	else
++		ret = cistpl_funce_common(card, buf, size);
++
++	if (ret) {
++		printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u "
++		       "type %u\n", sdio_card_func_id(card, func),
++		       size, buf[0]);
++		return ret;
++	}
++
++	return 0;
++}
++
++typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
++			   const unsigned char *, unsigned);
++
++struct cis_tpl {
++	unsigned char code;
++	unsigned char min_size;
++	tpl_parse_t *parse;
++};
++
++static const struct cis_tpl cis_tpl_list[] = {
++	{	0x15,	3,	cistpl_vers_1	},
++	{	0x20,	4,	cistpl_manfid	},
++	{	0x21,	2,	cistpl_funcid	},
++	{	0x22,	0,	cistpl_funce	},
++};
++
++static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
++{
++	int ret;
++	struct sdio_func_tuple *this, **prev;
++	unsigned i, ptr = 0;
++
++	/*
++	 * Note that this works for the common CIS (function number 0) as
++	 * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS
++	 * have the same offset.
++	 */
++	for (i = 0; i < 3; i++) {
++		unsigned char x, fn;
++
++		if (func)
++			fn = func->num;
++		else
++			fn = 0;
++
++		ret = mmc_io_rw_direct(card, 0, 0,
++			SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);
++		if (ret)
++			return ret;
++		ptr |= x << (i * 8);
++	}
++
++	if (func)
++		prev = &func->tuples;
++	else
++		prev = &card->tuples;
++
++	BUG_ON(*prev);
++
++	do {
++		unsigned char tpl_code, tpl_link;
++
++		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);
++		if (ret)
++			break;
++
++		/* 0xff means we're done */
++		if (tpl_code == 0xff)
++			break;
++
++		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
++		if (ret)
++			break;
++
++		this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
++		if (!this)
++			return -ENOMEM;
++
++		for (i = 0; i < tpl_link; i++) {
++			ret = mmc_io_rw_direct(card, 0, 0,
++					       ptr + i, 0, &this->data[i]);
++			if (ret)
++				break;
++		}
++		if (ret) {
++			kfree(this);
++			break;
++		}
++
++		for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
++			if (cis_tpl_list[i].code == tpl_code)
++				break;
++		if (i >= ARRAY_SIZE(cis_tpl_list)) {
++			/* this tuple is unknown to the core */
++			this->next = NULL;
++			this->code = tpl_code;
++			this->size = tpl_link;
++			*prev = this;
++			prev = &this->next;
++			printk(KERN_DEBUG
++			       "%s: queuing CIS tuple 0x%02x length %u\n",
++			       sdio_card_func_id(card, func),
++			       tpl_code, tpl_link);
++		} else {
++			const struct cis_tpl *tpl = cis_tpl_list + i;
++			if (tpl_link < tpl->min_size) {
++				printk(KERN_ERR
++				       "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u\n",
++				       sdio_card_func_id(card, func),
++				       tpl_code, tpl_link, tpl->min_size);
++				ret = -EINVAL;
++			} else {
++				ret = tpl->parse(card, func,
++						 this->data, tpl_link);
++			}
++			kfree(this);
++		}
++
++		ptr += tpl_link;
++	} while (!ret);
++
++	/*
++	 * Link in all unknown tuples found in the common CIS so that
++	 * drivers don't have to go digging in two places.
++	 */
++	if (func)
++		*prev = card->tuples;
++
++	return ret;
++}
++
++int sdio_read_common_cis(struct mmc_card *card)
++{
++	return sdio_read_cis(card, NULL);
++}
++
++void sdio_free_common_cis(struct mmc_card *card)
++{
++	struct sdio_func_tuple *tuple, *victim;
++
++	tuple = card->tuples;
++
++	while (tuple) {
++		victim = tuple;
++		tuple = tuple->next;
++		kfree(victim);
++	}
++
++	card->tuples = NULL;
++}
++
++int sdio_read_func_cis(struct sdio_func *func)
++{
++	int ret;
++
++	ret = sdio_read_cis(func->card, func);
++	if (ret)
++		return ret;
++
++	/*
++	 * Since we've linked to tuples in the card structure,
++	 * we must make sure we have a reference to it.
++	 */
++	get_device(&func->card->dev);
++
++	/*
++	 * Vendor/device id is optional for function CIS, so
++	 * copy it from the card structure as needed.
++	 */
++	if (func->vendor == 0) {
++		func->vendor = func->card->cis.vendor;
++		func->device = func->card->cis.device;
++	}
++
++	return 0;
++}
++
++void sdio_free_func_cis(struct sdio_func *func)
++{
++	struct sdio_func_tuple *tuple, *victim;
++
++	tuple = func->tuples;
++
++	while (tuple && tuple != func->card->tuples) {
++		victim = tuple;
++		tuple = tuple->next;
++		kfree(victim);
++	}
++
++	func->tuples = NULL;
++
++	/*
++	 * We have now removed the link to the tuples in the
++	 * card structure, so remove the reference.
++	 */
++	put_device(&func->card->dev);
++}
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_cis.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_cis.h	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,23 @@
++/*
++ * linux/drivers/mmc/core/sdio_cis.h
++ *
++ * Author:	Nicolas Pitre
++ * Created:	June 11, 2007
++ * Copyright:	MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#ifndef _MMC_SDIO_CIS_H
++#define _MMC_SDIO_CIS_H
++
++int sdio_read_common_cis(struct mmc_card *card);
++void sdio_free_common_cis(struct mmc_card *card);
++
++int sdio_read_func_cis(struct sdio_func *func);
++void sdio_free_func_cis(struct sdio_func *func);
++
++#endif
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_io.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_io.c	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,562 @@
++/*
++ *  linux/drivers/mmc/core/sdio_io.c
++ *
++ *  Copyright 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "sdio_ops.h"
++
++/**
++ *	sdio_claim_host - exclusively claim a bus for a certain SDIO function
++ *	@func: SDIO function that will be accessed
++ *
++ *	Claim a bus for a set of operations. The SDIO function given
++ *	is used to figure out which bus is relevant.
++ */
++void sdio_claim_host(struct sdio_func *func)
++{
++	BUG_ON(!func);
++	BUG_ON(!func->card);
++
++	mmc_claim_host(func->card->host);
++}
++
++EXPORT_SYMBOL_GPL(sdio_claim_host);
++
++/**
++ *	sdio_release_host - release a bus for a certain SDIO function
++ *	@func: SDIO function that was accessed
++ *
++ *	Release a bus, allowing others to claim the bus for their
++ *	operations.
++ */
++void sdio_release_host(struct sdio_func *func)
++{
++	BUG_ON(!func);
++	BUG_ON(!func->card);
++
++	mmc_release_host(func->card->host);
++}
++
++EXPORT_SYMBOL_GPL(sdio_release_host);
++
++/**
++ *	sdio_enable_func - enables a SDIO function for usage
++ *	@func: SDIO function to enable
++ *
++ *	Powers up and activates a SDIO function so that register
++ *	access is possible.
++ */
++int sdio_enable_func(struct sdio_func *func)
++{
++	int ret;
++	unsigned char reg;
++	unsigned long timeout;
++
++	BUG_ON(!func);
++	BUG_ON(!func->card);
++
++	pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func));
++
++	ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
++	if (ret)
++		goto err;
++
++	reg |= 1 << func->num;
++
++	ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
++	if (ret)
++		goto err;
++
++	/*
++	 * FIXME: This should timeout based on information in the CIS,
++	 * but we don't have card to parse that yet.
++	 */
++	timeout = jiffies + HZ;
++
++	while (1) {
++		ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, &reg);
++		if (ret)
++			goto err;
++		if (reg & (1 << func->num))
++			break;
++		ret = -ETIME;
++		if (time_after(jiffies, timeout))
++			goto err;
++	}
++
++	pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func));
++
++	return 0;
++
++err:
++	pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func));
++	return ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_enable_func);
++
++/**
++ *	sdio_disable_func - disable a SDIO function
++ *	@func: SDIO function to disable
++ *
++ *	Powers down and deactivates a SDIO function. Register access
++ *	to this function will fail until the function is reenabled.
++ */
++int sdio_disable_func(struct sdio_func *func)
++{
++	int ret;
++	unsigned char reg;
++
++	BUG_ON(!func);
++	BUG_ON(!func->card);
++
++	pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func));
++
++	ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
++	if (ret)
++		goto err;
++
++	reg &= ~(1 << func->num);
++
++	ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
++	if (ret)
++		goto err;
++
++	pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func));
++
++	return 0;
++
++err:
++	pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func));
++	return -EIO;
++}
++
++EXPORT_SYMBOL_GPL(sdio_disable_func);
++
++/**
++ *	sdio_set_block_size - set the block size of an SDIO function
++ *	@func: SDIO function to change
++ *	@blksz: new block size or 0 to use the default.
++ *
++ *	The default block size is the largest supported by both the function
++ *	and the host, with a maximum of 512 to ensure that arbitrarily sized
++ *	data transfer use the optimal (least) number of commands.
++ *
++ *	A driver may call this to override the default block size set by the
++ *	core. This can be used to set a block size greater than the maximum
++ *	that reported by the card; it is the driver's responsibility to ensure
++ *	it uses a value that the card supports.
++ *
++ *	Returns 0 on success, -EINVAL if the host does not support the
++ *	requested block size, or -EIO (etc.) if one of the resultant FBR block
++ *	size register writes failed.
++ *
++ */
++int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
++{
++	int ret;
++
++	if (blksz > func->card->host->max_blk_size)
++		return -EINVAL;
++
++	if (blksz == 0) {
++		blksz = min(min(
++			func->max_blksize,
++			func->card->host->max_blk_size),
++			512u);
++	}
++
++	ret = mmc_io_rw_direct(func->card, 1, 0,
++		SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
++		blksz & 0xff, NULL);
++	if (ret)
++		return ret;
++	ret = mmc_io_rw_direct(func->card, 1, 0,
++		SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
++		(blksz >> 8) & 0xff, NULL);
++	if (ret)
++		return ret;
++	func->cur_blksize = blksz;
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(sdio_set_block_size);
++
++/* Split an arbitrarily sized data transfer into several
++ * IO_RW_EXTENDED commands. */
++static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
++	unsigned addr, int incr_addr, u8 *buf, unsigned size)
++{
++	unsigned remainder = size;
++	unsigned max_blocks;
++	int ret;
++
++	/* Do the bulk of the transfer using block mode (if supported). */
++	if (func->card->cccr.multi_block) {
++		/* Blocks per command is limited by host count, host transfer
++		 * size (we only use a single sg entry) and the maximum for
++		 * IO_RW_EXTENDED of 511 blocks. */
++		max_blocks = min(min(
++			func->card->host->max_blk_count,
++			func->card->host->max_seg_size / func->cur_blksize),
++			511u);
++
++		while (remainder > func->cur_blksize) {
++			unsigned blocks;
++
++			blocks = remainder / func->cur_blksize;
++			if (blocks > max_blocks)
++				blocks = max_blocks;
++			size = blocks * func->cur_blksize;
++
++			ret = mmc_io_rw_extended(func->card, write,
++				func->num, addr, incr_addr, buf,
++				blocks, func->cur_blksize);
++			if (ret)
++				return ret;
++
++			remainder -= size;
++			buf += size;
++			if (incr_addr)
++				addr += size;
++		}
++	}
++
++	/* Write the remainder using byte mode. */
++	while (remainder > 0) {
++		size = remainder;
++		if (size > func->cur_blksize)
++			size = func->cur_blksize;
++		if (size > 512)
++			size = 512; /* maximum size for byte mode */
++
++		ret = mmc_io_rw_extended(func->card, write, func->num, addr,
++			 incr_addr, buf, 1, size);
++		if (ret)
++			return ret;
++
++		remainder -= size;
++		buf += size;
++		if (incr_addr)
++			addr += size;
++	}
++	return 0;
++}
++
++/**
++ *	sdio_readb - read a single byte from a SDIO function
++ *	@func: SDIO function to access
++ *	@addr: address to read
++ *	@err_ret: optional status value from transfer
++ *
++ *	Reads a single byte from the address space of a given SDIO
++ *	function. If there is a problem reading the address, 0xff
++ *	is returned and @err_ret will contain the error code.
++ */
++unsigned char sdio_readb(struct sdio_func *func, unsigned int addr,
++	int *err_ret)
++{
++	int ret;
++	unsigned char val;
++
++	BUG_ON(!func);
++
++	if (err_ret)
++		*err_ret = 0;
++
++	ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val);
++	if (ret) {
++		if (err_ret)
++			*err_ret = ret;
++		return 0xFF;
++	}
++
++	return val;
++}
++
++EXPORT_SYMBOL_GPL(sdio_readb);
++
++/**
++ *	sdio_writeb - write a single byte to a SDIO function
++ *	@func: SDIO function to access
++ *	@b: byte to write
++ *	@addr: address to write to
++ *	@err_ret: optional status value from transfer
++ *
++ *	Writes a single byte to the address space of a given SDIO
++ *	function. @err_ret will contain the status of the actual
++ *	transfer.
++ */
++void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
++	int *err_ret)
++{
++	int ret;
++
++	BUG_ON(!func);
++
++	ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL);
++	if (err_ret)
++		*err_ret = ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_writeb);
++
++/**
++ *	sdio_memcpy_fromio - read a chunk of memory from a SDIO function
++ *	@func: SDIO function to access
++ *	@dst: buffer to store the data
++ *	@addr: address to begin reading from
++ *	@count: number of bytes to read
++ *
++ *	Reads from the address space of a given SDIO function. Return
++ *	value indicates if the transfer succeeded or not.
++ */
++int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
++	unsigned int addr, int count)
++{
++	return sdio_io_rw_ext_helper(func, 0, addr, 1, dst, count);
++}
++
++EXPORT_SYMBOL_GPL(sdio_memcpy_fromio);
++
++/**
++ *	sdio_memcpy_toio - write a chunk of memory to a SDIO function
++ *	@func: SDIO function to access
++ *	@addr: address to start writing to
++ *	@src: buffer that contains the data to write
++ *	@count: number of bytes to write
++ *
++ *	Writes to the address space of a given SDIO function. Return
++ *	value indicates if the transfer succeeded or not.
++ */
++int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
++	void *src, int count)
++{
++	return sdio_io_rw_ext_helper(func, 1, addr, 1, src, count);
++}
++EXPORT_SYMBOL_GPL(sdio_memcpy_toio);
++
++/**
++ *	sdio_readsb - read from a FIFO on a SDIO function
++ *	@func: SDIO function to access
++ *	@dst: buffer to store the data
++ *	@addr: address of (single byte) FIFO
++ *	@count: number of bytes to read
++ *
++ *	Reads from the specified FIFO of a given SDIO function. Return
++ *	value indicates if the transfer succeeded or not.
++ */
++int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,
++	int count)
++{
++	return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);
++}
++
++EXPORT_SYMBOL_GPL(sdio_readsb);
++
++/**
++ *	sdio_writesb - write to a FIFO of a SDIO function
++ *	@func: SDIO function to access
++ *	@addr: address of (single byte) FIFO
++ *	@src: buffer that contains the data to write
++ *	@count: number of bytes to write
++ *
++ *	Writes to the specified FIFO of a given SDIO function. Return
++ *	value indicates if the transfer succeeded or not.
++ */
++int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src,
++	int count)
++{
++	return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count);
++}
++
++EXPORT_SYMBOL_GPL(sdio_writesb);
++
++/**
++ *	sdio_readw - read a 16 bit integer from a SDIO function
++ *	@func: SDIO function to access
++ *	@addr: address to read
++ *	@err_ret: optional status value from transfer
++ *
++ *	Reads a 16 bit integer from the address space of a given SDIO
++ *	function. If there is a problem reading the address, 0xffff
++ *	is returned and @err_ret will contain the error code.
++ */
++unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,
++	int *err_ret)
++{
++	int ret;
++
++	if (err_ret)
++		*err_ret = 0;
++
++	ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2);
++	if (ret) {
++		if (err_ret)
++			*err_ret = ret;
++		return 0xFFFF;
++	}
++
++	return le16_to_cpu(*(u16*)func->tmpbuf);
++}
++
++EXPORT_SYMBOL_GPL(sdio_readw);
++
++/**
++ *	sdio_writew - write a 16 bit integer to a SDIO function
++ *	@func: SDIO function to access
++ *	@b: integer to write
++ *	@addr: address to write to
++ *	@err_ret: optional status value from transfer
++ *
++ *	Writes a 16 bit integer to the address space of a given SDIO
++ *	function. @err_ret will contain the status of the actual
++ *	transfer.
++ */
++void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr,
++	int *err_ret)
++{
++	int ret;
++
++	*(u16*)func->tmpbuf = cpu_to_le16(b);
++
++	ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);
++	if (err_ret)
++		*err_ret = ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_writew);
++
++/**
++ *	sdio_readl - read a 32 bit integer from a SDIO function
++ *	@func: SDIO function to access
++ *	@addr: address to read
++ *	@err_ret: optional status value from transfer
++ *
++ *	Reads a 32 bit integer from the address space of a given SDIO
++ *	function. If there is a problem reading the address,
++ *	0xffffffff is returned and @err_ret will contain the error
++ *	code.
++ */
++unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,
++	int *err_ret)
++{
++	int ret;
++
++	if (err_ret)
++		*err_ret = 0;
++
++	ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4);
++	if (ret) {
++		if (err_ret)
++			*err_ret = ret;
++		return 0xFFFFFFFF;
++	}
++
++	return le32_to_cpu(*(u32*)func->tmpbuf);
++}
++
++EXPORT_SYMBOL_GPL(sdio_readl);
++
++/**
++ *	sdio_writel - write a 32 bit integer to a SDIO function
++ *	@func: SDIO function to access
++ *	@b: integer to write
++ *	@addr: address to write to
++ *	@err_ret: optional status value from transfer
++ *
++ *	Writes a 32 bit integer to the address space of a given SDIO
++ *	function. @err_ret will contain the status of the actual
++ *	transfer.
++ */
++void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr,
++	int *err_ret)
++{
++	int ret;
++
++	*(u32*)func->tmpbuf = cpu_to_le32(b);
++
++	ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);
++	if (err_ret)
++		*err_ret = ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_writel);
++
++/**
++ *	sdio_f0_readb - read a single byte from SDIO function 0
++ *	@func: an SDIO function of the card
++ *	@addr: address to read
++ *	@err_ret: optional status value from transfer
++ *
++ *	Reads a single byte from the address space of SDIO function 0.
++ *	If there is a problem reading the address, 0xff is returned
++ *	and @err_ret will contain the error code.
++ */
++unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr,
++	int *err_ret)
++{
++	int ret;
++	unsigned char val;
++
++	BUG_ON(!func);
++
++	if (err_ret)
++		*err_ret = 0;
++
++	ret = mmc_io_rw_direct(func->card, 0, 0, addr, 0, &val);
++	if (ret) {
++		if (err_ret)
++			*err_ret = ret;
++		return 0xFF;
++	}
++
++	return val;
++}
++
++EXPORT_SYMBOL_GPL(sdio_f0_readb);
++
++/**
++ *	sdio_f0_writeb - write a single byte to SDIO function 0
++ *	@func: an SDIO function of the card
++ *	@b: byte to write
++ *	@addr: address to write to
++ *	@err_ret: optional status value from transfer
++ *
++ *	Writes a single byte to the address space of SDIO function 0.
++ *	@err_ret will contain the status of the actual transfer.
++ *
++ *	Only writes to the vendor specific CCCR registers (0xF0 -
++ *	0xFF) are permiited; @err_ret will be set to -EINVAL for *
++ *	writes outside this range.
++ */
++void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
++	int *err_ret)
++{
++	int ret;
++
++	BUG_ON(!func);
++
++	if (addr < 0xF0 || addr > 0xFF) {
++		if (err_ret)
++			*err_ret = -EINVAL;
++		return;
++	}
++
++	ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL);
++	if (err_ret)
++		*err_ret = ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_f0_writeb);
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_irq.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_irq.c	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,245 @@
++/*
++ * linux/drivers/mmc/core/sdio_irq.c
++ *
++ * Author:      Nicolas Pitre
++ * Created:     June 18, 2007
++ * Copyright:   MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/kthread.h>
++#include <linux/wait.h>
++#include <linux/delay.h>
++
++#include <linux/mmc/core.h>
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "sdio_ops.h"
++
++static int process_sdio_pending_irqs(struct mmc_card *card)
++{
++	int i, ret;
++	unsigned char pending;
++
++	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
++	if (ret) {
++		printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n",
++		       mmc_card_id(card), ret);
++		return ret;
++	}
++
++	for (i = 1; i <= 7; i++) {
++		if (pending & (1 << i)) {
++			struct sdio_func *func = card->sdio_func[i - 1];
++			if (!func) {
++				printk(KERN_WARNING "%s: pending IRQ for "
++					"non-existant function\n",
++					sdio_func_id(func));
++			} else if (func->irq_handler) {
++				func->irq_handler(func);
++			} else
++				printk(KERN_WARNING "%s: pending IRQ with no handler\n",
++				       sdio_func_id(func));
++		}
++	}
++
++	return 0;
++}
++
++static int sdio_irq_thread(void *_host)
++{
++	struct mmc_host *host = _host;
++	struct sched_param param = { .sched_priority = 1 };
++	unsigned long period;
++	int ret;
++
++	sched_setscheduler(current, SCHED_FIFO, &param);
++
++	/*
++	 * We want to allow for SDIO cards to work even on non SDIO
++	 * aware hosts.  One thing that non SDIO host cannot do is
++	 * asynchronous notification of pending SDIO card interrupts
++	 * hence we poll for them in that case.
++	 */
++	period = (host->caps & MMC_CAP_SDIO_IRQ) ?
++		MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(10);
++
++	pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",
++		 mmc_hostname(host), period);
++
++	do {
++		/*
++		 * We claim the host here on drivers behalf for a couple
++		 * reasons:
++		 *
++		 * 1) it is already needed to retrieve the CCCR_INTx;
++		 * 2) we want the driver(s) to clear the IRQ condition ASAP;
++		 * 3) we need to control the abort condition locally.
++		 *
++		 * Just like traditional hard IRQ handlers, we expect SDIO
++		 * IRQ handlers to be quick and to the point, so that the
++		 * holding of the host lock does not cover too much work
++		 * that doesn't require that lock to be held.
++		 */
++		ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
++		if (ret)
++			break;
++		ret = process_sdio_pending_irqs(host->card);
++		mmc_release_host(host);
++
++		/*
++		 * Give other threads a chance to run in the presence of
++		 * errors.  FIXME: determine if due to card removal and
++		 * possibly exit this thread if so.
++		 */
++		if (ret)
++			ssleep(1);
++
++		set_task_state(current, TASK_INTERRUPTIBLE);
++		if (host->caps & MMC_CAP_SDIO_IRQ)
++			host->ops->enable_sdio_irq(host, 1);
++		if (!kthread_should_stop())
++			schedule_timeout(period);
++		set_task_state(current, TASK_RUNNING);
++	} while (!kthread_should_stop());
++
++	if (host->caps & MMC_CAP_SDIO_IRQ)
++		host->ops->enable_sdio_irq(host, 0);
++
++	pr_debug("%s: IRQ thread exiting with code %d\n",
++		 mmc_hostname(host), ret);
++
++	return ret;
++}
++
++static int sdio_card_irq_get(struct mmc_card *card)
++{
++	struct mmc_host *host = card->host;
++
++	WARN_ON(!host->claimed);
++
++	if (!host->sdio_irqs++) {
++		atomic_set(&host->sdio_irq_thread_abort, 0);
++		host->sdio_irq_thread =
++			kthread_run(sdio_irq_thread, host, "ksdiorqd");
++		if (IS_ERR(host->sdio_irq_thread)) {
++			int err = PTR_ERR(host->sdio_irq_thread);
++			host->sdio_irqs--;
++			return err;
++		}
++	}
++
++	return 0;
++}
++
++static int sdio_card_irq_put(struct mmc_card *card)
++{
++	struct mmc_host *host = card->host;
++
++	WARN_ON(!host->claimed);
++	BUG_ON(host->sdio_irqs < 1);
++
++	if (!--host->sdio_irqs) {
++		atomic_set(&host->sdio_irq_thread_abort, 1);
++		kthread_stop(host->sdio_irq_thread);
++	}
++
++	return 0;
++}
++
++/**
++ *	sdio_claim_irq - claim the IRQ for a SDIO function
++ *	@func: SDIO function
++ *	@handler: IRQ handler callback
++ *
++ *	Claim and activate the IRQ for the given SDIO function. The provided
++ *	handler will be called when that IRQ is asserted.  The host is always
++ *	claimed already when the handler is called so the handler must not
++ *	call sdio_claim_host() nor sdio_release_host().
++ */
++int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
++{
++	int ret;
++	unsigned char reg;
++
++	BUG_ON(!func);
++	BUG_ON(!func->card);
++
++	pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));
++
++	if (func->irq_handler) {
++		pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));
++		return -EBUSY;
++	}
++
++	ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
++	if (ret)
++		return ret;
++
++	reg |= 1 << func->num;
++
++	reg |= 1; /* Master interrupt enable */
++
++	ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
++	if (ret)
++		return ret;
++
++	func->irq_handler = handler;
++	ret = sdio_card_irq_get(func->card);
++	if (ret)
++		func->irq_handler = NULL;
++
++	return ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_claim_irq);
++
++/**
++ *	sdio_release_irq - release the IRQ for a SDIO function
++ *	@func: SDIO function
++ *
++ *	Disable and release the IRQ for the given SDIO function.
++ */
++int sdio_release_irq(struct sdio_func *func)
++{
++	int ret;
++	unsigned char reg;
++
++	BUG_ON(!func);
++	BUG_ON(!func->card);
++
++	pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));
++
++	if (func->irq_handler) {
++		func->irq_handler = NULL;
++		sdio_card_irq_put(func->card);
++	}
++
++	ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
++	if (ret)
++		return ret;
++
++	reg &= ~(1 << func->num);
++
++	/* Disable master interrupt with the last function interrupt */
++	if (!(reg & 0xFE))
++		reg = 0;
++
++	ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++EXPORT_SYMBOL_GPL(sdio_release_irq);
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_ops.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_ops.c	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,148 @@
++/*
++ *  linux/drivers/mmc/sdio_ops.c
++ *
++ *  Copyright 2006-2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <asm/scatterlist.h>
++#include <linux/scatterlist.h>
++
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/mmc.h>
++#include <linux/mmc/sdio.h>
++
++#include "core.h"
++
++int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
++{
++	struct mmc_command cmd;
++	int i, err = 0;
++
++	BUG_ON(!host);
++
++	memset(&cmd, 0, sizeof(struct mmc_command));
++
++	cmd.opcode = SD_IO_SEND_OP_COND;
++	cmd.arg = ocr;
++	cmd.flags = MMC_RSP_R4 | MMC_CMD_BCR;
++
++	for (i = 100; i; i--) {
++		err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
++		if (err)
++			break;
++
++		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
++			break;
++
++		err = -ETIMEDOUT;
++
++		mmc_delay(10);
++	}
++
++	if (rocr)
++		*rocr = cmd.resp[0];
++
++	return err;
++}
++
++int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
++	unsigned addr, u8 in, u8* out)
++{
++	struct mmc_command cmd;
++	int err;
++
++	BUG_ON(!card);
++	BUG_ON(fn > 7);
++
++	memset(&cmd, 0, sizeof(struct mmc_command));
++
++	cmd.opcode = SD_IO_RW_DIRECT;
++	cmd.arg = write ? 0x80000000 : 0x00000000;
++	cmd.arg |= fn << 28;
++	cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;
++	cmd.arg |= addr << 9;
++	cmd.arg |= in;
++	cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
++
++	err = mmc_wait_for_cmd(card->host, &cmd, 0);
++	if (err)
++		return err;
++
++	if (cmd.resp[0] & R5_ERROR)
++		return -EIO;
++	if (cmd.resp[0] & R5_FUNCTION_NUMBER)
++		return -EINVAL;
++	if (cmd.resp[0] & R5_OUT_OF_RANGE)
++		return -ERANGE;
++
++	if (out)
++		*out = cmd.resp[0] & 0xFF;
++
++	return 0;
++}
++
++int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
++	unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
++{
++	struct mmc_request mrq;
++	struct mmc_command cmd;
++	struct mmc_data data;
++	struct scatterlist sg;
++
++	BUG_ON(!card);
++	BUG_ON(fn > 7);
++	BUG_ON(blocks == 1 && blksz > 512);
++	WARN_ON(blocks == 0);
++	WARN_ON(blksz == 0);
++
++	memset(&mrq, 0, sizeof(struct mmc_request));
++	memset(&cmd, 0, sizeof(struct mmc_command));
++	memset(&data, 0, sizeof(struct mmc_data));
++
++	mrq.cmd = &cmd;
++	mrq.data = &data;
++
++	cmd.opcode = SD_IO_RW_EXTENDED;
++	cmd.arg = write ? 0x80000000 : 0x00000000;
++	cmd.arg |= fn << 28;
++	cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
++	cmd.arg |= addr << 9;
++	if (blocks == 1 && blksz <= 512)
++		cmd.arg |= (blksz == 512) ? 0 : blksz;	/* byte mode */
++	else
++		cmd.arg |= 0x08000000 | blocks;		/* block mode */
++	cmd.flags = MMC_RSP_R5 | MMC_CMD_ADTC;
++
++	data.blksz = blksz;
++	data.blocks = blocks;
++	data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
++	data.sg = &sg;
++	data.sg_len = 1;
++
++	sg_init_one(&sg, buf, blksz * blocks);
++
++	mmc_set_data_timeout(&data, card, 0);
++
++	mmc_wait_for_req(card->host, &mrq);
++
++	if (cmd.error)
++		return cmd.error;
++	if (data.error)
++		return data.error;
++
++	if (cmd.resp[0] & R5_ERROR)
++		return -EIO;
++	if (cmd.resp[0] & R5_FUNCTION_NUMBER)
++		return -EINVAL;
++	if (cmd.resp[0] & R5_OUT_OF_RANGE)
++		return -ERANGE;
++
++	return 0;
++}
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_ops.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sdio_ops.h	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,22 @@
++/*
++ *  linux/drivers/mmc/sdio_ops.c
++ *
++ *  Copyright 2006-2007 Pierre Ossman
++ *
++ * 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.
++ */
++
++#ifndef _MMC_SDIO_OPS_H
++#define _MMC_SDIO_OPS_H
++
++int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
++int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
++	unsigned addr, u8 in, u8* out);
++int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
++	unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
++
++#endif
++
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sd_ops.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/sd_ops.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sd_ops.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/sd_ops.h
++ *  linux/drivers/mmc/core/sd_ops.h
+  *
+  *  Copyright 2006-2007 Pierre Ossman
+  *
+@@ -21,11 +21,40 @@
+ #include "core.h"
+ #include "sd_ops.h"
+ 
++static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
++{
++	int err;
++	struct mmc_command cmd;
++
++	BUG_ON(!host);
++	BUG_ON(card && (card->host != host));
++
++	cmd.opcode = MMC_APP_CMD;
++
++	if (card) {
++		cmd.arg = card->rca << 16;
++		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
++	} else {
++		cmd.arg = 0;
++		cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR;
++	}
++
++	err = mmc_wait_for_cmd(host, &cmd, 0);
++	if (err)
++		return err;
++
++	/* Check that card supported application commands */
++	if (!(cmd.resp[0] & R1_APP_CMD))
++		return -EOPNOTSUPP;
++
++	return 0;
++}
++
+ /**
+  *	mmc_wait_for_app_cmd - start an application command and wait for
+  			       completion
+  *	@host: MMC host to start command
+- *	@rca: RCA to send MMC_APP_CMD to
++ *	@card: Card to send MMC_APP_CMD to
+  *	@cmd: MMC command to start
+  *	@retries: maximum number of retries
+  *
+@@ -44,7 +73,7 @@
+ 	BUG_ON(!cmd);
+ 	BUG_ON(retries < 0);
+ 
+-	err = MMC_ERR_INVALID;
++	err = -EIO;
+ 
+ 	/*
+ 	 * We have to resend MMC_APP_CMD for each attempt so
+@@ -54,7 +83,7 @@
+ 		memset(&mrq, 0, sizeof(struct mmc_request));
+ 
+ 		err = mmc_app_cmd(host, card);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			continue;
+ 
+ 		memset(&mrq, 0, sizeof(struct mmc_request));
+@@ -68,7 +97,7 @@
+ 		mmc_wait_for_req(host, &mrq);
+ 
+ 		err = cmd->error;
+-		if (cmd->error == MMC_ERR_NONE)
++		if (!cmd->error)
+ 			break;
+ 	}
+ 
+@@ -77,35 +106,6 @@
+ 
+ EXPORT_SYMBOL(mmc_wait_for_app_cmd);
+ 
+-int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
+-{
+-	int err;
+-	struct mmc_command cmd;
+-
+-	BUG_ON(!host);
+-	BUG_ON(card && (card->host != host));
+-
+-	cmd.opcode = MMC_APP_CMD;
+-
+-	if (card) {
+-		cmd.arg = card->rca << 16;
+-		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+-	} else {
+-		cmd.arg = 0;
+-		cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR;
+-	}
+-
+-	err = mmc_wait_for_cmd(host, &cmd, 0);
+-	if (err != MMC_ERR_NONE)
+-		return err;
+-
+-	/* Check that card supported application commands */
+-	if (!(cmd.resp[0] & R1_APP_CMD))
+-		return MMC_ERR_FAILED;
+-
+-	return MMC_ERR_NONE;
+-}
+-
+ int mmc_app_set_bus_width(struct mmc_card *card, int width)
+ {
+ 	int err;
+@@ -127,14 +127,14 @@
+ 		cmd.arg = SD_BUS_WIDTH_4;
+ 		break;
+ 	default:
+-		return MMC_ERR_INVALID;
++		return -EINVAL;
+ 	}
+ 
+ 	err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		return err;
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+@@ -152,13 +152,13 @@
+ 
+ 	for (i = 100; i; i--) {
+ 		err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
+-		if (err != MMC_ERR_NONE)
++		if (err)
+ 			break;
+ 
+ 		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+ 			break;
+ 
+-		err = MMC_ERR_TIMEOUT;
++		err = -ETIMEDOUT;
+ 
+ 		mmc_delay(10);
+ 	}
+@@ -185,13 +185,13 @@
+ 	cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
+ 
+ 	err = mmc_wait_for_cmd(host, &cmd, 0);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		return err;
+ 
+ 	if ((cmd.resp[0] & 0xFF) != test_pattern)
+-		return MMC_ERR_FAILED;
++		return -EIO;
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
+@@ -209,12 +209,12 @@
+ 	cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
+ 
+ 	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		return err;
+ 
+ 	*rca = cmd.resp[0] >> 16;
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
+@@ -230,7 +230,7 @@
+ 	BUG_ON(!scr);
+ 
+ 	err = mmc_app_cmd(card->host, card);
+-	if (err != MMC_ERR_NONE)
++	if (err)
+ 		return err;
+ 
+ 	memset(&mrq, 0, sizeof(struct mmc_request));
+@@ -256,15 +256,15 @@
+ 
+ 	mmc_wait_for_req(card->host, &mrq);
+ 
+-	if (cmd.error != MMC_ERR_NONE)
++	if (cmd.error)
+ 		return cmd.error;
+-	if (data.error != MMC_ERR_NONE)
++	if (data.error)
+ 		return data.error;
+ 
+ 	scr[0] = ntohl(scr[0]);
+ 	scr[1] = ntohl(scr[1]);
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
+@@ -306,11 +306,11 @@
+ 
+ 	mmc_wait_for_req(card->host, &mrq);
+ 
+-	if (cmd.error != MMC_ERR_NONE)
++	if (cmd.error)
+ 		return cmd.error;
+-	if (data.error != MMC_ERR_NONE)
++	if (data.error)
+ 		return data.error;
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sysfs.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/sysfs.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sysfs.c	2007-11-13 11:35:23.000000000 +0100
+@@ -2,6 +2,7 @@
+  *  linux/drivers/mmc/core/sysfs.c
+  *
+  *  Copyright (C) 2003 Russell King, All Rights Reserved.
++ *  Copyright 2007 Pierre Ossman
+  *
+  * 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
+@@ -9,352 +10,34 @@
+  *
+  *  MMC sysfs/driver model support.
+  */
+-#include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/device.h>
+-#include <linux/idr.h>
+-#include <linux/workqueue.h>
+ 
+ #include <linux/mmc/card.h>
+-#include <linux/mmc/host.h>
+ 
+ #include "sysfs.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)
+-#define cls_dev_to_mmc_host(d)	container_of(d, struct mmc_host, class_dev)
+-
+-#define MMC_ATTR(name, fmt, args...)					\
+-static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf)	\
+-{									\
+-	struct mmc_card *card = dev_to_mmc_card(dev);			\
+-	return sprintf(buf, fmt, args);					\
+-}
+-
+-MMC_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
+-	card->raw_cid[2], card->raw_cid[3]);
+-MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
+-	card->raw_csd[2], card->raw_csd[3]);
+-MMC_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]);
+-MMC_ATTR(date, "%02d/%04d\n", card->cid.month, 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%06x\n", card->cid.manfid);
+-MMC_ATTR(name, "%s\n", card->cid.prod_name);
+-MMC_ATTR(oemid, "0x%04x\n", card->cid.oemid);
+-MMC_ATTR(serial, "0x%08x\n", card->cid.serial);
+-
+-#define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
+-
+-static struct device_attribute mmc_dev_attrs[] = {
+-	MMC_ATTR_RO(cid),
+-	MMC_ATTR_RO(csd),
+-	MMC_ATTR_RO(date),
+-	MMC_ATTR_RO(fwrev),
+-	MMC_ATTR_RO(hwrev),
+-	MMC_ATTR_RO(manfid),
+-	MMC_ATTR_RO(name),
+-	MMC_ATTR_RO(oemid),
+-	MMC_ATTR_RO(serial),
+-	__ATTR_NULL
+-};
+-
+-static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO(scr);
+-
+-
+-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_uevent(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 retval = 0, i = 0, length = 0;
+-
+-#define add_env(fmt,val) do {					\
+-	retval = add_uevent_var(envp, num_envp, &i,		\
+-				buf, buf_size, &length,		\
+-				fmt, val);			\
+-	if (retval)						\
+-		return retval;					\
+-} while (0);
+-
+-	for (i = 0; i < 12; i++)
+-		ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0';
+-	ccc[12] = '\0';
+-
+-	add_env("MMC_CCC=%s", ccc);
+-	add_env("MMC_MANFID=%06x", card->cid.manfid);
+-	add_env("MMC_NAME=%s", mmc_card_name(card));
+-	add_env("MMC_OEMID=%04x", card->cid.oemid);
+-#undef add_env
+-	envp[i] = NULL;
+-
+-	return 0;
+-}
+-
+-static int mmc_bus_suspend(struct device *dev, pm_message_t state)
++int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs)
+ {
+-	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;
+-}
++	int error = 0;
++	int i;
+ 
+-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 int mmc_bus_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_bus_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;
+-}
+-
+-static struct bus_type mmc_bus_type = {
+-	.name		= "mmc",
+-	.dev_attrs	= mmc_dev_attrs,
+-	.match		= mmc_bus_match,
+-	.uevent		= mmc_bus_uevent,
+-	.probe		= mmc_bus_probe,
+-	.remove		= mmc_bus_remove,
+-	.suspend	= mmc_bus_suspend,
+-	.resume		= mmc_bus_resume,
+-};
+-
+-/**
+- *	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;
+-	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);
+-
+-
+-/*
+- * 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 = mmc_classdev(host);
+-	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;
+-
+-	snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
+-		 "%s:%04x", mmc_hostname(card->host), card->rca);
+-
+-	ret = device_add(&card->dev);
+-	if (ret == 0) {
+-		if (mmc_card_sd(card)) {
+-			ret = device_create_file(&card->dev, &mmc_dev_attr_scr);
+-			if (ret)
+-				device_del(&card->dev);
++	for (i = 0; attr_name(attrs[i]); i++) {
++		error = device_create_file(&card->dev, &attrs[i]);
++		if (error) {
++			while (--i >= 0)
++				device_remove_file(&card->dev, &attrs[i]);
++			break;
+ 		}
+ 	}
+-	if (ret == 0)
+-		mmc_card_set_present(card);
+-	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)) {
+-		if (mmc_card_sd(card))
+-			device_remove_file(&card->dev, &mmc_dev_attr_scr);
+-
+-		device_del(&card->dev);
+-	}
+-
+-	put_device(&card->dev);
+-}
+-
+-
+-static void mmc_host_classdev_release(struct device *dev)
+-{
+-	struct mmc_host *host = cls_dev_to_mmc_host(dev);
+-	kfree(host);
+-}
+-
+-static struct class mmc_host_class = {
+-	.name		= "mmc_host",
+-	.dev_release	= mmc_host_classdev_release,
+-};
+-
+-static DEFINE_IDR(mmc_host_idr);
+-static DEFINE_SPINLOCK(mmc_host_lock);
+-
+-/*
+- * Internal function. Allocate a new MMC host.
+- */
+-struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev)
+-{
+-	struct mmc_host *host;
+-
+-	host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
+-	if (host) {
+-		memset(host, 0, sizeof(struct mmc_host) + extra);
+-
+-		host->parent = dev;
+-		host->class_dev.parent = dev;
+-		host->class_dev.class = &mmc_host_class;
+-		device_initialize(&host->class_dev);
+-	}
+-
+-	return host;
+-}
+-
+-/*
+- * Internal function. Register a new MMC host with the MMC class.
+- */
+-int mmc_add_host_sysfs(struct mmc_host *host)
+-{
+-	int err;
+-
+-	if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
+-		return -ENOMEM;
+-
+-	spin_lock(&mmc_host_lock);
+-	err = idr_get_new(&mmc_host_idr, host, &host->index);
+-	spin_unlock(&mmc_host_lock);
+-	if (err)
+-		return err;
+-
+-	snprintf(host->class_dev.bus_id, BUS_ID_SIZE,
+-		 "mmc%d", host->index);
+-
+-	return device_add(&host->class_dev);
+-}
+-
+-/*
+- * Internal function. Unregister a MMC host with the MMC class.
+- */
+-void mmc_remove_host_sysfs(struct mmc_host *host)
+-{
+-	device_del(&host->class_dev);
+ 
+-	spin_lock(&mmc_host_lock);
+-	idr_remove(&mmc_host_idr, host->index);
+-	spin_unlock(&mmc_host_lock);
++	return error;
+ }
+ 
+-/*
+- * Internal function. Free a MMC host.
+- */
+-void mmc_free_host_sysfs(struct mmc_host *host)
++void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs)
+ {
+-	put_device(&host->class_dev);
+-}
++	int i;
+ 
+-static struct workqueue_struct *workqueue;
+-
+-/*
+- * Internal function. Schedule delayed work in the MMC work queue.
+- */
+-int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay)
+-{
+-	return queue_delayed_work(workqueue, work, delay);
+-}
+-
+-/*
+- * Internal function. Flush all scheduled work from the MMC work queue.
+- */
+-void mmc_flush_scheduled_work(void)
+-{
+-	flush_workqueue(workqueue);
+-}
+-
+-static int __init mmc_init(void)
+-{
+-	int ret;
+-
+-	workqueue = create_singlethread_workqueue("kmmcd");
+-	if (!workqueue)
+-		return -ENOMEM;
+-
+-	ret = bus_register(&mmc_bus_type);
+-	if (ret == 0) {
+-		ret = class_register(&mmc_host_class);
+-		if (ret)
+-			bus_unregister(&mmc_bus_type);
+-	}
+-	return ret;
+-}
+-
+-static void __exit mmc_exit(void)
+-{
+-	class_unregister(&mmc_host_class);
+-	bus_unregister(&mmc_bus_type);
+-	destroy_workqueue(workqueue);
++	for (i = 0; attr_name(attrs[i]); i++)
++		device_remove_file(&card->dev, &attrs[i]);
+ }
+ 
+-module_init(mmc_init);
+-module_exit(mmc_exit);
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/at91_mci.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/at91_mci.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/at91_mci.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/at91_mci.c - ATMEL AT91 MCI Driver
++ *  linux/drivers/mmc/host/at91_mci.c - ATMEL AT91 MCI Driver
+  *
+  *  Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved
+  *
+@@ -78,14 +78,12 @@
+ 
+ #define DRIVER_NAME "at91_mci"
+ 
+-#undef	SUPPORT_4WIRE
+-
+ #define FL_SENT_COMMAND	(1 << 0)
+ #define FL_SENT_STOP	(1 << 1)
+ 
+ #define AT91_MCI_ERRORS	(AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE	\
+ 		| AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE		\
+-		| AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)			
++		| AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)
+ 
+ #define at91_mci_read(host, reg)	__raw_readl((host)->baseaddr + (reg))
+ #define at91_mci_write(host, reg, val)	__raw_writel((val), (host)->baseaddr + (reg))
+@@ -131,7 +129,7 @@
+ /*
+  * Copy from sg to a dma block - used for transfers
+  */
+-static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data)
++static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data)
+ {
+ 	unsigned int len, i, size;
+ 	unsigned *dmabuf = host->buffer;
+@@ -180,7 +178,7 @@
+ /*
+  * Prepare a dma read
+  */
+-static void at91mci_pre_dma_read(struct at91mci_host *host)
++static void at91_mci_pre_dma_read(struct at91mci_host *host)
+ {
+ 	int i;
+ 	struct scatterlist *sg;
+@@ -248,7 +246,7 @@
+ /*
+  * Handle after a dma read
+  */
+-static void at91mci_post_dma_read(struct at91mci_host *host)
++static void at91_mci_post_dma_read(struct at91mci_host *host)
+ {
+ 	struct mmc_command *cmd;
+ 	struct mmc_data *data;
+@@ -268,8 +266,6 @@
+ 	}
+ 
+ 	while (host->in_use_index < host->transfer_index) {
+-		unsigned int *buffer;
+-
+ 		struct scatterlist *sg;
+ 
+ 		pr_debug("finishing index %d\n", host->in_use_index);
+@@ -280,29 +276,31 @@
+ 
+ 		dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
+ 
+-		/* Swap the contents of the buffer */
+-		buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
+-		pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
+-
+ 		data->bytes_xfered += sg->length;
+ 
+ 		if (cpu_is_at91rm9200()) {	/* AT91RM9200 errata */
++			unsigned int *buffer;
+ 			int index;
+ 
++			/* Swap the contents of the buffer */
++			buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
++			pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
++
+ 			for (index = 0; index < (sg->length / 4); index++)
+ 				buffer[index] = swab32(buffer[index]);
++
++			kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+ 		}
+ 
+-		kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+ 		flush_dcache_page(sg->page);
+ 	}
+ 
+ 	/* Is there another transfer to trigger? */
+ 	if (host->transfer_index < data->sg_len)
+-		at91mci_pre_dma_read(host);
++		at91_mci_pre_dma_read(host);
+ 	else {
++		at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX);
+ 		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
+-		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+ 	}
+ 
+ 	pr_debug("post dma read done\n");
+@@ -323,7 +321,6 @@
+ 
+ 	/* Now wait for cmd ready */
+ 	at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE);
+-	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+ 
+ 	cmd = host->cmd;
+ 	if (!cmd) return;
+@@ -331,18 +328,53 @@
+ 	data = cmd->data;
+ 	if (!data) return;
+ 
++	if (cmd->data->flags & MMC_DATA_MULTI) {
++		pr_debug("multiple write : wait for BLKE...\n");
++		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
++	} else
++		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
++
+ 	data->bytes_xfered = host->total_length;
+ }
+ 
++/*Handle after command sent ready*/
++static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
++{
++	if (!host->cmd)
++		return 1;
++	else if (!host->cmd->data) {
++		if (host->flags & FL_SENT_STOP) {
++			/*After multi block write, we must wait for NOTBUSY*/
++			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
++		} else return 1;
++	} else if (host->cmd->data->flags & MMC_DATA_WRITE) {
++		/*After sendding multi-block-write command, start DMA transfer*/
++		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE);
++		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
++		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
++	}
++
++	/* command not completed, have to wait */
++	return 0;
++}
++
++
+ /*
+  * Enable the controller
+  */
+ static void at91_mci_enable(struct at91mci_host *host)
+ {
++	unsigned int mr;
++
+ 	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+ 	at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+ 	at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
+-	at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a);
++	mr = AT91_MCI_PDCMODE | 0x34a;
++
++	if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
++		mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF;
++
++	at91_mci_write(host, AT91_MCI_MR, mr);
+ 
+ 	/* use Slot A or B (only one at same time) */
+ 	at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b);
+@@ -358,9 +390,8 @@
+ 
+ /*
+  * Send a command
+- * return the interrupts to enable
+  */
+-static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
++static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
+ {
+ 	unsigned int cmdr, mr;
+ 	unsigned int block_length;
+@@ -371,8 +402,7 @@
+ 
+ 	host->cmd = cmd;
+ 
+-	/* Not sure if this is needed */
+-#if 0
++	/* Needed for leaving busy state before CMD1 */
+ 	if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) {
+ 		pr_debug("Clearing timeout\n");
+ 		at91_mci_write(host, AT91_MCI_ARGR, 0);
+@@ -382,7 +412,7 @@
+ 			pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR));
+ 		}
+ 	}
+-#endif
++
+ 	cmdr = cmd->opcode;
+ 
+ 	if (mmc_resp_type(cmd) == MMC_RSP_NONE)
+@@ -439,50 +469,48 @@
+ 		at91_mci_write(host, ATMEL_PDC_TCR, 0);
+ 		at91_mci_write(host, ATMEL_PDC_TNPR, 0);
+ 		at91_mci_write(host, ATMEL_PDC_TNCR, 0);
++		ier = AT91_MCI_CMDRDY;
++	} else {
++		/* zero block length and PDC mode */
++		mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
++		at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
++
++		/*
++		 * Disable the PDC controller
++		 */
++		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+ 
+-		at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
+-		at91_mci_write(host, AT91_MCI_CMDR, cmdr);
+-		return AT91_MCI_CMDRDY;
+-	}
+-
+-	mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;	/* zero block length and PDC mode */
+-	at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
+-
+-	/*
+-	 * Disable the PDC controller
+-	 */
+-	at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+-
+-	if (cmdr & AT91_MCI_TRCMD_START) {
+-		data->bytes_xfered = 0;
+-		host->transfer_index = 0;
+-		host->in_use_index = 0;
+-		if (cmdr & AT91_MCI_TRDIR) {
+-			/*
+-			 * Handle a read
+-			 */
+-			host->buffer = NULL;
+-			host->total_length = 0;
++		if (cmdr & AT91_MCI_TRCMD_START) {
++			data->bytes_xfered = 0;
++			host->transfer_index = 0;
++			host->in_use_index = 0;
++			if (cmdr & AT91_MCI_TRDIR) {
++				/*
++				 * Handle a read
++				 */
++				host->buffer = NULL;
++				host->total_length = 0;
+ 
+-			at91mci_pre_dma_read(host);
+-			ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
+-		}
+-		else {
+-			/*
+-			 * Handle a write
+-			 */
+-			host->total_length = block_length * blocks;
+-			host->buffer = dma_alloc_coherent(NULL,
+-						  host->total_length,
+-						  &host->physical_address, GFP_KERNEL);
+-
+-			at91mci_sg_to_dma(host, data);
+-
+-			pr_debug("Transmitting %d bytes\n", host->total_length);
+-
+-			at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
+-			at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4);
+-			ier = AT91_MCI_TXBUFE;
++				at91_mci_pre_dma_read(host);
++				ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
++			}
++			else {
++				/*
++				 * Handle a write
++				 */
++				host->total_length = block_length * blocks;
++				host->buffer = dma_alloc_coherent(NULL,
++						host->total_length,
++						&host->physical_address, GFP_KERNEL);
++
++				at91_mci_sg_to_dma(host, data);
++
++				pr_debug("Transmitting %d bytes\n", host->total_length);
++
++				at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
++				at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4);
++				ier = AT91_MCI_CMDRDY;
++			}
+ 		}
+ 	}
+ 
+@@ -497,39 +525,24 @@
+ 	if (cmdr & AT91_MCI_TRCMD_START) {
+ 		if (cmdr & AT91_MCI_TRDIR)
+ 			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN);
+-		else
+-			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
+ 	}
+-	return ier;
+-}
+-
+-/*
+- * Wait for a command to complete
+- */
+-static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd)
+-{
+-	unsigned int ier;
+-
+-	ier = at91_mci_send_command(host, cmd);
+ 
+-	pr_debug("setting ier to %08X\n", ier);
+-
+-	/* Stop on errors or the required value */
++	/* Enable selected interrupts */
+ 	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier);
+ }
+ 
+ /*
+  * Process the next step in the request
+  */
+-static void at91mci_process_next(struct at91mci_host *host)
++static void at91_mci_process_next(struct at91mci_host *host)
+ {
+ 	if (!(host->flags & FL_SENT_COMMAND)) {
+ 		host->flags |= FL_SENT_COMMAND;
+-		at91mci_process_command(host, host->request->cmd);
++		at91_mci_send_command(host, host->request->cmd);
+ 	}
+ 	else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
+ 		host->flags |= FL_SENT_STOP;
+-		at91mci_process_command(host, host->request->stop);
++		at91_mci_send_command(host, host->request->stop);
+ 	}
+ 	else
+ 		mmc_request_done(host->mmc, host->request);
+@@ -538,7 +551,7 @@
+ /*
+  * Handle a command that has been completed
+  */
+-static void at91mci_completed_command(struct at91mci_host *host)
++static void at91_mci_completed_command(struct at91mci_host *host)
+ {
+ 	struct mmc_command *cmd = host->cmd;
+ 	unsigned int status;
+@@ -564,26 +577,24 @@
+ 			AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE |
+ 			AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) {
+ 		if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) {
+-			cmd->error = MMC_ERR_NONE;
++			cmd->error = 0;
+ 		}
+ 		else {
+ 			if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE))
+-				cmd->error = MMC_ERR_TIMEOUT;
++				cmd->error = -ETIMEDOUT;
+ 			else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE))
+-				cmd->error = MMC_ERR_BADCRC;
+-			else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE))
+-				cmd->error = MMC_ERR_FIFO;
++				cmd->error = -EILSEQ;
+ 			else
+-				cmd->error = MMC_ERR_FAILED;
++				cmd->error = -EIO;
+ 
+ 			pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n",
+ 				 cmd->error, cmd->opcode, cmd->retries);
+ 		}
+ 	}
+ 	else
+-		cmd->error = MMC_ERR_NONE;
++		cmd->error = 0;
+ 
+-	at91mci_process_next(host);
++	at91_mci_process_next(host);
+ }
+ 
+ /*
+@@ -595,7 +606,7 @@
+ 	host->request = mrq;
+ 	host->flags = 0;
+ 
+-	at91mci_process_next(host);
++	at91_mci_process_next(host);
+ }
+ 
+ /*
+@@ -663,15 +674,15 @@
+ 
+ 	int_status = at91_mci_read(host, AT91_MCI_SR);
+ 	int_mask = at91_mci_read(host, AT91_MCI_IMR);
+-	
++
+ 	pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask,
+ 		int_status & int_mask);
+-	
++
+ 	int_status = int_status & int_mask;
+ 
+ 	if (int_status & AT91_MCI_ERRORS) {
+ 		completed = 1;
+-		
++
+ 		if (int_status & AT91_MCI_UNRE)
+ 			pr_debug("MMC: Underrun error\n");
+ 		if (int_status & AT91_MCI_OVRE)
+@@ -698,29 +709,33 @@
+ 			at91_mci_handle_transmitted(host);
+ 		}
+ 
++		if (int_status & AT91_MCI_ENDRX) {
++			pr_debug("ENDRX\n");
++			at91_mci_post_dma_read(host);
++		}
++
+ 		if (int_status & AT91_MCI_RXBUFF) {
+ 			pr_debug("RX buffer full\n");
+-			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
++			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
++			at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX);
++			completed = 1;
+ 		}
+ 
+ 		if (int_status & AT91_MCI_ENDTX)
+ 			pr_debug("Transmit has ended\n");
+ 
+-		if (int_status & AT91_MCI_ENDRX) {
+-			pr_debug("Receive has ended\n");
+-			at91mci_post_dma_read(host);
+-		}
+-
+ 		if (int_status & AT91_MCI_NOTBUSY) {
+ 			pr_debug("Card is ready\n");
+-			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
++			completed = 1;
+ 		}
+ 
+ 		if (int_status & AT91_MCI_DTIP)
+ 			pr_debug("Data transfer in progress\n");
+ 
+-		if (int_status & AT91_MCI_BLKE)
++		if (int_status & AT91_MCI_BLKE) {
+ 			pr_debug("Block transfer has ended\n");
++			completed = 1;
++		}
+ 
+ 		if (int_status & AT91_MCI_TXRDY)
+ 			pr_debug("Ready to transmit\n");
+@@ -730,14 +745,14 @@
+ 
+ 		if (int_status & AT91_MCI_CMDRDY) {
+ 			pr_debug("Command ready\n");
+-			completed = 1;
++			completed = at91_mci_handle_cmdrdy(host);
+ 		}
+ 	}
+ 
+ 	if (completed) {
+ 		pr_debug("Completed command\n");
+ 		at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+-		at91mci_completed_command(host);
++		at91_mci_completed_command(host);
+ 	} else
+ 		at91_mci_write(host, AT91_MCI_IDR, int_status);
+ 
+@@ -830,11 +845,11 @@
+ 	host->bus_mode = 0;
+ 	host->board = pdev->dev.platform_data;
+ 	if (host->board->wire4) {
+-#ifdef SUPPORT_4WIRE
+-		mmc->caps |= MMC_CAP_4_BIT_DATA;
+-#else
+-		printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n");
+-#endif
++		if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
++			mmc->caps |= MMC_CAP_4_BIT_DATA;
++		else
++			printk("AT91 MMC: 4 wire bus mode not supported"
++				" - using 1 wire\n");
+ 	}
+ 
+ 	/*
+@@ -886,8 +901,10 @@
+ 	/*
+ 	 * Add host to MMC layer
+ 	 */
+-	if (host->board->det_pin)
++	if (host->board->det_pin) {
+ 		host->present = !at91_get_gpio_value(host->board->det_pin);
++		device_init_wakeup(&pdev->dev, 1);
++	}
+ 	else
+ 		host->present = -1;
+ 
+@@ -923,6 +940,7 @@
+ 	host = mmc_priv(mmc);
+ 
+ 	if (host->present != -1) {
++		device_init_wakeup(&pdev->dev, 0);
+ 		free_irq(host->board->det_pin, host);
+ 		cancel_delayed_work(&host->mmc->detect);
+ 	}
+@@ -949,8 +967,12 @@
+ static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state)
+ {
+ 	struct mmc_host *mmc = platform_get_drvdata(pdev);
++	struct at91mci_host *host = mmc_priv(mmc);
+ 	int ret = 0;
+ 
++	if (device_may_wakeup(&pdev->dev))
++		enable_irq_wake(host->board->det_pin);
++
+ 	if (mmc)
+ 		ret = mmc_suspend_host(mmc, state);
+ 
+@@ -960,8 +982,12 @@
+ static int at91_mci_resume(struct platform_device *pdev)
+ {
+ 	struct mmc_host *mmc = platform_get_drvdata(pdev);
++	struct at91mci_host *host = mmc_priv(mmc);
+ 	int ret = 0;
+ 
++	if (device_may_wakeup(&pdev->dev))
++		disable_irq_wake(host->board->det_pin);
++
+ 	if (mmc)
+ 		ret = mmc_resume_host(mmc);
+ 
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/au1xmmc.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/au1xmmc.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/au1xmmc.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * linux/drivers/mmc/au1xmmc.c - AU1XX0 MMC driver
++ * linux/drivers/mmc/host/au1xmmc.c - AU1XX0 MMC driver
+  *
+  *  Copyright (c) 2005, Advanced Micro Devices, Inc.
+  *
+@@ -208,7 +208,7 @@
+ 	default:
+ 		printk(KERN_INFO "au1xmmc: unhandled response type %02x\n",
+ 			mmc_resp_type(cmd));
+-		return MMC_ERR_INVALID;
++		return -EINVAL;
+ 	}
+ 
+ 	if (flags & MMC_DATA_READ) {
+@@ -253,7 +253,7 @@
+ 		IRQ_ON(host, SD_CONFIG_CR);
+ 	}
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ }
+ 
+ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
+@@ -278,7 +278,7 @@
+ 	while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB))
+ 		status = au_readl(HOST_STATUS(host));
+ 
+-	data->error = MMC_ERR_NONE;
++	data->error = 0;
+ 	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir);
+ 
+         /* Process any errors */
+@@ -288,14 +288,14 @@
+ 		crc |= ((status & 0x07) == 0x02) ? 0 : 1;
+ 
+ 	if (crc)
+-		data->error = MMC_ERR_BADCRC;
++		data->error = -EILSEQ;
+ 
+ 	/* Clear the CRC bits */
+ 	au_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host));
+ 
+ 	data->bytes_xfered = 0;
+ 
+-	if (data->error == MMC_ERR_NONE) {
++	if (!data->error) {
+ 		if (host->flags & HOST_F_DMA) {
+ 			u32 chan = DMA_CHANNEL(host);
+ 
+@@ -475,7 +475,7 @@
+ 		return;
+ 
+ 	cmd = mrq->cmd;
+-	cmd->error = MMC_ERR_NONE;
++	cmd->error = 0;
+ 
+ 	if (cmd->flags & MMC_RSP_PRESENT) {
+ 		if (cmd->flags & MMC_RSP_136) {
+@@ -512,11 +512,11 @@
+         /* Figure out errors */
+ 
+ 	if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC))
+-		cmd->error = MMC_ERR_BADCRC;
++		cmd->error = -EILSEQ;
+ 
+ 	trans = host->flags & (HOST_F_XMIT | HOST_F_RECV);
+ 
+-	if (!trans || cmd->error != MMC_ERR_NONE) {
++	if (!trans || cmd->error) {
+ 
+ 		IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF);
+ 		tasklet_schedule(&host->finish_task);
+@@ -589,7 +589,7 @@
+ 				   data->sg_len, host->dma.dir);
+ 
+ 	if (host->dma.len == 0)
+-		return MMC_ERR_TIMEOUT;
++		return -ETIMEDOUT;
+ 
+ 	au_writel(data->blksz - 1, HOST_BLKSIZE(host));
+ 
+@@ -640,11 +640,11 @@
+ 			//IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF);
+ 	}
+ 
+-	return MMC_ERR_NONE;
++	return 0;
+ 
+  dataerr:
+ 	dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir);
+-	return MMC_ERR_TIMEOUT;
++	return -ETIMEDOUT;
+ }
+ 
+ /* static void au1xmmc_request
+@@ -656,7 +656,7 @@
+ 
+ 	struct au1xmmc_host *host = mmc_priv(mmc);
+ 	unsigned int flags = 0;
+-	int ret = MMC_ERR_NONE;
++	int ret = 0;
+ 
+ 	WARN_ON(irqs_disabled());
+ 	WARN_ON(host->status != HOST_S_IDLE);
+@@ -672,10 +672,10 @@
+ 		ret = au1xmmc_prepare_data(host, mrq->data);
+ 	}
+ 
+-	if (ret == MMC_ERR_NONE)
++	if (!ret)
+ 		ret = au1xmmc_send_command(host, 0, mrq->cmd, flags);
+ 
+-	if (ret != MMC_ERR_NONE) {
++	if (ret) {
+ 		mrq->cmd->error = ret;
+ 		au1xmmc_finish_request(host);
+ 	}
+@@ -764,10 +764,10 @@
+ 
+ 		if (host->mrq && (status & STATUS_TIMEOUT)) {
+ 			if (status & SD_STATUS_RAT)
+-				host->mrq->cmd->error = MMC_ERR_TIMEOUT;
++				host->mrq->cmd->error = -ETIMEDOUT;
+ 
+ 			else if (status & SD_STATUS_DT)
+-				host->mrq->data->error = MMC_ERR_TIMEOUT;
++				host->mrq->data->error = -ETIMEDOUT;
+ 
+ 			/* In PIO mode, interrupts might still be enabled */
+ 			IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/imxmmc.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/imxmmc.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/imxmmc.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/imxmmc.c - Motorola i.MX MMCI driver
++ *  linux/drivers/mmc/host/imxmmc.c - Motorola i.MX MMCI driver
+  *
+  *  Copyright (C) 2004 Sascha Hauer, Pengutronix <sascha at saschahauer.de>
+  *  Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa at pikron.com>
+@@ -428,11 +428,11 @@
+ 	if ( stat & STATUS_ERR_MASK ) {
+ 		dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat);
+ 		if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
+-			data->error = MMC_ERR_BADCRC;
++			data->error = -EILSEQ;
+ 		else if(stat & STATUS_TIME_OUT_READ)
+-			data->error = MMC_ERR_TIMEOUT;
++			data->error = -ETIMEDOUT;
+ 		else
+-			data->error = MMC_ERR_FAILED;
++			data->error = -EIO;
+ 	} else {
+ 		data->bytes_xfered = host->dma_size;
+ 	}
+@@ -458,10 +458,10 @@
+ 
+ 	if (stat & STATUS_TIME_OUT_RESP) {
+ 		dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n");
+-		cmd->error = MMC_ERR_TIMEOUT;
++		cmd->error = -ETIMEDOUT;
+ 	} else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+ 		dev_dbg(mmc_dev(host->mmc), "cmd crc error\n");
+-		cmd->error = MMC_ERR_BADCRC;
++		cmd->error = -EILSEQ;
+ 	}
+ 
+ 	if(cmd->flags & MMC_RSP_PRESENT) {
+@@ -482,7 +482,7 @@
+ 	dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n",
+ 		cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error);
+ 
+-	if (data && (cmd->error == MMC_ERR_NONE) && !(stat & STATUS_ERR_MASK)) {
++	if (data && !cmd->error && !(stat & STATUS_ERR_MASK)) {
+ 		if (host->req->data->flags & MMC_DATA_WRITE) {
+ 
+ 			/* Wait for FIFO to be empty before starting DMA write */
+@@ -491,7 +491,7 @@
+ 			if(imxmci_busy_wait_for_status(host, &stat,
+ 				STATUS_APPL_BUFF_FE,
+ 				40, "imxmci_cmd_done DMA WR") < 0) {
+-				cmd->error = MMC_ERR_FIFO;
++				cmd->error = -EIO;
+ 				imxmci_finish_data(host, stat);
+ 				if(host->req)
+ 					imxmci_finish_request(host, host->req);
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/mmci.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/mmci.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/mmci.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/mmci.c - ARM PrimeCell MMCI PL180/1 driver
++ *  linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver
+  *
+  *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+  *
+@@ -154,11 +154,11 @@
+ 	}
+ 	if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
+ 		if (status & MCI_DATACRCFAIL)
+-			data->error = MMC_ERR_BADCRC;
++			data->error = -EILSEQ;
+ 		else if (status & MCI_DATATIMEOUT)
+-			data->error = MMC_ERR_TIMEOUT;
++			data->error = -ETIMEDOUT;
+ 		else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
+-			data->error = MMC_ERR_FIFO;
++			data->error = -EIO;
+ 		status |= MCI_DATAEND;
+ 
+ 		/*
+@@ -193,12 +193,12 @@
+ 	cmd->resp[3] = readl(base + MMCIRESPONSE3);
+ 
+ 	if (status & MCI_CMDTIMEOUT) {
+-		cmd->error = MMC_ERR_TIMEOUT;
++		cmd->error = -ETIMEDOUT;
+ 	} else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
+-		cmd->error = MMC_ERR_BADCRC;
++		cmd->error = -EILSEQ;
+ 	}
+ 
+-	if (!cmd->data || cmd->error != MMC_ERR_NONE) {
++	if (!cmd->data || cmd->error) {
+ 		if (host->data)
+ 			mmci_stop_data(host);
+ 		mmci_request_end(host, cmd->mrq);
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/mmci.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/mmci.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/mmci.h	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/mmci.h - ARM PrimeCell MMCI PL180/1 driver
++ *  linux/drivers/mmc/host/mmci.h - ARM PrimeCell MMCI PL180/1 driver
+  *
+  *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+  *
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/omap.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/omap.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/omap.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/media/mmc/omap.c
++ *  linux/drivers/mmc/host/omap.c
+  *
+  *  Copyright (C) 2004 Nokia Corporation
+  *  Written by Tuukka Tikkanen and Juha Yrjölä<juha.yrjola at nokia.com>
+@@ -263,7 +263,7 @@
+ 		enum dma_data_direction dma_data_dir;
+ 
+ 		BUG_ON(host->dma_ch < 0);
+-		if (data->error != MMC_ERR_NONE)
++		if (data->error)
+ 			omap_stop_dma(host->dma_ch);
+ 		/* Release DMA channel lazily */
+ 		mod_timer(&host->dma_timer, jiffies + HZ);
+@@ -368,7 +368,7 @@
+ 		}
+ 	}
+ 
+-	if (host->data == NULL || cmd->error != MMC_ERR_NONE) {
++	if (host->data == NULL || cmd->error) {
+ 		host->mrq = NULL;
+ 		clk_disable(host->fclk);
+ 		mmc_request_done(host->mmc, cmd->mrq);
+@@ -475,14 +475,14 @@
+ 		if (status & OMAP_MMC_STAT_DATA_TOUT) {
+ 			dev_dbg(mmc_dev(host->mmc), "data timeout\n");
+ 			if (host->data) {
+-				host->data->error |= MMC_ERR_TIMEOUT;
++				host->data->error = -ETIMEDOUT;
+ 				transfer_error = 1;
+ 			}
+ 		}
+ 
+ 		if (status & OMAP_MMC_STAT_DATA_CRC) {
+ 			if (host->data) {
+-				host->data->error |= MMC_ERR_BADCRC;
++				host->data->error = -EILSEQ;
+ 				dev_dbg(mmc_dev(host->mmc),
+ 					 "data CRC error, bytes left %d\n",
+ 					host->total_bytes_left);
+@@ -504,7 +504,7 @@
+ 					dev_err(mmc_dev(host->mmc),
+ 						"command timeout, CMD %d\n",
+ 						host->cmd->opcode);
+-				host->cmd->error = MMC_ERR_TIMEOUT;
++				host->cmd->error = -ETIMEDOUT;
+ 				end_command = 1;
+ 			}
+ 		}
+@@ -514,7 +514,7 @@
+ 				dev_err(mmc_dev(host->mmc),
+ 					"command CRC error (CMD%d, arg 0x%08x)\n",
+ 					host->cmd->opcode, host->cmd->arg);
+-				host->cmd->error = MMC_ERR_BADCRC;
++				host->cmd->error = -EILSEQ;
+ 				end_command = 1;
+ 			} else
+ 				dev_err(mmc_dev(host->mmc),
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/pxamci.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/pxamci.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/pxamci.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/pxa.c - PXA MMCI driver
++ *  linux/drivers/mmc/host/pxa.c - PXA MMCI driver
+  *
+  *  Copyright (C) 2003 Russell King, All Rights Reserved.
+  *
+@@ -226,7 +226,7 @@
+ 	}
+ 
+ 	if (stat & STAT_TIME_OUT_RESPONSE) {
+-		cmd->error = MMC_ERR_TIMEOUT;
++		cmd->error = -ETIMEDOUT;
+ 	} else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+ #ifdef CONFIG_PXA27x
+ 		/*
+@@ -239,11 +239,11 @@
+ 			pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode);
+ 		} else
+ #endif
+-		cmd->error = MMC_ERR_BADCRC;
++		cmd->error = -EILSEQ;
+ 	}
+ 
+ 	pxamci_disable_irq(host, END_CMD_RES);
+-	if (host->data && cmd->error == MMC_ERR_NONE) {
++	if (host->data && !cmd->error) {
+ 		pxamci_enable_irq(host, DATA_TRAN_DONE);
+ 	} else {
+ 		pxamci_finish_request(host, host->mrq);
+@@ -264,9 +264,9 @@
+ 		     host->dma_dir);
+ 
+ 	if (stat & STAT_READ_TIME_OUT)
+-		data->error = MMC_ERR_TIMEOUT;
++		data->error = -ETIMEDOUT;
+ 	else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))
+-		data->error = MMC_ERR_BADCRC;
++		data->error = -EILSEQ;
+ 
+ 	/*
+ 	 * There appears to be a hardware design bug here.  There seems to
+@@ -274,7 +274,7 @@
+ 	 * This means that if there was an error on any block, we mark all
+ 	 * data blocks as being in error.
+ 	 */
+-	if (data->error == MMC_ERR_NONE)
++	if (!data->error)
+ 		data->bytes_xfered = data->blocks * data->blksz;
+ 	else
+ 		data->bytes_xfered = 0;
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/pxamci.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/pxamci.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/pxamci.h	2007-11-13 11:35:23.000000000 +0100
+@@ -1,25 +1,3 @@
+-#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)
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/sdhci.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/sdhci.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/sdhci.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver
++ *  linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver
+  *
+  *  Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
+  *
+@@ -34,6 +34,7 @@
+ /* Controller doesn't like some resets when there is no card inserted. */
+ #define SDHCI_QUIRK_NO_CARD_NO_RESET			(1<<2)
+ #define SDHCI_QUIRK_SINGLE_POWER_WRITE			(1<<3)
++#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS		(1<<4)
+ 
+ static const struct pci_device_id pci_ids[] __devinitdata = {
+ 	{
+@@ -70,6 +71,32 @@
+ 		.driver_data	= SDHCI_QUIRK_SINGLE_POWER_WRITE,
+ 	},
+ 
++	{
++		.vendor		= PCI_VENDOR_ID_ENE,
++		.device		= PCI_DEVICE_ID_ENE_CB712_SD_2,
++		.subvendor	= PCI_ANY_ID,
++		.subdevice	= PCI_ANY_ID,
++		.driver_data	= SDHCI_QUIRK_SINGLE_POWER_WRITE,
++	},
++
++	{
++		.vendor         = PCI_VENDOR_ID_ENE,
++		.device         = PCI_DEVICE_ID_ENE_CB714_SD,
++		.subvendor      = PCI_ANY_ID,
++		.subdevice      = PCI_ANY_ID,
++		.driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
++				  SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS,
++	},
++
++	{
++		.vendor         = PCI_VENDOR_ID_ENE,
++		.device         = PCI_DEVICE_ID_ENE_CB714_SD_2,
++		.subvendor      = PCI_ANY_ID,
++		.subdevice      = PCI_ANY_ID,
++		.driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
++				  SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS,
++	},
++
+ 	{	/* Generic SD host controller */
+ 		PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
+ 	},
+@@ -353,16 +380,14 @@
+ 	if (data == NULL)
+ 		return;
+ 
+-	DBG("blksz %04x blks %04x flags %08x\n",
+-		data->blksz, data->blocks, data->flags);
+-	DBG("tsac %d ms nsac %d clk\n",
+-		data->timeout_ns / 1000000, data->timeout_clks);
+-
+ 	/* Sanity checks */
+ 	BUG_ON(data->blksz * data->blocks > 524288);
+ 	BUG_ON(data->blksz > host->mmc->max_blk_size);
+ 	BUG_ON(data->blocks > 65535);
+ 
++	host->data = data;
++	host->data_early = 0;
++
+ 	/* timeout in us */
+ 	target_timeout = data->timeout_ns / 1000 +
+ 		data->timeout_clks / host->clock;
+@@ -421,11 +446,11 @@
+ {
+ 	u16 mode;
+ 
+-	WARN_ON(host->data);
+-
+ 	if (data == NULL)
+ 		return;
+ 
++	WARN_ON(!host->data);
++
+ 	mode = SDHCI_TRNS_BLK_CNT_EN;
+ 	if (data->blocks > 1)
+ 		mode |= SDHCI_TRNS_MULTI;
+@@ -455,27 +480,25 @@
+ 	/*
+ 	 * Controller doesn't count down when in single block mode.
+ 	 */
+-	if ((data->blocks == 1) && (data->error == MMC_ERR_NONE))
++	if ((data->blocks == 1) && !data->error)
+ 		blocks = 0;
+ 	else
+ 		blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT);
+ 	data->bytes_xfered = data->blksz * (data->blocks - blocks);
+ 
+-	if ((data->error == MMC_ERR_NONE) && blocks) {
++	if (!data->error && blocks) {
+ 		printk(KERN_ERR "%s: Controller signalled completion even "
+ 			"though there were blocks left.\n",
+ 			mmc_hostname(host->mmc));
+-		data->error = MMC_ERR_FAILED;
++		data->error = -EIO;
+ 	}
+ 
+-	DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered);
+-
+ 	if (data->stop) {
+ 		/*
+ 		 * The controller needs a reset of internal state machines
+ 		 * upon error conditions.
+ 		 */
+-		if (data->error != MMC_ERR_NONE) {
++		if (data->error) {
+ 			sdhci_reset(host, SDHCI_RESET_CMD);
+ 			sdhci_reset(host, SDHCI_RESET_DATA);
+ 		}
+@@ -493,8 +516,6 @@
+ 
+ 	WARN_ON(host->cmd);
+ 
+-	DBG("Sending cmd (%x)\n", cmd->opcode);
+-
+ 	/* Wait max 10 ms */
+ 	timeout = 10;
+ 
+@@ -512,7 +533,7 @@
+ 			printk(KERN_ERR "%s: Controller never released "
+ 				"inhibit bit(s).\n", mmc_hostname(host->mmc));
+ 			sdhci_dumpregs(host);
+-			cmd->error = MMC_ERR_FAILED;
++			cmd->error = -EIO;
+ 			tasklet_schedule(&host->finish_tasklet);
+ 			return;
+ 		}
+@@ -533,7 +554,7 @@
+ 	if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
+ 		printk(KERN_ERR "%s: Unsupported response type!\n",
+ 			mmc_hostname(host->mmc));
+-		cmd->error = MMC_ERR_INVALID;
++		cmd->error = -EINVAL;
+ 		tasklet_schedule(&host->finish_tasklet);
+ 		return;
+ 	}
+@@ -580,13 +601,12 @@
+ 		}
+ 	}
+ 
+-	host->cmd->error = MMC_ERR_NONE;
++	host->cmd->error = 0;
+ 
+-	DBG("Ending cmd (%x)\n", host->cmd->opcode);
++	if (host->data && host->data_early)
++		sdhci_finish_data(host);
+ 
+-	if (host->cmd->data)
+-		host->data = host->cmd->data;
+-	else
++	if (!host->cmd->data)
+ 		tasklet_schedule(&host->finish_tasklet);
+ 
+ 	host->cmd = NULL;
+@@ -702,7 +722,7 @@
+ 	host->mrq = mrq;
+ 
+ 	if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+-		host->mrq->cmd->error = MMC_ERR_TIMEOUT;
++		host->mrq->cmd->error = -ENOMEDIUM;
+ 		tasklet_schedule(&host->finish_tasklet);
+ 	} else
+ 		sdhci_send_command(host, mrq->cmd);
+@@ -751,6 +771,14 @@
+ 
+ 	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+ 
++	/*
++	 * Some (ENE) controllers go apeshit on some ios operation,
++	 * signalling timeout and CRC errors even on CMD0. Resetting
++	 * it on each ios seems to solve the problem.
++	 */
++	if(host->chip->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
++		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
++
+ 	mmiowb();
+ 	spin_unlock_irqrestore(&host->lock, flags);
+ }
+@@ -772,10 +800,35 @@
+ 	return !(present & SDHCI_WRITE_PROTECT);
+ }
+ 
++static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
++{
++	struct sdhci_host *host;
++	unsigned long flags;
++	u32 ier;
++
++	host = mmc_priv(mmc);
++
++	spin_lock_irqsave(&host->lock, flags);
++
++	ier = readl(host->ioaddr + SDHCI_INT_ENABLE);
++
++	ier &= ~SDHCI_INT_CARD_INT;
++	if (enable)
++		ier |= SDHCI_INT_CARD_INT;
++
++	writel(ier, host->ioaddr + SDHCI_INT_ENABLE);
++	writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
++
++	mmiowb();
++
++	spin_unlock_irqrestore(&host->lock, flags);
++}
++
+ static const struct mmc_host_ops sdhci_ops = {
+ 	.request	= sdhci_request,
+ 	.set_ios	= sdhci_set_ios,
+ 	.get_ro		= sdhci_get_ro,
++	.enable_sdio_irq = sdhci_enable_sdio_irq,
+ };
+ 
+ /*****************************************************************************\
+@@ -803,7 +856,7 @@
+ 			sdhci_reset(host, SDHCI_RESET_CMD);
+ 			sdhci_reset(host, SDHCI_RESET_DATA);
+ 
+-			host->mrq->cmd->error = MMC_ERR_FAILED;
++			host->mrq->cmd->error = -ENOMEDIUM;
+ 			tasklet_schedule(&host->finish_tasklet);
+ 		}
+ 	}
+@@ -827,15 +880,13 @@
+ 
+ 	mrq = host->mrq;
+ 
+-	DBG("Ending request, cmd (%x)\n", mrq->cmd->opcode);
+-
+ 	/*
+ 	 * The controller needs a reset of internal state machines
+ 	 * upon error conditions.
+ 	 */
+-	if ((mrq->cmd->error != MMC_ERR_NONE) ||
+-		(mrq->data && ((mrq->data->error != MMC_ERR_NONE) ||
+-		(mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) {
++	if (mrq->cmd->error ||
++		(mrq->data && (mrq->data->error ||
++		(mrq->data->stop && mrq->data->stop->error)))) {
+ 
+ 		/* Some controllers need this kick or reset won't work here */
+ 		if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
+@@ -880,13 +931,13 @@
+ 		sdhci_dumpregs(host);
+ 
+ 		if (host->data) {
+-			host->data->error = MMC_ERR_TIMEOUT;
++			host->data->error = -ETIMEDOUT;
+ 			sdhci_finish_data(host);
+ 		} else {
+ 			if (host->cmd)
+-				host->cmd->error = MMC_ERR_TIMEOUT;
++				host->cmd->error = -ETIMEDOUT;
+ 			else
+-				host->mrq->cmd->error = MMC_ERR_TIMEOUT;
++				host->mrq->cmd->error = -ETIMEDOUT;
+ 
+ 			tasklet_schedule(&host->finish_tasklet);
+ 		}
+@@ -907,27 +958,23 @@
+ 	BUG_ON(intmask == 0);
+ 
+ 	if (!host->cmd) {
+-		printk(KERN_ERR "%s: Got command interrupt even though no "
+-			"command operation was in progress.\n",
+-			mmc_hostname(host->mmc));
++		printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
++			"though no command operation was in progress.\n",
++			mmc_hostname(host->mmc), (unsigned)intmask);
+ 		sdhci_dumpregs(host);
+ 		return;
+ 	}
+ 
+-	if (intmask & SDHCI_INT_RESPONSE)
+-		sdhci_finish_command(host);
+-	else {
+-		if (intmask & SDHCI_INT_TIMEOUT)
+-			host->cmd->error = MMC_ERR_TIMEOUT;
+-		else if (intmask & SDHCI_INT_CRC)
+-			host->cmd->error = MMC_ERR_BADCRC;
+-		else if (intmask & (SDHCI_INT_END_BIT | SDHCI_INT_INDEX))
+-			host->cmd->error = MMC_ERR_FAILED;
+-		else
+-			host->cmd->error = MMC_ERR_INVALID;
++	if (intmask & SDHCI_INT_TIMEOUT)
++		host->cmd->error = -ETIMEDOUT;
++	else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
++			SDHCI_INT_INDEX))
++		host->cmd->error = -EILSEQ;
+ 
++	if (host->cmd->error)
+ 		tasklet_schedule(&host->finish_tasklet);
+-	}
++	else if (intmask & SDHCI_INT_RESPONSE)
++		sdhci_finish_command(host);
+ }
+ 
+ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
+@@ -942,22 +989,20 @@
+ 		if (intmask & SDHCI_INT_DATA_END)
+ 			return;
+ 
+-		printk(KERN_ERR "%s: Got data interrupt even though no "
+-			"data operation was in progress.\n",
+-			mmc_hostname(host->mmc));
++		printk(KERN_ERR "%s: Got data interrupt 0x%08x even "
++			"though no data operation was in progress.\n",
++			mmc_hostname(host->mmc), (unsigned)intmask);
+ 		sdhci_dumpregs(host);
+ 
+ 		return;
+ 	}
+ 
+ 	if (intmask & SDHCI_INT_DATA_TIMEOUT)
+-		host->data->error = MMC_ERR_TIMEOUT;
+-	else if (intmask & SDHCI_INT_DATA_CRC)
+-		host->data->error = MMC_ERR_BADCRC;
+-	else if (intmask & SDHCI_INT_DATA_END_BIT)
+-		host->data->error = MMC_ERR_FAILED;
++		host->data->error = -ETIMEDOUT;
++	else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT))
++		host->data->error = -EILSEQ;
+ 
+-	if (host->data->error != MMC_ERR_NONE)
++	if (host->data->error)
+ 		sdhci_finish_data(host);
+ 	else {
+ 		if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
+@@ -972,8 +1017,18 @@
+ 			writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS),
+ 				host->ioaddr + SDHCI_DMA_ADDRESS);
+ 
+-		if (intmask & SDHCI_INT_DATA_END)
+-			sdhci_finish_data(host);
++		if (intmask & SDHCI_INT_DATA_END) {
++			if (host->cmd) {
++				/*
++				 * Data managed to finish before the
++				 * command completed. Make sure we do
++				 * things in the proper order.
++				 */
++				host->data_early = 1;
++			} else {
++				sdhci_finish_data(host);
++			}
++		}
+ 	}
+ }
+ 
+@@ -982,6 +1037,7 @@
+ 	irqreturn_t result;
+ 	struct sdhci_host* host = dev_id;
+ 	u32 intmask;
++	int cardint = 0;
+ 
+ 	spin_lock(&host->lock);
+ 
+@@ -1016,13 +1072,20 @@
+ 
+ 	intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
+ 
++	intmask &= ~SDHCI_INT_ERROR;
++
+ 	if (intmask & SDHCI_INT_BUS_POWER) {
+ 		printk(KERN_ERR "%s: Card is consuming too much power!\n",
+ 			mmc_hostname(host->mmc));
+ 		writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);
+ 	}
+ 
+-	intmask &= SDHCI_INT_BUS_POWER;
++	intmask &= ~SDHCI_INT_BUS_POWER;
++
++	if (intmask & SDHCI_INT_CARD_INT)
++		cardint = 1;
++
++	intmask &= ~SDHCI_INT_CARD_INT;
+ 
+ 	if (intmask) {
+ 		printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
+@@ -1038,6 +1101,12 @@
+ out:
+ 	spin_unlock(&host->lock);
+ 
++	/*
++	 * We have to delay this as it calls back into the driver.
++	 */
++	if (cardint)
++		mmc_signal_sdio_irq(host->mmc);
++
+ 	return result;
+ }
+ 
+@@ -1277,7 +1346,8 @@
+ 	mmc->ops = &sdhci_ops;
+ 	mmc->f_min = host->max_clk / 256;
+ 	mmc->f_max = host->max_clk;
+-	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
++	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE |
++		MMC_CAP_BYTEBLOCK | MMC_CAP_SDIO_IRQ;
+ 
+ 	if (caps & SDHCI_CAN_DO_HISPD)
+ 		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+@@ -1326,12 +1396,11 @@
+ 	 */
+ 	mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;
+ 	if (mmc->max_blk_size >= 3) {
+-		printk(KERN_ERR "%s: Invalid maximum block size.\n",
++		printk(KERN_WARNING "%s: Invalid maximum block size, assuming 512\n",
+ 			host->slot_descr);
+-		ret = -ENODEV;
+-		goto unmap;
+-	}
+-	mmc->max_blk_size = 512 << mmc->max_blk_size;
++		mmc->max_blk_size = 512;
++	} else
++		mmc->max_blk_size = 512 << mmc->max_blk_size;
+ 
+ 	/*
+ 	 * Maximum block count.
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/sdhci.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/sdhci.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/sdhci.h	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/sdhci.h - Secure Digital Host Controller Interface driver
++ *  linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver
+  *
+  *  Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
+  *
+@@ -107,6 +107,7 @@
+ #define  SDHCI_INT_CARD_INSERT	0x00000040
+ #define  SDHCI_INT_CARD_REMOVE	0x00000080
+ #define  SDHCI_INT_CARD_INT	0x00000100
++#define  SDHCI_INT_ERROR	0x00008000
+ #define  SDHCI_INT_TIMEOUT	0x00010000
+ #define  SDHCI_INT_CRC		0x00020000
+ #define  SDHCI_INT_END_BIT	0x00040000
+@@ -181,6 +182,7 @@
+ 	struct mmc_request	*mrq;		/* Current request */
+ 	struct mmc_command	*cmd;		/* Current command */
+ 	struct mmc_data		*data;		/* Current data request */
++	int			data_early:1;	/* Data finished before cmd */
+ 
+ 	struct scatterlist	*cur_sg;	/* We're working on this */
+ 	int			num_sg;		/* Entries left */
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/tifm_sd.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/tifm_sd.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/tifm_sd.c	2007-11-13 11:35:23.000000000 +0100
+@@ -404,14 +404,14 @@
+ 	struct tifm_dev *sock = host->dev;
+ 	struct mmc_command *cmd = host->req->cmd;
+ 
+-	if (cmd->error != MMC_ERR_NONE)
++	if (cmd->error)
+ 		goto finish_request;
+ 
+ 	if (!(host->cmd_flags & CMD_READY))
+ 		return;
+ 
+ 	if (cmd->data) {
+-		if (cmd->data->error != MMC_ERR_NONE) {
++		if (cmd->data->error) {
+ 			if ((host->cmd_flags & SCMD_ACTIVE)
+ 			    && !(host->cmd_flags & SCMD_READY))
+ 				return;
+@@ -504,7 +504,7 @@
+ {
+ 	struct tifm_sd *host;
+ 	unsigned int host_status = 0;
+-	int cmd_error = MMC_ERR_NONE;
++	int cmd_error = 0;
+ 	struct mmc_command *cmd = NULL;
+ 	unsigned long flags;
+ 
+@@ -521,15 +521,15 @@
+ 			writel(host_status & TIFM_MMCSD_ERRMASK,
+ 			       sock->addr + SOCK_MMCSD_STATUS);
+ 			if (host_status & TIFM_MMCSD_CTO)
+-				cmd_error = MMC_ERR_TIMEOUT;
++				cmd_error = -ETIMEDOUT;
+ 			else if (host_status & TIFM_MMCSD_CCRC)
+-				cmd_error = MMC_ERR_BADCRC;
++				cmd_error = -EILSEQ;
+ 
+ 			if (cmd->data) {
+ 				if (host_status & TIFM_MMCSD_DTO)
+-					cmd->data->error = MMC_ERR_TIMEOUT;
++					cmd->data->error = -ETIMEDOUT;
+ 				else if (host_status & TIFM_MMCSD_DCRC)
+-					cmd->data->error = MMC_ERR_BADCRC;
++					cmd->data->error = -EILSEQ;
+ 			}
+ 
+ 			writel(TIFM_FIFO_INT_SETALL,
+@@ -722,7 +722,7 @@
+ 	return;
+ 
+ err_out:
+-	mrq->cmd->error = MMC_ERR_TIMEOUT;
++	mrq->cmd->error = -ETIMEDOUT;
+ 	mmc_request_done(mmc, mrq);
+ }
+ 
+@@ -1012,9 +1012,9 @@
+ 		writel(TIFM_FIFO_INT_SETALL,
+ 		       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+ 		writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+-		host->req->cmd->error = MMC_ERR_TIMEOUT;
++		host->req->cmd->error = -ENOMEDIUM;
+ 		if (host->req->stop)
+-			host->req->stop->error = MMC_ERR_TIMEOUT;
++			host->req->stop->error = -ENOMEDIUM;
+ 		tasklet_schedule(&host->finish_tasklet);
+ 	}
+ 	spin_unlock_irqrestore(&sock->lock, flags);
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/wbsd.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/wbsd.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/wbsd.c	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
++ *  linux/drivers/mmc/host/wbsd.c - Winbond W83L51xD SD/MMC driver
+  *
+  *  Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved.
+  *
+@@ -207,8 +207,6 @@
+ {
+ 	unsigned long dmaflags;
+ 
+-	DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode);
+-
+ 	if (host->dma >= 0) {
+ 		/*
+ 		 * Release ISA DMA controller.
+@@ -319,7 +317,7 @@
+ 	 * Correct response type?
+ 	 */
+ 	if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) {
+-		cmd->error = MMC_ERR_INVALID;
++		cmd->error = -EILSEQ;
+ 		return;
+ 	}
+ 
+@@ -339,7 +337,7 @@
+ 	 * Correct response type?
+ 	 */
+ 	if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) {
+-		cmd->error = MMC_ERR_INVALID;
++		cmd->error = -EILSEQ;
+ 		return;
+ 	}
+ 
+@@ -360,8 +358,6 @@
+ 	int i;
+ 	u8 status, isr;
+ 
+-	DBGF("Sending cmd (%x)\n", cmd->opcode);
+-
+ 	/*
+ 	 * Clear accumulated ISR. The interrupt routine
+ 	 * will fill this one with events that occur during
+@@ -376,7 +372,7 @@
+ 	for (i = 3; i >= 0; i--)
+ 		outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR);
+ 
+-	cmd->error = MMC_ERR_NONE;
++	cmd->error = 0;
+ 
+ 	/*
+ 	 * Wait for the request to complete.
+@@ -396,13 +392,13 @@
+ 
+ 		/* Card removed? */
+ 		if (isr & WBSD_INT_CARD)
+-			cmd->error = MMC_ERR_TIMEOUT;
++			cmd->error = -ENOMEDIUM;
+ 		/* Timeout? */
+ 		else if (isr & WBSD_INT_TIMEOUT)
+-			cmd->error = MMC_ERR_TIMEOUT;
++			cmd->error = -ETIMEDOUT;
+ 		/* CRC? */
+ 		else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC))
+-			cmd->error = MMC_ERR_BADCRC;
++			cmd->error = -EILSEQ;
+ 		/* All ok */
+ 		else {
+ 			if (cmd->flags & MMC_RSP_136)
+@@ -411,8 +407,6 @@
+ 				wbsd_get_short_reply(host, cmd);
+ 		}
+ 	}
+-
+-	DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error);
+ }
+ 
+ /*
+@@ -550,11 +544,6 @@
+ 	unsigned long dmaflags;
+ 	unsigned int size;
+ 
+-	DBGF("blksz %04x blks %04x flags %08x\n",
+-		data->blksz, data->blocks, data->flags);
+-	DBGF("tsac %d ms nsac %d clk\n",
+-		data->timeout_ns / 1000000, data->timeout_clks);
+-
+ 	/*
+ 	 * Calculate size.
+ 	 */
+@@ -596,7 +585,7 @@
+ 			((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH);
+ 		wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+ 	} else {
+-		data->error = MMC_ERR_INVALID;
++		data->error = -EINVAL;
+ 		return;
+ 	}
+ 
+@@ -618,7 +607,7 @@
+ 		 */
+ 		BUG_ON(size > 0x10000);
+ 		if (size > 0x10000) {
+-			data->error = MMC_ERR_INVALID;
++			data->error = -EINVAL;
+ 			return;
+ 		}
+ 
+@@ -680,7 +669,7 @@
+ 		}
+ 	}
+ 
+-	data->error = MMC_ERR_NONE;
++	data->error = 0;
+ }
+ 
+ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
+@@ -735,8 +724,8 @@
+ 				"%d bytes left.\n",
+ 				mmc_hostname(host->mmc), count);
+ 
+-			if (data->error == MMC_ERR_NONE)
+-				data->error = MMC_ERR_FAILED;
++			if (!data->error)
++				data->error = -EIO;
+ 		} else {
+ 			/*
+ 			 * Transfer data from DMA buffer to
+@@ -746,14 +735,12 @@
+ 				wbsd_dma_to_sg(host, data);
+ 		}
+ 
+-		if (data->error != MMC_ERR_NONE) {
++		if (data->error) {
+ 			if (data->bytes_xfered)
+ 				data->bytes_xfered -= data->blksz;
+ 		}
+ 	}
+ 
+-	DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered);
+-
+ 	wbsd_request_end(host, host->mrq);
+ }
+ 
+@@ -780,11 +767,10 @@
+ 	host->mrq = mrq;
+ 
+ 	/*
+-	 * If there is no card in the slot then
+-	 * timeout immediatly.
++	 * Check that there is actually a card in the slot.
+ 	 */
+ 	if (!(host->flags & WBSD_FCARD_PRESENT)) {
+-		cmd->error = MMC_ERR_TIMEOUT;
++		cmd->error = -ENOMEDIUM;
+ 		goto done;
+ 	}
+ 
+@@ -820,7 +806,7 @@
+ 				"supported by this controller.\n",
+ 				mmc_hostname(host->mmc), cmd->opcode);
+ #endif
+-			cmd->error = MMC_ERR_INVALID;
++			cmd->error = -EINVAL;
+ 
+ 			goto done;
+ 		};
+@@ -832,7 +818,7 @@
+ 	if (cmd->data) {
+ 		wbsd_prepare_data(host, cmd->data);
+ 
+-		if (cmd->data->error != MMC_ERR_NONE)
++		if (cmd->data->error)
+ 			goto done;
+ 	}
+ 
+@@ -843,7 +829,7 @@
+ 	 * will be finished after the data has
+ 	 * transfered.
+ 	 */
+-	if (cmd->data && (cmd->error == MMC_ERR_NONE)) {
++	if (cmd->data && !cmd->error) {
+ 		/*
+ 		 * Dirty fix for hardware bug.
+ 		 */
+@@ -1046,7 +1032,7 @@
+ 				mmc_hostname(host->mmc));
+ 			wbsd_reset(host);
+ 
+-			host->mrq->cmd->error = MMC_ERR_FAILED;
++			host->mrq->cmd->error = -ENOMEDIUM;
+ 			tasklet_schedule(&host->finish_tasklet);
+ 		}
+ 
+@@ -1110,7 +1096,7 @@
+ 
+ 	DBGF("CRC error\n");
+ 
+-	data->error = MMC_ERR_BADCRC;
++	data->error = -EILSEQ;
+ 
+ 	tasklet_schedule(&host->finish_tasklet);
+ 
+@@ -1134,7 +1120,7 @@
+ 
+ 	DBGF("Timeout\n");
+ 
+-	data->error = MMC_ERR_TIMEOUT;
++	data->error = -ETIMEDOUT;
+ 
+ 	tasklet_schedule(&host->finish_tasklet);
+ 
+@@ -1279,7 +1265,7 @@
+ 	return 0;
+ }
+ 
+-static void __devexit wbsd_free_mmc(struct device *dev)
++static void wbsd_free_mmc(struct device *dev)
+ {
+ 	struct mmc_host *mmc;
+ 	struct wbsd_host *host;
+@@ -1371,7 +1357,7 @@
+ 	return 0;
+ }
+ 
+-static void __devexit wbsd_release_regions(struct wbsd_host *host)
++static void wbsd_release_regions(struct wbsd_host *host)
+ {
+ 	if (host->base)
+ 		release_region(host->base, 8);
+@@ -1447,7 +1433,7 @@
+ 		"Falling back on FIFO.\n", dma);
+ }
+ 
+-static void __devexit wbsd_release_dma(struct wbsd_host *host)
++static void wbsd_release_dma(struct wbsd_host *host)
+ {
+ 	if (host->dma_addr) {
+ 		dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
+@@ -1497,7 +1483,7 @@
+ 	return 0;
+ }
+ 
+-static void __devexit wbsd_release_irq(struct wbsd_host *host)
++static void  wbsd_release_irq(struct wbsd_host *host)
+ {
+ 	if (!host->irq)
+ 		return;
+@@ -1548,7 +1534,7 @@
+  * Release all resources for the host.
+  */
+ 
+-static void __devexit wbsd_release_resources(struct wbsd_host *host)
++static void wbsd_release_resources(struct wbsd_host *host)
+ {
+ 	wbsd_release_dma(host);
+ 	wbsd_release_irq(host);
+Index: linux-2.6.22-atheros-ng/drivers/mmc/host/wbsd.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/host/wbsd.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/host/wbsd.h	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver
++ *  linux/drivers/mmc/host/wbsd.h - Winbond W83L51xD SD/MMC driver
+  *
+  *  Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved.
+  *
+Index: linux-2.6.22-atheros-ng/include/linux/mmc/card.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/include/linux/mmc/card.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/include/linux/mmc/card.h	2007-11-13 11:35:23.000000000 +0100
+@@ -55,7 +55,28 @@
+ 	unsigned int		hs_max_dtr;
+ };
+ 
++struct sdio_cccr {
++	unsigned int		sdio_vsn;
++	unsigned int		sd_vsn;
++	unsigned int		multi_block:1,
++				low_speed:1,
++				wide_bus:1,
++				high_power:1,
++				high_speed:1;
++};
++
++struct sdio_cis {
++	unsigned short		vendor;
++	unsigned short		device;
++	unsigned short		blksize;
++	unsigned int		max_dtr;
++};
++
+ struct mmc_host;
++struct sdio_func;
++struct sdio_func_tuple;
++
++#define SDIO_MAX_FUNCS		7
+ 
+ /*
+  * MMC device
+@@ -67,11 +88,13 @@
+ 	unsigned int		type;		/* card type */
+ #define MMC_TYPE_MMC		0		/* MMC card */
+ #define MMC_TYPE_SD		1		/* SD card */
++#define MMC_TYPE_SDIO		2		/* SDIO card */
+ 	unsigned int		state;		/* (our) card state */
+ #define MMC_STATE_PRESENT	(1<<0)		/* present in sysfs */
+ #define MMC_STATE_READONLY	(1<<1)		/* card is read-only */
+ #define MMC_STATE_HIGHSPEED	(1<<2)		/* card is in high speed mode */
+ #define MMC_STATE_BLOCKADDR	(1<<3)		/* card uses block-addressing */
++
+ 	u32			raw_cid[4];	/* raw card CID */
+ 	u32			raw_csd[4];	/* raw card CSD */
+ 	u32			raw_scr[2];	/* raw card SCR */
+@@ -80,10 +103,17 @@
+ 	struct mmc_ext_csd	ext_csd;	/* mmc v4 extended card specific */
+ 	struct sd_scr		scr;		/* extra SD information */
+ 	struct sd_switch_caps	sw_caps;	/* switch (CMD6) caps */
++
++	unsigned int		sdio_funcs;	/* number of SDIO functions */
++	struct sdio_cccr	cccr;		/* common card info */
++	struct sdio_cis		cis;		/* common tuple info */
++	struct sdio_func	*sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */
++	struct sdio_func_tuple	*tuples;	/* unknown common tuples */
+ };
+ 
+ #define mmc_card_mmc(c)		((c)->type == MMC_TYPE_MMC)
+ #define mmc_card_sd(c)		((c)->type == MMC_TYPE_SD)
++#define mmc_card_sdio(c)	((c)->type == MMC_TYPE_SDIO)
+ 
+ #define mmc_card_present(c)	((c)->state & MMC_STATE_PRESENT)
+ #define mmc_card_readonly(c)	((c)->state & MMC_STATE_READONLY)
+Index: linux-2.6.22-atheros-ng/include/linux/mmc/core.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/include/linux/mmc/core.h	2007-11-08 14:56:32.000000000 +0100
++++ linux-2.6.22-atheros-ng/include/linux/mmc/core.h	2007-11-13 11:36:26.000000000 +0100
+@@ -41,6 +41,8 @@
+ #define MMC_RSP_R1B	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
+ #define MMC_RSP_R2	(MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
+ #define MMC_RSP_R3	(MMC_RSP_PRESENT)
++#define MMC_RSP_R4	(MMC_RSP_PRESENT)
++#define MMC_RSP_R5	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+ #define MMC_RSP_R6	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+ #define MMC_RSP_R7	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+ 
+@@ -54,15 +56,19 @@
+ 	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_DMA		4
+-#define MMC_ERR_BUSY		5
+-#define MMC_ERR_FAILED		6
+-#define MMC_ERR_INVALID		7
+-#define MMC_ERR_CANCELED	8
++ /*
++  * Standard errno values are used for errors, but some have specific
++  * meaning in the MMC layer:
++  *
++  * ETIMEDOUT    Card took too long to respond
++  * EILSEQ       Basic format problem with the received or sent data
++  *              (e.g. CRC check failed, incorrect opcode in response
++  *              or bad end bit)
++  * EINVAL       Request cannot be performed because of restrictions
++  *              in hardware and/or the driver
++  * ENOMEDIUM    Host can determine that the slot is empty and is
++  *              actively failing requests
++  */
+ 
+ 	struct mmc_data		*data;		/* data segment associated with cmd */
+ 	struct mmc_request	*mrq;		/* associated request */
+@@ -102,14 +108,25 @@
+ struct mmc_host;
+ struct mmc_card;
+ 
+-extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
++extern void 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_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
+ 	struct mmc_command *, int);
+ 
+ extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int);
+ 
+-extern void mmc_claim_host(struct mmc_host *host);
++extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
+ extern void mmc_release_host(struct mmc_host *host);
+ 
++/**
++ *	mmc_claim_host - exclusively claim a host
++ *	@host: mmc host to claim
++ *
++ *	Claim a host for a set of operations.
++ */
++static inline void mmc_claim_host(struct mmc_host *host)
++{
++	__mmc_claim_host(host, NULL);
++}
++
+ #endif
+Index: linux-2.6.22-atheros-ng/include/linux/mmc/host.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/include/linux/mmc/host.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/include/linux/mmc/host.h	2007-11-13 11:35:23.000000000 +0100
+@@ -51,6 +51,7 @@
+ 	void	(*request)(struct mmc_host *host, struct mmc_request *req);
+ 	void	(*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
+ 	int	(*get_ro)(struct mmc_host *host);
++	void	(*enable_sdio_irq)(struct mmc_host *host, int enable);
+ };
+ 
+ struct mmc_card;
+@@ -90,6 +91,7 @@
+ #define MMC_CAP_BYTEBLOCK	(1 << 2)	/* Can do non-log2 block sizes */
+ #define MMC_CAP_MMC_HIGHSPEED	(1 << 3)	/* Can do MMC high-speed timing */
+ #define MMC_CAP_SD_HIGHSPEED	(1 << 4)	/* Can do SD high-speed timing */
++#define MMC_CAP_SDIO_IRQ	(1 << 5)	/* Can signal pending SDIO IRQs */
+ 
+ 	/* host specific block data */
+ 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */
+@@ -124,6 +126,10 @@
+ 	unsigned int		bus_refs;	/* reference counter */
+ 	unsigned int		bus_dead:1;	/* bus has been released */
+ 
++	unsigned int		sdio_irqs;
++	struct task_struct	*sdio_irq_thread;
++	atomic_t		sdio_irq_thread_abort;
++
+ 	unsigned long		private[0] ____cacheline_aligned;
+ };
+ 
+@@ -147,5 +153,11 @@
+ extern void mmc_detect_change(struct mmc_host *, unsigned long delay);
+ extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
+ 
++static inline void mmc_signal_sdio_irq(struct mmc_host *host)
++{
++	host->ops->enable_sdio_irq(host, 0);
++	wake_up_process(host->sdio_irq_thread);
++}
++
+ #endif
+ 
+Index: linux-2.6.22-atheros-ng/include/linux/mmc/sdio_func.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/include/linux/mmc/sdio_func.h	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,150 @@
++/*
++ *  linux/include/linux/mmc/sdio_func.h
++ *
++ *  Copyright 2007 Pierre Ossman
++ *
++ * 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.
++ */
++
++#ifndef MMC_SDIO_FUNC_H
++#define MMC_SDIO_FUNC_H
++
++#include <linux/device.h>
++#include <linux/mod_devicetable.h>
++
++struct mmc_card;
++struct sdio_func;
++
++typedef void (sdio_irq_handler_t)(struct sdio_func *);
++
++/*
++ * SDIO function CIS tuple (unknown to the core)
++ */
++struct sdio_func_tuple {
++	struct sdio_func_tuple *next;
++	unsigned char code;
++	unsigned char size;
++	unsigned char data[0];
++};
++
++/*
++ * SDIO function devices
++ */
++struct sdio_func {
++	struct mmc_card		*card;		/* the card this device belongs to */
++	struct device		dev;		/* the device */
++	sdio_irq_handler_t	*irq_handler;	/* IRQ callback */
++	unsigned int		num;		/* function number */
++
++	unsigned char		class;		/* standard interface class */
++	unsigned short		vendor;		/* vendor id */
++	unsigned short		device;		/* device id */
++
++	unsigned		max_blksize;	/* maximum block size */
++	unsigned		cur_blksize;	/* current block size */
++
++	unsigned int		state;		/* function state */
++#define SDIO_STATE_PRESENT	(1<<0)		/* present in sysfs */
++
++	u8			tmpbuf[4];	/* DMA:able scratch buffer */
++
++	struct sdio_func_tuple *tuples;
++};
++
++#define sdio_func_present(f)	((f)->state & SDIO_STATE_PRESENT)
++
++#define sdio_func_set_present(f) ((f)->state |= SDIO_STATE_PRESENT)
++
++#define sdio_func_id(f)		((f)->dev.bus_id)
++
++#define sdio_get_drvdata(f)	dev_get_drvdata(&(f)->dev)
++#define sdio_set_drvdata(f,d)	dev_set_drvdata(&(f)->dev, d)
++
++/*
++ * SDIO function device driver
++ */
++struct sdio_driver {
++	char *name;
++	const struct sdio_device_id *id_table;
++
++	int (*probe)(struct sdio_func *, const struct sdio_device_id *);
++	void (*remove)(struct sdio_func *);
++
++	struct device_driver drv;
++};
++
++/**
++ * SDIO_DEVICE - macro used to describe a specific SDIO device
++ * @vend: the 16 bit manufacturer code
++ * @dev: the 16 bit function id
++ *
++ * This macro is used to create a struct sdio_device_id that matches a
++ * specific device. The class field will be set to SDIO_ANY_ID.
++ */
++#define SDIO_DEVICE(vend,dev) \
++	.class = SDIO_ANY_ID, \
++	.vendor = (vend), .device = (dev)
++
++/**
++ * SDIO_DEVICE_CLASS - macro used to describe a specific SDIO device class
++ * @dev_class: the 8 bit standard interface code
++ *
++ * This macro is used to create a struct sdio_device_id that matches a
++ * specific standard SDIO function type.  The vendor and device fields will
++ * be set to SDIO_ANY_ID.
++ */
++#define SDIO_DEVICE_CLASS(dev_class) \
++	.class = (dev_class), \
++	.vendor = SDIO_ANY_ID, .device = SDIO_ANY_ID
++
++extern int sdio_register_driver(struct sdio_driver *);
++extern void sdio_unregister_driver(struct sdio_driver *);
++
++/*
++ * SDIO I/O operations
++ */
++extern void sdio_claim_host(struct sdio_func *func);
++extern void sdio_release_host(struct sdio_func *func);
++
++extern int sdio_enable_func(struct sdio_func *func);
++extern int sdio_disable_func(struct sdio_func *func);
++
++extern int sdio_set_block_size(struct sdio_func *func, unsigned blksz);
++
++extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);
++extern int sdio_release_irq(struct sdio_func *func);
++
++extern unsigned char sdio_readb(struct sdio_func *func,
++	unsigned int addr, int *err_ret);
++extern unsigned short sdio_readw(struct sdio_func *func,
++	unsigned int addr, int *err_ret);
++extern unsigned long sdio_readl(struct sdio_func *func,
++	unsigned int addr, int *err_ret);
++
++extern int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
++	unsigned int addr, int count);
++extern int sdio_readsb(struct sdio_func *func, void *dst,
++	unsigned int addr, int count);
++
++extern void sdio_writeb(struct sdio_func *func, unsigned char b,
++	unsigned int addr, int *err_ret);
++extern void sdio_writew(struct sdio_func *func, unsigned short b,
++	unsigned int addr, int *err_ret);
++extern void sdio_writel(struct sdio_func *func, unsigned long b,
++	unsigned int addr, int *err_ret);
++
++extern int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
++	void *src, int count);
++extern int sdio_writesb(struct sdio_func *func, unsigned int addr,
++	void *src, int count);
++
++extern unsigned char sdio_f0_readb(struct sdio_func *func,
++	unsigned int addr, int *err_ret);
++extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
++	unsigned int addr, int *err_ret);
++
++#endif
++
+Index: linux-2.6.22-atheros-ng/include/linux/mmc/sdio.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/include/linux/mmc/sdio.h	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,159 @@
++/*
++ *  linux/include/linux/mmc/sdio.h
++ *
++ *  Copyright 2006-2007 Pierre Ossman
++ *
++ * 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.
++ */
++
++#ifndef MMC_SDIO_H
++#define MMC_SDIO_H
++
++/* SDIO commands                         type  argument     response */
++#define SD_IO_SEND_OP_COND          5 /* bcr  [23:0] OCR         R4  */
++#define SD_IO_RW_DIRECT            52 /* ac   [31:0] See below   R5  */
++#define SD_IO_RW_EXTENDED          53 /* adtc [31:0] See below   R5  */
++
++/*
++ * SD_IO_RW_DIRECT argument format:
++ *
++ *      [31] R/W flag
++ *      [30:28] Function number
++ *      [27] RAW flag
++ *      [25:9] Register address
++ *      [7:0] Data
++ */
++
++/*
++ * SD_IO_RW_EXTENDED argument format:
++ *
++ *      [31] R/W flag
++ *      [30:28] Function number
++ *      [27] Block mode
++ *      [26] Increment address
++ *      [25:9] Register address
++ *      [8:0] Byte/block count
++ */
++
++/*
++  SDIO status in R5
++  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 R5_COM_CRC_ERROR	(1 << 15)	/* er, b */
++#define R5_ILLEGAL_COMMAND	(1 << 14)	/* er, b */
++#define R5_ERROR		(1 << 11)	/* erx, c */
++#define R5_FUNCTION_NUMBER	(1 << 9)	/* er, c */
++#define R5_OUT_OF_RANGE		(1 << 8)	/* er, c */
++#define R5_STATUS(x)		(x & 0xCB00)
++#define R5_IO_CURRENT_STATE(x)	((x & 0x3000) >> 12) /* s, b */
++
++/*
++ * Card Common Control Registers (CCCR)
++ */
++
++#define SDIO_CCCR_CCCR		0x00
++
++#define  SDIO_CCCR_REV_1_00	0	/* CCCR/FBR Version 1.00 */
++#define  SDIO_CCCR_REV_1_10	1	/* CCCR/FBR Version 1.10 */
++#define  SDIO_CCCR_REV_1_20	2	/* CCCR/FBR Version 1.20 */
++
++#define  SDIO_SDIO_REV_1_00	0	/* SDIO Spec Version 1.00 */
++#define  SDIO_SDIO_REV_1_10	1	/* SDIO Spec Version 1.10 */
++#define  SDIO_SDIO_REV_1_20	2	/* SDIO Spec Version 1.20 */
++#define  SDIO_SDIO_REV_2_00	3	/* SDIO Spec Version 2.00 */
++
++#define SDIO_CCCR_SD		0x01
++
++#define  SDIO_SD_REV_1_01	0	/* SD Physical Spec Version 1.01 */
++#define  SDIO_SD_REV_1_10	1	/* SD Physical Spec Version 1.10 */
++#define  SDIO_SD_REV_2_00	2	/* SD Physical Spec Version 2.00 */
++
++#define SDIO_CCCR_IOEx		0x02
++#define SDIO_CCCR_IORx		0x03
++
++#define SDIO_CCCR_IENx		0x04	/* Function/Master Interrupt Enable */
++#define SDIO_CCCR_INTx		0x05	/* Function Interrupt Pending */
++
++#define SDIO_CCCR_ABORT		0x06	/* function abort/card reset */
++
++#define SDIO_CCCR_IF		0x07	/* bus interface controls */
++
++#define  SDIO_BUS_WIDTH_1BIT	0x00
++#define  SDIO_BUS_WIDTH_4BIT	0x02
++
++#define  SDIO_BUS_CD_DISABLE     0x80	/* disable pull-up on DAT3 (pin 1) */
++
++#define SDIO_CCCR_CAPS		0x08
++
++#define  SDIO_CCCR_CAP_SDC	0x01	/* can do CMD52 while data transfer */
++#define  SDIO_CCCR_CAP_SMB	0x02	/* can do multi-block xfers (CMD53) */
++#define  SDIO_CCCR_CAP_SRW	0x04	/* supports read-wait protocol */
++#define  SDIO_CCCR_CAP_SBS	0x08	/* supports suspend/resume */
++#define  SDIO_CCCR_CAP_S4MI	0x10	/* interrupt during 4-bit CMD53 */
++#define  SDIO_CCCR_CAP_E4MI	0x20	/* enable ints during 4-bit CMD53 */
++#define  SDIO_CCCR_CAP_LSC	0x40	/* low speed card */
++#define  SDIO_CCCR_CAP_4BLS	0x80	/* 4 bit low speed card */
++
++#define SDIO_CCCR_CIS		0x09	/* common CIS pointer (3 bytes) */
++
++/* Following 4 regs are valid only if SBS is set */
++#define SDIO_CCCR_SUSPEND	0x0c
++#define SDIO_CCCR_SELx		0x0d
++#define SDIO_CCCR_EXECx		0x0e
++#define SDIO_CCCR_READYx	0x0f
++
++#define SDIO_CCCR_BLKSIZE	0x10
++
++#define SDIO_CCCR_POWER		0x12
++
++#define  SDIO_POWER_SMPC	0x01	/* Supports Master Power Control */
++#define  SDIO_POWER_EMPC	0x02	/* Enable Master Power Control */
++
++#define SDIO_CCCR_SPEED		0x13
++
++#define  SDIO_SPEED_SHS		0x01	/* Supports High-Speed mode */
++#define  SDIO_SPEED_EHS		0x02	/* Enable High-Speed mode */
++
++/*
++ * Function Basic Registers (FBR)
++ */
++
++#define SDIO_FBR_BASE(f)	((f) * 0x100) /* base of function f's FBRs */
++
++#define SDIO_FBR_STD_IF		0x00
++
++#define  SDIO_FBR_SUPPORTS_CSA	0x40	/* supports Code Storage Area */
++#define  SDIO_FBR_ENABLE_CSA	0x80	/* enable Code Storage Area */
++
++#define SDIO_FBR_STD_IF_EXT	0x01
++
++#define SDIO_FBR_POWER		0x02
++
++#define  SDIO_FBR_POWER_SPS	0x01	/* Supports Power Selection */
++#define  SDIO_FBR_POWER_EPS	0x02	/* Enable (low) Power Selection */
++
++#define SDIO_FBR_CIS		0x09	/* CIS pointer (3 bytes) */
++
++
++#define SDIO_FBR_CSA		0x0C	/* CSA pointer (3 bytes) */
++
++#define SDIO_FBR_CSA_DATA	0x0F
++
++#define SDIO_FBR_BLKSIZE	0x10	/* block size (2 bytes) */
++
++#endif
++
+Index: linux-2.6.22-atheros-ng/include/linux/mmc/sdio_ids.h
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22-atheros-ng/include/linux/mmc/sdio_ids.h	2007-11-13 11:35:23.000000000 +0100
+@@ -0,0 +1,23 @@
++/*
++ * SDIO Classes, Interface Types, Manufacturer IDs, etc.
++ */
++
++#ifndef MMC_SDIO_IDS_H
++#define MMC_SDIO_IDS_H
++
++/*
++ * Standard SDIO Function Interfaces
++ */
++
++#define SDIO_CLASS_NONE		0x00	/* Not a SDIO standard interface */
++#define SDIO_CLASS_UART		0x01	/* standard UART interface */
++#define SDIO_CLASS_BT_A		0x02	/* Type-A BlueTooth std interface */
++#define SDIO_CLASS_BT_B		0x03	/* Type-B BlueTooth std interface */
++#define SDIO_CLASS_GPS		0x04	/* GPS standard interface */
++#define SDIO_CLASS_CAMERA	0x05	/* Camera standard interface */
++#define SDIO_CLASS_PHS		0x06	/* PHS standard interface */
++#define SDIO_CLASS_WLAN		0x07	/* WLAN interface */
++#define SDIO_CLASS_ATA		0x08	/* Embedded SDIO-ATA std interface */
++
++
++#endif
+Index: linux-2.6.22-atheros-ng/include/linux/mod_devicetable.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/include/linux/mod_devicetable.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/include/linux/mod_devicetable.h	2007-11-13 11:35:23.000000000 +0100
+@@ -333,4 +333,15 @@
+ #define PA_HVERSION_ANY_ID	0xffff
+ #define PA_SVERSION_ANY_ID	0xffffffff
+ 
++/* SDIO */
++
++#define SDIO_ANY_ID (~0)
++
++struct sdio_device_id {
++	__u8	class;			/* Standard interface or SDIO_ANY_ID */
++	__u16	vendor;			/* Vendor or SDIO_ANY_ID */
++	__u16	device;			/* Device ID or SDIO_ANY_ID */
++	kernel_ulong_t driver_data;	/* Data private to the driver */
++};
++
+ #endif /* LINUX_MOD_DEVICETABLE_H */
+Index: linux-2.6.22-atheros-ng/scripts/mod/file2alias.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/scripts/mod/file2alias.c	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/scripts/mod/file2alias.c	2007-11-13 11:35:23.000000000 +0100
+@@ -476,6 +476,22 @@
+ 	return 1;
+ }
+ 
++/* Looks like: sdio:cNvNdN. */
++static int do_sdio_entry(const char *filename,
++			struct sdio_device_id *id, char *alias)
++{
++	id->class = TO_NATIVE(id->class);
++	id->vendor = TO_NATIVE(id->vendor);
++	id->device = TO_NATIVE(id->device);
++
++	strcpy(alias, "sdio:");
++	ADD(alias, "c", id->class != (__u8)SDIO_ANY_ID, id->class);
++	ADD(alias, "v", id->vendor != (__u16)SDIO_ANY_ID, id->vendor);
++	ADD(alias, "d", id->device != (__u16)SDIO_ANY_ID, id->device);
++
++	return 1;
++}
++
+ /* Ignore any prefix, eg. v850 prepends _ */
+ static inline int sym_is(const char *symbol, const char *name)
+ {
+@@ -587,6 +603,10 @@
+ 		do_table(symval, sym->st_size,
+ 			 sizeof(struct parisc_device_id), "parisc",
+ 			 do_parisc_entry, mod);
++	else if (sym_is(symname, "__mod_sdio_device_table"))
++		do_table(symval, sym->st_size,
++			 sizeof(struct sdio_device_id), "sdio",
++			 do_sdio_entry, mod);
+ }
+ 
+ /* Now add out buffered information to the generated C source */
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sd_ops.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/sd_ops.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sd_ops.h	2007-11-13 11:35:23.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/drivers/mmc/sd_ops.h
++ *  linux/drivers/mmc/core/sd_ops.h
+  *
+  *  Copyright 2006-2007 Pierre Ossman
+  *
+@@ -12,7 +12,6 @@
+ #ifndef _MMC_SD_OPS_H
+ #define _MMC_SD_OPS_H
+ 
+-int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
+ int mmc_app_set_bus_width(struct mmc_card *card, int width);
+ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
+ int mmc_send_if_cond(struct mmc_host *host, u32 ocr);
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/sysfs.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/sysfs.h	2007-07-09 01:32:17.000000000 +0200
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/sysfs.h	2007-11-13 11:35:23.000000000 +0100
+@@ -11,17 +11,16 @@
+ #ifndef _MMC_CORE_SYSFS_H
+ #define _MMC_CORE_SYSFS_H
+ 
+-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);
++#define MMC_ATTR_FN(name, fmt, args...)					\
++static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf)	\
++{									\
++	struct mmc_card *card = container_of(dev, struct mmc_card, dev);\
++	return sprintf(buf, fmt, args);					\
++}
+ 
+-struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev);
+-int mmc_add_host_sysfs(struct mmc_host *host);
+-void mmc_remove_host_sysfs(struct mmc_host *host);
+-void mmc_free_host_sysfs(struct mmc_host *host);
++#define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
+ 
+-int mmc_schedule_work(struct work_struct *work);
+-int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay);
+-void mmc_flush_scheduled_work(void);
++int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs);
++void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs);
+ 
+ #endif

Added: developers/sameo/patches/ar6k-ng/110-s3c24xx_sdio_merge.patch
===================================================================
--- developers/sameo/patches/ar6k-ng/110-s3c24xx_sdio_merge.patch	2007-12-10 17:51:36 UTC (rev 3627)
+++ developers/sameo/patches/ar6k-ng/110-s3c24xx_sdio_merge.patch	2007-12-10 22:31:54 UTC (rev 3628)
@@ -0,0 +1,258 @@
+Index: linux-2.6.22.4/include/linux/mmc/host.h
+===================================================================
+--- linux-2.6.22.4.orig/include/linux/mmc/host.h	2007-09-29 05:27:44.122600789 +0200
++++ linux-2.6.22.4/include/linux/mmc/host.h	2007-09-29 05:27:44.358614240 +0200
+@@ -66,7 +66,14 @@
+ 	unsigned int		f_max;
+ 	u32			ocr_avail;
+ 
+-#define MMC_VDD_165_195		0x00000080	/* VDD voltage 1.65 - 1.95 */
++#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 */
+@@ -84,6 +91,7 @@
+ #define MMC_VDD_34_35		0x00400000	/* VDD voltage 3.4 ~ 3.5 */
+ #define MMC_VDD_35_36		0x00800000	/* VDD voltage 3.5 ~ 3.6 */
+ 
++
+ 	unsigned long		caps;		/* Host capabilities */
+ 
+ #define MMC_CAP_4_BIT_DATA	(1 << 0)	/* Can the host do 4 bit transfers */
+Index: linux-2.6.22.4/drivers/mmc/core/sd.c
+===================================================================
+--- linux-2.6.22.4.orig/drivers/mmc/core/sd.c	2007-09-29 05:27:43.202548358 +0200
++++ linux-2.6.22.4/drivers/mmc/core/sd.c	2007-09-29 05:27:44.362614466 +0200
+@@ -633,13 +633,6 @@
+ 		ocr &= ~0x7F;
+ 	}
+ 
+-	if (ocr & MMC_VDD_165_195) {
+-		printk(KERN_WARNING "%s: SD card claims to support the "
+-		       "incompletely defined 'low voltage range'. This "
+-		       "will be ignored.\n", mmc_hostname(host));
+-		ocr &= ~MMC_VDD_165_195;
+-	}
+-
+ 	host->ocr = mmc_select_voltage(host, ocr);
+ 
+ 	/*
+Index: linux-2.6.22.4/drivers/mmc/core/sdio.c
+===================================================================
+--- linux-2.6.22.4.orig/drivers/mmc/core/sdio.c	2007-09-29 05:27:43.478564088 +0200
++++ linux-2.6.22.4/drivers/mmc/core/sdio.c	2007-10-07 01:37:32.380757691 +0200
+@@ -245,13 +245,6 @@
+ 		ocr &= ~0x7F;
+ 	}
+ 
+-	if (ocr & MMC_VDD_165_195) {
+-		printk(KERN_WARNING "%s: SDIO card claims to support the "
+-		       "incompletely defined 'low voltage range'. This "
+-		       "will be ignored.\n", mmc_hostname(host));
+-		ocr &= ~MMC_VDD_165_195;
+-	}
+-
+ 	host->ocr = mmc_select_voltage(host, ocr);
+ 
+ 	/*
+Index: linux-2.6.22.4/drivers/mmc/host/sdhci.c
+===================================================================
+--- linux-2.6.22.4.orig/drivers/mmc/host/sdhci.c	2007-09-29 05:27:43.938590304 +0200
++++ linux-2.6.22.4/drivers/mmc/host/sdhci.c	2007-09-29 05:27:44.406616970 +0200
+@@ -679,7 +679,7 @@
+ 	pwr = SDHCI_POWER_ON;
+ 
+ 	switch (1 << power) {
+-	case MMC_VDD_165_195:
++	case MMC_VDD_18_19:
+ 		pwr |= SDHCI_POWER_180;
+ 		break;
+ 	case MMC_VDD_29_30:
+Index: linux-2.6.22.4/drivers/mmc/host/mmc_debug.c
+===================================================================
+--- linux-2.6.22.4.orig/drivers/mmc/host/mmc_debug.c	2007-09-29 05:27:39.122315835 +0200
++++ linux-2.6.22.4/drivers/mmc/host/mmc_debug.c	2007-09-29 05:27:44.426618110 +0200
+@@ -46,15 +46,11 @@
+ char *mmc_err2str(int err)
+ {
+ 	switch(err) {
+-		case MMC_ERR_NONE:	return "OK";
+-		case MMC_ERR_TIMEOUT:	return "TIMEOUT";
+-		case MMC_ERR_BADCRC: 	return "BADCRC";
+-		case MMC_ERR_FIFO: 	return "FIFO";
+-		case MMC_ERR_FAILED: 	return "FAILED";
+-		case MMC_ERR_INVALID: 	return "INVALID";
+-		case MMC_ERR_BUSY:	return "BUSY";
+-		case MMC_ERR_DMA:	return "DMA";
+-		case MMC_ERR_CANCELED:	return "CANCELED";
++		case 0:	return "OK";
++		case -ETIMEDOUT:	return "TIMEOUT";
++		case -EILSEQ: 	return "BADCRC";
++		case -EIO: 	return "IOERR";
++		case -EINVAL: 	return "INVALID";
+ 		default: return "UNKNOWN";
+ 	}
+ }
+Index: linux-2.6.22.4/drivers/mmc/host/s3cmci.c
+===================================================================
+--- linux-2.6.22.4.orig/drivers/mmc/host/s3cmci.c	2007-09-29 05:27:39.366329742 +0200
++++ linux-2.6.22.4/drivers/mmc/host/s3cmci.c	2007-10-01 21:35:03.637395039 +0200
+@@ -14,6 +14,7 @@
+ #include <linux/mmc/mmc.h>
+ #include <linux/mmc/host.h>
+ #include <linux/platform_device.h>
++#include <linux/timer.h>
+ #include <linux/irq.h>
+ 
+ #include <asm/dma.h>
+@@ -129,7 +130,7 @@
+ 	if (!cmd)
+ 		return;
+ 
+-	if (cmd->error == MMC_ERR_NONE) {
++	if (cmd->error == 0) {
+ 
+ 		dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
+ 			host->dbgmsg_cmd, cmd->resp[0]);
+@@ -142,7 +143,7 @@
+ 	if (!cmd->data)
+ 		return;
+ 
+-	if (cmd->data->error == MMC_ERR_NONE) {
++	if (cmd->data->error == 0) {
+ 		dbg(host, dbglvl, "DAT[%s] %s\n",
+ 			mmc_err2str(cmd->data->error), host->dbgmsg_dat);
+ 	} else {
+@@ -334,7 +335,7 @@
+ 				(host->pio_active == XFER_READ)?"read":"write",
+ 				host->pio_count, host->pio_words);
+ 
+-			host->mrq->data->error = MMC_ERR_DMA;
++			host->mrq->data->error = -EIO;
+ 		}
+ 
+ 		finalize_request(host);
+@@ -431,7 +432,7 @@
+ 	}
+ 
+ 	if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
+-		cmd->error = MMC_ERR_TIMEOUT;
++		cmd->error = -ETIMEDOUT;
+ 		host->status = "error: command timeout";
+ 		goto fail_transfer;
+ 	}
+@@ -452,7 +453,7 @@
+ 				    "fixup: ignore CRC fail with long rsp\n");
+ 			} else {
+ #if 0
+-				cmd->error = MMC_ERR_BADCRC;
++				cmd->error = -EILSEQ;
+ 				host->status = "error: bad command crc";
+ 				goto fail_transfer;
+ #endif
+@@ -482,32 +483,32 @@
+ 	/* Check for FIFO failure */
+ 	if (host->is2440) {
+ 		if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
+-			host->mrq->data->error = MMC_ERR_FIFO;
++			host->mrq->data->error = -EIO;
+ 			host->status = "error: 2440 fifo failure";
+ 			goto fail_transfer;
+ 		}
+ 	} else {
+ 		if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
+-			cmd->data->error = MMC_ERR_FIFO;
++			cmd->data->error = -EIO;
+ 			host->status = "error:  fifo failure";
+ 			goto fail_transfer;
+ 		}
+ 	}
+ 
+ 	if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
+-		cmd->data->error = MMC_ERR_BADCRC;
++		cmd->data->error = -EILSEQ;
+ 		host->status = "error: bad data crc (outgoing)";
+ 		goto fail_transfer;
+ 	}
+ 
+ 	if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
+-		cmd->data->error = MMC_ERR_BADCRC;
++		cmd->data->error = -EILSEQ;
+ 		host->status = "error: bad data crc (incoming)";
+ 		goto fail_transfer;
+ 	}
+ 
+ 	if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
+-		cmd->data->error = MMC_ERR_TIMEOUT;
++		cmd->data->error = -ETIMEDOUT;
+ 		host->status = "error: data timeout";
+ 		goto fail_transfer;
+ 	}
+@@ -618,7 +628,7 @@
+ 
+ 
+ fail_request:
+-	host->mrq->data->error = MMC_ERR_DMA;
++	host->mrq->data->error = -EIO;
+ 	host->complete_what = COMPLETION_FINALIZE;
+ 	writel(0, host->base + host->sdiimsk);
+ 	goto out;
+@@ -637,8 +647,8 @@
+ 	if (!mrq)
+ 		return;
+ 
+-	if (cmd->data && (cmd->error == MMC_ERR_NONE) &&
+-		  (cmd->data->error == MMC_ERR_NONE)) {
++	if (cmd->data && (cmd->error == 0) &&
++		  (cmd->data->error == 0)) {
+ 
+ 		if (host->dodma && (!host->dma_complete)) {
+ 			dbg(host, dbg_dma, "DMA Missing!\n");
+@@ -687,7 +697,7 @@
+ 
+ 	// Calulate the amout of bytes transfer, but only if there was
+ 	// no error
+-	if (mrq->data->error == MMC_ERR_NONE) {
++	if (mrq->data->error == 0) {
+ 		mrq->data->bytes_xfered =
+ 			(mrq->data->blocks * mrq->data->blksz);
+ 	} else {
+@@ -696,7 +706,7 @@
+ 
+ 	// If we had an error while transfering data we flush the
+ 	// DMA channel and the fifo to clear out any garbage
+-	if (mrq->data->error != MMC_ERR_NONE) {
++	if (mrq->data->error != 0) {
+ 		if (host->dodma)
+ 			s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+ 
+@@ -956,8 +966,8 @@
+ 		host->dcnt++;
+ 
+ 		if (res) {
+-			cmd->error = MMC_ERR_DMA;
+-			cmd->data->error = MMC_ERR_DMA;
++			cmd->error = -EIO;
++			cmd->data->error = -EIO;
+ 
+ 			mmc_request_done(mmc, mrq);
+ 			return;
+@@ -971,8 +981,8 @@
+ 		}
+ 
+ 		if (res) {
+-			cmd->error = MMC_ERR_DMA;
+-			cmd->data->error = MMC_ERR_DMA;
++			cmd->error = -EIO;
++			cmd->data->error = -EIO;
+ 
+ 			mmc_request_done(mmc, mrq);
+ 			return;
+

Added: developers/sameo/patches/ar6k-ng/120-sdio_ext_ops.patch
===================================================================
--- developers/sameo/patches/ar6k-ng/120-sdio_ext_ops.patch	2007-12-10 17:51:36 UTC (rev 3627)
+++ developers/sameo/patches/ar6k-ng/120-sdio_ext_ops.patch	2007-12-10 22:31:54 UTC (rev 3628)
@@ -0,0 +1,77 @@
+Index: linux-2.6.22.4/drivers/mmc/core/sdio.c
+===================================================================
+--- linux-2.6.22.4.orig/drivers/mmc/core/sdio.c	2007-09-29 05:27:44.390616060 +0200
++++ linux-2.6.22.4/drivers/mmc/core/sdio.c	2007-10-01 20:06:10.105454379 +0200
+@@ -138,7 +138,7 @@
+ 	return ret;
+ }
+ 
+-static int sdio_enable_wide(struct mmc_card *card)
++int sdio_set_wide(struct mmc_card *card, u8 wide)
+ {
+ 	int ret;
+ 	u8 ctrl;
+@@ -146,23 +146,27 @@
+ 	if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
+ 		return 0;
+ 
+-	if (card->cccr.low_speed && !card->cccr.wide_bus)
++	if (wide && card->cccr.low_speed && !card->cccr.wide_bus)
+ 		return 0;
+ 
+ 	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
+ 	if (ret)
+ 		return ret;
+ 
+-	ctrl |= SDIO_BUS_WIDTH_4BIT;
++	if (wide)
++		ctrl |= SDIO_BUS_WIDTH_4BIT;
++	else
++		ctrl &= ~SDIO_BUS_WIDTH_4BIT;
+ 
+ 	ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
+ 	if (ret)
+ 		return ret;
+ 
+-	mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
++	mmc_set_bus_width(card->host, (wide ? MMC_BUS_WIDTH_4 : MMC_BUS_WIDTH_1));
+ 
+ 	return 0;
+ }
++EXPORT_SYMBOL_GPL(sdio_set_wide);
+ 
+ /*
+  * Host is being removed. Free up the current card.
+@@ -321,7 +325,7 @@
+ 	/*
+ 	 * Switch to wider bus (if supported).
+ 	 */
+-	err = sdio_enable_wide(card);
++	err = sdio_set_wide(card, 1);
+ 	if (err)
+ 		goto remove;
+ 
+@@ -348,8 +352,10 @@
+ 	 */
+ 	for (i = 0;i < funcs;i++) {
+ 		err = sdio_add_func(host->card->sdio_func[i]);
+-		if (err)
++		if (err) {
++			err = 0;
+ 			goto remove_added;
++		}
+ 	}
+ 
+ 	return 0;
+Index: linux-2.6.22.4/include/linux/mmc/sdio_func.h
+===================================================================
+--- linux-2.6.22.4.orig/include/linux/mmc/sdio_func.h	2007-09-29 05:27:44.162603068 +0200
++++ linux-2.6.22.4/include/linux/mmc/sdio_func.h	2007-09-29 05:27:44.602628144 +0200
+@@ -116,6 +116,7 @@
+ 
+ extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);
+ extern int sdio_release_irq(struct sdio_func *func);
++extern int sdio_set_wide(struct mmc_card *card, u8 wide);
+ 
+ extern unsigned char sdio_readb(struct sdio_func *func,
+ 	unsigned int addr, int *err_ret);

Added: developers/sameo/patches/ar6k-ng/130-gta02v2.patch
===================================================================
--- developers/sameo/patches/ar6k-ng/130-gta02v2.patch	2007-12-10 17:51:36 UTC (rev 3627)
+++ developers/sameo/patches/ar6k-ng/130-gta02v2.patch	2007-12-10 22:31:54 UTC (rev 3628)
@@ -0,0 +1,30 @@
+---
+ arch/arm/mach-s3c2440/Kconfig      |    1 +
+ arch/arm/mach-s3c2440/mach-gta02.c |    2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+Index: linux-2.6.22-atheros-ng/arch/arm/mach-s3c2440/Kconfig
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/arch/arm/mach-s3c2440/Kconfig	2007-11-08 14:56:32.000000000 +0100
++++ linux-2.6.22-atheros-ng/arch/arm/mach-s3c2440/Kconfig	2007-11-13 11:36:42.000000000 +0100
+@@ -76,6 +76,7 @@
+ config MACH_NEO1973_GTA02
+ 	bool "FIC Neo1973 GSM Phone (GTA02 Hardware)"
+ 	select CPU_S3C2440
++	select CPU_S3C2442
+ 	select SENSORS_PCF50633
+ 	help
+ 	   Say Y here if you are using the FIC Neo1973 GSM Phone
+Index: linux-2.6.22-atheros-ng/arch/arm/mach-s3c2440/mach-gta02.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/arch/arm/mach-s3c2440/mach-gta02.c	2007-11-13 11:35:09.000000000 +0100
++++ linux-2.6.22-atheros-ng/arch/arm/mach-s3c2440/mach-gta02.c	2007-12-10 23:24:45.000000000 +0100
+@@ -310,7 +310,7 @@
+ 	&s3c_device_wdt,
+ 	&s3c_device_i2c,
+ 	&s3c_device_iis,
+-	// &s3c_device_sdi, /* FIXME: temporary disable to avoid s3cmci bind */
++	&s3c_device_sdi,
+ 	&s3c_device_usbgadget,
+ 	&s3c_device_nand,
+ 	&s3c_device_ts,

Added: developers/sameo/patches/ar6k-ng/140-s3c24xx_mci_platform.patch
===================================================================
--- developers/sameo/patches/ar6k-ng/140-s3c24xx_mci_platform.patch	2007-12-10 17:51:36 UTC (rev 3627)
+++ developers/sameo/patches/ar6k-ng/140-s3c24xx_mci_platform.patch	2007-12-10 22:31:54 UTC (rev 3628)
@@ -0,0 +1,144 @@
+---
+ arch/arm/mach-s3c2440/mach-gta02.c  |   56 ++++++++++++++++++++++++++++++------
+ arch/arm/plat-s3c24xx/devs.c        |   10 ++++++
+ include/asm-arm/plat-s3c24xx/devs.h |    1 
+ 3 files changed, 59 insertions(+), 8 deletions(-)
+
+Index: linux-2.6.22-atheros-ng/arch/arm/plat-s3c24xx/devs.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/arch/arm/plat-s3c24xx/devs.c	2007-11-13 11:40:06.000000000 +0100
++++ linux-2.6.22-atheros-ng/arch/arm/plat-s3c24xx/devs.c	2007-11-13 11:43:31.000000000 +0100
+@@ -421,6 +421,16 @@
+ 
+ EXPORT_SYMBOL(s3c_device_sdi);
+ 
++struct platform_device s3c_2440_device_sdi = {
++	.name		  = "s3c2440-sdi",
++	.id		  = -1,
++	.num_resources	  = ARRAY_SIZE(s3c_sdi_resource),
++	.resource	  = s3c_sdi_resource,
++};
++
++EXPORT_SYMBOL(s3c_2440_device_sdi);
++
++
+ /* High-speed MMC/SD */
+ 
+ static struct resource s3c_hsmmc_resource[] = {
+Index: linux-2.6.22-atheros-ng/arch/arm/mach-s3c2440/mach-gta02.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/arch/arm/mach-s3c2440/mach-gta02.c	2007-11-13 11:42:29.000000000 +0100
++++ linux-2.6.22-atheros-ng/arch/arm/mach-s3c2440/mach-gta02.c	2007-12-10 21:26:50.000000000 +0100
+@@ -305,12 +305,41 @@
+ 	},
+ };
+ 
++static struct resource gta02_sdio_resources[] = {
++	[0] = {
++		.flags	= IORESOURCE_IRQ,
++		.start	= IRQ_SDI,
++		.end	= IRQ_SDI,
++	},
++	[1] = {
++		.flags = IORESOURCE_MEM,
++		.start = S3C2410_PA_SDI,
++		.end   = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,
++	},
++	[2] = {
++		.flags = IORESOURCE_DMA,
++		.start = 0, /* Channel 0 for SDI */
++		.end = 0,
++	},
++};
++
++
++static struct platform_device gta02_sdio_dev = {
++        .name           = "s3c24xx-sdio",
++        .id             = -1,
++        .dev            = {
++                                .coherent_dma_mask      = 0xffffffff,
++        },
++        .resource       = gta02_sdio_resources,
++        .num_resources  = ARRAY_SIZE(gta02_sdio_resources),
++};
++
+ static struct platform_device *gta02_devices[] __initdata = {
+ 	&s3c_device_usb,
+ 	&s3c_device_wdt,
+ 	&s3c_device_i2c,
+ 	&s3c_device_iis,
+-	&s3c_device_sdi,
++	&s3c_2440_device_sdi,
+ 	&s3c_device_usbgadget,
+ 	&s3c_device_nand,
+ 	&s3c_device_ts,
+@@ -633,13 +662,14 @@
+ {
+ 	s3c_device_usb.dev.platform_data = &gta02_usb_info;
+ 	s3c_device_nand.dev.platform_data = &gta02_nand_info;
+-	s3c_device_sdi.dev.platform_data = &gta02_mmc_cfg;
++	s3c_2440_device_sdi.dev.platform_data = &gta02_mmc_cfg;
+ 	s3c_device_spi1.dev.platform_data = &gta02_spi_acc_cfg;
+ 
+ 	/* Only GTA02v1 has a SD_DETECT GPIO.  Since the slot is not
+ 	 * hot-pluggable, this is not required anyway */
+ 	switch (system_rev) {
+ 	case GTA02v1_SYSTEM_REV:
++	case GTA02v2_SYSTEM_REV:
+ 		break;
+ 	default:
+ 		gta02_mmc_cfg.gpio_detect = 0;
+@@ -650,18 +680,26 @@
+ 	s3c24xx_udc_set_platdata(&gta02_udc_cfg);
+ 	set_s3c2410ts_info(&gta02_ts_cfg);
+ 
+-	/* FIXME: hardcoded WLAN module power-up */
+-	s3c2410_gpio_cfgpin(GTA02_CHIP_PWD, S3C2410_GPIO_OUTPUT);
+-	s3c2410_gpio_setpin(GTA02_CHIP_PWD, 0);
+-
+ 	s3c2410_gpio_cfgpin(GTA02_GPIO_nWLAN_RESET, S3C2410_GPIO_OUTPUT);
++
++	/* Power is down */
++	s3c2410_gpio_setpin(GTA02_CHIP_PWD, 1);
++	mdelay(100);
++
+ 	switch (system_rev) {
+ 	case GTA02v1_SYSTEM_REV:
++		s3c2410_gpio_setpin(GTA02_CHIP_PWD, 0);
+ 		break;
+ 	default:
++		printk("Powering up the module\n");
++		s3c2410_gpio_cfgpin(GTA02_GPIO_nWLAN_RESET, S3C2410_GPIO_OUTPUT);
++		/* Chip is in reset state */
+ 		s3c2410_gpio_setpin(GTA02_GPIO_nWLAN_RESET, 0);
+-		udelay(2000);
+-		udelay(2000);
++		mdelay(100);
++		/* Power is up */
++		s3c2410_gpio_setpin(GTA02_CHIP_PWD, 0);
++		mdelay(100);
++		/* Chip is out of reset */
+ 		s3c2410_gpio_setpin(GTA02_GPIO_nWLAN_RESET, 1);
+ 		break;
+ 	}
+@@ -677,6 +715,8 @@
+ 	mangle_glamo_res_by_system_rev();
+ 	platform_device_register(&gta02_glamo_dev);
+ 
++	platform_device_register(&gta02_sdio_dev);
++
+ 	platform_add_devices(gta02_devices, ARRAY_SIZE(gta02_devices));
+ 
+ 	s3c2410_pm_init();
+Index: linux-2.6.22-atheros-ng/include/asm-arm/plat-s3c24xx/devs.h
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/include/asm-arm/plat-s3c24xx/devs.h	2007-11-13 11:45:12.000000000 +0100
++++ linux-2.6.22-atheros-ng/include/asm-arm/plat-s3c24xx/devs.h	2007-11-13 11:45:16.000000000 +0100
+@@ -29,6 +29,7 @@
+ extern struct platform_device s3c_device_rtc;
+ extern struct platform_device s3c_device_adc;
+ extern struct platform_device s3c_device_sdi;
++extern struct platform_device s3c_2440_device_sdi;
+ extern struct platform_device s3c_device_hsmmc;
+ 
+ extern struct platform_device s3c_device_spi0;

Added: developers/sameo/patches/ar6k-ng/150-mmc_powerup_delay.patch
===================================================================
--- developers/sameo/patches/ar6k-ng/150-mmc_powerup_delay.patch	2007-12-10 17:51:36 UTC (rev 3627)
+++ developers/sameo/patches/ar6k-ng/150-mmc_powerup_delay.patch	2007-12-10 22:31:54 UTC (rev 3628)
@@ -0,0 +1,26 @@
+---
+ drivers/mmc/core/core.c |    6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+Index: linux-2.6.22-atheros-ng/drivers/mmc/core/core.c
+===================================================================
+--- linux-2.6.22-atheros-ng.orig/drivers/mmc/core/core.c	2007-12-10 23:28:26.000000000 +0100
++++ linux-2.6.22-atheros-ng/drivers/mmc/core/core.c	2007-12-10 23:28:28.000000000 +0100
+@@ -462,13 +462,15 @@
+ 	host->ios.timing = MMC_TIMING_LEGACY;
+ 	mmc_set_ios(host);
+ 
+-	mmc_delay(1);
++//	mmc_delay(1);
++	mmc_delay(50);
+ 
+ 	host->ios.clock = host->f_min;
+ 	host->ios.power_mode = MMC_POWER_ON;
+ 	mmc_set_ios(host);
+ 
+-	mmc_delay(2);
++//	mmc_delay(2);
++	mmc_delay(50);
+ }
+ 
+ static void mmc_power_off(struct mmc_host *host)

Added: developers/sameo/patches/ar6k-ng/s3c2440_wlan_gpio_cfg.patch
===================================================================
--- developers/sameo/patches/ar6k-ng/s3c2440_wlan_gpio_cfg.patch	2007-12-10 17:51:36 UTC (rev 3627)
+++ developers/sameo/patches/ar6k-ng/s3c2440_wlan_gpio_cfg.patch	2007-12-10 22:31:54 UTC (rev 3628)
@@ -0,0 +1,19 @@
+---
+ arch/arm/mach-s3c2440/mach-gta02.c |    3 +++
+ 1 file changed, 3 insertions(+)
+
+Index: linux-2.6.22/arch/arm/mach-s3c2440/mach-gta02.c
+===================================================================
+--- linux-2.6.22.orig/arch/arm/mach-s3c2440/mach-gta02.c	2007-11-09 15:11:09.000000000 +0100
++++ linux-2.6.22/arch/arm/mach-s3c2440/mach-gta02.c	2007-11-09 15:24:36.000000000 +0100
+@@ -651,7 +651,10 @@
+ 	set_s3c2410ts_info(&gta02_ts_cfg);
+ 
+ 	/* FIXME: hardcoded WLAN module power-up */
++	s3c2410_gpio_cfgpin(GTA02_CHIP_PWD, S3C2410_GPIO_OUTPUT);
+ 	s3c2410_gpio_setpin(GTA02_CHIP_PWD, 0);
++
++	s3c2410_gpio_cfgpin(GTA02_GPIO_nWLAN_RESET, S3C2410_GPIO_OUTPUT);
+ 	switch (system_rev) {
+ 	case GTA02v1_SYSTEM_REV:
+ 		break;

Added: developers/sameo/patches/ar6k-ng/series
===================================================================
--- developers/sameo/patches/ar6k-ng/series	2007-12-10 17:51:36 UTC (rev 3627)
+++ developers/sameo/patches/ar6k-ng/series	2007-12-10 22:31:54 UTC (rev 3628)
@@ -0,0 +1,6 @@
+s3c2440_wlan_gpio_cfg.patch
+100-sdio_merge.patch
+110-s3c24xx_sdio_merge.patch
+130-gta02v2.patch
+140-s3c24xx_mci_platform.patch
+150-mmc_powerup_delay.patch





More information about the commitlog mailing list