bk://bk.arm.linux.org.uk/linux-2.6-mmc
rmk@flint.arm.linux.org.uk|ChangeSet|20040819211027|28392 rmk

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/08/19 22:10:27+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] MMCI optimisations.
#   
#   Optimise register accesses; dereferencing "host" each time introduces
#   extra loads.
#   
#   No need to check MCI_TXFIFOEMPTY when checking if we have a data IRQ.
#   
#   Since we support clock bypass mode, the maximum clock rate is the
#   MCLK rate itself.
# 
# drivers/mmc/mmci.c
#   2004/08/19 22:08:32+01:00 rmk@flint.arm.linux.org.uk +33 -20
#   Optimise register accesses; dereferencing "host" each time introduces
#   extra loads.
#   
#   No need to check MCI_TXFIFOEMPTY when checking if we have a data IRQ.
#   
#   Maximum clock rate is the MCLK rate.
# 
# ChangeSet
#   2004/08/19 21:58:33+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Cleanup: Make MMCI debug macro take host, format and arguments.
# 
# drivers/mmc/mmci.c
#   2004/08/19 21:56:52+01:00 rmk@flint.arm.linux.org.uk +11 -12
#   DBG() takes host, format and arguments.
# 
# ChangeSet
#   2004/08/19 21:51:52+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Avoid potential oops in MMCI.
#   
#   Avoid calling mmci_data_irq if we do not have a data phase.
# 
# drivers/mmc/mmci.c
#   2004/08/19 21:49:50+01:00 rmk@flint.arm.linux.org.uk +1 -1
#   Don't call mmci_data_irq if we have no data phase.
# 
# ChangeSet
#   2004/08/19 21:44:28+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Fix race condition in MMCI write-path data channel.
#   
#   There seems to be a hardware race condition in the data channel where
#   we could end up receiving IRQs for more data than we have available.
#   Fix this by disabling IRQs when we run out of data; if the device
#   really does want more data, we'll underrun and flag an error
#   upstream.
# 
# drivers/mmc/mmci.c
#   2004/08/19 21:42:41+01:00 rmk@flint.arm.linux.org.uk +40 -24
#   Fix race condition in write-path data channel.
# 
# ChangeSet
#   2004/08/19 21:28:08+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Update PXAMCI for later kernels.
#   
#   Remove obsolete platform_device_resource/irq functions.  The driver
#   model now has equivalents, so these can be removed.
#   
#   Use pxa_set_cken() to enable/disable the clock to the MMC interface.
# 
# drivers/mmc/pxamci.c
#   2004/08/19 21:26:22+01:00 rmk@flint.arm.linux.org.uk +5 -26
#   Remove obsolete platform_device_resource/irq functions.  The driver
#   model now has equivalents, so these can be removed.
#   
#   Use pxa_set_cken() to enable/disable the clock to the MMC interface.
# 
# ChangeSet
#   2004/08/19 02:03:19-07:00 akpm@bix.(none) 
#   Merge bk://bk.arm.linux.org.uk/linux-2.6-mmc
#   into bix.(none):/usr/src/bk-mmc
# 
# drivers/Makefile
#   2004/08/19 02:03:15-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# arch/arm/Kconfig
#   2004/08/19 02:03:15-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2004/07/08 10:34:43+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Fix end of request handling.
#   
#   We were mixing end_request with end_that_request_chunk, which
#   is apparantly bad news.  Also, the handycapped pxamci driver
#   was telling us that it had transferred all data successfully
#   on error, which is obviously wrong.
# 
# drivers/mmc/pxamci.c
#   2004/07/08 10:28:32+01:00 rmk@flint.arm.linux.org.uk +4 -1
#   Indicate no data transferred on error.
# 
# drivers/mmc/mmc_queue.c
#   2004/07/08 10:28:11+01:00 rmk@flint.arm.linux.org.uk +2 -6
#   Fix end of request handling - mixing end_request with
#   end_that_request_chunk is apparantly bad.
# 
# drivers/mmc/mmc_block.c
#   2004/07/08 10:27:31+01:00 rmk@flint.arm.linux.org.uk +39 -21
#   Fix end of request handling - mixing end_request with
#   end_that_request_chunk is apparantly bad.
# 
# ChangeSet
#   2004/07/08 10:18:22+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Use a consistent naming to refer to mmc_request,
#         mmc_blk_request and request structures to avoid confusion.
# 
# include/linux/mmc/mmc.h
#   2004/07/08 10:10:04+01:00 rmk@flint.arm.linux.org.uk +3 -3
#   Use a consistent naming to refer to mmc_request, mmc_blk_request
#   and request structures.
# 
# drivers/mmc/pxamci.c
#   2004/07/08 10:10:04+01:00 rmk@flint.arm.linux.org.uk +18 -17
#   Use a consistent naming to refer to mmc_request, mmc_blk_request
#   and request structures.
# 
# drivers/mmc/mmci.h
#   2004/07/08 10:10:03+01:00 rmk@flint.arm.linux.org.uk +1 -1
#   Use a consistent naming to refer to mmc_request, mmc_blk_request
#   and request structures.
# 
# drivers/mmc/mmci.c
#   2004/07/08 10:10:02+01:00 rmk@flint.arm.linux.org.uk +14 -14
#   Use a consistent naming to refer to mmc_request, mmc_blk_request
#   and request structures.
# 
# drivers/mmc/mmc.c
#   2004/07/08 10:10:01+01:00 rmk@flint.arm.linux.org.uk +31 -32
#   Use a consistent naming to refer to mmc_request, mmc_blk_request
#   and request structures.
# 
# drivers/mmc/mmc_block.c
#   2004/07/08 10:09:54+01:00 rmk@flint.arm.linux.org.uk +32 -32
#   Use a consistent naming to refer to mmc_request, mmc_blk_request
#   and request structures.
# 
# ChangeSet
#   2004/07/06 18:34:38+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Fix PXA MCI driver name.
# 
# drivers/mmc/pxamci.c
#   2004/07/06 18:31:33+01:00 rmk@flint.arm.linux.org.uk +1 -1
#   PXA MCI driver name is now pxa2xx-mci.
# 
# ChangeSet
#   2004/06/22 21:06:23+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Fix some review points from Jens Axboe
#   
#   Remove "suspended" flag, queue plugging/unplugging and associated
#   checks, remove check for oversized requests.
# 
# drivers/mmc/mmc_queue.c
#   2004/06/22 21:03:47+01:00 rmk@flint.arm.linux.org.uk +2 -2
#   No need to check if queue is plugged.
#   Check whether dma_mask is zero.
# 
# drivers/mmc/mmc_block.c
#   2004/06/22 21:03:47+01:00 rmk@flint.arm.linux.org.uk +3 -22
#   "suspended" flag is useless; removed.
#   Remove queue plugging/unplugging - not required.
#   Remove check for excessive requests - this check is done by the
#   higher layers.
# 
# ChangeSet
#   2004/06/22 20:34:12+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] MMCI updates.
#   
#   Calculate data timeout correctly.
#   Obtain MCLK rate from clock subsystem.
#   Limit max clock rate properly.
#   Use clock bypass mode for fast data rates.
# 
# drivers/mmc/mmci.h
#   2004/06/22 20:32:03+01:00 rmk@flint.arm.linux.org.uk +6 -7
#   MMCI updates.
#   - Calculate data timeout correctly.
#   - Obtain MCLK rate from clock subsystem.
#   - Limit max clock rate properly.
#   - Use clock bypass mode for fast data rates.
# 
# drivers/mmc/mmci.c
#   2004/06/22 20:32:02+01:00 rmk@flint.arm.linux.org.uk +68 -29
#   MMCI updates.
#   - Calculate data timeout correctly.
#   - Obtain MCLK rate from clock subsystem.
#   - Limit max clock rate properly.
#   - Use clock bypass mode for fast data rates.
# 
# ChangeSet
#   2004/06/01 21:47:40+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Fix PXA MMC interface issues.
#   
#   - Wait for STAT_CLK_EN to clear rather than waiting for the CLK_IS_OFF
#     interrupt when stopping the MMC clock.
#   - Always return the number of data blocks transferred no matter what.
#   - Set the device driver data correctly.
# 
# drivers/mmc/pxamci.c
#   2004/06/01 21:45:43+01:00 rmk@flint.arm.linux.org.uk +22 -17
#   Fix PXA interface issues:
#   - Wait for STAT_CLK_EN to clear rather than waiting for the CLK_IS_OFF
#     interrupt when stopping the MMC clock.
#   - Always return the number of data blocks transferred no matter what.
#   - Set the device driver data correctly.
# 
# ChangeSet
#   2004/05/06 15:07:13+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Add PXA MMC interface support.
#   
#   This patch adds support for the Intel PXA MCI interface.
# 
# drivers/mmc/pxamci.h
#   2004/05/06 15:04:47+01:00 rmk@flint.arm.linux.org.uk +94 -0
# 
# drivers/mmc/pxamci.h
#   2004/05/06 15:04:47+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/pxamci.h
# 
# drivers/mmc/pxamci.c
#   2004/05/06 15:04:40+01:00 rmk@flint.arm.linux.org.uk +590 -0
# 
# drivers/mmc/pxamci.c
#   2004/05/06 15:04:40+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/pxamci.c
# 
# drivers/mmc/Makefile
#   2004/05/06 15:04:40+01:00 rmk@flint.arm.linux.org.uk +1 -0
#   Add PXA MMC Makefile entry.
# 
# drivers/mmc/Kconfig
#   2004/05/06 15:04:39+01:00 rmk@flint.arm.linux.org.uk +10 -0
#   Add PXA MMC Kconfig entry.
# 
# ChangeSet
#   2004/05/06 15:00:42+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Add ARM MMCI Primecell driver.
#   
#   This patch adds initial support for the ARM MMCI Primecell - one of
#   the drivers for a MMC interface.
# 
# drivers/mmc/mmci.h
#   2004/05/06 14:58:22+01:00 rmk@flint.arm.linux.org.uk +149 -0
# 
# drivers/mmc/mmci.h
#   2004/05/06 14:58:22+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/mmci.h
# 
# drivers/mmc/mmci.c
#   2004/05/06 14:58:15+01:00 rmk@flint.arm.linux.org.uk +523 -0
# 
# include/asm-arm/mach/mmc.h
#   2004/05/06 14:58:15+01:00 rmk@flint.arm.linux.org.uk +16 -0
#   Add MMC platform data.
# 
# drivers/mmc/mmci.c
#   2004/05/06 14:58:15+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/mmci.c
# 
# drivers/mmc/Makefile
#   2004/05/06 14:58:15+01:00 rmk@flint.arm.linux.org.uk +1 -0
#   Add ARM MMCI makefile entry.
# 
# drivers/mmc/Kconfig
#   2004/05/06 14:58:15+01:00 rmk@flint.arm.linux.org.uk +10 -0
#   Add ARM MMCI PrimeCell configuration.
# 
# ChangeSet
#   2004/05/06 14:53:21+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Add Kconfig/Makefile changes for MMC support.
#   
#   Since this is currently ARM-only, I've dropped the drivers/Kconfig
#   change for the time being.
# 
# drivers/Makefile
#   2004/05/06 14:51:21+01:00 rmk@flint.arm.linux.org.uk +1 -0
#   Add mmc/ directory to Makefile
# 
# arch/arm/Kconfig
#   2004/05/06 14:51:21+01:00 rmk@flint.arm.linux.org.uk +2 -0
#   Add sourcing of drivers/mmc/Kconfig
# 
# ChangeSet
#   2004/05/06 14:48:15+01:00 rmk@flint.arm.linux.org.uk 
#   [MMC] Add MMC core support
#   
#   This patch adds core support to the Linux kernel for driving MMC
#   interfaces found on embedded devices, such as found in the Intel
#   PXA and ARM MMCI primecell.  This patch does _not_ add support
#   for SD or SDIO cards.
#                                                                                   
#   It is vaguely based upon the handhelds.org MMC code, but the bulk
#   of the core has been rewritten from scratch.
# 
# include/linux/mmc/protocol.h
#   2004/05/06 14:46:03+01:00 rmk@flint.arm.linux.org.uk +203 -0
# 
# include/linux/mmc/protocol.h
#   2004/05/06 14:46:03+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/include/linux/mmc/protocol.h
# 
# include/linux/mmc/mmc.h
#   2004/05/06 14:45:56+01:00 rmk@flint.arm.linux.org.uk +88 -0
# 
# include/linux/mmc/mmc.h
#   2004/05/06 14:45:56+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/include/linux/mmc/mmc.h
# 
# include/linux/mmc/host.h
#   2004/05/06 14:45:49+01:00 rmk@flint.arm.linux.org.uk +102 -0
# 
# include/linux/mmc/host.h
#   2004/05/06 14:45:49+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/include/linux/mmc/host.h
# 
# include/linux/mmc/card.h
#   2004/05/06 14:45:36+01:00 rmk@flint.arm.linux.org.uk +83 -0
# 
# include/linux/mmc/card.h
#   2004/05/06 14:45:36+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/include/linux/mmc/card.h
# 
# include/asm-arm/mach/mmc.h
#   2004/05/06 14:45:28+01:00 rmk@flint.arm.linux.org.uk +0 -0
# 
# include/asm-arm/mach/mmc.h
#   2004/05/06 14:45:28+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/include/asm-arm/mach/mmc.h
# 
# drivers/mmc/mmc_sysfs.c
#   2004/05/06 14:45:14+01:00 rmk@flint.arm.linux.org.uk +231 -0
# 
# drivers/mmc/mmc_sysfs.c
#   2004/05/06 14:45:14+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/mmc_sysfs.c
# 
# drivers/mmc/mmc_queue.h
#   2004/05/06 14:44:59+01:00 rmk@flint.arm.linux.org.uk +29 -0
# 
# drivers/mmc/mmc_queue.h
#   2004/05/06 14:44:59+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/mmc_queue.h
# 
# drivers/mmc/mmc_queue.c
#   2004/05/06 14:44:52+01:00 rmk@flint.arm.linux.org.uk +175 -0
# 
# drivers/mmc/mmc_queue.c
#   2004/05/06 14:44:52+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/mmc_queue.c
# 
# drivers/mmc/mmc_block.c
#   2004/05/06 14:44:45+01:00 rmk@flint.arm.linux.org.uk +498 -0
# 
# drivers/mmc/mmc_block.c
#   2004/05/06 14:44:45+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/mmc_block.c
# 
# drivers/mmc/mmc.h
#   2004/05/06 14:44:38+01:00 rmk@flint.arm.linux.org.uk +16 -0
# 
# drivers/mmc/mmc.h
#   2004/05/06 14:44:38+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/mmc.h
# 
# drivers/mmc/mmc.c
#   2004/05/06 14:44:31+01:00 rmk@flint.arm.linux.org.uk +823 -0
# 
# drivers/mmc/mmc.c
#   2004/05/06 14:44:31+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/mmc.c
# 
# drivers/mmc/Makefile
#   2004/05/06 14:44:24+01:00 rmk@flint.arm.linux.org.uk +19 -0
# 
# drivers/mmc/Makefile
#   2004/05/06 14:44:24+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/Makefile
# 
# drivers/mmc/Kconfig
#   2004/05/06 14:44:16+01:00 rmk@flint.arm.linux.org.uk +32 -0
# 
# drivers/mmc/Kconfig
#   2004/05/06 14:44:16+01:00 rmk@flint.arm.linux.org.uk +0 -0
#   BitKeeper file /usr/src/bk/linux-2.6-mmc/drivers/mmc/Kconfig
# 
diff -Nru a/arch/arm/Kconfig b/arch/arm/Kconfig
--- a/arch/arm/Kconfig	2004-08-20 02:18:22 -07:00
+++ b/arch/arm/Kconfig	2004-08-20 02:18:22 -07:00
@@ -661,6 +661,8 @@
 
 source "drivers/usb/Kconfig"
 
+source "drivers/mmc/Kconfig"
+
 
 menu "Kernel hacking"
 
diff -Nru a/drivers/Makefile b/drivers/Makefile
--- a/drivers/Makefile	2004-08-20 02:18:22 -07:00
+++ b/drivers/Makefile	2004-08-20 02:18:22 -07:00
@@ -50,4 +50,5 @@
 obj-$(CONFIG_MCA)		+= mca/
 obj-$(CONFIG_EISA)		+= eisa/
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq/
+obj-$(CONFIG_MMC)		+= mmc/
 obj-y				+= firmware/
diff -Nru a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/Kconfig	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,52 @@
+#
+# MMC subsystem configuration
+#
+
+menu "MMC/SD Card support"
+
+config MMC
+	tristate "MMC support"
+	help
+	  MMC is the "multi-media card" bus protocol.
+
+	  If you want MMC support, you should say Y here and also
+	  to the specific driver for your MMC interface.
+
+config MMC_DEBUG
+	bool "MMC debugging"
+	depends on MMC != n
+	help
+	  This is an option for use by developers; most people should
+	  say N here.  This enables MMC core and driver debugging.
+
+config MMC_BLOCK
+	tristate "MMC block device driver"
+	depends on MMC
+	default y
+	help
+	  Say Y here to enable the MMC block device driver support.
+	  This provides a block device driver, which you can use to
+	  mount the filesystem. Almost everyone wishing MMC support
+	  should say Y or M here.
+
+config MMC_ARMMMCI
+	tristate "ARM AMBA Multimedia Card Interface support"
+	depends on ARM_AMBA && MMC
+	help
+	  This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card
+	  Interface (PL180 and PL181) support.  If you have an ARM(R)
+	  platform with a Multimedia Card slot, say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_PXA
+	tristate "Intel PXA255 Multimedia Card Interface support"
+	depends on ARCH_PXA && MMC
+	help
+	  This selects the Intel(R) PXA(R) Multimedia card Interface.
+	  If you have a PXA(R) platform with a Multimedia Card slot,
+	  say Y or M here.
+
+	  If unsure, say N.
+
+endmenu
diff -Nru a/drivers/mmc/Makefile b/drivers/mmc/Makefile
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/Makefile	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,21 @@
+#
+# Makefile for the kernel mmc device drivers.
+#
+
+#
+# Core
+#
+obj-$(CONFIG_MMC)		+= mmc_core.o
+
+#
+# Media drivers
+#
+obj-$(CONFIG_MMC_BLOCK)		+= mmc_block.o
+
+#
+# Host drivers
+#
+obj-$(CONFIG_MMC_ARMMMCI)	+= mmci.o
+obj-$(CONFIG_MMC_PXA)		+= pxamci.o
+
+mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
diff -Nru a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/mmc.c	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,822 @@
+/*
+ *  linux/drivers/mmc/mmc.c
+ *
+ *  Copyright (C) 2003 Russell King, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+
+#include "mmc.h"
+
+#ifdef CONFIG_MMC_DEBUG
+#define DBG(x...)	printk(KERN_DEBUG x)
+#else
+#define DBG(x...)	do { } while (0)
+#endif
+
+#define CMD_RETRIES	3
+
+/*
+ * OCR Bit positions to 10s of Vdd mV.
+ */
+static const unsigned short mmc_ocr_bit_to_vdd[] = {
+	150,	155,	160,	165,	170,	180,	190,	200,
+	210,	220,	230,	240,	250,	260,	270,	280,
+	290,	300,	310,	320,	330,	340,	350,	360
+};
+
+static const unsigned int tran_exp[] = {
+	10000,		100000,		1000000,	10000000,
+	0,		0,		0,		0
+};
+
+static const unsigned char tran_mant[] = {
+	0,	10,	12,	13,	15,	20,	25,	30,
+	35,	40,	45,	50,	55,	60,	70,	80,
+};
+
+static const unsigned int tacc_exp[] = {
+	1,	10,	100,	1000,	10000,	100000,	1000000, 10000000,
+};
+
+static const unsigned int tacc_mant[] = {
+	0,	10,	12,	13,	15,	20,	25,	30,
+	35,	40,	45,	50,	55,	60,	70,	80,
+};
+
+
+/**
+ *	mmc_request_done - finish processing an MMC command
+ *	@host: MMC host which completed command
+ *	@mrq: MMC request which completed
+ *
+ *	MMC drivers should call this function when they have completed
+ *	their processing of a command.  This should be called before the
+ *	data part of the command has completed.
+ */
+void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
+{
+	struct mmc_command *cmd = mrq->cmd;
+	int err = mrq->cmd->error;
+	DBG("MMC: req done (%02x): %d: %08x %08x %08x %08x\n", cmd->opcode,
+	    err, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
+
+	if (err && cmd->retries) {
+		cmd->retries--;
+		cmd->error = 0;
+		host->ops->request(host, mrq);
+	} else 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
+mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
+{
+	DBG("MMC: starting cmd %02x arg %08x flags %08x\n",
+	    mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags);
+
+	WARN_ON(host->card_busy == NULL);
+
+	mrq->cmd->error = 0;
+	mrq->cmd->mrq = mrq;
+	if (mrq->data) {
+		mrq->cmd->data = mrq->data;
+		mrq->data->error = 0;
+		mrq->data->mrq = mrq;
+		if (mrq->stop) {
+			mrq->data->stop = mrq->stop;
+			mrq->stop->error = 0;
+			mrq->stop->mrq = mrq;
+		}
+	}
+	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)
+{
+	DECLARE_COMPLETION(complete);
+
+	mrq->done_data = &complete;
+	mrq->done = mmc_wait_done;
+
+	mmc_start_request(host, mrq);
+
+	wait_for_completion(&complete);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(mmc_wait_for_req);
+
+/**
+ *	mmc_wait_for_cmd - start a command and wait for completion
+ *	@host: MMC host to start command
+ *	@cmd: MMC command to start
+ *	@retries: maximum number of retries
+ *
+ *	Start a new MMC command for a host, and wait for the command
+ *	to complete.  Return any error that occurred while the command
+ *	was executing.  Do not attempt to parse the response.
+ */
+int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
+{
+	struct mmc_request mrq;
+
+	BUG_ON(host->card_busy == NULL);
+
+	memset(&mrq, 0, sizeof(struct mmc_request));
+
+	memset(cmd->resp, 0, sizeof(cmd->resp));
+	cmd->retries = retries;
+
+	mrq.cmd = cmd;
+	cmd->data = NULL;
+
+	mmc_wait_for_req(host, &mrq);
+
+	return cmd->error;
+}
+
+EXPORT_SYMBOL(mmc_wait_for_cmd);
+
+
+
+/**
+ *	__mmc_claim_host - exclusively claim a host
+ *	@host: mmc host to claim
+ *	@card: mmc card to claim host for
+ *
+ *	Claim a host for a set of operations.  If a valid card
+ *	is passed and this wasn't the last card selected, select
+ *	the card before returning.
+ *
+ *	Note: you should use mmc_card_claim_host or mmc_claim_host.
+ */
+int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int err = 0;
+
+	add_wait_queue(&host->wq, &wait);
+	spin_lock_irqsave(&host->lock, flags);
+	while (1) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		if (host->card_busy == NULL)
+			break;
+		spin_unlock_irqrestore(&host->lock, flags);
+		schedule();
+		spin_lock_irqsave(&host->lock, flags);
+	}
+	set_current_state(TASK_RUNNING);
+	host->card_busy = card;
+	spin_unlock_irqrestore(&host->lock, flags);
+	remove_wait_queue(&host->wq, &wait);
+
+	if (card != (void *)-1 && host->card_selected != card) {
+		struct mmc_command cmd;
+
+		host->card_selected = card;
+
+		cmd.opcode = MMC_SELECT_CARD;
+		cmd.arg = card->rca << 16;
+		cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+
+		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+	}
+
+	return err;
+}
+
+EXPORT_SYMBOL(__mmc_claim_host);
+
+/**
+ *	mmc_release_host - release a host
+ *	@host: mmc host to release
+ *
+ *	Release a MMC host, allowing others to claim the host
+ *	for their operations.
+ */
+void mmc_release_host(struct mmc_host *host)
+{
+	unsigned long flags;
+
+	BUG_ON(host->card_busy == NULL);
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->card_busy = NULL;
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	wake_up(&host->wq);
+}
+
+EXPORT_SYMBOL(mmc_release_host);
+
+/*
+ * Ensure that no card is selected.
+ */
+static void mmc_deselect_cards(struct mmc_host *host)
+{
+	struct mmc_command cmd;
+
+	if (host->card_selected) {
+		host->card_selected = NULL;
+
+		cmd.opcode = MMC_SELECT_CARD;
+		cmd.arg = 0;
+		cmd.flags = MMC_RSP_NONE;
+
+		mmc_wait_for_cmd(host, &cmd, 0);
+	}
+}
+
+
+static inline void mmc_delay(unsigned int ms)
+{
+	if (ms < HZ / 1000) {
+		yield();
+		mdelay(ms);
+	} else {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(ms * HZ / 1000);
+	}
+}
+
+/*
+ * Mask off any voltages we don't support and select
+ * the lowest voltage
+ */
+static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
+{
+	int bit;
+
+	ocr &= host->ocr_avail;
+
+	bit = ffs(ocr);
+	if (bit) {
+		bit -= 1;
+
+		ocr = 3 << bit;
+
+		host->ios.vdd = bit;
+		host->ops->set_ios(host, &host->ios);
+	} else {
+		ocr = 0;
+	}
+
+	return ocr;
+}
+
+static void mmc_decode_cid(struct mmc_cid *cid, u32 *resp)
+{
+	memset(cid, 0, sizeof(struct mmc_cid));
+
+	cid->manfid	  = resp[0] >> 8;
+	cid->prod_name[0] = resp[0];
+	cid->prod_name[1] = resp[1] >> 24;
+	cid->prod_name[2] = resp[1] >> 16;
+	cid->prod_name[3] = resp[1] >> 8;
+	cid->prod_name[4] = resp[1];
+	cid->prod_name[5] = resp[2] >> 24;
+	cid->prod_name[6] = resp[2] >> 16;
+	cid->prod_name[7] = '\0';
+	cid->hwrev	  = (resp[2] >> 12) & 15;
+	cid->fwrev	  = (resp[2] >> 8) & 15;
+	cid->serial	  = (resp[2] & 255) << 16 | (resp[3] >> 16);
+	cid->month	  = (resp[3] >> 12) & 15;
+	cid->year	  = (resp[3] >> 8) & 15;
+}
+
+static void mmc_decode_csd(struct mmc_csd *csd, u32 *resp)
+{
+	unsigned int e, m;
+
+	csd->mmc_prot	 = (resp[0] >> 26) & 15;
+	m = (resp[0] >> 19) & 15;
+	e = (resp[0] >> 16) & 7;
+	csd->tacc_ns	 = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
+	csd->tacc_clks	 = ((resp[0] >> 8) & 255) * 100;
+
+	m = (resp[0] >> 3) & 15;
+	e = resp[0] & 7;
+	csd->max_dtr	  = tran_exp[e] * tran_mant[m];
+	csd->cmdclass	  = (resp[1] >> 20) & 0xfff;
+
+	e = (resp[2] >> 15) & 7;
+	m = (resp[1] << 2 | resp[2] >> 30) & 0x3fff;
+	csd->capacity	  = (1 + m) << (e + 2);
+
+	csd->read_blkbits = (resp[1] >> 16) & 15;
+}
+
+/*
+ * Locate a MMC card on this MMC host given a CID.
+ */
+static struct mmc_card *
+mmc_find_card(struct mmc_host *host, struct mmc_cid *cid)
+{
+	struct mmc_card *card;
+
+	list_for_each_entry(card, &host->cards, node) {
+		if (memcmp(&card->cid, cid, sizeof(struct mmc_cid)) == 0)
+			return card;
+	}
+	return NULL;
+}
+
+/*
+ * Allocate a new MMC card, and assign a unique RCA.
+ */
+static struct mmc_card *
+mmc_alloc_card(struct mmc_host *host, struct mmc_cid *cid, unsigned int *frca)
+{
+	struct mmc_card *card, *c;
+	unsigned int rca = *frca;
+
+	card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
+	if (!card)
+		return ERR_PTR(-ENOMEM);
+
+	mmc_init_card(card, host);
+	memcpy(&card->cid, cid, sizeof(struct mmc_cid));
+
+ again:
+	list_for_each_entry(c, &host->cards, node)
+		if (c->rca == rca) {
+			rca++;
+			goto again;
+		}
+
+	card->rca = rca;
+
+	*frca = rca;
+
+	return card;
+}
+
+/*
+ * Apply power to the MMC stack.
+ */
+static void mmc_power_up(struct mmc_host *host)
+{
+	struct mmc_command cmd;
+	int bit = fls(host->ocr_avail) - 1;
+
+	host->ios.vdd = bit;
+	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+	host->ios.power_mode = MMC_POWER_UP;
+	host->ops->set_ios(host, &host->ios);
+
+	mmc_delay(1);
+
+	host->ios.clock = host->f_min;
+	host->ios.power_mode = MMC_POWER_ON;
+	host->ops->set_ios(host, &host->ios);
+
+	mmc_delay(2);
+
+	cmd.opcode = MMC_GO_IDLE_STATE;
+	cmd.arg = 0;
+	cmd.flags = MMC_RSP_NONE;
+
+	mmc_wait_for_cmd(host, &cmd, 0);
+}
+
+static void mmc_power_off(struct mmc_host *host)
+{
+	host->ios.clock = 0;
+	host->ios.vdd = 0;
+	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+	host->ios.power_mode = MMC_POWER_OFF;
+	host->ops->set_ios(host, &host->ios);
+}
+
+static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+	struct mmc_command cmd;
+	int i, err = 0;
+
+	cmd.opcode = MMC_SEND_OP_COND;
+	cmd.arg = ocr;
+	cmd.flags = MMC_RSP_SHORT;
+
+	for (i = 100; i; i--) {
+		err = mmc_wait_for_cmd(host, &cmd, 0);
+		if (err != MMC_ERR_NONE)
+			break;
+
+		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+			break;
+
+		err = MMC_ERR_TIMEOUT;
+	}
+
+	if (rocr)
+		*rocr = cmd.resp[0];
+
+	return err;
+}
+
+/*
+ * Discover cards by requesting their CID.  If this command
+ * times out, it is not an error; there are no further cards
+ * to be discovered.  Add new cards to the list.
+ *
+ * Create a mmc_card entry for each discovered card, assigning
+ * it an RCA, and save the CID.
+ */
+static void mmc_discover_cards(struct mmc_host *host)
+{
+	struct mmc_card *card;
+	unsigned int first_rca = 1, err;
+
+	while (1) {
+		struct mmc_command cmd;
+		struct mmc_cid cid;
+
+		cmd.opcode = MMC_ALL_SEND_CID;
+		cmd.arg = 0;
+		cmd.flags = MMC_RSP_LONG | MMC_RSP_CRC;
+
+		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+		if (err == MMC_ERR_TIMEOUT) {
+			err = MMC_ERR_NONE;
+			break;
+		}
+		if (err != MMC_ERR_NONE) {
+			printk(KERN_ERR "%s: error requesting CID: %d\n",
+				host->host_name, err);
+			break;
+		}
+
+		mmc_decode_cid(&cid, cmd.resp);
+
+		card = mmc_find_card(host, &cid);
+		if (!card) {
+			card = mmc_alloc_card(host, &cid, &first_rca);
+			if (IS_ERR(card)) {
+				err = PTR_ERR(card);
+				break;
+			}
+			list_add(&card->node, &host->cards);
+		}
+
+		card->state &= ~MMC_STATE_DEAD;
+
+		cmd.opcode = MMC_SET_RELATIVE_ADDR;
+		cmd.arg = card->rca << 16;
+		cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+
+		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+		if (err != MMC_ERR_NONE)
+			card->state |= MMC_STATE_DEAD;
+	}
+}
+
+static void mmc_read_csds(struct mmc_host *host)
+{
+	struct mmc_card *card;
+
+	list_for_each_entry(card, &host->cards, node) {
+		struct mmc_command cmd;
+		int err;
+
+		if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
+			continue;
+
+		cmd.opcode = MMC_SEND_CSD;
+		cmd.arg = card->rca << 16;
+		cmd.flags = MMC_RSP_LONG | MMC_RSP_CRC;
+
+		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+		if (err != MMC_ERR_NONE) {
+			card->state |= MMC_STATE_DEAD;
+			continue;
+		}
+
+		mmc_decode_csd(&card->csd, cmd.resp);
+	}
+}
+
+static unsigned int mmc_calculate_clock(struct mmc_host *host)
+{
+	struct mmc_card *card;
+	unsigned int max_dtr = host->f_max;
+
+	list_for_each_entry(card, &host->cards, node)
+		if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr)
+			max_dtr = card->csd.max_dtr;
+
+	DBG("MMC: selected %d.%03dMHz transfer rate\n",
+	    max_dtr / 1000000, (max_dtr / 1000) % 1000);
+
+	return max_dtr;
+}
+
+/*
+ * Check whether cards we already know about are still present.
+ * We do this by requesting status, and checking whether a card
+ * responds.
+ *
+ * A request for status does not cause a state change in data
+ * transfer mode.
+ */
+static void mmc_check_cards(struct mmc_host *host)
+{
+	struct list_head *l, *n;
+
+	mmc_deselect_cards(host);
+
+	list_for_each_safe(l, n, &host->cards) {
+		struct mmc_card *card = mmc_list_to_card(l);
+		struct mmc_command cmd;
+		int err;
+
+		cmd.opcode = MMC_SEND_STATUS;
+		cmd.arg = card->rca << 16;
+		cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+
+		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+		if (err == MMC_ERR_NONE)
+			continue;
+
+		card->state |= MMC_STATE_DEAD;
+	}
+}
+
+static void mmc_setup(struct mmc_host *host)
+{
+	if (host->ios.power_mode != MMC_POWER_ON) {
+		int err;
+		u32 ocr;
+
+		mmc_power_up(host);
+
+		err = mmc_send_op_cond(host, 0, &ocr);
+		if (err != MMC_ERR_NONE)
+			return;
+
+		host->ocr = mmc_select_voltage(host, ocr);
+	} else {
+		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+		host->ios.clock = host->f_min;
+		host->ops->set_ios(host, &host->ios);
+
+		/*
+		 * We should remember the OCR mask from the existing
+		 * cards, and detect the new cards OCR mask, combine
+		 * the two and re-select the VDD.  However, if we do
+		 * change VDD, we should do an idle, and then do a
+		 * full re-initialisation.  We would need to notify
+		 * drivers so that they can re-setup the cards as
+		 * well, while keeping their queues at bay.
+		 *
+		 * For the moment, we take the easy way out - if the
+		 * new cards don't like our currently selected VDD,
+		 * they drop off the bus.
+		 */
+	}
+
+	if (host->ocr == 0)
+		return;
+
+	/*
+	 * Send the selected OCR multiple times... until the cards
+	 * all get the idea that they should be ready for CMD2.
+	 * (My SanDisk card seems to need this.)
+	 */
+	mmc_send_op_cond(host, host->ocr, NULL);
+
+	mmc_discover_cards(host);
+
+	/*
+	 * Ok, now switch to push-pull mode.
+	 */
+	host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+	host->ops->set_ios(host, &host->ios);
+
+	mmc_read_csds(host);
+}
+
+
+/**
+ *	mmc_detect_change - process change of state on a MMC socket
+ *	@host: host which changed state.
+ *
+ *	All we know is that card(s) have been inserted or removed
+ *	from the socket(s).  We don't know which socket or cards.
+ */
+void mmc_detect_change(struct mmc_host *host)
+{
+	schedule_work(&host->detect);
+}
+
+EXPORT_SYMBOL(mmc_detect_change);
+
+
+static void mmc_rescan(void *data)
+{
+	struct mmc_host *host = data;
+	struct list_head *l, *n;
+
+	mmc_claim_host(host);
+
+	if (host->ios.power_mode == MMC_POWER_ON)
+		mmc_check_cards(host);
+
+	mmc_setup(host);
+
+	if (!list_empty(&host->cards)) {
+		/*
+		 * (Re-)calculate the fastest clock rate which the
+		 * attached cards and the host support.
+		 */
+		host->ios.clock = mmc_calculate_clock(host);
+		host->ops->set_ios(host, &host->ios);
+	}
+
+	mmc_release_host(host);
+
+	list_for_each_safe(l, n, &host->cards) {
+		struct mmc_card *card = mmc_list_to_card(l);
+
+		/*
+		 * If this is a new and good card, register it.
+		 */
+		if (!mmc_card_present(card) && !mmc_card_dead(card)) {
+			if (mmc_register_card(card))
+				card->state |= MMC_STATE_DEAD;
+			else
+				card->state |= MMC_STATE_PRESENT;
+		}
+
+		/*
+		 * If this card is dead, destroy it.
+		 */
+		if (mmc_card_dead(card)) {
+			list_del(&card->node);
+			mmc_remove_card(card);
+		}
+	}
+
+	/*
+	 * If we discover that there are no cards on the
+	 * bus, turn off the clock and power down.
+	 */
+	if (list_empty(&host->cards))
+		mmc_power_off(host);
+}
+
+
+/**
+ *	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 = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
+	if (host) {
+		memset(host, 0, sizeof(struct mmc_host) + extra);
+
+		host->priv = host + 1;
+
+		spin_lock_init(&host->lock);
+		init_waitqueue_head(&host->wq);
+		INIT_LIST_HEAD(&host->cards);
+		INIT_WORK(&host->detect, mmc_rescan, host);
+
+		host->dev = dev;
+	}
+
+	return host;
+}
+
+EXPORT_SYMBOL(mmc_alloc_host);
+
+/**
+ *	mmc_add_host - initialise host hardware
+ *	@host: mmc host
+ */
+int mmc_add_host(struct mmc_host *host)
+{
+	static unsigned int host_num;
+
+	snprintf(host->host_name, sizeof(host->host_name),
+		 "mmc%d", host_num++);
+
+	mmc_power_off(host);
+	mmc_detect_change(host);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(mmc_add_host);
+
+/**
+ *	mmc_remove_host - remove host hardware
+ *	@host: mmc host
+ *
+ *	Unregister and remove all cards associated with this host,
+ *	and power down the MMC bus.
+ */
+void mmc_remove_host(struct mmc_host *host)
+{
+	struct list_head *l, *n;
+
+	list_for_each_safe(l, n, &host->cards) {
+		struct mmc_card *card = mmc_list_to_card(l);
+
+		mmc_remove_card(card);
+	}
+
+	mmc_power_off(host);
+}
+
+EXPORT_SYMBOL(mmc_remove_host);
+
+/**
+ *	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)
+{
+	flush_scheduled_work();
+	kfree(host);
+}
+
+EXPORT_SYMBOL(mmc_free_host);
+
+#ifdef CONFIG_PM
+
+/**
+ *	mmc_suspend_host - suspend a host
+ *	@host: mmc host
+ *	@state: suspend mode (PM_SUSPEND_xxx)
+ */
+int mmc_suspend_host(struct mmc_host *host, u32 state)
+{
+	mmc_claim_host(host);
+	mmc_deselect_cards(host);
+	mmc_power_off(host);
+	mmc_release_host(host);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(mmc_suspend_host);
+
+/**
+ *	mmc_resume_host - resume a previously suspended host
+ *	@host: mmc host
+ */
+int mmc_resume_host(struct mmc_host *host)
+{
+	mmc_detect_change(host);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(mmc_resume_host);
+
+#endif
+
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/mmc/mmc.h b/drivers/mmc/mmc.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/mmc.h	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,16 @@
+/*
+ *  linux/drivers/mmc/mmc.h
+ *
+ *  Copyright (C) 2003 Russell King, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _MMC_H
+#define _MMC_H
+/* core-internal functions */
+void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
+int mmc_register_card(struct mmc_card *card);
+void mmc_remove_card(struct mmc_card *card);
+#endif
diff -Nru a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/mmc_block.c	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,497 @@
+/*
+ * Block driver for media (i.e., flash cards)
+ *
+ * Copyright 2002 Hewlett-Packard Company
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Author:  Andrew Christian
+ *          28 May 2002
+ */
+#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/protocol.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "mmc_queue.h"
+
+/*
+ * max 8 partitions per card
+ */
+#define MMC_SHIFT	3
+
+static int mmc_major;
+static int maxsectors = 8;
+
+/*
+ * There is one mmc_blk_data per slot.
+ */
+struct mmc_blk_data {
+	spinlock_t	lock;
+	struct gendisk	*disk;
+	struct mmc_queue queue;
+
+	unsigned int	usage;
+	unsigned int	block_bits;
+};
+
+static DECLARE_MUTEX(open_lock);
+
+static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
+{
+	struct mmc_blk_data *md;
+
+	down(&open_lock);
+	md = disk->private_data;
+	if (md && md->usage == 0)
+		md = NULL;
+	if (md)
+		md->usage++;
+	up(&open_lock);
+
+	return md;
+}
+
+static void mmc_blk_put(struct mmc_blk_data *md)
+{
+	down(&open_lock);
+	md->usage--;
+	if (md->usage == 0) {
+		put_disk(md->disk);
+		mmc_cleanup_queue(&md->queue);
+		kfree(md);
+	}
+	up(&open_lock);
+}
+
+static int mmc_blk_open(struct inode *inode, struct file *filp)
+{
+	struct mmc_blk_data *md;
+	int ret = -ENXIO;
+
+	md = mmc_blk_get(inode->i_bdev->bd_disk);
+	if (md) {
+		if (md->usage == 2)
+			check_disk_change(inode->i_bdev);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int mmc_blk_release(struct inode *inode, struct file *filp)
+{
+	struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data;
+
+	mmc_blk_put(md);
+	return 0;
+}
+
+static int
+mmc_blk_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct block_device *bdev = inode->i_bdev;
+
+	if (cmd == HDIO_GETGEO) {
+		struct hd_geometry geo;
+
+		memset(&geo, 0, sizeof(struct hd_geometry));
+
+		geo.cylinders	= get_capacity(bdev->bd_disk) / (4 * 16);
+		geo.heads	= 4;
+		geo.sectors	= 16;
+		geo.start	= get_start_sect(bdev);
+
+		return copy_to_user((void *)arg, &geo, sizeof(geo))
+			? -EFAULT : 0;
+	}
+
+	return -ENOTTY;
+}
+
+static struct block_device_operations mmc_bdops = {
+	.open			= mmc_blk_open,
+	.release		= mmc_blk_release,
+	.ioctl			= mmc_blk_ioctl,
+	.owner			= THIS_MODULE,
+};
+
+struct mmc_blk_request {
+	struct mmc_request	mrq;
+	struct mmc_command	cmd;
+	struct mmc_command	stop;
+	struct mmc_data		data;
+};
+
+static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req)
+{
+	struct mmc_blk_data *md = mq->data;
+	int stat = BLKPREP_OK;
+
+	/*
+	 * If we have no device, we haven't finished initialising.
+	 */
+	if (!md || !mq->card) {
+		printk(KERN_ERR "%s: killing request - no device/host\n",
+		       req->rq_disk->disk_name);
+		stat = BLKPREP_KILL;
+	}
+
+	return stat;
+}
+
+static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+{
+	struct mmc_blk_data *md = mq->data;
+	struct mmc_card *card = md->queue.card;
+	int ret;
+
+	if (mmc_card_claim_host(card))
+		goto cmd_err;
+
+	do {
+		struct mmc_blk_request brq;
+		struct mmc_command cmd;
+
+		memset(&brq, 0, sizeof(struct mmc_blk_request));
+		brq.mrq.cmd = &brq.cmd;
+		brq.mrq.data = &brq.data;
+
+		brq.cmd.arg = req->sector << 9;
+		brq.cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+		brq.data.req = req;
+		brq.data.timeout_ns = card->csd.tacc_ns * 10;
+		brq.data.timeout_clks = card->csd.tacc_clks * 10;
+		brq.data.blksz_bits = md->block_bits;
+		brq.data.blocks = req->current_nr_sectors >> (md->block_bits - 9);
+		brq.stop.opcode = MMC_STOP_TRANSMISSION;
+		brq.stop.arg = 0;
+		brq.stop.flags = MMC_RSP_SHORT | MMC_RSP_CRC | MMC_RSP_BUSY;
+
+		if (rq_data_dir(req) == READ) {
+			brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
+			brq.data.flags |= MMC_DATA_READ;
+		} else {
+			brq.cmd.opcode = MMC_WRITE_BLOCK;
+			brq.cmd.flags |= MMC_RSP_BUSY;
+			brq.data.flags |= MMC_DATA_WRITE;
+			brq.data.blocks = 1;
+		}
+		brq.mrq.stop = brq.data.blocks > 1 ? &brq.stop : NULL;
+
+		mmc_wait_for_req(card->host, &brq.mrq);
+		if (brq.cmd.error) {
+			printk(KERN_ERR "%s: error %d sending read/write command\n",
+			       req->rq_disk->disk_name, brq.cmd.error);
+			goto cmd_err;
+		}
+
+		if (brq.data.error) {
+			printk(KERN_ERR "%s: error %d transferring data\n",
+			       req->rq_disk->disk_name, brq.data.error);
+			goto cmd_err;
+		}
+
+		if (brq.stop.error) {
+			printk(KERN_ERR "%s: error %d sending stop command\n",
+			       req->rq_disk->disk_name, brq.stop.error);
+			goto cmd_err;
+		}
+
+		do {
+			int err;
+
+			cmd.opcode = MMC_SEND_STATUS;
+			cmd.arg = card->rca << 16;
+			cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+			err = mmc_wait_for_cmd(card->host, &cmd, 5);
+			if (err) {
+				printk(KERN_ERR "%s: error %d requesting status\n",
+				       req->rq_disk->disk_name, err);
+				goto cmd_err;
+			}
+		} while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+
+#if 0
+		if (cmd.resp[0] & ~0x00000900)
+			printk(KERN_ERR "%s: status = %08x\n",
+			       req->rq_disk->disk_name, cmd.resp[0]);
+		if (mmc_decode_status(cmd.resp))
+			goto cmd_err;
+#endif
+
+		/*
+		 * A block was successfully transferred.
+		 */
+		spin_lock_irq(&md->lock);
+		ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered);
+		if (!ret) {
+			/*
+			 * The whole request completed successfully.
+			 */
+			add_disk_randomness(req->rq_disk);
+			blkdev_dequeue_request(req);
+			end_that_request_last(req);
+		}
+		spin_unlock_irq(&md->lock);
+	} while (ret);
+
+	mmc_card_release_host(card);
+
+	return 1;
+
+ cmd_err:
+	mmc_card_release_host(card);
+
+	/*
+	 * This is a little draconian, but until we get proper
+	 * error handling sorted out here, its the best we can
+	 * do - especially as some hosts have no idea how much
+	 * data was transferred before the error occurred.
+	 */
+	spin_lock_irq(&md->lock);
+	do {
+		ret = end_that_request_chunk(req, 0,
+				req->current_nr_sectors << 9);
+	} while (ret);
+
+	add_disk_randomness(req->rq_disk);
+	blkdev_dequeue_request(req);
+	end_that_request_last(req);
+	spin_unlock_irq(&md->lock);
+
+	return 0;
+}
+
+#define MMC_NUM_MINORS	(256 >> MMC_SHIFT)
+
+static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))];
+
+static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
+{
+	struct mmc_blk_data *md;
+	int devidx, ret;
+
+	devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);
+	if (devidx >= MMC_NUM_MINORS)
+		return ERR_PTR(-ENOSPC);
+	__set_bit(devidx, dev_use);
+
+	md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
+	if (md) {
+		memset(md, 0, sizeof(struct mmc_blk_data));
+
+		md->disk = alloc_disk(1 << MMC_SHIFT);
+		if (md->disk == NULL) {
+			kfree(md);
+			md = ERR_PTR(-ENOMEM);
+			goto out;
+		}
+
+		spin_lock_init(&md->lock);
+		md->usage = 1;
+
+		ret = mmc_init_queue(&md->queue, card, &md->lock);
+		if (ret) {
+			put_disk(md->disk);
+			kfree(md);
+			md = ERR_PTR(ret);
+			goto out;
+		}
+		md->queue.prep_fn = mmc_blk_prep_rq;
+		md->queue.issue_fn = mmc_blk_issue_rq;
+		md->queue.data = md;
+
+		md->disk->major	= mmc_major;
+		md->disk->first_minor = devidx << MMC_SHIFT;
+		md->disk->fops = &mmc_bdops;
+		md->disk->private_data = md;
+		md->disk->queue = md->queue.queue;
+		md->disk->driverfs_dev = &card->dev;
+
+		sprintf(md->disk->disk_name, "mmcblk%d", devidx);
+		sprintf(md->disk->devfs_name, "mmc/blk%d", devidx);
+
+		md->block_bits = md->queue.card->csd.read_blkbits;
+
+		blk_queue_max_sectors(md->queue.queue, maxsectors);
+		blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits);
+		set_capacity(md->disk, md->queue.card->csd.capacity);
+	}
+ out:
+	return md;
+}
+
+static int
+mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
+{
+	struct mmc_command cmd;
+	int err;
+
+	mmc_card_claim_host(card);
+	cmd.opcode = MMC_SET_BLOCKLEN;
+	cmd.arg = 1 << card->csd.read_blkbits;
+	cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC;
+	err = mmc_wait_for_cmd(card->host, &cmd, 5);
+	mmc_card_release_host(card);
+
+	if (err) {
+		printk(KERN_ERR "%s: unable to set block size to %d: %d\n",
+			md->disk->disk_name, cmd.arg, err);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mmc_blk_probe(struct mmc_card *card)
+{
+	struct mmc_blk_data *md;
+	int err;
+
+	if (card->csd.cmdclass & ~0x1ff)
+		return -ENODEV;
+
+	if (card->csd.read_blkbits < 9) {
+		printk(KERN_WARNING "%s: read blocksize too small (%u)\n",
+			mmc_card_id(card), 1 << card->csd.read_blkbits);
+		return -ENODEV;
+	}
+
+	md = mmc_blk_alloc(card);
+	if (IS_ERR(md))
+		return PTR_ERR(md);
+
+	err = mmc_blk_set_blksize(md, card);
+	if (err)
+		goto out;
+
+	printk(KERN_INFO "%s: %s %s %dKiB\n",
+		md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
+		(card->csd.capacity << card->csd.read_blkbits) / 1024);
+
+	mmc_set_drvdata(card, md);
+	add_disk(md->disk);
+	return 0;
+
+ out:
+	mmc_blk_put(md);
+
+	return err;
+}
+
+static void mmc_blk_remove(struct mmc_card *card)
+{
+	struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+	if (md) {
+		int devidx;
+
+		del_gendisk(md->disk);
+
+		/*
+		 * I think this is needed.
+		 */
+		md->disk->queue = NULL;
+
+		devidx = md->disk->first_minor >> MMC_SHIFT;
+		__clear_bit(devidx, dev_use);
+
+		mmc_blk_put(md);
+	}
+	mmc_set_drvdata(card, NULL);
+}
+
+#ifdef CONFIG_PM
+static int mmc_blk_suspend(struct mmc_card *card, u32 state)
+{
+	struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+	if (md) {
+		blk_stop_queue(md->queue.queue);
+	}
+	return 0;
+}
+
+static int mmc_blk_resume(struct mmc_card *card)
+{
+	struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+	if (md) {
+		mmc_blk_set_blksize(md, md->queue.card);
+		blk_start_queue(md->queue.queue);
+	}
+	return 0;
+}
+#else
+#define	mmc_blk_suspend	NULL
+#define mmc_blk_resume	NULL
+#endif
+
+static struct mmc_driver mmc_driver = {
+	.drv		= {
+		.name	= "mmcblk",
+	},
+	.probe		= mmc_blk_probe,
+	.remove		= mmc_blk_remove,
+	.suspend	= mmc_blk_suspend,
+	.resume		= mmc_blk_resume,
+};
+
+static int __init mmc_blk_init(void)
+{
+	int res = -ENOMEM;
+
+	res = register_blkdev(mmc_major, "mmc");
+	if (res < 0) {
+		printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n",
+		       mmc_major, res);
+		goto out;
+	}
+	if (mmc_major == 0)
+		mmc_major = res;
+
+	devfs_mk_dir("mmc");
+	return mmc_register_driver(&mmc_driver);
+
+ out:
+	return res;
+}
+
+static void __exit mmc_blk_exit(void)
+{
+	mmc_unregister_driver(&mmc_driver);
+	devfs_remove("mmc");
+	unregister_blkdev(mmc_major, "mmc");
+}
+
+module_init(mmc_blk_init);
+module_exit(mmc_blk_exit);
+module_param(maxsectors, int, 0444);
+
+MODULE_PARM_DESC(maxsectors, "Maximum number of sectors for a single request");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
diff -Nru a/drivers/mmc/mmc_queue.c b/drivers/mmc/mmc_queue.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/mmc_queue.c	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,171 @@
+/*
+ *  linux/drivers/mmc/mmc_queue.c
+ *
+ *  Copyright (C) 2003 Russell King, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/blkdev.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include "mmc_queue.h"
+
+/*
+ * Prepare a MMC request.  Essentially, this means passing the
+ * preparation off to the media driver.  The media driver will
+ * create a mmc_io_request in req->special.
+ */
+static int mmc_prep_request(struct request_queue *q, struct request *req)
+{
+	struct mmc_queue *mq = q->queuedata;
+	int ret = BLKPREP_KILL;
+
+	if (req->flags & REQ_SPECIAL) {
+		/*
+		 * Special commands already have the command
+		 * blocks already setup in req->special.
+		 */
+		BUG_ON(!req->special);
+
+		ret = BLKPREP_OK;
+	} else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {
+		/*
+		 * Block I/O requests need translating according
+		 * to the protocol.
+		 */
+		ret = mq->prep_fn(mq, req);
+	} else {
+		/*
+		 * Everything else is invalid.
+		 */
+		blk_dump_rq_flags(req, "MMC bad request");
+	}
+
+	if (ret == BLKPREP_OK)
+		req->flags |= REQ_DONTPREP;
+
+	return ret;
+}
+
+static int mmc_queue_thread(void *d)
+{
+	struct mmc_queue *mq = d;
+	struct request_queue *q = mq->queue;
+	DECLARE_WAITQUEUE(wait, current);
+
+	/*
+	 * 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;
+
+	daemonize("mmcqd");
+
+	spin_lock_irq(&current->sighand->siglock);
+	sigfillset(&current->blocked);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	mq->thread = current;
+	complete(&mq->thread_complete);
+
+	add_wait_queue(&mq->thread_wq, &wait);
+	do {
+		struct request *req = NULL;
+
+		spin_lock_irq(q->queue_lock);
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!blk_queue_plugged(q))
+			mq->req = req = elv_next_request(q);
+		spin_unlock(q->queue_lock);
+
+		if (!req) {
+			if (!mq->thread)
+				break;
+			schedule();
+			continue;
+		}
+		set_current_state(TASK_RUNNING);
+
+		mq->issue_fn(mq, req);
+	} while (1);
+	remove_wait_queue(&mq->thread_wq, &wait);
+
+	complete_and_exit(&mq->thread_complete, 0);
+	return 0;
+}
+
+/*
+ * Generic MMC request handler.  This is called for any queue on a
+ * particular host.  When the host is not busy, we look for a request
+ * on any queue on this host, and attempt to issue it.  This may
+ * not be the queue we were asked to process.
+ */
+static void mmc_request(request_queue_t *q)
+{
+	struct mmc_queue *mq = q->queuedata;
+
+	if (!mq->req)
+		wake_up(&mq->thread_wq);
+}
+
+/**
+ * mmc_init_queue - initialise a queue structure.
+ * @mq: mmc queue
+ * @card: mmc card to attach this queue
+ * @lock: queue lock
+ *
+ * Initialise a MMC card request queue.
+ */
+int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock)
+{
+	u64 limit = BLK_BOUNCE_HIGH;
+	int ret;
+
+	if (card->host->dev->dma_mask && *card->host->dev->dma_mask)
+		limit = *card->host->dev->dma_mask;
+
+	mq->card = card;
+	mq->queue = blk_init_queue(mmc_request, lock);
+	if (!mq->queue)
+		return -ENOMEM;
+
+	blk_queue_prep_rq(mq->queue, mmc_prep_request);
+	blk_queue_bounce_limit(mq->queue, limit);
+
+	mq->queue->queuedata = mq;
+	mq->req = NULL;
+
+	init_completion(&mq->thread_complete);
+	init_waitqueue_head(&mq->thread_wq);
+
+	ret = kernel_thread(mmc_queue_thread, mq, CLONE_KERNEL);
+	if (ret < 0) {
+		blk_cleanup_queue(mq->queue);
+	} else {
+		wait_for_completion(&mq->thread_complete);
+		init_completion(&mq->thread_complete);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+EXPORT_SYMBOL(mmc_init_queue);
+
+void mmc_cleanup_queue(struct mmc_queue *mq)
+{
+	mq->thread = NULL;
+	wake_up(&mq->thread_wq);
+	wait_for_completion(&mq->thread_complete);
+	blk_cleanup_queue(mq->queue);
+
+	mq->card = NULL;
+}
+
+EXPORT_SYMBOL(mmc_cleanup_queue);
diff -Nru a/drivers/mmc/mmc_queue.h b/drivers/mmc/mmc_queue.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/mmc_queue.h	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,29 @@
+#ifndef MMC_QUEUE_H
+#define MMC_QUEUE_H
+
+struct request;
+struct task_struct;
+
+struct mmc_queue {
+	struct mmc_card		*card;
+	struct completion	thread_complete;
+	wait_queue_head_t	thread_wq;
+	struct task_struct	*thread;
+	struct request		*req;
+	int			(*prep_fn)(struct mmc_queue *, struct request *);
+	int			(*issue_fn)(struct mmc_queue *, struct request *);
+	void			*data;
+	struct request_queue	*queue;
+};
+
+struct mmc_io_request {
+	struct request		*rq;
+	int			num;
+	struct mmc_command	selcmd;		/* mmc_queue private */
+	struct mmc_command	cmd[4];		/* max 4 commands */
+};
+
+extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *);
+extern void mmc_cleanup_queue(struct mmc_queue *);
+
+#endif
diff -Nru a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/mmc_sysfs.c	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,231 @@
+/*
+ *  linux/drivers/mmc/mmc_sysfs.c
+ *
+ *  Copyright (C) 2003 Russell King, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  MMC sysfs/driver model support.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+
+#include "mmc.h"
+
+#define dev_to_mmc_card(d)	container_of(d, struct mmc_card, dev)
+#define to_mmc_driver(d)	container_of(d, struct mmc_driver, drv)
+
+static void mmc_release_card(struct device *dev)
+{
+	struct mmc_card *card = dev_to_mmc_card(dev);
+
+	kfree(card);
+}
+
+/*
+ * This currently matches any MMC driver to any MMC card - drivers
+ * themselves make the decision whether to drive this card in their
+ * probe method.
+ */
+static int mmc_bus_match(struct device *dev, struct device_driver *drv)
+{
+	return 1;
+}
+
+static int
+mmc_bus_hotplug(struct device *dev, char **envp, int num_envp, char *buf,
+		int buf_size)
+{
+	struct mmc_card *card = dev_to_mmc_card(dev);
+	char ccc[13];
+	int i = 0;
+
+#define add_env(fmt,val)						\
+	({								\
+		int len, ret = -ENOMEM;					\
+		if (i < num_envp) {					\
+			envp[i++] = buf;				\
+			len = snprintf(buf, buf_size, fmt, val) + 1;	\
+			buf_size -= len;				\
+			buf += len;					\
+			if (buf_size >= 0)				\
+				ret = 0;				\
+		}							\
+		ret;							\
+	})
+
+	for (i = 0; i < 12; i++)
+		ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0';
+	ccc[12] = '\0';
+
+	i = 0;
+	add_env("MMC_CCC=%s", ccc);
+	add_env("MMC_MANFID=%03x", card->cid.manfid);
+	add_env("MMC_SLOT_NAME=%s", card->dev.bus_id);
+
+	return 0;
+}
+
+static int mmc_bus_suspend(struct device *dev, u32 state)
+{
+	struct mmc_driver *drv = to_mmc_driver(dev->driver);
+	struct mmc_card *card = dev_to_mmc_card(dev);
+	int ret = 0;
+
+	if (dev->driver && drv->suspend)
+		ret = drv->suspend(card, state);
+	return ret;
+}
+
+static int mmc_bus_resume(struct device *dev)
+{
+	struct mmc_driver *drv = to_mmc_driver(dev->driver);
+	struct mmc_card *card = dev_to_mmc_card(dev);
+	int ret = 0;
+
+	if (dev->driver && drv->resume)
+		ret = drv->resume(card);
+	return ret;
+}
+
+static struct bus_type mmc_bus_type = {
+	.name		= "mmc",
+	.match		= mmc_bus_match,
+	.hotplug	= mmc_bus_hotplug,
+	.suspend	= mmc_bus_suspend,
+	.resume		= mmc_bus_resume,
+};
+
+
+static int mmc_drv_probe(struct device *dev)
+{
+	struct mmc_driver *drv = to_mmc_driver(dev->driver);
+	struct mmc_card *card = dev_to_mmc_card(dev);
+
+	return drv->probe(card);
+}
+
+static int mmc_drv_remove(struct device *dev)
+{
+	struct mmc_driver *drv = to_mmc_driver(dev->driver);
+	struct mmc_card *card = dev_to_mmc_card(dev);
+
+	drv->remove(card);
+
+	return 0;
+}
+
+
+/**
+ *	mmc_register_driver - register a media driver
+ *	@drv: MMC media driver
+ */
+int mmc_register_driver(struct mmc_driver *drv)
+{
+	drv->drv.bus = &mmc_bus_type;
+	drv->drv.probe = mmc_drv_probe;
+	drv->drv.remove = mmc_drv_remove;
+	return driver_register(&drv->drv);
+}
+
+EXPORT_SYMBOL(mmc_register_driver);
+
+/**
+ *	mmc_unregister_driver - unregister a media driver
+ *	@drv: MMC media driver
+ */
+void mmc_unregister_driver(struct mmc_driver *drv)
+{
+	drv->drv.bus = &mmc_bus_type;
+	driver_unregister(&drv->drv);
+}
+
+EXPORT_SYMBOL(mmc_unregister_driver);
+
+
+#define MMC_ATTR(name, fmt, args...)					\
+static ssize_t mmc_dev_show_##name (struct device *dev, char *buf)	\
+{									\
+	struct mmc_card *card = dev_to_mmc_card(dev);			\
+	return sprintf(buf, fmt, args);					\
+}									\
+static DEVICE_ATTR(name, S_IRUGO, mmc_dev_show_##name, NULL)
+
+MMC_ATTR(date, "%02d/%04d\n", card->cid.month, 1997 + card->cid.year);
+MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
+MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
+MMC_ATTR(manfid, "0x%03x\n", card->cid.manfid);
+MMC_ATTR(serial, "0x%06x\n", card->cid.serial);
+MMC_ATTR(name, "%s\n", card->cid.prod_name);
+
+static struct device_attribute *mmc_dev_attributes[] = {
+	&dev_attr_date,
+	&dev_attr_fwrev,
+	&dev_attr_hwrev,
+	&dev_attr_manfid,
+	&dev_attr_serial,
+	&dev_attr_name,
+};
+
+/*
+ * Internal function.  Initialise a MMC card structure.
+ */
+void mmc_init_card(struct mmc_card *card, struct mmc_host *host)
+{
+	memset(card, 0, sizeof(struct mmc_card));
+	card->host = host;
+	device_initialize(&card->dev);
+	card->dev.parent = card->host->dev;
+	card->dev.bus = &mmc_bus_type;
+	card->dev.release = mmc_release_card;
+}
+
+/*
+ * Internal function.  Register a new MMC card with the driver model.
+ */
+int mmc_register_card(struct mmc_card *card)
+{
+	int ret, i;
+
+	snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
+		 "%s:%04x", card->host->host_name, card->rca);
+
+	ret = device_add(&card->dev);
+	if (ret == 0)
+		for (i = 0; i < ARRAY_SIZE(mmc_dev_attributes); i++)
+			device_create_file(&card->dev, mmc_dev_attributes[i]);
+
+	return ret;
+}
+
+/*
+ * Internal function.  Unregister a new MMC card with the
+ * driver model, and (eventually) free it.
+ */
+void mmc_remove_card(struct mmc_card *card)
+{
+	if (mmc_card_present(card))
+		device_del(&card->dev);
+
+	put_device(&card->dev);
+}
+
+
+static int __init mmc_init(void)
+{
+	return bus_register(&mmc_bus_type);
+}
+
+static void __exit mmc_exit(void)
+{
+	bus_unregister(&mmc_bus_type);
+}
+
+module_init(mmc_init);
+module_exit(mmc_exit);
diff -Nru a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/mmci.c	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,590 @@
+/*
+ *  linux/drivers/mmc/mmci.c - ARM PrimeCell MMCI PL180/1 driver
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware/amba.h>
+#include <asm/hardware/clock.h>
+#include <asm/mach/mmc.h>
+
+#include "mmci.h"
+
+#define DRIVER_NAME "mmci-pl18x"
+
+#ifdef CONFIG_MMC_DEBUG
+#define DBG(host,fmt,args...)	\
+	pr_debug("%s: %s: " fmt, host->mmc->host_name, __func__ , args)
+#else
+#define DBG(host,fmt,args...)	do { } while (0)
+#endif
+
+static unsigned int fmax = 515633;
+
+static void
+mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
+{
+	writel(0, host->base + MMCICOMMAND);
+	host->mrq = NULL;
+	host->cmd = NULL;
+	host->data = NULL;
+	host->buffer = NULL;
+
+	if (mrq->data)
+		mrq->data->bytes_xfered = host->data_xfered;
+
+	/*
+	 * Need to drop the host lock here; mmc_request_done may call
+	 * back into the driver...
+	 */
+	spin_unlock(&host->lock);
+	mmc_request_done(host->mmc, mrq);
+	spin_lock(&host->lock);
+}
+
+static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
+{
+	unsigned int datactrl, timeout, irqmask;
+	void *base;
+
+	DBG(host, "blksz %04x blks %04x flags %08x\n",
+	    1 << data->blksz_bits, data->blocks, data->flags);
+
+	host->data = data;
+	host->buffer = data->req->buffer;
+	host->size = data->blocks << data->blksz_bits;
+	host->data_xfered = 0;
+
+	timeout = data->timeout_clks +
+		  ((unsigned long long)data->timeout_ns * host->cclk) /
+		   1000000000ULL;
+
+	base = host->base;
+	writel(timeout, base + MMCIDATATIMER);
+	writel(host->size, base + MMCIDATALENGTH);
+
+	datactrl = MCI_DPSM_ENABLE | data->blksz_bits << 4;
+	if (data->flags & MMC_DATA_READ) {
+		datactrl |= MCI_DPSM_DIRECTION;
+		irqmask = MCI_RXFIFOHALFFULLMASK;
+	} else {
+		/*
+		 * We don't actually need to include "FIFO empty" here
+		 * since its implicit in "FIFO half empty".
+		 */
+		irqmask = MCI_TXFIFOHALFEMPTYMASK;
+	}
+
+	writel(datactrl, base + MMCIDATACTRL);
+	writel(irqmask, base + MMCIMASK1);
+}
+
+static void
+mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
+{
+	void *base = host->base;
+
+	DBG(host, "op %02x arg %08x flags %08x\n",
+	    cmd->opcode, cmd->arg, cmd->flags);
+
+	if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
+		writel(0, base + MMCICOMMAND);
+		udelay(1);
+	}
+
+	c |= cmd->opcode | MCI_CPSM_ENABLE;
+	switch (cmd->flags & MMC_RSP_MASK) {
+	case MMC_RSP_NONE:
+	default:
+		break;
+	case MMC_RSP_LONG:
+		c |= MCI_CPSM_LONGRSP;
+	case MMC_RSP_SHORT:
+		c |= MCI_CPSM_RESPONSE;
+		break;
+	}
+	if (/*interrupt*/0)
+		c |= MCI_CPSM_INTERRUPT;
+
+	host->cmd = cmd;
+
+	writel(cmd->arg, base + MMCIARGUMENT);
+	writel(c, base + MMCICOMMAND);
+}
+
+static void
+mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
+	      unsigned int status)
+{
+	if (status & MCI_DATABLOCKEND) {
+		host->data_xfered += 1 << data->blksz_bits;
+	}
+	if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
+		if (status & MCI_DATACRCFAIL)
+			data->error = MMC_ERR_BADCRC;
+		else if (status & MCI_DATATIMEOUT)
+			data->error = MMC_ERR_TIMEOUT;
+		else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
+			data->error = MMC_ERR_FIFO;
+		status |= MCI_DATAEND;
+	}
+	if (status & MCI_DATAEND) {
+		host->data = NULL;
+		if (!data->stop) {
+			mmci_request_end(host, data->mrq);
+		} else {
+			mmci_start_command(host, data->stop, 0);
+		}
+	}
+}
+
+static void
+mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
+	     unsigned int status)
+{
+	void *base = host->base;
+
+	host->cmd = NULL;
+
+	cmd->resp[0] = readl(base + MMCIRESPONSE0);
+	cmd->resp[1] = readl(base + MMCIRESPONSE1);
+	cmd->resp[2] = readl(base + MMCIRESPONSE2);
+	cmd->resp[3] = readl(base + MMCIRESPONSE3);
+
+	if (status & MCI_CMDTIMEOUT) {
+		cmd->error = MMC_ERR_TIMEOUT;
+	} else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
+		cmd->error = MMC_ERR_BADCRC;
+	}
+
+	if (!cmd->data || cmd->error != MMC_ERR_NONE) {
+		mmci_request_end(host, cmd->mrq);
+	} else if (!(cmd->data->flags & MMC_DATA_READ)) {
+		mmci_start_data(host, cmd->data);
+	}
+}
+
+/*
+ * PIO data transfer IRQ handler.
+ */
+static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mmci_host *host = dev_id;
+	void *base = host->base;
+	u32 status;
+	int ret = 0;
+
+	do {
+		status = readl(base + MMCISTATUS);
+
+		if (!(status & (MCI_RXDATAAVLBL|MCI_RXFIFOHALFFULL|
+				MCI_TXFIFOHALFEMPTY)))
+			break;
+
+		DBG(host, "irq1 %08x\n", status);
+
+		if (status & (MCI_RXDATAAVLBL|MCI_RXFIFOHALFFULL)) {
+			unsigned int count = host->size - (readl(base + MMCIFIFOCNT) << 2);
+			if (count < 0)
+				count = 0;
+			if (count && host->buffer) {
+				readsl(base + MMCIFIFO, host->buffer, count >> 2);
+				host->buffer += count;
+				host->size -= count;
+				if (host->size == 0)
+					host->buffer = NULL;
+			} else {
+				static int first = 1;
+				if (first) {
+					first = 0;
+					printk(KERN_ERR "MMCI: sinking excessive data\n");
+				}
+				readl(base + MMCIFIFO);
+			}
+		}
+
+		/*
+		 * We only need to test the half-empty flag here - if
+		 * the FIFO is completely empty, then by definition
+		 * it is more than half empty.
+		 */
+		if (status & MCI_TXFIFOHALFEMPTY) {
+			unsigned int maxcnt = status & MCI_TXFIFOEMPTY ?
+					      MCI_FIFOSIZE : MCI_FIFOHALFSIZE;
+			unsigned int count = min(host->size, maxcnt);
+
+			writesl(base + MMCIFIFO, host->buffer, count >> 2);
+
+			host->buffer += count;
+			host->size -= count;
+
+			/*
+			 * If we run out of data, disable the data IRQs;
+			 * this prevents a race where the FIFO becomes
+			 * empty before the chip itself has disabled the
+			 * data path.
+			 */
+			if (host->size == 0)
+				writel(0, base + MMCIMASK1);
+		}
+
+		ret = 1;
+	} while (status);
+
+	return IRQ_RETVAL(ret);
+}
+
+/*
+ * Handle completion of command and data transfers.
+ */
+static irqreturn_t mmci_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mmci_host *host = dev_id;
+	u32 status;
+	int ret = 0;
+
+	spin_lock(&host->lock);
+
+	do {
+		struct mmc_command *cmd;
+		struct mmc_data *data;
+
+		status = readl(host->base + MMCISTATUS);
+		writel(status, host->base + MMCICLEAR);
+
+		if (!(status & MCI_IRQMASK))
+			break;
+
+		DBG(host, "irq0 %08x\n", status);
+
+		data = host->data;
+		if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|
+			      MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data)
+			mmci_data_irq(host, data, status);
+
+		cmd = host->cmd;
+		if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
+			mmci_cmd_irq(host, cmd, status);
+
+		ret = 1;
+	} while (status);
+
+	spin_unlock(&host->lock);
+
+	return IRQ_RETVAL(ret);
+}
+
+static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct mmci_host *host = mmc_priv(mmc);
+
+	WARN_ON(host->mrq != NULL);
+
+	spin_lock_irq(&host->lock);
+
+	host->mrq = mrq;
+
+	if (mrq->data && mrq->data->flags & MMC_DATA_READ)
+		mmci_start_data(host, mrq->data);
+
+	mmci_start_command(host, mrq->cmd, 0);
+
+	spin_unlock_irq(&host->lock);
+}
+
+static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct mmci_host *host = mmc_priv(mmc);
+	u32 clk = 0, pwr = 0;
+
+	DBG(host, "clock %uHz busmode %u powermode %u Vdd %u\n",
+	    ios->clock, ios->bus_mode, ios->power_mode, ios->vdd);
+
+	if (ios->clock) {
+		if (ios->clock >= host->mclk) {
+			clk = MCI_CLK_BYPASS;
+			host->cclk = host->mclk;
+		} else {
+			clk = host->mclk / (2 * ios->clock) - 1;
+			if (clk > 256)
+				clk = 255;
+			host->cclk = host->mclk / (2 * (clk + 1));
+		}
+		clk |= MCI_CLK_ENABLE;
+	}
+
+	if (host->plat->translate_vdd)
+		pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
+
+	switch (ios->power_mode) {
+	case MMC_POWER_OFF:
+		break;
+	case MMC_POWER_UP:
+		pwr |= MCI_PWR_UP;
+		break;
+	case MMC_POWER_ON:
+		pwr |= MCI_PWR_ON;
+		break;
+	}
+
+	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+		pwr |= MCI_ROD;
+
+	writel(clk, host->base + MMCICLOCK);
+
+	if (host->pwr != pwr) {
+		host->pwr = pwr;
+		writel(pwr, host->base + MMCIPOWER);
+	}
+}
+
+static struct mmc_host_ops mmci_ops = {
+	.request	= mmci_request,
+	.set_ios	= mmci_set_ios,
+};
+
+static void mmci_check_status(unsigned long data)
+{
+	struct mmci_host *host = (struct mmci_host *)data;
+	unsigned int status;
+
+	status = host->plat->status(mmc_dev(host->mmc));
+	if (status ^ host->oldstat)
+		mmc_detect_change(host->mmc);
+
+	host->oldstat = status;
+	mod_timer(&host->timer, jiffies + HZ);
+}
+
+static int mmci_probe(struct amba_device *dev, void *id)
+{
+	struct mmc_platform_data *plat = dev->dev.platform_data;
+	struct mmci_host *host;
+	struct mmc_host *mmc;
+	int ret;
+
+	/* must have platform data */
+	if (!plat) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = amba_request_regions(dev, DRIVER_NAME);
+	if (ret)
+		goto out;
+
+	mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto rel_regions;
+	}
+
+	host = mmc_priv(mmc);
+	host->clk = clk_get(&dev->dev, "MCLK");
+	if (IS_ERR(host->clk)) {
+		ret = PTR_ERR(host->clk);
+		host->clk = NULL;
+		goto host_free;
+	}
+
+	ret = clk_use(host->clk);
+	if (ret)
+		goto clk_free;
+
+	ret = clk_enable(host->clk);
+	if (ret)
+		goto clk_unuse;
+
+	host->plat = plat;
+	host->mclk = clk_get_rate(host->clk);
+	host->mmc = mmc;
+	host->base = ioremap(dev->res.start, SZ_4K);
+	if (!host->base) {
+		ret = -ENOMEM;
+		goto clk_disable;
+	}
+
+	mmc->ops = &mmci_ops;
+	mmc->f_min = (host->mclk + 511) / 512;
+	mmc->f_max = min(host->mclk, fmax);
+	mmc->ocr_avail = plat->ocr_mask;
+
+	spin_lock_init(&host->lock);
+
+	writel(0, host->base + MMCIMASK0);
+	writel(0, host->base + MMCIMASK1);
+	writel(0xfff, host->base + MMCICLEAR);
+
+	ret = request_irq(dev->irq[0], mmci_irq, SA_SHIRQ, DRIVER_NAME " (cmd)", host);
+	if (ret)
+		goto unmap;
+
+	ret = request_irq(dev->irq[1], mmci_pio_irq, SA_SHIRQ, DRIVER_NAME " (pio)", host);
+	if (ret)
+		goto irq0_free;
+
+	writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+
+	amba_set_drvdata(dev, mmc);
+
+	mmc_add_host(mmc);
+
+	printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%08lx irq %d,%d\n",
+		mmc->host_name, amba_rev(dev), amba_config(dev),
+		dev->res.start, dev->irq[0], dev->irq[1]);
+
+	init_timer(&host->timer);
+	host->timer.data = (unsigned long)host;
+	host->timer.function = mmci_check_status;
+	host->timer.expires = jiffies + HZ;
+	add_timer(&host->timer);
+
+	return 0;
+
+ irq0_free:
+	free_irq(dev->irq[0], host);
+ unmap:
+	iounmap(host->base);
+ clk_disable:
+	clk_disable(host->clk);
+ clk_unuse:
+	clk_unuse(host->clk);
+ clk_free:
+	clk_put(host->clk);
+ host_free:
+	mmc_free_host(mmc);
+ rel_regions:
+	amba_release_regions(dev);
+ out:
+	return ret;
+}
+
+static int mmci_remove(struct amba_device *dev)
+{
+	struct mmc_host *mmc = amba_get_drvdata(dev);
+
+	amba_set_drvdata(dev, NULL);
+
+	if (mmc) {
+		struct mmci_host *host = mmc_priv(mmc);
+
+		del_timer_sync(&host->timer);
+
+		mmc_remove_host(mmc);
+
+		writel(0, host->base + MMCIMASK0);
+		writel(0, host->base + MMCIMASK1);
+
+		writel(0, host->base + MMCICOMMAND);
+		writel(0, host->base + MMCIDATACTRL);
+
+		free_irq(dev->irq[0], host);
+		free_irq(dev->irq[1], host);
+
+		iounmap(host->base);
+		clk_disable(host->clk);
+		clk_unuse(host->clk);
+		clk_put(host->clk);
+
+		mmc_free_host(mmc);
+
+		amba_release_regions(dev);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mmci_suspend(struct amba_device *dev, u32 state)
+{
+	struct mmc_host *mmc = amba_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc) {
+		struct mmci_host *host = mmc_priv(mmc);
+
+		ret = mmc_suspend_host(mmc, state);
+		if (ret == 0)
+			writel(0, host->base + MMCIMASK0);
+	}
+
+	return ret;
+}
+
+static int mmci_resume(struct amba_device *dev)
+{
+	struct mmc_host *mmc = amba_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc) {
+		struct mmci_host *host = mmc_priv(mmc);
+
+		writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+
+		ret = mmc_resume_host(mmc);
+	}
+
+	return ret;
+}
+#else
+#define mmci_suspend	NULL
+#define mmci_resume	NULL
+#endif
+
+static struct amba_id mmci_ids[] = {
+	{
+		.id	= 0x00041180,
+		.mask	= 0x000fffff,
+	},
+	{
+		.id	= 0x00041181,
+		.mask	= 0x000fffff,
+	},
+	{ 0, 0 },
+};
+
+static struct amba_driver mmci_driver = {
+	.drv		= {
+		.name	= DRIVER_NAME,
+	},
+	.probe		= mmci_probe,
+	.remove		= mmci_remove,
+	.suspend	= mmci_suspend,
+	.resume		= mmci_resume,
+	.id_table	= mmci_ids,
+};
+
+static int __init mmci_init(void)
+{
+	return amba_driver_register(&mmci_driver);
+}
+
+static void __exit mmci_exit(void)
+{
+	amba_driver_unregister(&mmci_driver);
+}
+
+module_init(mmci_init);
+module_exit(mmci_exit);
+module_param(fmax, uint, 0444);
+
+MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver");
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/mmc/mmci.h b/drivers/mmc/mmci.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/mmci.h	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,148 @@
+/*
+ *  linux/drivers/mmc/mmci.h - ARM PrimeCell MMCI PL180/1 driver
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define MMCIPOWER		0x000
+#define MCI_PWR_OFF		0x00
+#define MCI_PWR_UP		0x02
+#define MCI_PWR_ON		0x03
+#define MCI_OD			(1 << 6)
+#define MCI_ROD			(1 << 7)
+
+#define MMCICLOCK		0x004
+#define MCI_CLK_ENABLE		(1 << 8)
+#define MCI_CLK_PWRSAVE		(1 << 9)
+#define MCI_CLK_BYPASS		(1 << 10)
+
+#define MMCIARGUMENT		0x008
+#define MMCICOMMAND		0x00c
+#define MCI_CPSM_RESPONSE	(1 << 6)
+#define MCI_CPSM_LONGRSP	(1 << 7)
+#define MCI_CPSM_INTERRUPT	(1 << 8)
+#define MCI_CPSM_PENDING	(1 << 9)
+#define MCI_CPSM_ENABLE		(1 << 10)
+
+#define MMCIRESPCMD		0x010
+#define MMCIRESPONSE0		0x014
+#define MMCIRESPONSE1		0x018
+#define MMCIRESPONSE2		0x01c
+#define MMCIRESPONSE3		0x020
+#define MMCIDATATIMER		0x024
+#define MMCIDATALENGTH		0x028
+#define MMCIDATACTRL		0x02c
+#define MCI_DPSM_ENABLE		(1 << 0)
+#define MCI_DPSM_DIRECTION	(1 << 1)
+#define MCI_DPSM_MODE		(1 << 2)
+#define MCI_DPSM_DMAENABLE	(1 << 3)
+
+#define MMCIDATACNT		0x030
+#define MMCISTATUS		0x034
+#define MCI_CMDCRCFAIL		(1 << 0)
+#define MCI_DATACRCFAIL		(1 << 1)
+#define MCI_CMDTIMEOUT		(1 << 2)
+#define MCI_DATATIMEOUT		(1 << 3)
+#define MCI_TXUNDERRUN		(1 << 4)
+#define MCI_RXOVERRUN		(1 << 5)
+#define MCI_CMDRESPEND		(1 << 6)
+#define MCI_CMDSENT		(1 << 7)
+#define MCI_DATAEND		(1 << 8)
+#define MCI_DATABLOCKEND	(1 << 10)
+#define MCI_CMDACTIVE		(1 << 11)
+#define MCI_TXACTIVE		(1 << 12)
+#define MCI_RXACTIVE		(1 << 13)
+#define MCI_TXFIFOHALFEMPTY	(1 << 14)
+#define MCI_RXFIFOHALFFULL	(1 << 15)
+#define MCI_TXFIFOFULL		(1 << 16)
+#define MCI_RXFIFOFULL		(1 << 17)
+#define MCI_TXFIFOEMPTY		(1 << 18)
+#define MCI_RXFIFOEMPTY		(1 << 19)
+#define MCI_TXDATAAVLBL		(1 << 20)
+#define MCI_RXDATAAVLBL		(1 << 21)
+
+#define MMCICLEAR		0x038
+#define MCI_CMDCRCFAILCLR	(1 << 0)
+#define MCI_DATACRCFAILCLR	(1 << 1)
+#define MCI_CMDTIMEOUTCLR	(1 << 2)
+#define MCI_DATATIMEOUTCLR	(1 << 3)
+#define MCI_TXUNDERRUNCLR	(1 << 4)
+#define MCI_RXOVERRUNCLR	(1 << 5)
+#define MCI_CMDRESPENDCLR	(1 << 6)
+#define MCI_CMDSENTCLR		(1 << 7)
+#define MCI_DATAENDCLR		(1 << 8)
+#define MCI_DATABLOCKENDCLR	(1 << 10)
+
+#define MMCIMASK0		0x03c
+#define MCI_CMDCRCFAILMASK	(1 << 0)
+#define MCI_DATACRCFAILMASK	(1 << 1)
+#define MCI_CMDTIMEOUTMASK	(1 << 2)
+#define MCI_DATATIMEOUTMASK	(1 << 3)
+#define MCI_TXUNDERRUNMASK	(1 << 4)
+#define MCI_RXOVERRUNMASK	(1 << 5)
+#define MCI_CMDRESPENDMASK	(1 << 6)
+#define MCI_CMDSENTMASK		(1 << 7)
+#define MCI_DATAENDMASK		(1 << 8)
+#define MCI_DATABLOCKENDMASK	(1 << 10)
+#define MCI_CMDACTIVEMASK	(1 << 11)
+#define MCI_TXACTIVEMASK	(1 << 12)
+#define MCI_RXACTIVEMASK	(1 << 13)
+#define MCI_TXFIFOHALFEMPTYMASK	(1 << 14)
+#define MCI_RXFIFOHALFFULLMASK	(1 << 15)
+#define MCI_TXFIFOFULLMASK	(1 << 16)
+#define MCI_RXFIFOFULLMASK	(1 << 17)
+#define MCI_TXFIFOEMPTYMASK	(1 << 18)
+#define MCI_RXFIFOEMPTYMASK	(1 << 19)
+#define MCI_TXDATAAVLBLMASK	(1 << 20)
+#define MCI_RXDATAAVLBLMASK	(1 << 21)
+
+#define MMCIMASK1		0x040
+#define MMCIFIFOCNT		0x048
+#define MMCIFIFO		0x080 /* to 0x0bc */
+
+#define MCI_IRQMASK	\
+	(MCI_CMDCRCFAIL|MCI_DATACRCFAIL|MCI_CMDTIMEOUT|MCI_DATATIMEOUT|	\
+	MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_CMDRESPEND|MCI_CMDSENT|	\
+	MCI_DATAEND|MCI_DATABLOCKEND)
+
+#define MCI_IRQENABLE	\
+	(MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK|	\
+	MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK|	\
+	MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK|		\
+	MCI_DATABLOCKENDMASK)
+
+#define MCI_FIFOSIZE	16
+	
+#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
+
+struct clk;
+
+struct mmci_host {
+	void			*base;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+	struct mmc_host		*mmc;
+	struct clk		*clk;
+
+	unsigned int		data_xfered;
+
+	spinlock_t		lock;
+
+	unsigned int		mclk;
+	unsigned int		cclk;
+	u32			pwr;
+	struct mmc_platform_data *plat;
+
+	struct timer_list	timer;
+	unsigned int		oldstat;
+
+	/* pio stuff */
+	void			*buffer;
+	unsigned int		size;
+};
+
+#define to_mmci_host(mmc)	container_of(mmc, struct mmci_host, mmc)
diff -Nru a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/pxamci.c	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,578 @@
+/*
+ *  linux/drivers/mmc/pxa.c - PXA MMCI driver
+ *
+ *  Copyright (C) 2003 Russell King, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  This hardware is really sick:
+ *   - No way to clear interrupts.
+ *   - Have to turn off the clock whenever we touch the device.
+ *   - Doesn't tell you how many data blocks were transferred.
+ *  Yuck!
+ *
+ *	1 and 3 byte data transfers not supported
+ *	max block length up to 1023
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/sizes.h>
+
+#include "pxamci.h"
+
+#ifdef CONFIG_MMC_DEBUG
+#define DBG(x...)	printk(KERN_DEBUG x)
+#else
+#define DBG(x...)	do { } while (0)
+#endif
+
+struct pxamci_host {
+	struct mmc_host		*mmc;
+	spinlock_t		lock;
+	struct resource		*res;
+	void			*base;
+	int			irq;
+	int			dma;
+	unsigned int		clkrt;
+	unsigned int		cmdat;
+	unsigned int		imask;
+	unsigned int		power_mode;
+
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	dma_addr_t		sg_dma;
+	struct pxa_dma_desc	*sg_cpu;
+
+	dma_addr_t		dma_buf;
+	unsigned int		dma_size;
+	unsigned int		dma_dir;
+};
+
+/*
+ * The base MMC clock rate
+ */
+#define CLOCKRATE	20000000
+
+static inline unsigned int ns_to_clocks(unsigned int ns)
+{
+	return (ns * (CLOCKRATE / 1000000) + 999) / 1000;
+}
+
+static void pxamci_stop_clock(struct pxamci_host *host)
+{
+	if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
+		unsigned long timeout = 10000;
+		unsigned int v;
+
+		writel(STOP_CLOCK, host->base + MMC_STRPCL);
+
+		do {
+			v = readl(host->base + MMC_STAT);
+			if (!(v & STAT_CLK_EN))
+				break;
+			udelay(1);
+		} while (timeout--);
+
+		if (v & STAT_CLK_EN)
+			dev_err(mmc_dev(host->mmc), "unable to stop clock\n");
+	}
+}
+
+static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->imask &= ~mask;
+	writel(host->imask, host->base + MMC_I_MASK);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->imask |= mask;
+	writel(host->imask, host->base + MMC_I_MASK);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
+{
+	unsigned int nob = data->blocks;
+	unsigned int timeout, size;
+	dma_addr_t dma;
+	u32 dcmd;
+	int i;
+
+	host->data = data;
+
+	if (data->flags & MMC_DATA_STREAM)
+		nob = 0xffff;
+
+	writel(nob, host->base + MMC_NOB);
+	writel(1 << data->blksz_bits, host->base + MMC_BLKLEN);
+
+	timeout = ns_to_clocks(data->timeout_ns) + data->timeout_clks;
+	writel((timeout + 255) / 256, host->base + MMC_RDTO);
+
+	if (data->flags & MMC_DATA_READ) {
+		host->dma_dir = DMA_FROM_DEVICE;
+		dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG;
+		DRCMRTXMMC = 0;
+		DRCMRRXMMC = host->dma | DRCMR_MAPVLD;
+	} else {
+		host->dma_dir = DMA_TO_DEVICE;
+		dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC;
+		DRCMRRXMMC = 0;
+		DRCMRTXMMC = host->dma | DRCMR_MAPVLD;
+	}
+
+	dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
+
+	host->dma_size = data->blocks << data->blksz_bits;
+	host->dma_buf = dma_map_single(mmc_dev(host->mmc), data->req->buffer,
+				       host->dma_size, host->dma_dir);
+
+	for (i = 0, size = host->dma_size, dma = host->dma_buf; size; i++) {
+		u32 len = size;
+
+		if (len > DCMD_LENGTH)
+			len = 0x1000;
+
+		if (data->flags & MMC_DATA_READ) {
+			host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
+			host->sg_cpu[i].dtadr = dma;
+		} else {
+			host->sg_cpu[i].dsadr = dma;
+			host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
+		}
+		host->sg_cpu[i].dcmd = dcmd | len;
+
+		dma += len;
+		size -= len;
+
+		if (size) {
+			host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
+						 sizeof(struct pxa_dma_desc);
+		} else {
+			host->sg_cpu[i].ddadr = DDADR_STOP;
+		}
+	}
+	wmb();
+
+	DDADR(host->dma) = host->sg_dma;
+	DCSR(host->dma) = DCSR_RUN;
+}
+
+static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
+{
+	WARN_ON(host->cmd != NULL);
+	host->cmd = cmd;
+
+	if (cmd->flags & MMC_RSP_BUSY)
+		cmdat |= CMDAT_BUSY;
+
+	switch (cmd->flags & (MMC_RSP_MASK | MMC_RSP_CRC)) {
+	case MMC_RSP_SHORT | MMC_RSP_CRC:
+		cmdat |= CMDAT_RESP_SHORT;
+		break;
+	case MMC_RSP_SHORT:
+		cmdat |= CMDAT_RESP_R3;
+		break;
+	case MMC_RSP_LONG | MMC_RSP_CRC:
+		cmdat |= CMDAT_RESP_R2;
+		break;
+	default:
+		break;
+	}
+
+	writel(cmd->opcode, host->base + MMC_CMD);
+	writel(cmd->arg >> 16, host->base + MMC_ARGH);
+	writel(cmd->arg & 0xffff, host->base + MMC_ARGL);
+	writel(cmdat, host->base + MMC_CMDAT);
+	writel(host->clkrt, host->base + MMC_CLKRT);
+
+	writel(START_CLOCK, host->base + MMC_STRPCL);
+
+	pxamci_enable_irq(host, END_CMD_RES);
+}
+
+static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq)
+{
+	DBG("PXAMCI: request done\n");
+	host->mrq = NULL;
+	host->cmd = NULL;
+	host->data = NULL;
+	mmc_request_done(host->mmc, mrq);
+}
+
+static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
+{
+	struct mmc_command *cmd = host->cmd;
+	int i;
+	u32 v;
+
+	if (!cmd)
+		return 0;
+
+	host->cmd = NULL;
+
+	/*
+	 * Did I mention this is Sick.  We always need to
+	 * discard the upper 8 bits of the first 16-bit word.
+	 */
+	v = readl(host->base + MMC_RES) & 0xffff;
+	for (i = 0; i < 4; i++) {
+		u32 w1 = readl(host->base + MMC_RES) & 0xffff;
+		u32 w2 = readl(host->base + MMC_RES) & 0xffff;
+		cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8;
+		v = w2;
+	}
+
+	if (stat & STAT_TIME_OUT_RESPONSE) {
+		cmd->error = MMC_ERR_TIMEOUT;
+	} else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+		cmd->error = MMC_ERR_BADCRC;
+	}
+
+	pxamci_disable_irq(host, END_CMD_RES);
+	if (host->data && cmd->error == MMC_ERR_NONE) {
+		pxamci_enable_irq(host, DATA_TRAN_DONE);
+	} else {
+		pxamci_finish_request(host, host->mrq);
+	}
+
+	return 1;
+}
+
+static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
+{
+	struct mmc_data *data = host->data;
+
+	if (!data)
+		return 0;
+
+	DCSR(host->dma) = 0;
+	dma_unmap_single(mmc_dev(host->mmc), host->dma_buf, host->dma_size,
+			 host->dma_dir);
+
+	if (stat & STAT_READ_TIME_OUT)
+		data->error = MMC_ERR_TIMEOUT;
+	else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))
+		data->error = MMC_ERR_BADCRC;
+
+	/*
+	 * There appears to be a hardware design bug here.  There seems to
+	 * be no way to find out how much data was transferred to the card.
+	 * 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)
+		data->bytes_xfered = data->blocks << data->blksz_bits;
+	else
+		data->bytes_xfered = 0;
+
+	pxamci_disable_irq(host, DATA_TRAN_DONE);
+
+	host->data = NULL;
+	if (host->mrq->stop && data->error == MMC_ERR_NONE) {
+		pxamci_stop_clock(host);
+		pxamci_start_cmd(host, host->mrq->stop, 0);
+	} else {
+		pxamci_finish_request(host, host->mrq);
+	}
+
+	return 1;
+}
+
+static irqreturn_t pxamci_irq(int irq, void *devid, struct pt_regs *regs)
+{
+	struct pxamci_host *host = devid;
+	unsigned int ireg;
+	int handled = 0;
+
+	ireg = readl(host->base + MMC_I_REG);
+
+	DBG("PXAMCI: irq %08x\n", ireg);
+
+	if (ireg) {
+		unsigned stat = readl(host->base + MMC_STAT);
+
+		DBG("PXAMCI: stat %08x\n", stat);
+
+		if (ireg & END_CMD_RES)
+			handled |= pxamci_cmd_done(host, stat);
+		if (ireg & DATA_TRAN_DONE)
+			handled |= pxamci_data_done(host, stat);
+	}
+
+	return IRQ_RETVAL(handled);
+}
+
+static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct pxamci_host *host = mmc_priv(mmc);
+	unsigned int cmdat;
+
+	WARN_ON(host->mrq != NULL);
+
+	host->mrq = mrq;
+
+	pxamci_stop_clock(host);
+
+	cmdat = host->cmdat;
+	host->cmdat &= ~CMDAT_INIT;
+
+	if (mrq->data) {
+		pxamci_setup_data(host, mrq->data);
+
+		cmdat &= ~CMDAT_BUSY;
+		cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;
+		if (mrq->data->flags & MMC_DATA_WRITE)
+			cmdat |= CMDAT_WRITE;
+
+		if (mrq->data->flags & MMC_DATA_STREAM)
+			cmdat |= CMDAT_STREAM;
+	}
+
+	pxamci_start_cmd(host, mrq->cmd, cmdat);
+}
+
+static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct pxamci_host *host = mmc_priv(mmc);
+
+	DBG("pxamci_set_ios: clock %u power %u vdd %u.%02u\n",
+	    ios->clock, ios->power_mode, ios->vdd / 100,
+	    ios->vdd % 100);
+
+	if (ios->clock) {
+		unsigned int clk = CLOCKRATE / ios->clock;
+		if (CLOCKRATE / clk > ios->clock)
+			clk <<= 1;
+		host->clkrt = fls(clk) - 1;
+
+		/*
+		 * we write clkrt on the next command
+		 */
+	} else if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
+		/*
+		 * Ensure that the clock is off.
+		 */
+		writel(STOP_CLOCK, host->base + MMC_STRPCL);
+	}
+
+	if (host->power_mode != ios->power_mode) {
+		host->power_mode = ios->power_mode;
+
+		/*
+		 * power control?  none on the lubbock.
+		 */
+
+		if (ios->power_mode == MMC_POWER_ON)
+			host->cmdat |= CMDAT_INIT;
+	}
+
+	DBG("pxamci_set_ios: clkrt = %x cmdat = %x\n",
+	    host->clkrt, host->cmdat);
+}
+
+static struct mmc_host_ops pxamci_ops = {
+	.request	= pxamci_request,
+	.set_ios	= pxamci_set_ios,
+};
+
+static void pxamci_dma_irq(int dma, void *devid, struct pt_regs *regs)
+{
+	printk(KERN_ERR "DMA%d: IRQ???\n", dma);
+	DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
+}
+
+static int pxamci_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mmc_host *mmc;
+	struct pxamci_host *host = NULL;
+	struct resource *r;
+	int ret, irq;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!r || irq == NO_IRQ)
+		return -ENXIO;
+
+	r = request_mem_region(r->start, SZ_4K, "PXAMCI");
+	if (!r)
+		return -EBUSY;
+
+	mmc = mmc_alloc_host(sizeof(struct pxamci_host), dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	mmc->ops = &pxamci_ops;
+	mmc->f_min = 312500;
+	mmc->f_max = 20000000;
+	mmc->ocr_avail = MMC_VDD_32_33;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->dma = -1;
+
+	host->sg_cpu = dma_alloc_coherent(dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	spin_lock_init(&host->lock);
+	host->res = r;
+	host->irq = irq;
+	host->imask = TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
+		      END_CMD_RES|PRG_DONE|DATA_TRAN_DONE;
+
+	host->base = ioremap(r->start, SZ_4K);
+	if (!host->base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/*
+	 * Ensure that the host controller is shut down, and setup
+	 * with our defaults.
+	 */
+	pxamci_stop_clock(host);
+	writel(0, host->base + MMC_SPI);
+	writel(64, host->base + MMC_RESTO);
+
+	pxa_gpio_mode(GPIO6_MMCCLK_MD);
+	pxa_gpio_mode(GPIO8_MMCCS0_MD);
+	pxa_set_cken(CKEN12_MMC, 1);
+
+	host->dma = pxa_request_dma("PXAMCI", DMA_PRIO_LOW, pxamci_dma_irq, host);
+	if (host->dma < 0) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = request_irq(host->irq, pxamci_irq, 0, "PXAMCI", host);
+	if (ret)
+		goto out;
+
+	dev_set_drvdata(dev, mmc);
+
+	mmc_add_host(mmc);
+
+	return 0;
+
+ out:
+	if (host) {
+		if (host->dma >= 0)
+			pxa_free_dma(host->dma);
+		if (host->base)
+			iounmap(host->base);
+		if (host->sg_cpu)
+			dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+	}
+	if (mmc)
+		mmc_free_host(mmc);
+	release_resource(r);
+	return ret;
+}
+
+static int pxamci_remove(struct device *dev)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+
+	dev_set_drvdata(dev, NULL);
+
+	if (mmc) {
+		struct pxamci_host *host = mmc_priv(mmc);
+
+		mmc_remove_host(mmc);
+
+		pxamci_stop_clock(host);
+		writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
+		       END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
+		       host->base + MMC_I_MASK);
+
+		free_irq(host->irq, host);
+		pxa_free_dma(host->dma);
+		iounmap(host->base);
+		dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+		pxa_set_cken(CKEN12_MMC, 0);
+
+		release_resource(host->res);
+
+		mmc_free_host(mmc);
+	}
+	return 0;
+}
+
+static int pxamci_suspend(struct device *dev, u32 state, u32 level)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc && level == SUSPEND_DISABLE)
+		ret = mmc_suspend_host(mmc, state);
+
+	return ret;
+}
+
+static int pxamci_resume(struct device *dev, u32 level)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc && level == RESUME_ENABLE)
+		ret = mmc_resume_host(mmc);
+
+	return ret;
+}
+
+static struct device_driver pxamci_driver = {
+	.name		= "pxa2xx-mci",
+	.bus		= &platform_bus_type,
+	.probe		= pxamci_probe,
+	.remove		= pxamci_remove,
+	.suspend	= pxamci_suspend,
+	.resume		= pxamci_resume,
+};
+
+static int __init pxamci_init(void)
+{
+	return driver_register(&pxamci_driver);
+}
+
+static void __exit pxamci_exit(void)
+{
+	driver_unregister(&pxamci_driver);
+}
+
+module_init(pxamci_init);
+module_exit(pxamci_exit);
+
+MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver");
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/mmc/pxamci.h b/drivers/mmc/pxamci.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/mmc/pxamci.h	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,94 @@
+#undef MMC_STRPCL
+#undef MMC_STAT
+#undef MMC_CLKRT
+#undef MMC_SPI
+#undef MMC_CMDAT
+#undef MMC_RESTO
+#undef MMC_RDTO
+#undef MMC_BLKLEN
+#undef MMC_NOB
+#undef MMC_PRTBUF
+#undef MMC_I_MASK
+#undef END_CMD_RES
+#undef PRG_DONE
+#undef DATA_TRAN_DONE
+#undef MMC_I_REG
+#undef MMC_CMD
+#undef MMC_ARGH
+#undef MMC_ARGL
+#undef MMC_RES
+#undef MMC_RXFIFO
+#undef MMC_TXFIFO
+
+#define MMC_STRPCL	0x0000
+#define STOP_CLOCK		(1 << 0)
+#define START_CLOCK		(2 << 0)
+
+#define MMC_STAT	0x0004
+#define STAT_END_CMD_RES		(1 << 13)
+#define STAT_PRG_DONE			(1 << 12)
+#define STAT_DATA_TRAN_DONE		(1 << 11)
+#define STAT_CLK_EN			(1 << 8)
+#define STAT_RECV_FIFO_FULL		(1 << 7)
+#define STAT_XMIT_FIFO_EMPTY		(1 << 6)
+#define STAT_RES_CRC_ERR		(1 << 5)
+#define STAT_SPI_READ_ERROR_TOKEN	(1 << 4)
+#define STAT_CRC_READ_ERROR		(1 << 3)
+#define STAT_CRC_WRITE_ERROR		(1 << 2)
+#define STAT_TIME_OUT_RESPONSE		(1 << 1)
+#define STAT_READ_TIME_OUT		(1 << 0)
+
+#define MMC_CLKRT	0x0008		/* 3 bit */
+
+#define MMC_SPI		0x000c
+#define SPI_CS_ADDRESS		(1 << 3)
+#define SPI_CS_EN		(1 << 2)
+#define CRC_ON			(1 << 1)
+#define SPI_EN			(1 << 0)
+
+#define MMC_CMDAT	0x0010
+#define CMDAT_DMAEN		(1 << 7)
+#define CMDAT_INIT		(1 << 6)
+#define CMDAT_BUSY		(1 << 5)
+#define CMDAT_STREAM		(1 << 4)	/* 1 = stream */
+#define CMDAT_WRITE		(1 << 3)	/* 1 = write */
+#define CMDAT_DATAEN		(1 << 2)
+#define CMDAT_RESP_NONE		(0 << 0)
+#define CMDAT_RESP_SHORT	(1 << 0)
+#define CMDAT_RESP_R2		(2 << 0)
+#define CMDAT_RESP_R3		(3 << 0)
+
+#define MMC_RESTO	0x0014	/* 7 bit */
+
+#define MMC_RDTO	0x0018	/* 16 bit */
+
+#define MMC_BLKLEN	0x001c	/* 10 bit */
+
+#define MMC_NOB		0x0020	/* 16 bit */
+
+#define MMC_PRTBUF	0x0024
+#define BUF_PART_FULL		(1 << 0)
+
+#define MMC_I_MASK	0x0028
+#define TXFIFO_WR_REQ		(1 << 6)
+#define RXFIFO_RD_REQ		(1 << 5)
+#define CLK_IS_OFF		(1 << 4)
+#define STOP_CMD		(1 << 3)
+#define END_CMD_RES		(1 << 2)
+#define PRG_DONE		(1 << 1)
+#define DATA_TRAN_DONE		(1 << 0)
+
+#define MMC_I_REG	0x002c
+/* same as MMC_I_MASK */
+
+#define MMC_CMD		0x0030
+
+#define MMC_ARGH	0x0034	/* 16 bit */
+
+#define MMC_ARGL	0x0038	/* 16 bit */
+
+#define MMC_RES		0x003c	/* 16 bit */
+
+#define MMC_RXFIFO	0x0040	/* 8 bit */
+
+#define MMC_TXFIFO	0x0044	/* 8 bit */
diff -Nru a/include/asm-arm/mach/mmc.h b/include/asm-arm/mach/mmc.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/include/asm-arm/mach/mmc.h	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,16 @@
+/*
+ *  linux/include/asm-arm/mach/mmc.h
+ */
+#ifndef ASMARM_MACH_MMC_H
+#define ASMARM_MACH_MMC_H
+
+#include <linux/mmc/protocol.h>
+
+struct mmc_platform_data {
+	unsigned int mclk;			/* mmc base clock rate */
+	unsigned int ocr_mask;			/* available voltages */
+	u32 (*translate_vdd)(struct device *, unsigned int);
+	unsigned int (*status)(struct device *);
+};
+
+#endif
diff -Nru a/include/linux/mmc/card.h b/include/linux/mmc/card.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/include/linux/mmc/card.h	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,83 @@
+/*
+ *  linux/include/linux/mmc/card.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Card driver specific definitions.
+ */
+#ifndef LINUX_MMC_CARD_H
+#define LINUX_MMC_CARD_H
+
+#include <linux/mmc/mmc.h>
+
+struct mmc_cid {
+	unsigned int		manfid;
+	unsigned int		serial;
+	char			prod_name[8];
+	unsigned char		hwrev;
+	unsigned char		fwrev;
+	unsigned char		month;
+	unsigned char		year;
+};
+
+struct mmc_csd {
+	unsigned char		mmc_prot;
+	unsigned short		cmdclass;
+	unsigned short		tacc_clks;
+	unsigned int		tacc_ns;
+	unsigned int		max_dtr;
+	unsigned int		read_blkbits;
+	unsigned int		capacity;
+};
+
+struct mmc_host;
+
+/*
+ * MMC device
+ */
+struct mmc_card {
+	struct list_head	node;		/* node in hosts devices list */
+	struct mmc_host		*host;		/* the host this device belongs to */
+	struct device		dev;		/* the device */
+	unsigned int		rca;		/* relative card address of device */
+	unsigned int		state;		/* (our) card state */
+#define MMC_STATE_PRESENT	(1<<0)
+#define MMC_STATE_DEAD		(1<<1)
+	struct mmc_cid		cid;		/* card identification */
+	struct mmc_csd		csd;		/* card specific */
+};
+
+#define mmc_card_dead(c)	((c)->state & MMC_STATE_DEAD)
+#define mmc_card_present(c)	((c)->state & MMC_STATE_PRESENT)
+
+#define mmc_card_name(c)	((c)->cid.prod_name)
+#define mmc_card_id(c)		((c)->dev.bus_id)
+
+#define mmc_list_to_card(l)	container_of(l, struct mmc_card, node)
+#define mmc_get_drvdata(c)	dev_get_drvdata(&(c)->dev)
+#define mmc_set_drvdata(c,d)	dev_set_drvdata(&(c)->dev, d)
+
+/*
+ * MMC device driver (e.g., Flash card, I/O card...)
+ */
+struct mmc_driver {
+	struct device_driver drv;
+	int (*probe)(struct mmc_card *);
+	void (*remove)(struct mmc_card *);
+	int (*suspend)(struct mmc_card *, u32);
+	int (*resume)(struct mmc_card *);
+};
+
+extern int mmc_register_driver(struct mmc_driver *);
+extern void mmc_unregister_driver(struct mmc_driver *);
+
+static inline int mmc_card_claim_host(struct mmc_card *card)
+{
+	return __mmc_claim_host(card->host, card);
+}
+
+#define mmc_card_release_host(c)	mmc_release_host((c)->host)
+
+#endif
diff -Nru a/include/linux/mmc/host.h b/include/linux/mmc/host.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/include/linux/mmc/host.h	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,102 @@
+/*
+ *  linux/include/linux/mmc/host.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Host driver specific definitions.
+ */
+#ifndef LINUX_MMC_HOST_H
+#define LINUX_MMC_HOST_H
+
+#include <linux/mmc/mmc.h>
+
+struct mmc_ios {
+	unsigned int	clock;			/* clock rate */
+	unsigned short	vdd;
+
+#define	MMC_VDD_150	0
+#define	MMC_VDD_155	1
+#define	MMC_VDD_160	2
+#define	MMC_VDD_165	3
+#define	MMC_VDD_170	4
+#define	MMC_VDD_180	5
+#define	MMC_VDD_190	6
+#define	MMC_VDD_200	7
+#define	MMC_VDD_210	8
+#define	MMC_VDD_220	9
+#define	MMC_VDD_230	10
+#define	MMC_VDD_240	11
+#define	MMC_VDD_250	12
+#define	MMC_VDD_260	13
+#define	MMC_VDD_270	14
+#define	MMC_VDD_280	15
+#define	MMC_VDD_290	16
+#define	MMC_VDD_300	17
+#define	MMC_VDD_310	18
+#define	MMC_VDD_320	19
+#define	MMC_VDD_330	20
+#define	MMC_VDD_340	21
+#define	MMC_VDD_350	22
+#define	MMC_VDD_360	23
+
+	unsigned char	bus_mode;		/* command output mode */
+
+#define MMC_BUSMODE_OPENDRAIN	1
+#define MMC_BUSMODE_PUSHPULL	2
+
+	unsigned char	power_mode;		/* power supply mode */
+
+#define MMC_POWER_OFF		0
+#define MMC_POWER_UP		1
+#define MMC_POWER_ON		2
+};
+
+struct mmc_host_ops {
+	void	(*request)(struct mmc_host *host, struct mmc_request *req);
+	void	(*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
+};
+
+struct mmc_card;
+struct device;
+
+struct mmc_host {
+	struct device		*dev;
+	struct mmc_host_ops	*ops;
+	void			*priv;
+	unsigned int		f_min;
+	unsigned int		f_max;
+	u32			ocr_avail;
+	char			host_name[8];
+
+	/* private data */
+	struct mmc_ios		ios;		/* current io bus settings */
+	u32			ocr;		/* the current OCR setting */
+
+	struct list_head	cards;		/* devices attached to this host */
+
+	wait_queue_head_t	wq;
+	spinlock_t		lock;		/* card_busy lock */
+	struct mmc_card		*card_busy;	/* the MMC card claiming host */
+	struct mmc_card		*card_selected;	/* the selected MMC card */
+
+	struct work_struct	detect;
+};
+
+extern struct mmc_host *mmc_alloc_host(int extra, struct device *);
+extern int mmc_add_host(struct mmc_host *);
+extern void mmc_remove_host(struct mmc_host *);
+extern void mmc_free_host(struct mmc_host *);
+
+#define mmc_priv(x)	((void *)((x) + 1))
+#define mmc_dev(x)	((x)->dev)
+
+extern int mmc_suspend_host(struct mmc_host *, u32);
+extern int mmc_resume_host(struct mmc_host *);
+
+extern void mmc_detect_change(struct mmc_host *);
+extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
+
+#endif
+
diff -Nru a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/include/linux/mmc/mmc.h	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,88 @@
+/*
+ *  linux/include/linux/mmc/mmc.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef MMC_H
+#define MMC_H
+
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+struct request;
+struct mmc_data;
+struct mmc_request;
+
+struct mmc_command {
+	u32			opcode;
+	u32			arg;
+	u32			resp[4];
+	unsigned int		flags;		/* expected response type */
+#define MMC_RSP_NONE	(0 << 0)
+#define MMC_RSP_SHORT	(1 << 0)
+#define MMC_RSP_LONG	(2 << 0)
+#define MMC_RSP_MASK	(3 << 0)
+#define MMC_RSP_CRC	(1 << 3)		/* expect valid crc */
+#define MMC_RSP_BUSY	(1 << 4)		/* card may send busy */
+
+	unsigned int		retries;	/* max number of retries */
+	unsigned int		error;		/* command error */
+
+#define MMC_ERR_NONE	0
+#define MMC_ERR_TIMEOUT	1
+#define MMC_ERR_BADCRC	2
+#define MMC_ERR_FIFO	3
+#define MMC_ERR_FAILED	4
+#define MMC_ERR_INVALID	5
+
+	struct mmc_data		*data;		/* data segment associated with cmd */
+	struct mmc_request	*mrq;		/* assoicated request */
+};
+
+struct mmc_data {
+	unsigned int		timeout_ns;	/* data timeout (in ns, max 80ms) */
+	unsigned int		timeout_clks;	/* data timeout (in clocks) */
+	unsigned int		blksz_bits;	/* data block size */
+	unsigned int		blocks;		/* number of blocks */
+	struct request		*req;		/* request structure */
+	unsigned int		error;		/* data error */
+	unsigned int		flags;
+
+#define MMC_DATA_WRITE	(1 << 8)
+#define MMC_DATA_READ	(1 << 9)
+#define MMC_DATA_STREAM	(1 << 10)
+
+	unsigned int		bytes_xfered;
+
+	struct mmc_command	*stop;		/* stop command */
+	struct mmc_request	*mrq;		/* assoicated request */
+};
+
+struct mmc_request {
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+	struct mmc_command	*stop;
+
+	void			*done_data;	/* completion data */
+	void			(*done)(struct mmc_request *);/* completion function */
+};
+
+struct mmc_host;
+struct mmc_card;
+
+extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
+extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
+
+extern int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card);
+
+static inline void mmc_claim_host(struct mmc_host *host)
+{
+	__mmc_claim_host(host, (struct mmc_card *)-1);
+}
+
+extern void mmc_release_host(struct mmc_host *host);
+
+#endif
diff -Nru a/include/linux/mmc/protocol.h b/include/linux/mmc/protocol.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/include/linux/mmc/protocol.h	2004-08-20 02:18:22 -07:00
@@ -0,0 +1,203 @@
+/*
+ * Header for MultiMediaCard (MMC)
+ *
+ * Copyright 2002 Hewlett-Packard Company
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Based strongly on code by:
+ *
+ * Author: Yong-iL Joh <tolkien@mizi.com>
+ * Date  : $Date: 2002/06/18 12:37:30 $
+ *
+ * Author:  Andrew Christian
+ *          15 May 2002
+ */
+
+#ifndef MMC_MMC_PROTOCOL_H
+#define MMC_MMC_PROTOCOL_H
+
+/* Standard MMC commands (3.1)           type  argument     response */
+   /* class 1 */
+#define	MMC_GO_IDLE_STATE         0   /* bc                          */
+#define MMC_SEND_OP_COND          1   /* bcr  [31:0] OCR         R3  */
+#define MMC_ALL_SEND_CID          2   /* bcr                     R2  */
+#define MMC_SET_RELATIVE_ADDR     3   /* ac   [31:16] RCA        R1  */
+#define MMC_SET_DSR               4   /* bc   [31:16] RCA            */
+#define MMC_SELECT_CARD           7   /* ac   [31:16] RCA        R1  */
+#define MMC_SEND_CSD              9   /* ac   [31:16] RCA        R2  */
+#define MMC_SEND_CID             10   /* ac   [31:16] RCA        R2  */
+#define MMC_READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */
+#define MMC_STOP_TRANSMISSION    12   /* ac                      R1b */
+#define MMC_SEND_STATUS	         13   /* ac   [31:16] RCA        R1  */
+#define MMC_GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */
+
+  /* class 2 */
+#define MMC_SET_BLOCKLEN         16   /* ac   [31:0] block len   R1  */
+#define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1  */
+#define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1  */
+
+  /* class 3 */
+#define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1  */
+
+  /* class 4 */
+#define MMC_SET_BLOCK_COUNT      23   /* adtc [31:0] data addr   R1  */
+#define MMC_WRITE_BLOCK          24   /* adtc [31:0] data addr   R1  */
+#define MMC_WRITE_MULTIPLE_BLOCK 25   /* adtc                    R1  */
+#define MMC_PROGRAM_CID          26   /* adtc                    R1  */
+#define MMC_PROGRAM_CSD          27   /* adtc                    R1  */
+
+  /* class 6 */
+#define MMC_SET_WRITE_PROT       28   /* ac   [31:0] data addr   R1b */
+#define MMC_CLR_WRITE_PROT       29   /* ac   [31:0] data addr   R1b */
+#define MMC_SEND_WRITE_PROT      30   /* adtc [31:0] wpdata addr R1  */
+
+  /* class 5 */
+#define MMC_ERASE_GROUP_START    35   /* ac   [31:0] data addr   R1  */
+#define MMC_ERASE_GROUP_END      36   /* ac   [31:0] data addr   R1  */
+#define MMC_ERASE                37   /* ac                      R1b */
+
+  /* class 9 */
+#define MMC_FAST_IO              39   /* ac   <Complex>          R4  */
+#define MMC_GO_IRQ_STATE         40   /* bcr                     R5  */
+
+  /* class 7 */
+#define MMC_LOCK_UNLOCK          42   /* adtc                    R1b */
+
+  /* class 8 */
+#define MMC_APP_CMD              55   /* ac   [31:16] RCA        R1  */
+#define MMC_GEN_CMD              56   /* adtc [0] RD/WR          R1b */
+
+/*
+  MMC status in R1
+  Type
+  	e : error bit
+	s : status bit
+	r : detected and set for the actual command response
+	x : detected and set during command execution. the host must poll
+            the card by sending status command in order to read these bits.
+  Clear condition
+  	a : according to the card state
+	b : always related to the previous command. Reception of
+            a valid command will clear it (with a delay of one command)
+	c : clear by read
+ */
+
+#define R1_OUT_OF_RANGE		(1 << 31)	/* er, c */
+#define R1_ADDRESS_ERROR	(1 << 30)	/* erx, c */
+#define R1_BLOCK_LEN_ERROR	(1 << 29)	/* er, c */
+#define R1_ERASE_SEQ_ERROR      (1 << 28)	/* er, c */
+#define R1_ERASE_PARAM		(1 << 27)	/* ex, c */
+#define R1_WP_VIOLATION		(1 << 26)	/* erx, c */
+#define R1_CARD_IS_LOCKED	(1 << 25)	/* sx, a */
+#define R1_LOCK_UNLOCK_FAILED	(1 << 24)	/* erx, c */
+#define R1_COM_CRC_ERROR	(1 << 23)	/* er, b */
+#define R1_ILLEGAL_COMMAND	(1 << 22)	/* er, b */
+#define R1_CARD_ECC_FAILED	(1 << 21)	/* ex, c */
+#define R1_CC_ERROR		(1 << 20)	/* erx, c */
+#define R1_ERROR		(1 << 19)	/* erx, c */
+#define R1_UNDERRUN		(1 << 18)	/* ex, c */
+#define R1_OVERRUN		(1 << 17)	/* ex, c */
+#define R1_CID_CSD_OVERWRITE	(1 << 16)	/* erx, c, CID/CSD overwrite */
+#define R1_WP_ERASE_SKIP	(1 << 15)	/* sx, c */
+#define R1_CARD_ECC_DISABLED	(1 << 14)	/* sx, a */
+#define R1_ERASE_RESET		(1 << 13)	/* sr, c */
+#define R1_STATUS(x)            (x & 0xFFFFE000)
+#define R1_CURRENT_STATE(x)    	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
+#define R1_READY_FOR_DATA	(1 << 8)	/* sx, a */
+#define R1_APP_CMD		(1 << 7)	/* sr, c */
+
+/* These are unpacked versions of the actual responses */
+
+struct _mmc_csd {
+	u8  csd_structure;
+	u8  spec_vers;
+	u8  taac;
+	u8  nsac;
+	u8  tran_speed;
+	u16 ccc;
+	u8  read_bl_len;
+	u8  read_bl_partial;
+	u8  write_blk_misalign;
+	u8  read_blk_misalign;
+	u8  dsr_imp;
+	u16 c_size;
+	u8  vdd_r_curr_min;
+	u8  vdd_r_curr_max;
+	u8  vdd_w_curr_min;
+	u8  vdd_w_curr_max;
+	u8  c_size_mult;
+	union {
+		struct { /* MMC system specification version 3.1 */
+			u8  erase_grp_size;
+			u8  erase_grp_mult;
+		} v31;
+		struct { /* MMC system specification version 2.2 */
+			u8  sector_size;
+			u8  erase_grp_size;
+		} v22;
+	} erase;
+	u8  wp_grp_size;
+	u8  wp_grp_enable;
+	u8  default_ecc;
+	u8  r2w_factor;
+	u8  write_bl_len;
+	u8  write_bl_partial;
+	u8  file_format_grp;
+	u8  copy;
+	u8  perm_write_protect;
+	u8  tmp_write_protect;
+	u8  file_format;
+	u8  ecc;
+};
+
+#define MMC_VDD_145_150	0x00000001	/* VDD voltage 1.45 - 1.50 */
+#define MMC_VDD_150_155	0x00000002	/* VDD voltage 1.50 - 1.55 */
+#define MMC_VDD_155_160	0x00000004	/* VDD voltage 1.55 - 1.60 */
+#define MMC_VDD_160_165	0x00000008	/* VDD voltage 1.60 - 1.65 */
+#define MMC_VDD_165_170	0x00000010	/* VDD voltage 1.65 - 1.70 */
+#define MMC_VDD_17_18	0x00000020	/* VDD voltage 1.7 - 1.8 */
+#define MMC_VDD_18_19	0x00000040	/* VDD voltage 1.8 - 1.9 */
+#define MMC_VDD_19_20	0x00000080	/* VDD voltage 1.9 - 2.0 */
+#define MMC_VDD_20_21	0x00000100	/* VDD voltage 2.0 ~ 2.1 */
+#define MMC_VDD_21_22	0x00000200	/* VDD voltage 2.1 ~ 2.2 */
+#define MMC_VDD_22_23	0x00000400	/* VDD voltage 2.2 ~ 2.3 */
+#define MMC_VDD_23_24	0x00000800	/* VDD voltage 2.3 ~ 2.4 */
+#define MMC_VDD_24_25	0x00001000	/* VDD voltage 2.4 ~ 2.5 */
+#define MMC_VDD_25_26	0x00002000	/* VDD voltage 2.5 ~ 2.6 */
+#define MMC_VDD_26_27	0x00004000	/* VDD voltage 2.6 ~ 2.7 */
+#define MMC_VDD_27_28	0x00008000	/* VDD voltage 2.7 ~ 2.8 */
+#define MMC_VDD_28_29	0x00010000	/* VDD voltage 2.8 ~ 2.9 */
+#define MMC_VDD_29_30	0x00020000	/* VDD voltage 2.9 ~ 3.0 */
+#define MMC_VDD_30_31	0x00040000	/* VDD voltage 3.0 ~ 3.1 */
+#define MMC_VDD_31_32	0x00080000	/* VDD voltage 3.1 ~ 3.2 */
+#define MMC_VDD_32_33	0x00100000	/* VDD voltage 3.2 ~ 3.3 */
+#define MMC_VDD_33_34	0x00200000	/* VDD voltage 3.3 ~ 3.4 */
+#define MMC_VDD_34_35	0x00400000	/* VDD voltage 3.4 ~ 3.5 */
+#define MMC_VDD_35_36	0x00800000	/* VDD voltage 3.5 ~ 3.6 */
+#define MMC_CARD_BUSY	0x80000000	/* Card Power up status bit */
+
+
+/*
+ * CSD field definitions
+ */
+
+#define CSD_STRUCT_VER_1_0  0           /* Valid for system specification 1.0 - 1.2 */
+#define CSD_STRUCT_VER_1_1  1           /* Valid for system specification 1.4 - 2.2 */
+#define CSD_STRUCT_VER_1_2  2           /* Valid for system specification 3.1       */
+
+#define CSD_SPEC_VER_0      0           /* Implements system specification 1.0 - 1.2 */
+#define CSD_SPEC_VER_1      1           /* Implements system specification 1.4 */
+#define CSD_SPEC_VER_2      2           /* Implements system specification 2.0 - 2.2 */
+#define CSD_SPEC_VER_3      3           /* Implements system specification 3.1 */
+
+#endif  /* MMC_MMC_PROTOCOL_H */
+