pxa: move mmc drivers to drivers/mmc

introduce new macro CONFIG_PXA_MMC to activate it

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6aa24f5..1b0af12 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -30,6 +30,7 @@
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
+COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o
 
 COBJS	:= $(COBJS-y)
 SRCS	:= $(COBJS:.o=.c)
diff --git a/drivers/mmc/pxa_mmc.c b/drivers/mmc/pxa_mmc.c
new file mode 100644
index 0000000..33da75f
--- /dev/null
+++ b/drivers/mmc/pxa_mmc.c
@@ -0,0 +1,646 @@
+/*
+ * (C) Copyright 2003
+ * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <config.h>
+#include <common.h>
+#include <mmc.h>
+#include <asm/errno.h>
+#include <asm/arch/hardware.h>
+#include <part.h>
+
+#include "pxa_mmc.h"
+
+extern int fat_register_device(block_dev_desc_t * dev_desc, int part_no);
+
+static block_dev_desc_t mmc_dev;
+
+block_dev_desc_t *mmc_get_dev(int dev)
+{
+	return ((block_dev_desc_t *) & mmc_dev);
+}
+
+/*
+ * FIXME needs to read cid and csd info to determine block size
+ * and other parameters
+ */
+static uchar mmc_buf[MMC_BLOCK_SIZE];
+static uchar spec_ver;
+static int mmc_ready = 0;
+static int wide = 0;
+
+static uint32_t *
+/****************************************************/
+mmc_cmd(ushort cmd, ushort argh, ushort argl, ushort cmdat)
+/****************************************************/
+{
+	static uint32_t resp[4], a, b, c;
+	ulong status;
+	int i;
+
+	debug("mmc_cmd %u 0x%04x 0x%04x 0x%04x\n", cmd, argh, argl,
+	      cmdat | wide);
+	MMC_STRPCL = MMC_STRPCL_STOP_CLK;
+	MMC_I_MASK = ~MMC_I_MASK_CLK_IS_OFF;
+	while (!(MMC_I_REG & MMC_I_REG_CLK_IS_OFF)) ;
+	MMC_CMD = cmd;
+	MMC_ARGH = argh;
+	MMC_ARGL = argl;
+	MMC_CMDAT = cmdat | wide;
+	MMC_I_MASK = ~MMC_I_MASK_END_CMD_RES;
+	MMC_STRPCL = MMC_STRPCL_START_CLK;
+	while (!(MMC_I_REG & MMC_I_REG_END_CMD_RES)) ;
+
+	status = MMC_STAT;
+	debug("MMC status 0x%08x\n", status);
+	if (status & MMC_STAT_TIME_OUT_RESPONSE) {
+		return 0;
+	}
+
+	/* Linux says:
+	 * Did I mention this is Sick.  We always need to
+	 * discard the upper 8 bits of the first 16-bit word.
+	 */
+	a = (MMC_RES & 0xffff);
+	for (i = 0; i < 4; i++) {
+		b = (MMC_RES & 0xffff);
+		c = (MMC_RES & 0xffff);
+		resp[i] = (a << 24) | (b << 8) | (c >> 8);
+		a = c;
+		debug("MMC resp[%d] = %#08x\n", i, resp[i]);
+	}
+
+	return resp;
+}
+
+int
+/****************************************************/
+mmc_block_read(uchar * dst, ulong src, ulong len)
+/****************************************************/
+{
+	ushort argh, argl;
+	ulong status;
+
+	if (len == 0) {
+		return 0;
+	}
+
+	debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong) dst, src, len);
+
+	argh = len >> 16;
+	argl = len & 0xffff;
+
+	/* set block len */
+	mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1);
+
+	/* send read command */
+	argh = src >> 16;
+	argl = src & 0xffff;
+	MMC_STRPCL = MMC_STRPCL_STOP_CLK;
+	MMC_RDTO = 0xffff;
+	MMC_NOB = 1;
+	MMC_BLKLEN = len;
+	mmc_cmd(MMC_CMD_READ_SINGLE_BLOCK, argh, argl,
+		MMC_CMDAT_R1 | MMC_CMDAT_READ | MMC_CMDAT_BLOCK |
+		MMC_CMDAT_DATA_EN);
+
+	MMC_I_MASK = ~MMC_I_MASK_RXFIFO_RD_REQ;
+	while (len) {
+		if (MMC_I_REG & MMC_I_REG_RXFIFO_RD_REQ) {
+#ifdef CONFIG_PXA27X
+			int i;
+			for (i = min(len, 32); i; i--) {
+				*dst++ = *((volatile uchar *)&MMC_RXFIFO);
+				len--;
+			}
+#else
+			*dst++ = MMC_RXFIFO;
+			len--;
+#endif
+		}
+		status = MMC_STAT;
+		if (status & MMC_STAT_ERRORS) {
+			printf("MMC_STAT error %lx\n", status);
+			return -1;
+		}
+	}
+	MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE;
+	while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE)) ;
+	status = MMC_STAT;
+	if (status & MMC_STAT_ERRORS) {
+		printf("MMC_STAT error %lx\n", status);
+		return -1;
+	}
+	return 0;
+}
+
+int
+/****************************************************/
+mmc_block_write(ulong dst, uchar * src, int len)
+/****************************************************/
+{
+	ushort argh, argl;
+	ulong status;
+
+	if (len == 0) {
+		return 0;
+	}
+
+	debug("mmc_block_wr dst %lx src %lx len %d\n", dst, (ulong) src, len);
+
+	argh = len >> 16;
+	argl = len & 0xffff;
+
+	/* set block len */
+	mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1);
+
+	/* send write command */
+	argh = dst >> 16;
+	argl = dst & 0xffff;
+	MMC_STRPCL = MMC_STRPCL_STOP_CLK;
+	MMC_NOB = 1;
+	MMC_BLKLEN = len;
+	mmc_cmd(MMC_CMD_WRITE_BLOCK, argh, argl,
+		MMC_CMDAT_R1 | MMC_CMDAT_WRITE | MMC_CMDAT_BLOCK |
+		MMC_CMDAT_DATA_EN);
+
+	MMC_I_MASK = ~MMC_I_MASK_TXFIFO_WR_REQ;
+	while (len) {
+		if (MMC_I_REG & MMC_I_REG_TXFIFO_WR_REQ) {
+			int i, bytes = min(32, len);
+
+			for (i = 0; i < bytes; i++) {
+				MMC_TXFIFO = *src++;
+			}
+			if (bytes < 32) {
+				MMC_PRTBUF = MMC_PRTBUF_BUF_PART_FULL;
+			}
+			len -= bytes;
+		}
+		status = MMC_STAT;
+		if (status & MMC_STAT_ERRORS) {
+			printf("MMC_STAT error %lx\n", status);
+			return -1;
+		}
+	}
+	MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE;
+	while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE)) ;
+	MMC_I_MASK = ~MMC_I_MASK_PRG_DONE;
+	while (!(MMC_I_REG & MMC_I_REG_PRG_DONE)) ;
+	status = MMC_STAT;
+	if (status & MMC_STAT_ERRORS) {
+		printf("MMC_STAT error %lx\n", status);
+		return -1;
+	}
+	return 0;
+}
+
+int
+/****************************************************/
+mmc_read(ulong src, uchar * dst, int size)
+/****************************************************/
+{
+	ulong end, part_start, part_end, part_len, aligned_start, aligned_end;
+	ulong mmc_block_size, mmc_block_address;
+
+	if (size == 0) {
+		return 0;
+	}
+
+	if (!mmc_ready) {
+		printf("Please initial the MMC first\n");
+		return -1;
+	}
+
+	mmc_block_size = MMC_BLOCK_SIZE;
+	mmc_block_address = ~(mmc_block_size - 1);
+
+	src -= CONFIG_SYS_MMC_BASE;
+	end = src + size;
+	part_start = ~mmc_block_address & src;
+	part_end = ~mmc_block_address & end;
+	aligned_start = mmc_block_address & src;
+	aligned_end = mmc_block_address & end;
+
+	/* all block aligned accesses */
+	debug
+	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+	     src, (ulong) dst, end, part_start, part_end, aligned_start,
+	     aligned_end);
+	if (part_start) {
+		part_len = mmc_block_size - part_start;
+		debug
+		    ("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+		     src, (ulong) dst, end, part_start, part_end, aligned_start,
+		     aligned_end);
+		if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) <
+		    0) {
+			return -1;
+		}
+		memcpy(dst, mmc_buf + part_start, part_len);
+		dst += part_len;
+		src += part_len;
+	}
+	debug
+	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+	     src, (ulong) dst, end, part_start, part_end, aligned_start,
+	     aligned_end);
+	for (; src < aligned_end; src += mmc_block_size, dst += mmc_block_size) {
+		debug
+		    ("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+		     src, (ulong) dst, end, part_start, part_end, aligned_start,
+		     aligned_end);
+		if ((mmc_block_read((uchar *) (dst), src, mmc_block_size)) < 0) {
+			return -1;
+		}
+	}
+	debug
+	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+	     src, (ulong) dst, end, part_start, part_end, aligned_start,
+	     aligned_end);
+	if (part_end && src < end) {
+		debug
+		    ("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+		     src, (ulong) dst, end, part_start, part_end, aligned_start,
+		     aligned_end);
+		if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) {
+			return -1;
+		}
+		memcpy(dst, mmc_buf, part_end);
+	}
+	return 0;
+}
+
+int
+/****************************************************/
+mmc_write(uchar * src, ulong dst, int size)
+/****************************************************/
+{
+	ulong end, part_start, part_end, part_len, aligned_start, aligned_end;
+	ulong mmc_block_size, mmc_block_address;
+
+	if (size == 0) {
+		return 0;
+	}
+
+	if (!mmc_ready) {
+		printf("Please initial the MMC first\n");
+		return -1;
+	}
+
+	mmc_block_size = MMC_BLOCK_SIZE;
+	mmc_block_address = ~(mmc_block_size - 1);
+
+	dst -= CONFIG_SYS_MMC_BASE;
+	end = dst + size;
+	part_start = ~mmc_block_address & dst;
+	part_end = ~mmc_block_address & end;
+	aligned_start = mmc_block_address & dst;
+	aligned_end = mmc_block_address & end;
+
+	/* all block aligned accesses */
+	debug
+	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+	     src, (ulong) dst, end, part_start, part_end, aligned_start,
+	     aligned_end);
+	if (part_start) {
+		part_len = mmc_block_size - part_start;
+		debug
+		    ("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+		     (ulong) src, dst, end, part_start, part_end, aligned_start,
+		     aligned_end);
+		if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) <
+		    0) {
+			return -1;
+		}
+		memcpy(mmc_buf + part_start, src, part_len);
+		if ((mmc_block_write(aligned_start, mmc_buf, mmc_block_size)) <
+		    0) {
+			return -1;
+		}
+		dst += part_len;
+		src += part_len;
+	}
+	debug
+	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+	     src, (ulong) dst, end, part_start, part_end, aligned_start,
+	     aligned_end);
+	for (; dst < aligned_end; src += mmc_block_size, dst += mmc_block_size) {
+		debug
+		    ("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+		     src, (ulong) dst, end, part_start, part_end, aligned_start,
+		     aligned_end);
+		if ((mmc_block_write(dst, (uchar *) src, mmc_block_size)) < 0) {
+			return -1;
+		}
+	}
+	debug
+	    ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+	     src, (ulong) dst, end, part_start, part_end, aligned_start,
+	     aligned_end);
+	if (part_end && dst < end) {
+		debug
+		    ("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
+		     src, (ulong) dst, end, part_start, part_end, aligned_start,
+		     aligned_end);
+		if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) {
+			return -1;
+		}
+		memcpy(mmc_buf, src, part_end);
+		if ((mmc_block_write(aligned_end, mmc_buf, mmc_block_size)) < 0) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+ulong
+/****************************************************/
+mmc_bread(int dev_num, ulong blknr, lbaint_t blkcnt, void *dst)
+/****************************************************/
+{
+	int mmc_block_size = MMC_BLOCK_SIZE;
+	ulong src = blknr * mmc_block_size + CONFIG_SYS_MMC_BASE;
+
+	mmc_read(src, (uchar *) dst, blkcnt * mmc_block_size);
+	return blkcnt;
+}
+
+#ifdef __GNUC__
+#define likely(x)       __builtin_expect(!!(x), 1)
+#define unlikely(x)     __builtin_expect(!!(x), 0)
+#else
+#define likely(x)	(x)
+#define unlikely(x)	(x)
+#endif
+
+#define UNSTUFF_BITS(resp,start,size)					\
+	({								\
+		const int __size = size;				\
+		const uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1;	\
+		const int32_t __off = 3 - ((start) / 32);			\
+		const int32_t __shft = (start) & 31;			\
+		uint32_t __res;						\
+									\
+		__res = resp[__off] >> __shft;				\
+		if (__size + __shft > 32)				\
+			__res |= resp[__off-1] << ((32 - __shft) % 32);	\
+		__res & __mask;						\
+	})
+
+/*
+ * Given the decoded CSD structure, decode the raw CID to our CID structure.
+ */
+static void mmc_decode_cid(uint32_t * resp)
+{
+	if (IF_TYPE_SD == mmc_dev.if_type) {
+		/*
+		 * SD doesn't currently have a version field so we will
+		 * have to assume we can parse this.
+		 */
+		sprintf((char *)mmc_dev.vendor,
+			"Man %02x OEM %c%c \"%c%c%c%c%c\" Date %02u/%04u",
+			UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp, 112, 8),
+			UNSTUFF_BITS(resp, 104, 8), UNSTUFF_BITS(resp, 96, 8),
+			UNSTUFF_BITS(resp, 88, 8), UNSTUFF_BITS(resp, 80, 8),
+			UNSTUFF_BITS(resp, 72, 8), UNSTUFF_BITS(resp, 64, 8),
+			UNSTUFF_BITS(resp, 8, 4), UNSTUFF_BITS(resp, 12,
+							       8) + 2000);
+		sprintf((char *)mmc_dev.revision, "%d.%d",
+			UNSTUFF_BITS(resp, 60, 4), UNSTUFF_BITS(resp, 56, 4));
+		sprintf((char *)mmc_dev.product, "%u",
+			UNSTUFF_BITS(resp, 24, 32));
+	} else {
+		/*
+		 * The selection of the format here is based upon published
+		 * specs from sandisk and from what people have reported.
+		 */
+		switch (spec_ver) {
+		case 0:	/* MMC v1.0 - v1.2 */
+		case 1:	/* MMC v1.4 */
+			sprintf((char *)mmc_dev.vendor,
+				"Man %02x%02x%02x \"%c%c%c%c%c%c%c\" Date %02u/%04u",
+				UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp,
+									 112,
+									 8),
+				UNSTUFF_BITS(resp, 104, 8), UNSTUFF_BITS(resp,
+									 96, 8),
+				UNSTUFF_BITS(resp, 88, 8), UNSTUFF_BITS(resp,
+									80, 8),
+				UNSTUFF_BITS(resp, 72, 8), UNSTUFF_BITS(resp,
+									64, 8),
+				UNSTUFF_BITS(resp, 56, 8), UNSTUFF_BITS(resp,
+									48, 8),
+				UNSTUFF_BITS(resp, 12, 4), UNSTUFF_BITS(resp, 8,
+									4) +
+				1997);
+			sprintf((char *)mmc_dev.revision, "%d.%d",
+				UNSTUFF_BITS(resp, 44, 4), UNSTUFF_BITS(resp,
+									40, 4));
+			sprintf((char *)mmc_dev.product, "%u",
+				UNSTUFF_BITS(resp, 16, 24));
+			break;
+
+		case 2:	/* MMC v2.0 - v2.2 */
+		case 3:	/* MMC v3.1 - v3.3 */
+		case 4:	/* MMC v4 */
+			sprintf((char *)mmc_dev.vendor,
+				"Man %02x OEM %04x \"%c%c%c%c%c%c\" Date %02u/%04u",
+				UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp,
+									 104,
+									 16),
+				UNSTUFF_BITS(resp, 96, 8), UNSTUFF_BITS(resp,
+									88, 8),
+				UNSTUFF_BITS(resp, 80, 8), UNSTUFF_BITS(resp,
+									72, 8),
+				UNSTUFF_BITS(resp, 64, 8), UNSTUFF_BITS(resp,
+									56, 8),
+				UNSTUFF_BITS(resp, 12, 4), UNSTUFF_BITS(resp, 8,
+									4) +
+				1997);
+			sprintf((char *)mmc_dev.product, "%u",
+				UNSTUFF_BITS(resp, 16, 32));
+			sprintf((char *)mmc_dev.revision, "N/A");
+			break;
+
+		default:
+			printf("MMC card has unknown MMCA version %d\n",
+			       spec_ver);
+			break;
+		}
+	}
+	printf("%s card.\nVendor: %s\nProduct: %s\nRevision: %s\n",
+	       (IF_TYPE_SD == mmc_dev.if_type) ? "SD" : "MMC", mmc_dev.vendor,
+	       mmc_dev.product, mmc_dev.revision);
+}
+
+/*
+ * Given a 128-bit response, decode to our card CSD structure.
+ */
+static void mmc_decode_csd(uint32_t * resp)
+{
+	unsigned int mult, csd_struct;
+
+	if (IF_TYPE_SD == mmc_dev.if_type) {
+		csd_struct = UNSTUFF_BITS(resp, 126, 2);
+		if (csd_struct != 0) {
+			printf("SD: unrecognised CSD structure version %d\n",
+			       csd_struct);
+			return;
+		}
+	} else {
+		/*
+		 * We only understand CSD structure v1.1 and v1.2.
+		 * v1.2 has extra information in bits 15, 11 and 10.
+		 */
+		csd_struct = UNSTUFF_BITS(resp, 126, 2);
+		if (csd_struct != 1 && csd_struct != 2) {
+			printf("MMC: unrecognised CSD structure version %d\n",
+			       csd_struct);
+			return;
+		}
+
+		spec_ver = UNSTUFF_BITS(resp, 122, 4);
+		mmc_dev.if_type = IF_TYPE_MMC;
+	}
+
+	mult = 1 << (UNSTUFF_BITS(resp, 47, 3) + 2);
+	mmc_dev.lba = (1 + UNSTUFF_BITS(resp, 62, 12)) * mult;
+	mmc_dev.blksz = 1 << UNSTUFF_BITS(resp, 80, 4);
+
+	/* FIXME: The following just makes assumes that's the partition type -- should really read it */
+	mmc_dev.part_type = PART_TYPE_DOS;
+	mmc_dev.dev = 0;
+	mmc_dev.lun = 0;
+	mmc_dev.type = DEV_TYPE_HARDDISK;
+	mmc_dev.removable = 0;
+	mmc_dev.block_read = mmc_bread;
+
+	printf("Detected: %lu blocks of %lu bytes (%luMB) ",
+		mmc_dev.lba,
+		mmc_dev.blksz,
+		mmc_dev.lba * mmc_dev.blksz / (1024 * 1024));
+}
+
+int
+/****************************************************/
+mmc_legacy_init(int verbose)
+/****************************************************/
+{
+	int retries, rc = -ENODEV;
+	uint32_t cid_resp[4];
+	uint32_t *resp;
+	uint16_t rca = 0;
+
+	/* Reset device interface type */
+	mmc_dev.if_type = IF_TYPE_UNKNOWN;
+
+#if defined (CONFIG_LUBBOCK) || (defined (CONFIG_GUMSTIX) && !defined(CONFIG_PXA27X))
+	set_GPIO_mode(GPIO6_MMCCLK_MD);
+	set_GPIO_mode(GPIO8_MMCCS0_MD);
+#endif
+	CKEN |= CKEN12_MMC;	/* enable MMC unit clock */
+
+	MMC_CLKRT = MMC_CLKRT_0_3125MHZ;
+	MMC_RESTO = MMC_RES_TO_MAX;
+	MMC_SPI = MMC_SPI_DISABLE;
+
+	/* reset */
+	mmc_cmd(MMC_CMD_GO_IDLE_STATE, 0, 0, MMC_CMDAT_INIT | MMC_CMDAT_R0);
+	udelay(200000);
+	retries = 3;
+	while (retries--) {
+		resp = mmc_cmd(MMC_CMD_APP_CMD, 0, 0, MMC_CMDAT_R1);
+		if (!(resp[0] & 0x00000020)) {	/* Card does not support APP_CMD */
+			debug("Card does not support APP_CMD\n");
+			break;
+		}
+
+		/* Select 3.2-3.3V and 3.3-3.4V */
+		resp = mmc_cmd(SD_CMD_APP_SEND_OP_COND, 0x0030, 0x0000,
+				MMC_CMDAT_R3 | (retries < 2 ? 0
+					: MMC_CMDAT_INIT));
+		if (resp[0] & 0x80000000) {
+			mmc_dev.if_type = IF_TYPE_SD;
+			debug("Detected SD card\n");
+			break;
+		}
+#ifdef CONFIG_PXA27X
+		udelay(10000);
+#else
+		udelay(200000);
+#endif
+	}
+
+	if (retries <= 0 || !(IF_TYPE_SD == mmc_dev.if_type)) {
+		debug("Failed to detect SD Card, trying MMC\n");
+		resp =
+		    mmc_cmd(MMC_CMD_SEND_OP_COND, 0x00ff, 0x8000, MMC_CMDAT_R3);
+
+		retries = 10;
+		while (retries-- && resp && !(resp[0] & 0x80000000)) {
+#ifdef CONFIG_PXA27X
+			udelay(10000);
+#else
+			udelay(200000);
+#endif
+			resp =
+			    mmc_cmd(MMC_CMD_SEND_OP_COND, 0x00ff, 0x8000,
+				    MMC_CMDAT_R3);
+		}
+	}
+
+	/* try to get card id */
+	resp =
+	    mmc_cmd(MMC_CMD_ALL_SEND_CID, 0, 0, MMC_CMDAT_R2 | MMC_CMDAT_BUSY);
+	if (resp) {
+		memcpy(cid_resp, resp, sizeof(cid_resp));
+
+		/* MMC exists, get CSD too */
+		resp = mmc_cmd(MMC_CMD_SET_RELATIVE_ADDR, 0, 0, MMC_CMDAT_R1);
+		if (IF_TYPE_SD == mmc_dev.if_type)
+			rca = ((resp[0] & 0xffff0000) >> 16);
+		resp = mmc_cmd(MMC_CMD_SEND_CSD, rca, 0, MMC_CMDAT_R2);
+		if (resp) {
+			mmc_decode_csd(resp);
+			rc = 0;
+			mmc_ready = 1;
+		}
+
+		mmc_decode_cid(cid_resp);
+	}
+
+	MMC_CLKRT = 0;		/* 20 MHz */
+	resp = mmc_cmd(MMC_CMD_SELECT_CARD, rca, 0, MMC_CMDAT_R1);
+
+#ifdef CONFIG_PXA27X
+	if (IF_TYPE_SD == mmc_dev.if_type) {
+		resp = mmc_cmd(MMC_CMD_APP_CMD, rca, 0, MMC_CMDAT_R1);
+		resp = mmc_cmd(SD_CMD_APP_SET_BUS_WIDTH, 0, 2, MMC_CMDAT_R1);
+		wide = MMC_CMDAT_SD_4DAT;
+	}
+#endif
+
+	fat_register_device(&mmc_dev, 1);	/* partitions start counting with 1 */
+
+	return rc;
+}
diff --git a/drivers/mmc/pxa_mmc.h b/drivers/mmc/pxa_mmc.h
new file mode 100644
index 0000000..6fa4268
--- /dev/null
+++ b/drivers/mmc/pxa_mmc.h
@@ -0,0 +1,138 @@
+/*
+ *  linux/drivers/mmc/mmc_pxa.h
+ *
+ *  Author: Vladimir Shebordaev, Igor Oblakov
+ *  Copyright:  MontaVista Software Inc.
+ *
+ *  $Id: mmc_pxa.h,v 0.3.1.6 2002/09/25 19:25:48 ted Exp ted $
+ *
+ *  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_PXA_P_H__
+#define __MMC_PXA_P_H__
+
+/* PXA-250 MMC controller registers */
+
+/* MMC_STRPCL */
+#define MMC_STRPCL_STOP_CLK		(0x0001UL)
+#define MMC_STRPCL_START_CLK		(0x0002UL)
+
+/* MMC_STAT */
+#define MMC_STAT_END_CMD_RES		(0x0001UL << 13)
+#define MMC_STAT_PRG_DONE		(0x0001UL << 12)
+#define MMC_STAT_DATA_TRAN_DONE		(0x0001UL << 11)
+#define MMC_STAT_CLK_EN			(0x0001UL << 8)
+#define MMC_STAT_RECV_FIFO_FULL		(0x0001UL << 7)
+#define MMC_STAT_XMIT_FIFO_EMPTY	(0x0001UL << 6)
+#define MMC_STAT_RES_CRC_ERROR		(0x0001UL << 5)
+#define MMC_STAT_SPI_READ_ERROR_TOKEN   (0x0001UL << 4)
+#define MMC_STAT_CRC_READ_ERROR		(0x0001UL << 3)
+#define MMC_STAT_CRC_WRITE_ERROR	(0x0001UL << 2)
+#define MMC_STAT_TIME_OUT_RESPONSE	(0x0001UL << 1)
+#define MMC_STAT_READ_TIME_OUT		(0x0001UL)
+
+#define MMC_STAT_ERRORS (MMC_STAT_RES_CRC_ERROR|MMC_STAT_SPI_READ_ERROR_TOKEN\
+	|MMC_STAT_CRC_READ_ERROR|MMC_STAT_TIME_OUT_RESPONSE\
+	|MMC_STAT_READ_TIME_OUT|MMC_STAT_CRC_WRITE_ERROR)
+
+/* MMC_CLKRT */
+#define MMC_CLKRT_20MHZ			(0x0000UL)
+#define MMC_CLKRT_10MHZ			(0x0001UL)
+#define MMC_CLKRT_5MHZ			(0x0002UL)
+#define MMC_CLKRT_2_5MHZ		(0x0003UL)
+#define MMC_CLKRT_1_25MHZ		(0x0004UL)
+#define MMC_CLKRT_0_625MHZ		(0x0005UL)
+#define MMC_CLKRT_0_3125MHZ		(0x0006UL)
+
+/* MMC_SPI */
+#define MMC_SPI_DISABLE			(0x00UL)
+#define MMC_SPI_EN			(0x01UL)
+#define MMC_SPI_CS_EN			(0x01UL << 2)
+#define MMC_SPI_CS_ADDRESS		(0x01UL << 3)
+#define MMC_SPI_CRC_ON			(0x01UL << 1)
+
+/* MMC_CMDAT */
+#define MMC_CMDAT_SD_4DAT		(0x0001UL << 8)
+#define MMC_CMDAT_MMC_DMA_EN		(0x0001UL << 7)
+#define MMC_CMDAT_INIT			(0x0001UL << 6)
+#define MMC_CMDAT_BUSY			(0x0001UL << 5)
+#define MMC_CMDAT_BCR			(0x0003UL << 5)
+#define MMC_CMDAT_STREAM		(0x0001UL << 4)
+#define MMC_CMDAT_BLOCK			(0x0000UL << 4)
+#define MMC_CMDAT_WRITE			(0x0001UL << 3)
+#define MMC_CMDAT_READ			(0x0000UL << 3)
+#define MMC_CMDAT_DATA_EN		(0x0001UL << 2)
+#define MMC_CMDAT_R0			(0)
+#define MMC_CMDAT_R1			(0x0001UL)
+#define MMC_CMDAT_R2			(0x0002UL)
+#define MMC_CMDAT_R3			(0x0003UL)
+
+/* MMC_RESTO */
+#define MMC_RES_TO_MAX			(0x007fUL) /* [6:0] */
+
+/* MMC_RDTO */
+#define MMC_READ_TO_MAX			(0x0ffffUL) /* [15:0] */
+
+/* MMC_BLKLEN */
+#define MMC_BLK_LEN_MAX			(0x03ffUL) /* [9:0] */
+
+/* MMC_PRTBUF */
+#define MMC_PRTBUF_BUF_PART_FULL	(0x01UL)
+#define MMC_PRTBUF_BUF_FULL		(0x00UL    )
+
+/* MMC_I_MASK */
+#define MMC_I_MASK_TXFIFO_WR_REQ	(0x01UL << 6)
+#define MMC_I_MASK_RXFIFO_RD_REQ	(0x01UL << 5)
+#define MMC_I_MASK_CLK_IS_OFF		(0x01UL << 4)
+#define MMC_I_MASK_STOP_CMD		(0x01UL << 3)
+#define MMC_I_MASK_END_CMD_RES		(0x01UL << 2)
+#define MMC_I_MASK_PRG_DONE		(0x01UL << 1)
+#define MMC_I_MASK_DATA_TRAN_DONE       (0x01UL)
+#define MMC_I_MASK_ALL			(0x07fUL)
+
+
+/* MMC_I_REG */
+#define MMC_I_REG_TXFIFO_WR_REQ		(0x01UL << 6)
+#define MMC_I_REG_RXFIFO_RD_REQ		(0x01UL << 5)
+#define MMC_I_REG_CLK_IS_OFF		(0x01UL << 4)
+#define MMC_I_REG_STOP_CMD		(0x01UL << 3)
+#define MMC_I_REG_END_CMD_RES		(0x01UL << 2)
+#define MMC_I_REG_PRG_DONE		(0x01UL << 1)
+#define MMC_I_REG_DATA_TRAN_DONE	(0x01UL)
+#define MMC_I_REG_ALL			(0x007fUL)
+
+/* MMC_CMD */
+#define MMC_CMD_INDEX_MAX		(0x006fUL)  /* [5:0] */
+#define CMD(x)  (x)
+
+#define MMC_DEFAULT_RCA			1
+
+#define MMC_BLOCK_SIZE			512
+#define MMC_MAX_BLOCK_SIZE		512
+
+#define MMC_R1_IDLE_STATE		0x01
+#define MMC_R1_ERASE_STATE		0x02
+#define MMC_R1_ILLEGAL_CMD		0x04
+#define MMC_R1_COM_CRC_ERR		0x08
+#define MMC_R1_ERASE_SEQ_ERR		0x01
+#define MMC_R1_ADDR_ERR			0x02
+#define MMC_R1_PARAM_ERR		0x04
+
+#define MMC_R1B_WP_ERASE_SKIP		0x0002
+#define MMC_R1B_ERR			0x0004
+#define MMC_R1B_CC_ERR			0x0008
+#define MMC_R1B_CARD_ECC_ERR		0x0010
+#define MMC_R1B_WP_VIOLATION		0x0020
+#define MMC_R1B_ERASE_PARAM		0x0040
+#define MMC_R1B_OOR			0x0080
+#define MMC_R1B_IDLE_STATE		0x0100
+#define MMC_R1B_ERASE_RESET		0x0200
+#define MMC_R1B_ILLEGAL_CMD		0x0400
+#define MMC_R1B_COM_CRC_ERR		0x0800
+#define MMC_R1B_ERASE_SEQ_ERR		0x1000
+#define MMC_R1B_ADDR_ERR		0x2000
+#define MMC_R1B_PARAM_ERR		0x4000
+
+#endif /* __MMC_PXA_P_H__ */