diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/Config.in linux-2.4.21-rmk1-omap1/drivers/block/Config.in
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/Config.in linux-2.4.21-rmk1-omap1/drivers/block/Config.in
--- linux-2.4.21-rmk1-omap1-vanilla/drivers/block/Config.in	2002-11-29 01:53:12.000000000 +0200
+++ linux-2.4.21-rmk1-omap1/drivers/block/Config.in	2003-08-28 12:43:00.000000000 +0300
@@ -33,6 +33,10 @@
 if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
    source drivers/block/paride/Config.in
 fi
+bool 'MMC support' CONFIG_MMC_ALT
+if [ "$CONFIG_MMC_ALT" = "y" ]; then
+   source drivers/block/mmc/Config.in
+fi
 dep_tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA $CONFIG_PCI
 dep_tristate 'Compaq Smart Array 5xxx support' CONFIG_BLK_CPQ_CISS_DA $CONFIG_PCI 
 dep_mbool '       SCSI tape drive support for Smart Array 5xxx' CONFIG_CISS_SCSI_TAPE $CONFIG_BLK_CPQ_CISS_DA $CONFIG_SCSI
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/Makefile linux-2.4.21-rmk1-omap1/drivers/block/Makefile
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/Makefile linux-2.4.21-rmk1-omap1/drivers/block/Makefile
--- linux-2.4.21-rmk1-omap1-vanilla/drivers/block/Makefile	2003-08-28 12:27:31.000000000 +0300
+++ linux-2.4.21-rmk1-omap1/drivers/block/Makefile	2003-08-28 12:43:00.000000000 +0300
@@ -33,6 +33,7 @@
 obj-$(CONFIG_BLK_DEV_NBD)	+= nbd.o
 
 subdir-$(CONFIG_PARIDE) += paride
+subdir-$(CONFIG_MMC_ALT)	+= mmc
 
 ifeq ($(CONFIG_ARCH_ACORN),y)
 mod-subdirs	+= ../acorn/block
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/Makefile linux-2.4.21-rmk1-omap1/Makefile
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/Makefile linux-2.4.21-rmk1-omap1/Makefile
--- linux-2.4.21-rmk1-omap1-vanilla/Makefile	2003-08-28 12:27:56.000000000 +0300
+++ linux-2.4.21-rmk1-omap1/Makefile	2003-08-28 12:46:44.000000000 +0300
@@ -179,6 +180,7 @@
 DRIVERS-$(CONFIG_SGI_IP22) += drivers/sgi/sgi.a
 DRIVERS-$(CONFIG_VT) += drivers/video/video.o
 DRIVERS-$(CONFIG_PARIDE) += drivers/block/paride/paride.a
+DRIVERS-$(CONFIG_MMC_ALT) += drivers/block/mmc/mmc.o
 DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o
 DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a
 DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o
Binary files linux-2.4.21-rmk1-omap1-vanilla/arch/arm/boot/Image and linux-2.4.21-rmk1-omap1/arch/arm/boot/Image differ
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/Config.in linux-2.4.21-rmk1-omap1/drivers/block/mmc/Config.in
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/Config.in linux-2.4.21-rmk1-omap1/drivers/block/mmc/Config.in
--- linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/Config.in	1970-01-01 02:00:00.000000000 +0200
+++ linux-2.4.21-rmk1-omap1/drivers/block/mmc/Config.in	2003-08-28 12:30:32.000000000 +0300
@@ -0,0 +1,4 @@
+#
+# MMC driver configuration
+#
+tristate '  OMAP 1510 MMC support' CONFIG_OMAPMMC
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/Makefile linux-2.4.21-rmk1-omap1/drivers/block/mmc/Makefile
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/Makefile linux-2.4.21-rmk1-omap1/drivers/block/mmc/Makefile
--- linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/Makefile	1970-01-01 02:00:00.000000000 +0200
+++ linux-2.4.21-rmk1-omap1/drivers/block/mmc/Makefile	2003-08-28 12:30:32.000000000 +0300
@@ -0,0 +1,27 @@
+# Makefile for the kernel MMC device drivers.
+
+O_TARGET	:= mmc.o
+
+# All of the (potential) objects that export symbols.
+# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
+
+
+export-objs	:=
+
+
+# Object file lists.
+
+obj-y		:=
+obj-m		:=
+obj-n		:=
+obj-		:=
+
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_MMC_ALT)		+= mmc-common.o
+obj-$(CONFIG_OMAPMMC)		+= omap-mmc.o
+
+# Hand off to Rules.make.
+
+include $(TOPDIR)/Rules.make
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/mmc-common.c linux-2.4.21-rmk1-omap1/drivers/block/mmc/mmc-common.c
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/mmc-common.c linux-2.4.21-rmk1-omap1/drivers/block/mmc/mmc-common.c
--- linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/mmc-common.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-2.4.21-rmk1-omap1/drivers/block/mmc/mmc-common.c	2003-08-28 12:30:32.000000000 +0300
@@ -0,0 +1,1304 @@
+/*
+ * linux/drivers/block/mmc-common.c
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Copyright (C) 2003 Teraflops Ltd.
+ *
+ * Written by Tuukka Tikkanen http://www.teraflops.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id: mmc-common.c
+ * $Revision: 1.5
+ * $Date: 2003/03/25
+ *
+ */
+#include <linux/list.h>
+#include <asm/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/blkpg.h>
+#include <linux/interrupt.h>
+#include <linux/hdreg.h>
+
+#include <linux/pm.h>
+#include <linux/cpufreq.h>
+
+#include <linux/mmc.h>
+
+#define MAJOR_NR 242		// TODO: Assign proper major!!!
+#define MMC_NUM_DEVS 16		// 16 cards
+#define MMC_SHIFT 4		// 16 partitions per card
+#define DEVICE_NAME "mmc"
+#define DEVICE_NR(x) (MINOR(x) >> MMC_SHIFT)
+#include <linux/blk.h>
+
+/*
+ * List of things to do:
+ *
+ * Implement HDIO_GETGEO in a manner compatible with other (USB storage)
+ * MMC readers for Linux.
+ *
+ */
+
+#ifdef DEBUG
+#define DPRINTK(x...) printk(x)
+static unsigned char *cur_buf;
+#else
+#define DPRINTK(x...)
+#endif
+
+LIST_HEAD(mmc_devicelist);
+
+struct mmc_card;
+struct mmc_device;
+struct mmc_card;
+
+typedef enum {
+	MMCDEV_DOWN = 0,	// Power down
+	MMCDEV_HARD_RESET = 1,	// Power up + sending CMD0
+	MMCDEV_SOFT_RESET = 2,	// Sending CMD1
+	MMCDEV_VOLTAGE_NEGOTIATING = 3,	// Re-sending CMD1
+	MMCDEV_SCANNING_CARDS = 4,	// (Re)-sending CMD2/3
+	MMCDEV_GETTING_PARAMETERS = 5,	// Querying CSD
+	MMCDEV_OPERATIONAL = 6
+} mmc_dev_state_t;
+
+struct mmc_device {
+	struct list_head list;
+	const struct mmc_device_info *dev;
+	struct list_head cards;
+	struct mmc_card *current_target;
+	wait_queue_head_t scan_wait;
+	mmc_dev_state_t state;
+	int hard_retries;
+	int soft_retries;
+	// Data related to current read/write request
+	struct mmc_card *c_card;
+	u32 c_start;
+	u32 c_blks;
+	int c_write;
+	unsigned char *c_buf;
+	u32 multi_status;
+	struct mmc_command *multi_cmd;
+};
+
+#define MMC_CARD_SELECTED      1
+#define MMC_CARD_CSD_READ      2
+
+struct mmc_card {
+	struct list_head list;
+	struct mmc_device *bus;
+	u32 cid[4];
+	u32 rca;		// Card address on bus
+	s32 size;		// in 512 byte blocks
+	s32 set_block;
+	u32 flags;
+	u32 maxclock;
+};
+
+static void mmc_cmd1_handler(struct mmc_command *cmd);
+static void mmc_cmd2_handler(struct mmc_command *cmd);
+static void mmc_cmd3_handler(struct mmc_command *cmd);
+static void mmc_cmd9_handler(struct mmc_command *cmd);
+static void mmc_soft_reset_handler(struct mmc_command *cmd);
+static void mmc_req_blocklen_handler(struct mmc_command *cmd);
+static void mmc_req_cardsel_handler(struct mmc_command *cmd);
+static void mmc_read_tran_handler(struct mmc_command *cmd);
+static void mmc_read_multi_handler(struct mmc_command *cmd);
+static void mmc_read_stop_handler(struct mmc_command *cmd);
+static void mmc_write_tran_handler(struct mmc_command *cmd);
+static void mmc_status_until_idle_handler(struct mmc_command *cmd);
+static int mmc_handle_cmd_errors(struct mmc_command *cmd);
+static void get_card_parameters(struct mmc_device *dev);
+static int count_valid_cards(struct mmc_device *dev);
+static int slowest_card(struct mmc_device *dev);
+static void mmc_request(request_queue_t *);
+static int mmc_ioctl(struct inode *, struct file *, unsigned int,
+		     unsigned long);
+static int mmc_open(struct inode *, struct file *);
+static int mmc_release(struct inode *, struct file *);
+static int mmc_check_change(kdev_t);
+static int mmc_revalidate(kdev_t);
+static void mmc_trans_ok_fn(unsigned long);
+static void mmc_trans_fail_fn(unsigned long);
+
+static struct block_device_operations mmc_ops = {
+	ioctl:&mmc_ioctl,
+	open:&mmc_open,
+	release:&mmc_release,
+	revalidate:&mmc_revalidate,
+	check_media_change:&mmc_check_change
+};
+
+static struct gendisk mmc_gendisk = {
+	major:MAJOR_NR,
+	major_name:DEVICE_NAME,
+	minor_shift:MMC_SHIFT,
+	max_p:(1 << MMC_SHIFT),
+	fops:&mmc_ops
+};
+
+static u32 nextrca = 0x00020000;
+static int mmc_driver_up = 0;	// Reject low-level driver registrations if 0
+static int mmc_dev_sizes[MMC_NUM_DEVS << MMC_SHIFT];
+static struct hd_struct mmc_partitions[MMC_NUM_DEVS << MMC_SHIFT];
+static volatile int mmc_request_busy;
+static struct mmc_card *mmc_blk_devs[MMC_NUM_DEVS];
+DECLARE_TASKLET(mmc_trans_ok, mmc_trans_ok_fn, 0);
+DECLARE_TASKLET(mmc_trans_fail, mmc_trans_fail_fn, 0);
+
+static int
+mmc_handle_cmd_errors(struct mmc_command *cmd)
+{
+#ifdef DEBUG
+	int ii;
+#endif
+	//
+	// Check for command timeout.
+	// Timeouts use hard retries.
+	//
+	if (cmd->status & MMC_STATUS_CMD_TIMEOUT) {
+		if (cmd->bus->hard_retries > 0) {
+			cmd->bus->hard_retries--;
+			cmd->bus->soft_retries = 5;
+			cmd->bus->dev->start_cmd(cmd);
+			return MMC_STATUS_RETRYING;
+		}
+		return MMC_STATUS_CMD_TIMEOUT;
+	}
+	//
+	// Check for data timeout.
+	// Timeouts use hard retries.
+	//
+	if (cmd->status & MMC_STATUS_DATA_TIMEOUT) {
+		if (cmd->bus->hard_retries > 0) {
+			cmd->bus->hard_retries--;
+			cmd->bus->soft_retries = 5;
+			cmd->bus->dev->start_cmd(cmd);
+			printk("Data timeout, retrying...\n");
+			return MMC_STATUS_RETRYING;
+		}
+		return MMC_STATUS_DATA_TIMEOUT;
+	}
+	//
+	// Check for CRC error in response.
+	// CRC errors use soft retries.
+	// If soft retries are at 0, convert a hard retry into 5 soft retries.
+	// (The code tries bit more in case of CRC errors)
+	//
+	if (cmd->status & MMC_STATUS_CMD_CRC) {
+		if (cmd->bus->soft_retries <= 0 && cmd->bus->hard_retries > 0) {
+			cmd->bus->soft_retries = 5;
+			cmd->bus->hard_retries--;
+		}
+		if (cmd->bus->soft_retries > 0) {
+			cmd->bus->soft_retries--;
+			cmd->bus->dev->start_cmd(cmd);
+			return MMC_STATUS_RETRYING;
+		}
+		return MMC_STATUS_CMD_CRC;
+	}
+	//
+	// Check for CRC error in data stream.
+	// CRC errors use soft retries.
+	// If soft retries are at 0, convert a hard retry into 5 soft retries.
+	// (The code tries bit more in case of CRC errors)
+	//
+	if (cmd->status & MMC_STATUS_DATA_CRC) {
+#ifdef DEBUG
+		for (ii = 0; ii < 512; ii++) {
+			if (!(ii % 16))
+				printk("%03x ", ii);
+			printk(" %02x", cur_buf[ii]);
+			if ((ii % 16) == 15)
+				printk("\n");
+		}
+#endif
+		if (cmd->bus->soft_retries <= 0 && cmd->bus->hard_retries > 0) {
+			cmd->bus->soft_retries = 5;
+			cmd->bus->hard_retries--;
+		}
+		if (cmd->bus->soft_retries > 0) {
+			cmd->bus->soft_retries--;
+			cmd->bus->dev->start_cmd(cmd);
+			printk("Data CRC, retrying...\n");
+			return MMC_STATUS_RETRYING;
+		}
+		return MMC_STATUS_DATA_CRC;
+	}
+	cmd->bus->hard_retries = 5;
+	cmd->bus->soft_retries = 5;
+	return 0;		// OK
+}
+
+static int
+count_valid_cards(struct mmc_device *dev)
+{
+	struct list_head *ptr;
+	struct mmc_card *card;
+	int ret;
+
+	ret = 0;
+	for (ptr = dev->cards.next; ptr != &dev->cards; ptr = ptr->next) {
+		card = list_entry(ptr, struct mmc_card, list);
+		if (!(card->flags & MMC_CARD_CSD_READ))
+			continue;
+		if (card->size == 0)
+			continue;
+		ret++;
+	}
+	return ret;
+}
+
+static int
+slowest_card(struct mmc_device *dev)
+{
+	struct list_head *ptr;
+	struct mmc_card *card;
+	int ret;
+
+	// You may want to adjust this if you experience mysterious CRC
+	// errors, some hardware doesn't seem to be able to keep up maximum
+	// rates.
+	ret = 20000;	// 20MHz maximum is valid for <= 10 cards on single bus
+	for (ptr = dev->cards.next; ptr != &dev->cards; ptr = ptr->next) {
+		card = list_entry(ptr, struct mmc_card, list);
+		if (!(card->flags & MMC_CARD_CSD_READ))
+			continue;
+		if (card->size == 0)
+			continue;
+		if (card->maxclock < ret)
+			ret = card->maxclock;
+	}
+	return ret;
+}
+
+static void
+get_card_parameters(struct mmc_device *dev)
+{
+	// Get CSD for first card that doesn't have size specified
+	struct list_head *ptr;
+	struct mmc_card *entry = 0;
+	struct mmc_command *newcmd;
+
+	for (ptr = dev->cards.next; ptr != &dev->cards; ptr = ptr->next) {
+		entry = list_entry(ptr, struct mmc_card, list);
+		if (!(entry->flags & MMC_CARD_CSD_READ))
+			break;
+	}
+	if (ptr != &dev->cards) {
+		newcmd = kmalloc(sizeof (struct mmc_command), GFP_KERNEL);
+		if (newcmd == 0)
+			return;
+
+		printk("Getting card parameters for RCA %x\n", entry->rca);
+		dev->current_target = entry;
+
+		newcmd->type = MMC_CMDTYPE_AC;
+		newcmd->responsetype = 2;
+		newcmd->flags = 0;
+		newcmd->argument = entry->rca;
+		newcmd->cmd_id = 9;	// SEND_CSD
+		newcmd->bus = dev;
+		newcmd->result_handler = &mmc_cmd9_handler;
+		dev->dev->start_cmd(newcmd);
+		return;
+	}
+	// We have identified and fetched card parameters for all cards on the
+	// bus. Now:
+	// - Increase bus clock rate
+	// - Register block device(s). However, since we are now serving
+	//   interrupt, we will shedule a bottom-half to do it.
+	if (count_valid_cards(dev) > 0) {
+		dev->dev->set_bus_clock(slowest_card(dev));
+	} else {
+		printk("MMC: No valid cards found\n");
+	}
+	wake_up(&dev->scan_wait);
+}
+
+static void
+mmc_cmd9_handler(struct mmc_command *cmd)
+{
+	int ii;
+
+	//
+	// Handle outcome of a CMD9 command. (Used to read card CSD)
+	//
+	// If there is no need to retry (success or permanent failure), then
+	// call get_card_parameters again to see if there are any more cards
+	// on the bus with unknown parameters.
+	//
+
+	//
+	// Some numbers in CSD are expressed in "floating point" format
+	//
+	// Mantissa part
+	static const u32 csd_mantissa[16] = {
+		10, 10, 12, 13, 15, 20, 25, 30,
+		35, 40, 45, 50, 55, 60, 70, 80
+	};
+	// Time exponent (currently not used - this should be used to calculate
+	// data timeout (dto) value in bus cycles.
+#if 0
+	static const u32 csd_exponent1[8] = {
+		1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
+	};
+#endif
+	// Bit rate exponent
+	static const u32 csd_exponent2[8] = {
+		10, 100, 1000, 10000, 10000, 10000, 10000, 10000
+	};
+
+	struct mmc_card *card = cmd->bus->current_target;
+
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_CMD_TIMEOUT:
+	case MMC_STATUS_CMD_CRC:
+		cmd->bus->current_target = 0;
+		card->flags |= MMC_CARD_CSD_READ;
+		get_card_parameters(cmd->bus);
+		kfree(cmd);
+		return;
+	}
+
+	cmd->bus->current_target = 0;
+
+	/*
+	 * CSD received.
+	 * NOTE: CSD doublewords are arranged in little-endian order in command
+	 * structure.
+	 * NOTE for porting: This implementation depends on little-endian
+	 * architecture
+	 */
+	printk("CSD: %08x %08x %08x %08x\n",
+	       cmd->response[0], cmd->response[1],
+	       cmd->response[2], cmd->response[3]);
+
+	// Decode CSD
+	card->flags |= MMC_CARD_CSD_READ;
+	card->size = 1 << ((cmd->response[2] >> 16) & 0xf);
+	printk("Read  block size %d bytes\n",
+	       1 << ((cmd->response[2] >> 16) & 0xf));
+	printk("Write block size %d bytes\n",
+	       1 << ((cmd->response[0] >> 22) & 0xf));
+	if (cmd->response[2] & 0x8000)
+		printk("Partial read  OK\n");
+	if (cmd->response[0] & (1 << 21))
+		printk("Partial write OK\n");
+	card->size *=
+	    ((((cmd->response[2] & 0x3ff) << 2) | ((cmd->
+						    response[1] >> 30) & 0x3)) +
+	     1) * (4 << ((cmd->response[1] >> 15) & 7));
+	card->size /= 512;
+	printk("Card size %d 512-byte blocks (%d bytes)\n", card->size,
+	       card->size * 512);
+	printk("Card size base %d\n",
+	       (((cmd->response[2] & 0x3ff) << 2) | ((cmd->
+						      response[1] >> 30) & 0x3))
+	       + 1);
+	printk("Card size mult %d\n", 4 << ((cmd->response[1] >> 15) & 7));
+
+	card->maxclock = csd_mantissa[(cmd->response[3] & 0x78) >> 3];
+	card->maxclock *= csd_exponent2[cmd->response[3] & 7];
+	printk("Max transfer rate %d kHz\n", card->maxclock);
+
+	for (ii = 0; ii < MMC_NUM_DEVS; ii++) {
+		if (mmc_blk_devs[ii] == 0) {
+			mmc_blk_devs[ii] = card;
+			break;
+		}
+	}
+
+	get_card_parameters(cmd->bus);
+	kfree(cmd);
+
+	return;
+}
+
+static void
+mmc_cmd3_handler(struct mmc_command *cmd)
+{
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_CMD_TIMEOUT:
+	case MMC_STATUS_CMD_CRC:
+		printk(KERN_ERR "MMC: CMD3 failed\n");
+		// TODO: Do something?
+		break;
+	}
+
+	// More cards?
+	cmd->type = MMC_CMDTYPE_BCR;
+	cmd->responsetype = 2;
+	cmd->flags = MMC_CMD_OPENDRAIN;
+	cmd->argument = 0x00000000;
+	cmd->cmd_id = 2;	// ALL_SEND_CID
+	cmd->result_handler = &mmc_cmd2_handler;
+	cmd->bus->dev->start_cmd(cmd);
+	return;
+}
+
+static void
+mmc_cmd2_handler(struct mmc_command *cmd)
+{
+	struct mmc_card *newcard;
+
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_CMD_TIMEOUT:
+	case MMC_STATUS_CMD_CRC:
+		// No more cards
+		get_card_parameters(cmd->bus);
+		kfree(cmd);
+		return;
+	}
+
+	newcard = kmalloc(sizeof (struct mmc_card), GFP_KERNEL);
+	newcard->bus = cmd->bus;
+	newcard->cid[0] = cmd->response[0];
+	newcard->cid[1] = cmd->response[1];
+	newcard->cid[2] = cmd->response[2];
+	newcard->cid[3] = cmd->response[3];
+	newcard->size = 0;
+	newcard->set_block = -1;
+	newcard->flags = 0;
+	list_add_tail(&newcard->list, &cmd->bus->cards);
+
+	printk(KERN_INFO "MMC card detected, CID %08x%08x%08x%08x\n",
+	       newcard->cid[0],
+	       newcard->cid[1], newcard->cid[2], newcard->cid[3]);
+
+	cmd->type = MMC_CMDTYPE_AC;
+	cmd->responsetype = 1;
+	cmd->flags = 0;
+	cmd->argument = newcard->rca = nextrca;
+	cmd->cmd_id = 3;	// SET_RELATIVE_ADDR
+	cmd->result_handler = &mmc_cmd3_handler;
+	nextrca += 0x00010000;
+	cmd->bus->dev->start_cmd(cmd);
+	return;
+}
+
+static void
+mmc_cmd1_handler(struct mmc_command *cmd)
+{
+	cmd->bus->state = MMCDEV_VOLTAGE_NEGOTIATING;
+
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_CMD_TIMEOUT:
+		printk("MMC: No cards found\n");
+		// No cards
+		cmd->bus->state = MMCDEV_DOWN;
+		cmd->bus->dev->set_bus_clock(0);
+		cmd->bus->dev->set_bus_power(0);
+		wake_up(&cmd->bus->scan_wait);
+		kfree(cmd);
+		return;
+	case MMC_STATUS_CMD_CRC:
+		// TODO: ???
+		break;
+	}
+
+	if (!(cmd->response[0] & 0x80000000)) {
+		// At least one card is not ready
+		if (cmd->bus->soft_retries <= 0 && cmd->bus->hard_retries > 0) {
+			cmd->bus->soft_retries = 5;
+			cmd->bus->hard_retries--;
+		}
+		if (cmd->bus->soft_retries > 0) {
+			cmd->bus->soft_retries--;
+			cmd->bus->dev->start_cmd(cmd);
+			return;
+		}
+		/*
+		 * Give up, one or more cards are not willing to enter 
+		 * "ready" state. Proceed to CID/CSD detection in case 
+		 * some cards entered "ready"
+		 */
+	}
+	// All cards ready - identification/CSD
+	cmd->bus->state = MMCDEV_SCANNING_CARDS;
+	cmd->type = MMC_CMDTYPE_BCR;
+	cmd->responsetype = 2;
+	cmd->flags = MMC_CMD_OPENDRAIN;
+	cmd->argument = 0x00000000;
+	cmd->cmd_id = 2;	// ALL_SEND_CID
+	cmd->result_handler = &mmc_cmd2_handler;
+	cmd->bus->dev->start_cmd(cmd);
+}
+
+static void
+mmc_soft_reset_handler(struct mmc_command *cmd)
+{
+	int vbit, volt;
+
+	cmd->bus->state = MMCDEV_SOFT_RESET;
+	cmd->bus->hard_retries = 5;
+	cmd->bus->soft_retries = 5;
+
+	// Proceed to voltage query
+	cmd->type = MMC_CMDTYPE_BCR;
+	cmd->responsetype = 3;
+
+#ifdef CONFIG_ARCH_OMAP1510
+	/* Workaround for a hw bug on Omap-1510 */
+	cmd->flags = MMC_CMD_PREPEND_INIT | MMC_CMD_OPENDRAIN;
+#else
+	cmd->flags =  MMC_CMD_OPENDRAIN;
+#endif
+	cmd->argument = 0x00000000;
+	for (vbit = 8, volt = 200; vbit < 24; vbit++, volt += 10) {
+		if (cmd->bus->dev->voltage_min <= volt + 10 &&
+		    cmd->bus->dev->voltage_max >= volt)
+			cmd->argument |= (1 << vbit);
+	}
+	DPRINTK("MMC: Using voltage argument %08x\n", cmd->argument);
+	cmd->cmd_id = 1;	// SEND_OP_COND
+	cmd->result_handler = &mmc_cmd1_handler;
+	cmd->bus->dev->start_cmd(cmd);
+}
+
+int
+register_mmc_device(const struct mmc_device_info *mmcdev)
+{
+	int ii;
+	struct mmc_device *newdev;
+	struct mmc_command *newcmd;
+
+	if (mmc_driver_up == 0)
+		return -ENOSYS;
+
+	newdev = kmalloc(sizeof (struct mmc_device), GFP_KERNEL);
+	if (newdev == 0)
+		return -ENOMEM;
+
+	MOD_INC_USE_COUNT;
+
+	init_waitqueue_head(&newdev->scan_wait);
+	INIT_LIST_HEAD(&newdev->cards);
+	newdev->dev = mmcdev;
+	newdev->state = MMCDEV_HARD_RESET;
+
+	list_add_tail(&newdev->list, &mmc_devicelist);
+
+	// Start voltage negotiation
+	mmcdev->set_bus_power(mmcdev->voltage_max);
+	mmcdev->set_bus_clock(400);
+	mmcdev->set_data_timeout(20000);
+	newcmd = kmalloc(sizeof (struct mmc_command), GFP_KERNEL);
+	if (newcmd == 0) {
+		newdev->state = MMCDEV_DOWN;
+		mmcdev->set_bus_clock(0);
+		mmcdev->set_bus_power(0);
+		list_del(&newdev->list);
+		MOD_DEC_USE_COUNT;
+		return -ENOMEM;
+	}
+	newcmd->type = MMC_CMDTYPE_BC;
+	newcmd->responsetype = 0;
+	newcmd->flags = MMC_CMD_PREPEND_INIT;
+	newcmd->argument = 0;
+	newcmd->cmd_id = 0;	// GO_IDLE_STATE
+	newcmd->bus = newdev;
+	newcmd->result_handler = &mmc_soft_reset_handler;
+	mmcdev->start_cmd(newcmd);
+	sleep_on(&newdev->scan_wait);
+
+	// Now register all cards that were present
+
+	for (ii = 0; ii < MMC_NUM_DEVS; ii++) {
+		if (mmc_blk_devs[ii] == 0)
+			continue;
+		if (mmc_blk_devs[ii]->bus != newdev)
+			continue;
+		register_disk(&mmc_gendisk, MKDEV(MAJOR_NR, ii << MMC_SHIFT),
+			      1 << MMC_SHIFT, &mmc_ops, mmc_blk_devs[ii]->size);
+	}
+
+	return 0;
+}
+
+int
+unregister_mmc_device(const struct mmc_device_info *mmcdev)
+{
+	struct list_head *ptr;
+	struct mmc_device *entry = 0;
+
+	for (ptr = mmc_devicelist.next; ptr != &mmc_devicelist; ptr = ptr->next) {
+		entry = list_entry(ptr, struct mmc_device, list);
+		if (entry->dev == mmcdev)
+			break;
+	}
+	if (ptr != &mmc_devicelist) {
+		list_del(ptr);
+		kfree(entry);
+		MOD_DEC_USE_COUNT;
+		return 0;
+	}
+	return -ENODEV;
+}
+
+static int
+mmc_ioctl(struct inode *inode, struct file *fi, unsigned int num,
+	  unsigned long arg)
+{
+	struct hd_geometry *geo;
+	switch (num) {
+	case HDIO_GETGEO:
+		/*
+		 * Seems like there is no "standard" for calculating C/H/S for 
+		 * given MMC card. These formulas are compatible with my USB 
+		 * reader, YMMW.
+		 */
+		if (!arg)
+			return -EINVAL;
+		geo = (struct hd_geometry *) arg;
+		if (put_user(8, &geo->heads) ||
+		    put_user(32, &geo->sectors) ||
+		    put_user(mmc_gendisk.part[MINOR(inode->i_rdev)].
+			     nr_sects >> 8, &geo->cylinders)
+		    || put_user(mmc_gendisk.part[MINOR(inode->i_rdev)].
+				start_sect, &geo->start))
+			return -EFAULT;
+		return 0;
+
+	case BLKGETSIZE:
+		if (!access_ok(VERIFY_WRITE, arg, sizeof (long)))
+			return -EFAULT;
+		if (copy_to_user
+		    ((long *) arg,
+		     &mmc_gendisk.part[MINOR(inode->i_rdev)].nr_sects,
+		     sizeof (long)))
+			return -EFAULT;
+		return 0;
+
+	case BLKRRPART:
+		return mmc_revalidate(inode->i_rdev);
+
+	default:
+		return blk_ioctl(inode->i_rdev, num, arg);
+	}
+}
+
+static int
+mmc_open(struct inode *inode, struct file *fi)
+{
+	if (mmc_blk_devs[DEVICE_NR(inode->i_rdev)] == 0)
+		return -ENODEV;
+	if (mmc_gendisk.part[MINOR(inode->i_rdev)].nr_sects == 0)
+		return -ENODEV;
+	// TODO: let physical layer module know so it will increase use count
+	return 0;
+}
+
+static int
+mmc_release(struct inode *inode, struct file *fi)
+{
+	// TODO: let physical layer module know so it will decrease use count
+	return 0;
+}
+
+static int
+mmc_check_change(kdev_t id)
+{
+	return 0;		// TODO: Check if card has changed in this slot
+}
+
+static int
+mmc_revalidate(kdev_t id)
+{
+	int begin;
+	begin = DEVICE_NR(id) << MMC_SHIFT;
+	memset(mmc_gendisk.sizes + begin, 0, (1 << MMC_SHIFT) * sizeof (int));
+	memset(mmc_gendisk.part + begin, 0,
+	       (1 << MMC_SHIFT) * sizeof (struct hd_struct));
+	mmc_gendisk.part[begin].nr_sects = mmc_blk_devs[DEVICE_NR(id)]->size;
+
+	// Read partition table
+	register_disk(&mmc_gendisk, id, 1 << MMC_SHIFT,
+		      &mmc_ops, mmc_blk_devs[DEVICE_NR(id)]->size);
+	return 0;
+}
+
+static void
+mmc_start_data_transfer(struct mmc_device *dev, struct mmc_command *cmd)
+{
+	// cmd points to pre-allocated space that can be used.
+	if (dev->current_target != dev->c_card) {
+		// Card is not currently selected.
+		cmd->type = MMC_CMDTYPE_AC;
+		cmd->responsetype = 1;
+		cmd->flags = MMC_CMD_BUSYRESP;
+		cmd->argument = dev->c_card->rca;
+		cmd->cmd_id = 7;
+		cmd->bus = dev;
+		cmd->result_handler = &mmc_req_cardsel_handler;
+		DPRINTK("Selecting card\n");
+		dev->dev->start_cmd(cmd);
+		return;
+	}
+
+	if (dev->current_target->set_block != 512) {
+		// Wrong block size programmed.
+		cmd->type = MMC_CMDTYPE_AC;
+		cmd->responsetype = 1;
+		cmd->flags = 0;
+		cmd->argument = 512;
+		cmd->cmd_id = 16;
+		cmd->bus = dev;
+		cmd->result_handler = &mmc_req_blocklen_handler;
+		DPRINTK("Setting block length 512\n");
+		dev->dev->start_cmd(cmd);
+		return;
+	}
+
+	cmd->type = MMC_CMDTYPE_ADTC;
+	cmd->responsetype = 1;
+	cmd->argument = 512 * dev->c_start;
+	cmd->data_area = dev->c_buf;
+	cmd->block_size = 512;
+	cmd->bus = dev;
+	if (dev->c_write) {
+		cmd->cmd_id = 24;
+		cmd->flags = 0;
+		cmd->result_handler = &mmc_write_tran_handler;
+	} else {
+		if (dev->c_blks > 1) {
+			cmd->cmd_id = 18;
+			cmd->flags = MMC_CMD_DATADIR_IN | MMC_CMD_MULTIBLOCK;
+			cmd->num_blocks = dev->c_blks;
+			cmd->result_handler = &mmc_read_multi_handler;
+		} else {
+			cmd->cmd_id = 17;
+			cmd->flags = MMC_CMD_DATADIR_IN;
+			cmd->result_handler = &mmc_read_tran_handler;
+		}
+	}
+
+#ifdef DEBUG
+	cur_buf = dev->c_buf;
+#endif
+
+	dev->dev->start_cmd(cmd);
+
+	return;
+}
+
+static void
+mmc_status_handler(struct mmc_command *cmd)
+{
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_CMD_TIMEOUT:
+		printk("Status timeout\n");
+	case MMC_STATUS_CMD_CRC:
+		printk("Status error\n");
+		break;
+	case 0:
+		printk("Card status: %08x, ", cmd->response[0]);
+		switch ((cmd->response[0] >> 9) & 0xf) {
+		case 0:
+			printk("idle ");
+			break;
+		case 1:
+			printk("ready ");
+			break;
+		case 2:
+			printk("ident ");
+			break;
+		case 3:
+			printk("stby ");
+			break;
+		case 4:
+			printk("tran ");
+			break;
+		case 5:
+			printk("data ");
+			break;
+		case 6:
+			printk("rcv ");
+			break;
+		case 7:
+			printk("prg ");
+			break;
+		case 8:
+			printk("dis ");
+			break;
+		default:
+			printk("(unknown state %d)",
+			       (cmd->response[0] >> 9) & 0xf);
+			break;
+		}
+		if (cmd->response[0] & 0x80000000)
+			printk(" OUT_OF_RANGE");
+		if (cmd->response[0] & 0x40000000)
+			printk(" ADDRESS_ERROR");
+		if (cmd->response[0] & 0x20000000)
+			printk(" BLOCK_LEN_ERROR");
+		if (cmd->response[0] & 0x10000000)
+			printk(" ERASE_SEQ_ERROR");
+		if (cmd->response[0] & 0x08000000)
+			printk(" ERASE_PARAM");
+		if (cmd->response[0] & 0x04000000)
+			printk(" WP_VIOLATION");
+		if (cmd->response[0] & 0x02000000)
+			printk(" CARD_IS_LOCKED");
+		if (cmd->response[0] & 0x01000000)
+			printk(" LOCK_UNLOCK_FAILED");
+		if (cmd->response[0] & 0x00800000)
+			printk(" COMMAND_CRC_ERROR");
+		if (cmd->response[0] & 0x00400000)
+			printk(" ILLEGAL_COMMAND");
+		if (cmd->response[0] & 0x00200000)
+			printk(" ECC_FAILED");
+		if (cmd->response[0] & 0x00100000)
+			printk(" INTERNAL_CC_ERROR");
+		if (cmd->response[0] & 0x00080000)
+			printk(" UNKNOWN_ERROR");
+		if (cmd->response[0] & 0x00040000)
+			printk(" UNDERRUN");
+		if (cmd->response[0] & 0x00020000)
+			printk(" OVERRUN");
+		if (cmd->response[0] & 0x00010000)
+			printk(" CID/CSD_OVERWRITE");
+		if (cmd->response[0] & 0x00008000)
+			printk(" WP_ERASE_SKIP");
+		if (cmd->response[0] & 0x00004000)
+			printk(" ECC_DISABLED");
+		if (cmd->response[0] & 0x00002000)
+			printk(" ERASE_RESET");
+
+		if (cmd->response[0] & 0x00000100)
+			printk(" READY_FOR_DATA");
+		if (cmd->response[0] & 0x00000020)
+			printk(" APP_CMD");
+		printk("\n");
+		break;
+	}
+
+	tasklet_schedule(&mmc_trans_fail);
+	kfree(cmd);
+}
+
+static void
+mmc_query_status(struct mmc_device *dev, struct mmc_command *cmd)
+{
+	cmd->type = MMC_CMDTYPE_AC;
+	cmd->responsetype = 1;
+	cmd->flags = 0;
+	cmd->argument = dev->c_card->rca;
+	cmd->cmd_id = 13;
+	cmd->bus = dev;
+	cmd->result_handler = &mmc_status_handler;
+	DPRINTK("MMC: Querying status\n");
+	dev->dev->start_cmd(cmd);
+	return;
+}
+
+static void
+mmc_get_status_until_idle(struct mmc_device *dev, struct mmc_command *cmd)
+{
+	cmd->type = MMC_CMDTYPE_AC;
+	cmd->responsetype = 1;
+	cmd->flags = 0;
+	cmd->argument = dev->c_card->rca;
+	cmd->cmd_id = 13;
+	cmd->bus = dev;
+	cmd->result_handler = &mmc_status_until_idle_handler;
+
+	dev->dev->start_cmd(cmd);
+	return;
+}
+
+static void
+mmc_req_cardsel_handler(struct mmc_command *cmd)
+{
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case 0:
+		DPRINTK("Card selected\n");
+		cmd->bus->current_target = cmd->bus->c_card;
+		mmc_start_data_transfer(cmd->bus, cmd);
+		return;
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_CMD_TIMEOUT:
+		printk("Select timeout\n");
+		break;
+	case MMC_STATUS_CMD_CRC:
+		printk("Select error\n");
+		break;
+	}
+
+	cmd->bus->current_target = 0;
+	tasklet_schedule(&mmc_trans_fail);
+	kfree(cmd);
+	return;
+}
+
+static void
+mmc_req_blocklen_handler(struct mmc_command *cmd)
+{
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case 0:
+		// OK - Return to original plan.
+		DPRINTK("Block length programemd\n");
+		cmd->bus->current_target->set_block = 512;
+		mmc_start_data_transfer(cmd->bus, cmd);
+		return;
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_CMD_TIMEOUT:
+		printk("Blocklen timeout\n");
+		break;
+	case MMC_STATUS_CMD_CRC:
+		printk("Blocklen error\n");
+		break;
+	}
+
+	// Failed
+	cmd->bus->current_target = 0;
+	mmc_query_status(cmd->bus, cmd);
+	return;
+}
+
+static void
+mmc_read_tran_handler(struct mmc_command *cmd)
+{
+	// Called after CMD 17 (read single block) or indirectly after
+	// CMD 12 (stop transmission after read multiple blocks)
+
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case 0:
+		// OK - Schedule tasklet to wrap up this request
+		tasklet_schedule(&mmc_trans_ok);
+		kfree(cmd);
+		return;
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_DATA_TIMEOUT:
+		printk("MMC data timeout\n");
+		break;
+	case MMC_STATUS_DATA_CRC:
+		printk("MMC data CRC error\n");
+		break;
+	case MMC_STATUS_CMD_TIMEOUT:
+		printk("MMC read command response timeout\n");
+		break;
+	case MMC_STATUS_CMD_CRC:
+		printk("MMC read command/response CRC error\n");
+		break;
+	}
+
+	// Failed
+	cmd->bus->current_target = 0;
+	mmc_query_status(cmd->bus, cmd);
+	return;
+}
+
+static void
+mmc_read_stop_handler(struct mmc_command *cmd)
+{
+	struct mmc_command *multi_cmd;
+
+	multi_cmd = cmd->bus->multi_cmd;
+
+	// Called CMD 12 (stop transmission after read multiple blocks)
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case 0:
+		// OK - proceed to check the outcome of the actual command
+		kfree(cmd);
+		mmc_read_tran_handler(multi_cmd);
+		return;
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_CMD_TIMEOUT:
+		printk("MMC stop command response timeout\n");
+		break;
+	case MMC_STATUS_CMD_CRC:
+		printk("MMC stop command/response CRC error\n");
+		break;
+	}
+
+	/*
+	 * Failed
+	 * TODO: Big problems if data wire is flooded with unwanted data! 
+	 * Reset bus!
+	 */
+	cmd->bus->current_target = 0;
+	mmc_query_status(cmd->bus, multi_cmd);
+	kfree(cmd);
+	return;
+}
+
+static void
+mmc_read_multi_handler(struct mmc_command *cmd)
+{
+	struct mmc_command *stop_cmd;
+	//
+	// Check for command timeout.
+	// Timeouts use hard retries.
+	//
+	if (cmd->status & MMC_STATUS_CMD_TIMEOUT) {
+		if (cmd->bus->hard_retries > 0) {
+			cmd->bus->hard_retries--;
+			cmd->bus->soft_retries = 5;
+			cmd->bus->dev->start_cmd(cmd);
+			return;
+		}
+		printk("MMC read multi command response timeout\n");
+		cmd->bus->current_target = 0;
+		mmc_query_status(cmd->bus, cmd);
+		return;
+	}
+	// In any other case (including errors) issue a stop command first
+	stop_cmd = kmalloc(sizeof (struct mmc_command), GFP_ATOMIC);
+	if (stop_cmd == 0)
+		panic("Cannot stop MMC card\n");
+	// If there is no memory to stop the card, we cannot page to make
+	// more memory and hence our chances to recover are pretty slim anyway.
+	stop_cmd->cmd_id = 12;
+	stop_cmd->type = MMC_CMDTYPE_AC;
+	stop_cmd->responsetype = 1;
+	stop_cmd->flags = MMC_CMD_BUSYRESP;
+	stop_cmd->argument = 0;
+	stop_cmd->result_handler = &mmc_read_stop_handler;
+	stop_cmd->bus = cmd->bus;
+
+	cmd->bus->dev->start_cmd(stop_cmd);
+	cmd->bus->multi_status = cmd->status;
+	cmd->bus->multi_cmd = cmd;
+}
+
+static void
+mmc_write_tran_handler(struct mmc_command *cmd)
+{
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case 0:
+		// Write OK - verify card is out of busy state
+		mmc_get_status_until_idle(cmd->bus, cmd);
+		return;
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_DATA_TIMEOUT:
+		printk("MMC data timeout\n");
+		break;
+	case MMC_STATUS_DATA_CRC:
+		printk("MMC data CRC error\n");
+		break;
+	case MMC_STATUS_CMD_TIMEOUT:
+		printk("MMC write command response timeout\n");
+		break;
+	case MMC_STATUS_CMD_CRC:
+		printk("MMC write command/response CRC error\n");
+		break;
+	}
+
+	// Failed
+	cmd->bus->current_target = 0;
+	mmc_query_status(cmd->bus, cmd);
+	return;
+}
+
+static void
+mmc_status_until_idle_handler(struct mmc_command *cmd)
+{
+	switch (mmc_handle_cmd_errors(cmd)) {
+	case 0:
+		// OK - Check if card is still programming
+		if (!(cmd->response[0] & 0x00000100)) {
+			mmc_get_status_until_idle(cmd->bus, cmd);
+			return;
+		}
+
+		if (--cmd->bus->c_blks > 0) {
+			cmd->bus->c_start++;
+			cmd->bus->c_buf += 512;
+			mmc_start_data_transfer(cmd->bus, cmd);
+			return;
+		}
+
+		tasklet_schedule(&mmc_trans_ok);
+		kfree(cmd);
+		return;
+	case MMC_STATUS_RETRYING:
+		return;
+	case MMC_STATUS_CMD_TIMEOUT:
+		printk("MMC post-write status command response timeout\n");
+		break;
+	case MMC_STATUS_CMD_CRC:
+		printk("MMC post-write status command/response CRC error\n");
+		break;
+	}
+
+	tasklet_schedule(&mmc_trans_fail);
+	kfree(cmd);
+	return;
+}
+
+static void
+mmc_trans_fail_fn(unsigned long notused)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	end_request(0);
+	mmc_request_busy = 0;
+	if (!QUEUE_EMPTY)
+		mmc_request(0);
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+static void
+mmc_trans_ok_fn(unsigned long notused)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	end_request(1);
+	mmc_request_busy = 0;
+	if (!QUEUE_EMPTY)
+		mmc_request(0);
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+static void
+mmc_request(request_queue_t * q)
+{
+	struct mmc_device *req_bus;
+	struct mmc_command *cmd;
+
+	if (mmc_request_busy)
+		return;		// io_lock held, no race condition.
+
+	while (1) {
+		INIT_REQUEST;
+		// Does such card exist?
+		if (mmc_blk_devs[CURRENT_DEV] == 0) {
+			end_request(0);
+			continue;
+		}
+		// Access past end of device?
+		if (CURRENT->sector + CURRENT->current_nr_sectors >
+		    mmc_partitions[MINOR(CURRENT->rq_dev)].nr_sects) {
+			end_request(0);
+			continue;
+		}
+
+		if (CURRENT_DEV >= MMC_NUM_DEVS) {
+			end_request(0);
+			continue;
+		}
+
+		mmc_request_busy = 1;
+
+		// return  if operation started succesfully
+		// end_request and continue       if failed
+
+		cmd = kmalloc(sizeof (struct mmc_command), GFP_ATOMIC);
+		if (cmd == 0) {
+			mmc_request_busy = 0;
+			end_request(0);
+			continue;
+		}
+		// First, figure out which bus is going to handle this?
+		req_bus = mmc_blk_devs[CURRENT_DEV]->bus;
+
+		req_bus->c_card = mmc_blk_devs[CURRENT_DEV];
+		req_bus->c_start =
+		    CURRENT->sector +
+		    mmc_partitions[MINOR(CURRENT->rq_dev)].start_sect;
+		req_bus->c_blks = CURRENT->current_nr_sectors;
+		req_bus->c_write = (CURRENT->cmd == WRITE) ? 1 : 0;
+		req_bus->c_buf = CURRENT->buffer;
+
+		mmc_start_data_transfer(req_bus, cmd);
+		return;
+	}
+}
+
+static int __init
+mmc_init(void)
+{
+	int ret;
+	int ii;
+
+	mmc_request_busy = 0;
+	for (ii = 0; ii < MMC_NUM_DEVS; ii++)
+		mmc_blk_devs[ii] = 0;
+
+	ret = register_blkdev(MAJOR_NR, "mmc", &mmc_ops);
+	if (ret < 0) {
+		printk("Could not register major number for mmc device\n");
+		return ret;
+	}
+
+	memset(mmc_dev_sizes, 0, sizeof (mmc_dev_sizes));
+	blk_size[MAJOR_NR] = mmc_dev_sizes;
+
+	memset(mmc_partitions, 0, sizeof (mmc_partitions));
+	mmc_gendisk.sizes = mmc_dev_sizes;
+	mmc_gendisk.part = mmc_partitions;
+	mmc_gendisk.nr_real = 0;
+
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), mmc_request);
+
+	mmc_gendisk.next = gendisk_head;
+	gendisk_head = &mmc_gendisk;
+
+	mmc_driver_up = 1;
+
+	printk("MMC loaded\n");
+	return 0;
+}
+
+static void __exit
+mmc_exit(void)
+{
+	int ii;
+	struct gendisk **ptr;
+
+	if (mmc_driver_up) {
+		for (ii = 0; ii < (MMC_NUM_DEVS << MMC_SHIFT); ii++)
+			fsync_dev(MKDEV(MAJOR_NR, ii));
+		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+		blk_size[MAJOR_NR] = NULL;
+//              blksize_size[MAJOR_NR] = NULL;
+		unregister_blkdev(MAJOR_NR, "mmc");
+		// Remove gendisk interface entry
+		for (ptr = &gendisk_head; *ptr; ptr = &(*ptr)->next) {
+			if (*ptr == &mmc_gendisk) {
+				*ptr = mmc_gendisk.next;
+				break;
+			}
+		}
+	}
+	mmc_driver_up = 0;
+	printk(KERN_INFO "MMC driver unloaded\n");
+}
+
+module_init(mmc_init);
+module_exit(mmc_exit);
+MODULE_LICENSE("GPL");
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/omap-mmc.c linux-2.4.21-rmk1-omap1/drivers/block/mmc/omap-mmc.c
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/omap-mmc.c linux-2.4.21-rmk1-omap1/drivers/block/mmc/omap-mmc.c
--- linux-2.4.21-rmk1-omap1-vanilla/drivers/block/mmc/omap-mmc.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-2.4.21-rmk1-omap1/drivers/block/mmc/omap-mmc.c	2003-08-28 14:55:24.000000000 +0300
@@ -0,0 +1,670 @@
+/*
+ * linux/drivers/block/omap-mmc.c
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Copyright (C) 2003 Teraflops Ltd.
+ *
+ * Written by Tuukka Tikkanen http://www.teraflops.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id: omap-mmc.c
+ * $Revision: 1.5
+ * $Date: 2003/03/25
+ *
+ * NOTE:
+ * In order to make MMC function on EVM platform, you need to change the
+ * pin order on MMC connector, and add a pull-up resistor to counter an 
+ * extra pull-down resistor on EVM mainboard. (Not needed on Innovator)
+ *
+ * Diagram of MMC card (connector side):
+ *  /---------|
+ * /  1234567 |
+ * |	      |
+ * |	      |
+ *
+ * From EVM mainboard, connect directly all pins except:
+ * Pin 1 from EVM mainboard connects to pad 2 of MMC card
+ * Pin 2 from EVM mainboard connects to pad 7 of MMC card
+ * Pin 7 from EVM mainboard is not connected
+ * Add a 10k resistor between pins 4 and 7 of MMC card
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <linux/pm.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/irqs.h>
+
+#include <asm/arch/dma.h>
+
+#include <linux/mmc.h>
+
+#ifdef CONFIG_ARCH_OMAP1510
+#define MMC_BASE	0xfffb7800
+#define USE_DMA
+#define cpu_is_omap_1510()	1
+#else
+#define cpu_is_omap_1510()	0
+#endif
+
+#define MMC_REG_CMD	(MMC_BASE + 0x00)
+#define MMC_REG_ARGL	(MMC_BASE + 0x04)
+#define MMC_REG_ARGH	(MMC_BASE + 0x08)
+#define MMC_REG_CON	(MMC_BASE + 0x0c)
+#define MMC_REG_STAT	(MMC_BASE + 0x10)
+#define MMC_REG_IE	(MMC_BASE + 0x14)
+#define MMC_REG_CTO	(MMC_BASE + 0x18)
+#define MMC_REG_DTO	(MMC_BASE + 0x1c)
+#define MMC_REG_DATA	(MMC_BASE + 0x20)
+#define MMC_REG_BLEN	(MMC_BASE + 0x24)
+#define MMC_REG_NBLK	(MMC_BASE + 0x28)
+#define MMC_REG_BUF	(MMC_BASE + 0x2c)
+
+#define MMC_REG_REV	(MMC_BASE + 0x3c)
+
+#define MMC_REG_RSP0	(MMC_BASE + 0x40)
+#define MMC_REG_RSP1	(MMC_BASE + 0x44)
+#define MMC_REG_RSP2	(MMC_BASE + 0x48)
+#define MMC_REG_RSP3	(MMC_BASE + 0x4c)
+#define MMC_REG_RSP4	(MMC_BASE + 0x50)
+#define MMC_REG_RSP5	(MMC_BASE + 0x54)
+#define MMC_REG_RSP6	(MMC_BASE + 0x58)
+#define MMC_REG_RSP7	(MMC_BASE + 0x5c)
+
+#define OMSTAT_CARD_ERR	    (1<<14)
+#define OMSTAT_CARD_IRQ	    (1<<13)
+#define OMSTAT_OCR_BUSY	    (1<<12)
+#define OMSTAT_A_EMPTY	    (1<<11)
+#define OMSTAT_A_FULL	    (1<<10)
+#define OMSTAT_CMD_CRC	    (1<< 8)
+#define OMSTAT_CMD_TOUT	    (1<< 7)
+#define OMSTAT_DATA_CRC	    (1<< 6)
+#define OMSTAT_DATA_TOUT    (1<< 5)
+#define OMSTAT_END_BUSY	    (1<< 4)
+#define OMSTAT_END_OF_DATA  (1<< 3)
+#define OMSTAT_CARD_BUSY    (1<< 2)
+#define OMSTAT_END_OF_CMD   (1<< 0)
+
+#define FUNC_MUX_CTRL_A_MMC_MASK ((7 << 21) | (7 << 27))
+#define PULL_DWN_CTRL_2_MMC_MASK ((1<<16) | (1<<15) | (1<<13))
+#define PULL_DWN_CTRL_3_MMC_MASK ((1 << 8))
+
+#define MMC_READ(reg) (*((volatile unsigned short *)MMC_REG_##reg))
+#define MMC_WRITE(reg, val) \
+	(*((volatile unsigned short *)MMC_REG_##reg) = (val))
+
+#ifdef CONFIG_ARCH_OMAP1510
+#define MMC_READ_BYTE(reg) \
+	(*((volatile unsigned char *)MMC_REG_##reg))
+#define MMC_WRITE_BYTE(reg, val) \
+	(*((volatile unsigned char *)MMC_REG_##reg) = (val))
+#endif
+
+static int current_volts = 0;
+static int current_clock = 0;
+static int current_dto = 0;
+static struct mmc_command *current_command = 0;
+static unsigned short *dataptr;
+static int dataleft;
+static dma_regs_t *mmc_dma = 0;
+
+static void omapmmc_bus_power(int voltage);
+static void omapmmc_bus_clock(int newclock);
+static void omapmmc_data_timeout(int timeout);
+static int omapmmc_start_command(struct mmc_command *newcmd);
+
+const struct mmc_device_info omapmmc_info = {
+	set_bus_power:&omapmmc_bus_power,
+	set_bus_clock:&omapmmc_bus_clock,
+	set_data_timeout:&omapmmc_data_timeout,
+	start_cmd:&omapmmc_start_command,
+#if defined(CONFIG_OMAP_INNOVATOR)
+	voltage_min:320,
+	voltage_max:340,
+	flags:MMC_CAPABILITY_BUS_POWER_OFF
+#elif defined(CONFIG_OMAP_1510P1)
+	voltage_min:320,
+	voltage_max:340,
+	flags:0
+#else
+#error Please define minimum and maximum *operating* voltages of \
+MMC bus for this platform. If MMC bus power is controllable \
+(on/off and/or voltage) edit omapmmc_bus_power as well and/or add \
+MMC_CAPABILITY_BUS_POWER_OFF to flags.
+#endif
+};
+
+static struct pm_dev *omapmmc_pm;
+
+void
+update_bus_settings(void)
+{
+	int dsor;
+
+	if (current_clock == 0) {
+		/* Disable MMC_SD_CLK */
+		if (cpu_is_omap_1510())
+			outl(inl(MOD_CONF_CTRL_0) & ~(1 << 23), 
+			     MOD_CONF_CTRL_0);
+		dsor = 0;
+	} else {
+		/* Enable MMC_SD_CLK */
+		if (cpu_is_omap_1510())
+			outl(inl(MOD_CONF_CTRL_0) | (1 << 23), 
+			     MOD_CONF_CTRL_0);
+		dsor = 48000 / current_clock;
+		if (dsor < 1)
+			dsor = 1;
+		if (48000 / dsor > current_clock)
+			dsor++;
+		if (dsor > 250)
+			dsor = 250;
+	}
+
+	/* 
+	 * Machine dependant code section
+	 * Each machine capable of controlling BUS power for MMC
+	 * should do it here.
+	 */
+
+#ifdef CONFIG_OMAP_INNOVATOR
+	if (current_volts > 0) {
+		outb(readb(OMAP1510P1_FPGA_POWER) | (1 << 3),
+		     OMAP1510P1_FPGA_POWER);
+	} else {
+		outb(readb(OMAP1510P1_FPGA_POWER) & ~(1 << 3),
+		     OMAP1510P1_FPGA_POWER);
+	}
+#endif
+
+	/* End machine dependant code section */
+
+	MMC_WRITE(CON, dsor | ((current_volts != 0) ? (1 << 11) : 0));
+	MMC_WRITE(CTO, 200);
+	MMC_WRITE(DTO, (current_dto < 65000) ? current_dto : 65000);
+}
+
+static void
+omapmmc_bus_power(int voltage)
+{
+	// Voltage step = 0.01 V
+	if (current_command != 0 && voltage <= 0)
+		return;		// Fail silently if busy
+	if (voltage <= 0)
+		current_volts = 0;	// Off
+	else {
+		current_volts = voltage;
+		if (current_volts < omapmmc_info.voltage_min)
+			current_volts = omapmmc_info.voltage_min;
+		if (current_volts > omapmmc_info.voltage_max)
+			current_volts = omapmmc_info.voltage_max;
+	}
+	update_bus_settings();
+}
+
+static void
+omapmmc_bus_clock(int newclock)
+{
+	// Set new clock frequency, 1 kHz resolution
+	if (current_command != 0 && newclock <= 0)
+		return;		// Fail silently if busy
+	if (newclock > 0)
+		current_clock = newclock;
+	else
+		current_clock = 0;
+	update_bus_settings();
+}
+
+static void
+omapmmc_data_timeout(int timeout)
+{
+	if (current_command != 0)
+		return;		// Fail silently if busy
+	if (timeout > 0)
+		current_dto = timeout;
+	else
+		current_dto = 0;
+}
+
+/*
+ * Takes care of the MMC reads and writes using DMA
+ */
+static int
+omapmmc_dma_transfer(struct mmc_command *mmc_cmd, int write)
+{
+	if (mmc_cmd->block_size != 512)
+		return -ENOMEM;
+
+	if (!write) {
+		if (omap_request_dma(eMMCRx, "MMC read", 0, 0, &mmc_dma))
+			return -ENOMEM;
+
+		/* source TIPB, 16bit */
+		mmc_dma->csdp = 0x000d;
+
+		/* Dst++, sync on MMC Rx */
+		mmc_dma->ccr = 0x4020 | eMMCRx;
+
+		mmc_dma->cicr = 0x23;	// Interrupts
+		mmc_dma->cssa_l = MMC_REG_DATA & 0xffff;
+		mmc_dma->cssa_u = MMC_REG_DATA >> 16;
+		mmc_dma->cfn = 0x10;	// 16 frames/block
+		mmc_dma->cen = 0x10;	// 32 bytes/frame
+		MMC_WRITE(BLEN, mmc_cmd->block_size - 1);
+
+		if (current_command->flags & MMC_CMD_MULTIBLOCK) {
+			mmc_dma->cfn *= mmc_cmd->num_blocks;
+			MMC_WRITE(NBLK, mmc_cmd->num_blocks - 1);
+		} else {
+			MMC_WRITE(NBLK, 0);
+		}
+
+		/* 32 bytes -> AFULL/AEMPTY, enable Rx DMA */
+		MMC_WRITE(BUF, 0x8f0f);
+		omap_dma_setup(eMMCRx, eDmaIn);
+		omap_start_dma(mmc_dma, virt_to_phys(mmc_cmd->data_area), 0);
+
+		return 0;
+	} else {
+		if (omap_request_dma(eMMCTx, "MMC write", 0, 0, &mmc_dma))
+			return -ENOMEM;
+
+		/* Destination TIPB, 16bit */
+		mmc_dma->csdp = 0x0601;
+
+		/* Src++, sync on MMC Tx */
+		mmc_dma->ccr = 0x1020 | eMMCTx;
+		mmc_dma->cicr = 0x23;	// Interrupts
+		mmc_dma->cdsa_l = MMC_REG_DATA & 0xffff;
+		mmc_dma->cdsa_u = MMC_REG_DATA >> 16;
+		mmc_dma->cfn = 0x10;	// 16 frames/block
+		mmc_dma->cen = 0x10;	// 32 bytes/frame
+		MMC_WRITE(BLEN, mmc_cmd->block_size - 1);
+
+		if (current_command->flags & 
+		    MMC_CMD_MULTIBLOCK) {
+			mmc_dma->cfn *= mmc_cmd->num_blocks;
+			MMC_WRITE(NBLK, mmc_cmd->num_blocks - 1);
+		} else {
+			MMC_WRITE(NBLK, 0);
+		}
+
+		/* 32 bytes -> AFULL/AEMPTY, enable Tx DMA */
+		MMC_WRITE(BUF, 0x0f8f);
+		omap_dma_setup(eMMCTx, eDmaOut);
+		omap_start_dma(mmc_dma, virt_to_phys(mmc_cmd->data_area), 0);
+
+		return 0;
+	}
+}
+
+static int
+omapmmc_polling_transfer(struct mmc_command *mmc_cmd)
+{
+	if (cpu_is_omap_1510())
+		printk("MMC: Data transfer without DMA, this may hang.\n");
+	
+	dataptr = (unsigned short *)mmc_cmd->data_area;
+	dataleft = mmc_cmd->block_size;
+	if (mmc_cmd->flags & MMC_CMD_MULTIBLOCK) {
+		dataleft *= mmc_cmd->num_blocks;
+		MMC_WRITE(NBLK, mmc_cmd->num_blocks - 1);
+	} else {
+		MMC_WRITE(NBLK, 0);
+	}
+	MMC_WRITE(BLEN, mmc_cmd->block_size - 1);
+
+	/*
+	 * NOTE: The AE buffer value must be programmed
+	 *       on Omap-1610 for writes to work.
+	 * 
+	 * 32 bytes -> A_FULL, 0 bytes -> A_EMPTY
+	 */
+	MMC_WRITE(BUF, 0x1f1f);
+
+	return 0;
+}
+
+static int
+omapmmc_start_command(struct mmc_command *newcmd)
+{
+	u32 cmdreg;
+
+	if (current_command != 0)
+		return -EBUSY;
+	if (current_volts == 0)
+		return -EIO;	// No power, cannot run commands
+	if (current_clock == 0)
+		return -EIO;	// No clock, cannot run commands
+	if (newcmd->type < MMC_CMDTYPE_BC)
+		return -EINVAL;
+	if (newcmd->type > MMC_CMDTYPE_ADTC)
+		return -EINVAL;
+	if (newcmd->responsetype < 0)
+		return -EINVAL;
+	if (newcmd->responsetype > 6)
+		return -EINVAL;
+
+	current_command = newcmd;
+
+	current_command->status = 0;
+	cmdreg = current_command->cmd_id | 
+		(current_command->type << 12) | 
+		(current_command->responsetype << 8);
+
+	if (current_command->flags & MMC_CMD_OPENDRAIN)
+		cmdreg |= (1 << 6);
+	if (current_command->flags & MMC_CMD_BUSYRESP)
+		cmdreg |= (1 << 11);
+	if (current_command->flags & MMC_CMD_PREPEND_INIT)
+		cmdreg |= (1 << 7);
+	if (current_command->type == MMC_CMDTYPE_ADTC &&
+	    current_command->flags & MMC_CMD_DATADIR_IN)
+		cmdreg |= (1 << 15);
+
+	MMC_WRITE(ARGL, current_command->argument & 0xffff);
+	MMC_WRITE(ARGH, current_command->argument >> 16);
+
+	if (current_command->type == MMC_CMDTYPE_ADTC) {
+
+#ifdef USE_DMA
+		/* Try to use DMA first if available */
+		if (current_command->flags & MMC_CMD_DATADIR_IN) {
+			if (omapmmc_dma_transfer(current_command, 0) != 0)
+				mmc_dma = 0;
+			else
+				dataleft = 0;
+		} else {
+			if (omapmmc_dma_transfer(current_command, 1) != 0)
+				mmc_dma = 0;
+			else
+				dataleft = 0;
+		}
+#endif
+
+		/* Fall back to polling operation if DMA not availabe */
+		if (mmc_dma == 0) {
+			omapmmc_polling_transfer(current_command);
+		}
+	} else {
+		MMC_WRITE(BLEN, 0);
+		MMC_WRITE(NBLK, 0);
+		MMC_WRITE(BUF, 0);
+	}
+
+	MMC_WRITE(IE,
+		  OMSTAT_CARD_ERR | OMSTAT_OCR_BUSY |
+		  OMSTAT_A_EMPTY | OMSTAT_A_FULL |
+		  OMSTAT_CMD_CRC | OMSTAT_CMD_TOUT |
+		  OMSTAT_DATA_CRC | OMSTAT_DATA_TOUT | 
+		  OMSTAT_END_OF_DATA | OMSTAT_END_OF_CMD);
+	MMC_WRITE(CMD, cmdreg);
+
+	return 0;
+}
+
+static void
+omapmmc_irq_handler(int irq, void *notused, struct pt_regs *regs)
+{
+	u16 status;
+	int make_callback;
+	int end_command;
+	int end_transfer;
+	struct mmc_command *tmp_command = 0;
+	int ii;
+
+	if (current_command == 0) {
+		printk("OmapMMC: Spurious interrupt\n");
+		// Spurious interrupt
+		status = MMC_READ(STAT);
+		MMC_WRITE(STAT, status);
+		MMC_WRITE(IE, 0);
+
+		return;
+	}
+
+	make_callback = 0;
+	end_command = 0;
+	end_transfer = 0;
+
+	status = MMC_READ(STAT);
+	//printk("S 0x%04x\n", status);
+
+	if ((status & OMSTAT_A_FULL)
+	    || ((status & OMSTAT_END_OF_DATA) && (dataleft > 0))) {
+		// Buffer almost full
+		ii = dataleft / 2;
+		if (ii > 32)
+			ii = 32;
+		dataleft -= ii * 2;
+		while (ii-- > 0)
+			*dataptr++ = MMC_READ(DATA);
+	}
+
+	if (status & OMSTAT_A_EMPTY) {
+		// Buffer almost empty
+		ii = dataleft / 2;
+		if (ii > 32)
+			ii = 32;
+		dataleft -= ii * 2;
+		while (ii-- > 0)
+			MMC_WRITE(DATA, *dataptr++);
+	}
+
+	if (status & OMSTAT_END_OF_DATA) {
+		// Block sent/received
+		end_transfer = 1;
+		make_callback = 1;
+	}
+
+	if (status & OMSTAT_DATA_TOUT) {
+		// Data timeout
+		current_command->status |= MMC_STATUS_DATA_TIMEOUT;
+		make_callback = end_transfer = 1;
+	}
+
+	if (status & OMSTAT_DATA_CRC) {
+		// Data CRC error
+		current_command->status |= MMC_STATUS_DATA_CRC;
+		printk("OmapMMC: Data CRC, bytes left %d\n", dataleft);
+		make_callback = end_transfer = 1;
+	}
+
+	if (status & OMSTAT_CMD_TOUT) {
+		// Command timeout
+		current_command->status |= MMC_STATUS_CMD_TIMEOUT;
+		make_callback = end_transfer = end_command = 1;
+		make_callback = 1;
+	}
+
+	if (status & OMSTAT_CMD_CRC) {
+		// Command CRC error
+		current_command->status |= MMC_STATUS_CMD_CRC;
+		make_callback = end_transfer = end_command = 1;
+	}
+
+	if (status & OMSTAT_OCR_BUSY) {
+		// OCR Busy
+	}
+
+	if (status & OMSTAT_CARD_ERR) {
+		// Card status error
+		make_callback = end_transfer = end_command = 1;
+	}
+
+	if (status & OMSTAT_END_OF_CMD) {
+		// End of command phase
+		current_command->status |= MMC_STATUS_CMD_COMPLETE;
+		end_command = 1;
+		if (current_command->type != MMC_CMDTYPE_ADTC)
+			make_callback = 1;
+	}
+
+#if 0
+	/* Hardware busy/end busy does not seem to work - possibly related
+	 * to MMC driver missing interrupt when one is generated closely after
+	 * serving one. (Propably due to level interrupt being treated as
+	 * edge interrupt.)
+	 */
+	if (status & OMSTAT_CARD_BUSY) {
+	}
+	if (status & OMSTAT_END_BUSY) {
+	}
+#endif
+
+	MMC_WRITE(STAT, status);	// Reset all status bits that were set
+
+	if (end_transfer && mmc_dma != 0) {
+		omap_free_dma(mmc_dma);
+		mmc_dma = 0;
+	}
+
+	if (end_command && !(status & (3 << 7))) {
+		switch (current_command->responsetype) {
+		case 2:
+			current_command->response[0] =
+			    MMC_READ(RSP0) | (MMC_READ(RSP1) << 16);
+			current_command->response[1] =
+			    MMC_READ(RSP2) | (MMC_READ(RSP3) << 16);
+			current_command->response[2] =
+			    MMC_READ(RSP4) | (MMC_READ(RSP5) << 16);
+			current_command->response[3] =
+			    MMC_READ(RSP6) | (MMC_READ(RSP7) << 16);
+			break;
+		case 1:
+		case 3:
+		case 4:
+		case 5:
+		case 6:
+			current_command->response[0] =
+			    MMC_READ(RSP6) | (MMC_READ(RSP7) << 16);
+			break;
+		}
+	}
+
+	if (make_callback) {
+
+		/* Handler might start new command immediately, so clear
+		 * current_command before proceeding.
+		 */
+		MMC_WRITE(IE, 0);
+		tmp_command = current_command;
+		current_command = 0;
+
+		/* Hardware is done at this point */
+		tmp_command->result_handler(tmp_command);
+	}
+
+	return;
+}
+
+static int
+omapmmc_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
+{
+	printk("XXX in omapmmc_pm_callback\n");
+
+	return 0;
+}
+
+static int omap_1510_mmc_init(void)
+{
+	if (readl(COMP_MODE_CTRL_0) != 0xeaef) {
+		printk("OmapMMC: System is running in 1509 mode.\n"
+		       "	 Update bootloader for 1510 mode.\n");
+		return -EIO;
+	}
+
+	outl(inl(PULL_DWN_CTRL_2) & ~PULL_DWN_CTRL_2_MMC_MASK, 
+	     PULL_DWN_CTRL_2);
+	outl(inl(FUNC_MUX_CTRL_A) & ~FUNC_MUX_CTRL_A_MMC_MASK, 
+	     FUNC_MUX_CTRL_A);
+	outl(inl(FUNC_MUX_CTRL_B) & ~(7 << 0), FUNC_MUX_CTRL_B);
+
+	outl(inl(PULL_DWN_CTRL_3) & ~(1 << 8), PULL_DWN_CTRL_3);
+	outl(inl(FUNC_MUX_CTRL_D) & ~(7 << 12), FUNC_MUX_CTRL_D);
+}
+
+static int __init
+omapmmc_init(void)
+{
+	int ret;
+
+	if (cpu_is_omap_1510()) {
+		if (omap_1510_mmc_init())
+			return -ENODEV;
+	} else {
+		printk("MMC: Unknown Omap type, unable to initialize\n");
+		return -ENODEV;
+	}
+
+	/* 
+	 * Machine dependant code section
+	 * Each machine capable of controlling BUS power for MMC
+	 * should do necessary preparations here.
+	 */
+
+
+	/* End machine dependant code section */
+
+	printk(KERN_INFO "OmapMMC: Hardware revision %d.%d\n",
+	       MMC_READ_BYTE(REV) >> 4, MMC_READ_BYTE(REV) & 15);
+
+	/*
+	 * SA_INTERRUPT is needed as long as MMC interrupt is not treated
+	 * as level interrupt instead of edge.
+	 */
+	if (cpu_is_omap_1510())
+		ret = request_irq(INT_MMC, &omapmmc_irq_handler, 
+				  SA_INTERRUPT, "Omap-1510 MMC", 0);
+	if (ret != 0) {
+		printk(KERN_ERR
+		       "OmapMMC: Could not install interrupt handler\n");
+		return ret;
+	}
+
+	ret = register_mmc_device(&omapmmc_info);
+	if (ret != 0) {
+		printk(KERN_ERR "OmapMMC: Could not register MMC device\n");
+		free_irq(INT_MMC, 0);
+		return ret;
+	}
+
+	omapmmc_pm = pm_register(PM_MTD_DEV, PM_SYS_PCMCIA,
+				 omapmmc_pm_callback);
+	if (omapmmc_pm)
+		omapmmc_pm->data = (void *) &omapmmc_info;
+
+	printk("OmapMMC: Omap-1510 MMC started\n");
+	return 0;
+}
+
+static void __exit
+omapmmc_exit(void)
+{
+	pm_unregister(omapmmc_pm);
+	unregister_mmc_device(&omapmmc_info);
+	printk(KERN_INFO "OmapMMC: Driver unloaded\n");
+}
+
+module_init(omapmmc_init);
+module_exit(omapmmc_exit);
+MODULE_LICENSE("GPL");
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/include/linux/mmc.h linux-2.4.21-rmk1-omap1/include/linux/mmc.h
diff -urN --exclude-from=/home/arm/dontdiff_tml_arm linux-2.4.21-rmk1-omap1-vanilla/include/linux/mmc.h linux-2.4.21-rmk1-omap1/include/linux/mmc.h
--- linux-2.4.21-rmk1-omap1-vanilla/include/linux/mmc.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-2.4.21-rmk1-omap1/include/linux/mmc.h	2003-08-28 13:11:56.000000000 +0300
@@ -0,0 +1,92 @@
+/*
+ * linux/include/linux/mmc.h
+ *
+ * Copyright (C) 2003 Nokia Research Center
+ * Copyright (C) 2003 Teraflops Ltd.
+ *
+ * Written by Tuukka Tikkanen http://www.teraflops.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id: mmc.h
+ * $Revision: 1.5
+ * $Date: 2003/03/25
+ *
+ */
+#ifndef _LINUX_MMC_H
+#define _LINUX_MMC_H
+
+#include <asm/types.h>
+#include <linux/list.h>
+
+struct mmc_device;
+
+#define MMC_CMD_OPENDRAIN     1
+#define MMC_CMD_BUSYRESP      2
+#define MMC_CMD_PREPEND_INIT  4
+#define MMC_CMD_DATADIR_IN    8	 // From card
+#define MMC_CMD_MULTIBLOCK   16
+
+typedef enum {
+	MMC_CMDTYPE_BC =    0,
+	MMC_CMDTYPE_BCR =   1,
+	MMC_CMDTYPE_AC =    2,
+	MMC_CMDTYPE_ADTC =  3
+} mmc_cmdtype_t;
+
+#define MMC_STATUS_CMD_TIMEOUT	  1
+#define MMC_STATUS_DATA_TIMEOUT	  2
+#define MMC_STATUS_CMD_CRC	  4
+#define MMC_STATUS_DATA_CRC	  8
+#define MMC_STATUS_CARD_BUSY	  16
+#define MMC_STATUS_CMD_COMPLETE	  32 // CMD-line activity complete
+#define MMC_STATUS_DATA_COMPLETE  64 // DATA-line activity complete
+#define MMC_STATUS_RETRYING	  8192 // Set and used by generic layer
+
+#define MMC_CAPABILITY_BUS_POWER_OFF 1
+
+struct mmc_command {
+	// In
+	mmc_cmdtype_t	type;
+	int		responsetype; // 0: no resp, 1-6: R1-R6
+	u32		flags;
+	u32		argument;
+	void *		data_area;
+	u32		block_size;
+	u32		num_blocks;  // For MMC_CMD_MULTIBLOCK
+	u32		cmd_id;
+	// Out
+	u32		status;
+	u32		response[4];
+	void		(*result_handler)(struct mmc_command *);
+	// Private to generic MMC layer
+	struct mmc_device *	bus;
+};
+
+struct mmc_device_info {
+	void	(*set_bus_power)(int);	// 1/100 volts
+	void	(*set_bus_clock)(int);	// kHz
+	void	(*set_data_timeout)(int);  // Bus cycles
+	int	(*start_cmd)(struct mmc_command *);
+	// Device capabilities
+	int	voltage_min;
+	int	voltage_max;
+	int	flags;	// Boolean capabilities
+};
+
+extern int register_mmc_device(const struct mmc_device_info *mmcdev);
+extern int unregister_mmc_device(const struct mmc_device_info *mmcdev);
+
+#endif
