[coreboot-gerrit] New patch to review for coreboot: WIP: Add coreboot storage driver

Bora Guvendik (bora.guvendik@intel.com) gerrit at coreboot.org
Thu Jan 12 02:34:38 CET 2017


Bora Guvendik (bora.guvendik at intel.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18105

-gerrit

commit ccca22d1ec4939c9d5dfd15728b5ba7dd1c49196
Author: Bora Guvendik <bora.guvendik at intel.com>
Date:   Fri Dec 9 11:40:51 2016 -0800

    WIP: Add coreboot storage driver
    
    Port sdhci and mmc driver from depthcharge to
    coreboot. The purpose is to be able to start emmc
    initialization earlier in the boot process.
    
    BUG=chrome-os-partner:59875
    BRANCH=reef
    TEST=None
    
    Change-Id: I2b96e0fabc5277529f6621ecd228fde83e619390
    Signed-off-by: Bora Guvendik <bora.guvendik at intel.com>
---
 src/drivers/storage/Kconfig          |    3 +
 src/drivers/storage/Makefile.inc     |    5 +
 src/drivers/storage/mmc.c            | 1073 ++++++++++++++++++++++++++++++++++
 src/drivers/storage/mmc.h            |  327 +++++++++++
 src/drivers/storage/pci_sdhci.c      |  104 ++++
 src/drivers/storage/sdhci.c          |  721 +++++++++++++++++++++++
 src/drivers/storage/sdhci.h          |  363 ++++++++++++
 src/drivers/storage/storage_common.h |   28 +
 8 files changed, 2624 insertions(+)

diff --git a/src/drivers/storage/Kconfig b/src/drivers/storage/Kconfig
new file mode 100644
index 0000000..9f980ad
--- /dev/null
+++ b/src/drivers/storage/Kconfig
@@ -0,0 +1,3 @@
+config DRIVERS_STORAGE
+	bool
+	default n
diff --git a/src/drivers/storage/Makefile.inc b/src/drivers/storage/Makefile.inc
new file mode 100644
index 0000000..97d0d1f
--- /dev/null
+++ b/src/drivers/storage/Makefile.inc
@@ -0,0 +1,5 @@
+ifeq ($(CONFIG_DRIVERS_STORAGE),y)
+
+ramstage-y += sdhci.c mmc.c pci_sdhci.c
+
+endif
diff --git a/src/drivers/storage/mmc.c b/src/drivers/storage/mmc.c
new file mode 100644
index 0000000..e5e650e
--- /dev/null
+++ b/src/drivers/storage/mmc.c
@@ -0,0 +1,1073 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc.  All rights reserved.
+ *
+ * Based vaguely on the Linux code
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <endian.h>
+#include <stdint.h>
+#include <string.h>
+#include <delay.h>
+#include <timer.h>
+
+#include "config.h"
+#include "mmc.h"
+
+/* Set block count limit because of 16 bit register limit on some hardware*/
+#ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT
+#define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535
+#endif
+
+/* Set to 1 to turn on debug messages. */
+int32_t __mmc_debug = 0;
+int32_t __mmc_trace = 0;
+
+int32_t mmc_busy_wait_io(volatile uint32_t *address, uint32_t *output,
+		     uint32_t io_mask, uint32_t timeout_ms)
+{
+	uint32_t value = (uint32_t)-1;
+	struct mono_time current, end;
+
+	timer_monotonic_get(&current);
+	end = current;
+        mono_time_add_msecs(&end, timeout_ms);
+
+	if (!output)
+		output = &value;
+	for (; *output & io_mask; *output = readl(address)) {
+		timer_monotonic_get(&current);
+		if (mono_time_after(&current, &end))
+			return -1;
+	}
+	return 0;
+}
+
+int32_t mmc_busy_wait_io_until(volatile uint32_t *address, uint32_t *output,
+			   uint32_t io_mask, uint32_t timeout_ms)
+{
+	uint32_t value = 0;
+        struct mono_time current, end;
+
+        timer_monotonic_get(&current);
+        end = current;
+        mono_time_add_msecs(&end, timeout_ms);
+
+	if (!output)
+		output = &value;
+	for (; !(*output & io_mask); *output = readl(address)) {
+		timer_monotonic_get(&current);
+		if (mono_time_after(&current, &end))
+			return -1;
+	}
+	return 0;
+}
+
+static uint64_t extract_uint32_bits(const uint32_t *array, int32_t start, int32_t count)
+{
+	int32_t i;
+	uint64_t value = 0;
+
+	for (i = 0; i < count; i++, start++) {
+		value <<= 1;
+		value |= (array[start / 32] >> (31 - (start % 32))) & 0x1;
+	}
+	return value;
+}
+
+static int32_t mmc_send_cmd(MmcCtrlr *ctrlr, MmcCommand *cmd, MmcData *data)
+{
+	int32_t ret = -1, retries = 2;
+
+	mmc_trace("CMD_SEND:%d %p\n", cmd->cmdidx, ctrlr);
+	mmc_trace("\tARG\t\t\t %#8.8x\n", cmd->cmdarg);
+	mmc_trace("\tFLAG\t\t\t %d\n", cmd->flags);
+	if (data) {
+		mmc_trace("\t%s %d block(s) of %d bytes (%p)\n",
+			  data->flags == MMC_DATA_READ ? "READ" : "WRITE",
+			  data->blocks,
+			  data->blocksize,
+			  data->dest);
+	}
+
+	while (retries--) {
+		ret = ctrlr->send_cmd(ctrlr, cmd, data);
+
+		switch (cmd->resp_type) {
+		case MMC_RSP_NONE:
+			mmc_trace("\tMMC_RSP_NONE\n");
+			break;
+
+		case MMC_RSP_R1:
+			mmc_trace("\tMMC_RSP_R1,5,6,7 \t %#8.8x\n",
+				  cmd->response[0]);
+			break;
+
+		case MMC_RSP_R1b:
+			mmc_trace("\tMMC_RSP_R1b\t\t %#8.8x\n",
+				  cmd->response[0]);
+			break;
+
+		case MMC_RSP_R2:
+			mmc_trace("\tMMC_RSP_R2\t\t %#8.8x\n",
+				  cmd->response[0]);
+			mmc_trace("\t          \t\t %#8.8x\n",
+				  cmd->response[1]);
+			mmc_trace("\t          \t\t %#8.8x\n",
+				  cmd->response[2]);
+			mmc_trace("\t          \t\t %#8.8x\n",
+				  cmd->response[3]);
+			break;
+
+		case MMC_RSP_R3:
+			mmc_trace("\tMMC_RSP_R3,4\t\t %#8.8x\n",
+				  cmd->response[0]);
+			break;
+
+		default:
+			mmc_trace("\tERROR MMC rsp not supported\n");
+			break;
+		}
+		mmc_trace("\trv:\t\t\t %d\n", ret);
+
+		/* Retry failed commands, bail out otherwise.  */
+		if (!ret)
+			break;
+	}
+	return ret;
+}
+
+static int32_t mmc_send_status(MmcMedia *media, ssize_t tries)
+{
+	MmcCommand cmd;
+	cmd.cmdidx = MMC_CMD_SEND_STATUS;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.cmdarg = media->rca << 16;
+	cmd.flags = 0;
+
+	while (tries--) {
+		int32_t err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+		if (err)
+			return err;
+		else if (cmd.response[0] & MMC_STATUS_RDY_FOR_DATA)
+			break;
+		else if (cmd.response[0] & MMC_STATUS_MASK) {
+			mmc_error("Status Error: %#8.8x\n", cmd.response[0]);
+			return MMC_COMM_ERR;
+		}
+
+		udelay(100);
+	}
+
+	mmc_trace("CURR STATE:%d\n",
+		  (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9);
+
+	if (tries < 0) {
+		mmc_error("Timeout waiting card ready\n");
+		return MMC_TIMEOUT;
+	}
+	return 0;
+}
+
+int32_t mmc_set_blocklen(MmcCtrlr *ctrlr, int32_t len)
+{
+	MmcCommand cmd;
+	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.cmdarg = len;
+	cmd.flags = 0;
+
+	return mmc_send_cmd(ctrlr, &cmd, NULL);
+}
+
+uint32_t mmc_write(MmcMedia *media, uint32_t start, lba_t block_count,
+			  const void *src)
+{
+	MmcCommand cmd;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.flags = 0;
+
+	if (block_count > 1)
+		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+	else
+		cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
+
+	if (media->high_capacity)
+		cmd.cmdarg = start;
+	else
+		cmd.cmdarg = start * media->write_bl_len;
+
+	MmcData data;
+	data.src = src;
+	data.blocks = block_count;
+	data.blocksize = media->write_bl_len;
+	data.flags = MMC_DATA_WRITE;
+
+	if (mmc_send_cmd(media->ctrlr, &cmd, &data)) {
+		mmc_error("mmc write failed\n");
+		return 0;
+	}
+
+	/* SPI multiblock writes terminate using a special
+	 * token, not a STOP_TRANSMISSION request.
+	 */
+	if ((block_count > 1) && !(media->ctrlr->caps & MMC_AUTO_CMD12)) {
+		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+		cmd.cmdarg = 0;
+		cmd.resp_type = MMC_RSP_R1b;
+		cmd.flags = 0;
+		if (mmc_send_cmd(media->ctrlr, &cmd, NULL)) {
+			mmc_error("mmc fail to send stop cmd\n");
+			return 0;
+		}
+
+		/* Waiting for the ready status */
+		mmc_send_status(media, MMC_IO_RETRIES);
+	}
+
+	return block_count;
+}
+
+int32_t mmc_read(MmcMedia *media, void *dest, uint32_t start,
+		    lba_t block_count)
+{
+
+	MmcCommand cmd;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.flags = 0;
+
+	if (block_count > 1)
+		cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
+	else
+		cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
+
+	if (media->high_capacity)
+		cmd.cmdarg = start;
+	else
+		cmd.cmdarg = start * media->read_bl_len;
+
+	MmcData data;
+	data.dest = dest;
+	data.blocks = block_count;
+	data.blocksize = media->read_bl_len;
+	data.flags = MMC_DATA_READ;
+
+	if (mmc_send_cmd(media->ctrlr, &cmd, &data))
+		return 0;
+
+	if ((block_count > 1) && !(media->ctrlr->caps & MMC_AUTO_CMD12)) {
+		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+		cmd.cmdarg = 0;
+		cmd.resp_type = MMC_RSP_R1b;
+		cmd.flags = 0;
+		if (mmc_send_cmd(media->ctrlr, &cmd, NULL)) {
+			mmc_error("mmc fail to send stop cmd\n");
+			return 0;
+		}
+
+		/* Waiting for the ready status */
+		mmc_send_status(media, MMC_IO_RETRIES);
+	}
+
+	return block_count;
+}
+
+static int32_t mmc_go_idle(MmcMedia *media,int removable)
+{
+	// Some cards can't accept idle commands without delay.
+	if (removable)
+		mdelay(1);
+
+	MmcCommand cmd;
+	cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
+	cmd.cmdarg = 0;
+	cmd.resp_type = MMC_RSP_NONE;
+	cmd.flags = 0;
+
+	int32_t err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+	if (err)
+		return err;
+
+	// Some cards need more than half second to respond to next command (ex,
+	// SEND_OP_COND).
+	if (removable)
+		mdelay(2);
+
+	return 0;
+}
+
+static int32_t sd_send_op_cond(MmcMedia *media)
+{
+	int32_t err;
+	MmcCommand cmd;
+
+	int32_t tries = MMC_IO_RETRIES;
+	while (tries--) {
+		cmd.cmdidx = MMC_CMD_APP_CMD;
+		cmd.resp_type = MMC_RSP_R1;
+		cmd.cmdarg = 0;
+		cmd.flags = 0;
+
+		err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+		if (err)
+			return err;
+
+		cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
+		cmd.resp_type = MMC_RSP_R3;
+
+		/*
+		 * Most cards do not answer if some reserved bits
+		 * in the ocr are set. However, Some controller
+		 * can set bit 7 (reserved for low voltages), but
+		 * how to manage low voltages SD card is not yet
+		 * specified.
+		 */
+		cmd.cmdarg = (media->ctrlr->voltages & 0xff8000);
+
+		if (media->version == SD_VERSION_2)
+			cmd.cmdarg |= OCR_HCS;
+
+		err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+		if (err)
+			return err;
+
+		// OCR_BUSY means "initialization complete".
+		if (cmd.response[0] & OCR_BUSY)
+			break;
+
+		udelay(100);
+	}
+	if (tries < 0)
+		return MMC_UNUSABLE_ERR;
+
+	if (media->version != SD_VERSION_2)
+		media->version = SD_VERSION_1_0;
+
+	media->ocr = cmd.response[0];
+	media->high_capacity = ((media->ocr & OCR_HCS) == OCR_HCS);
+	media->rca = 0;
+	return 0;
+}
+
+/* We pass in the cmd since otherwise the init seems to fail */
+static int32_t mmc_send_op_cond_iter(MmcMedia *media, MmcCommand *cmd, int32_t use_arg)
+{
+	cmd->cmdidx = MMC_CMD_SEND_OP_COND;
+	cmd->resp_type = MMC_RSP_R3;
+
+	if (use_arg) {
+		uint32_t mask = media->op_cond_response &
+			(OCR_VOLTAGE_MASK | OCR_ACCESS_MODE);
+		cmd->cmdarg = media->ctrlr->voltages & mask;
+
+		if (media->ctrlr->caps & MMC_MODE_HC)
+			cmd->cmdarg |= OCR_HCS;
+	}
+	cmd->flags = 0;
+	int32_t err = mmc_send_cmd(media->ctrlr, cmd, NULL);
+	if (err)
+		return err;
+
+	media->op_cond_response = cmd->response[0];
+	return 0;
+}
+
+static int32_t mmc_send_op_cond(MmcMedia *media,int removable)
+{
+	MmcCommand cmd;
+	int32_t max_iters;
+	int32_t i;
+
+	/* Some cards seem to need this */
+	mmc_go_idle(media,removable);
+
+	/* Devices with hardcoded voltage do not need second iteration. */
+	cmd.cmdarg = media->ctrlr->hardcoded_voltage;
+	max_iters = cmd.cmdarg ? 1 : 2;
+
+	/* Ask the card for its capabilities unless required to be hardcoded. */
+	for (i = 0; i < max_iters; i++) {
+		int32_t err = mmc_send_op_cond_iter(media, &cmd, i != 0);
+		if (err)
+			return err;
+
+		// OCR_BUSY is active low, this bit set means
+		// "initialization complete".
+		if (media->op_cond_response & OCR_BUSY)
+			return 0;
+	}
+	return MMC_IN_PROGRESS;
+}
+
+static int32_t mmc_complete_op_cond(MmcMedia *media)
+{
+	MmcCommand cmd;
+
+	int32_t timeout = MMC_INIT_TIMEOUT_US_MS;
+        struct mono_time current, end;
+
+        timer_monotonic_get(&current);
+        end = current;
+        mono_time_add_msecs(&end, timeout);
+
+	while (1) {
+		// CMD1 queries whether initialization is done.
+		int32_t err = mmc_send_op_cond_iter(media, &cmd, 1);
+		if (err)
+			return err;
+
+		// OCR_BUSY means "initialization complete".
+		if (media->op_cond_response & OCR_BUSY)
+			break;
+
+		// Check if init timeout has expired.
+		timer_monotonic_get(&current);
+		if (mono_time_after(&current, &end))
+			return MMC_UNUSABLE_ERR;
+
+		udelay(100);
+	}
+
+	media->version = MMC_VERSION_UNKNOWN;
+	media->ocr = cmd.response[0];
+
+	media->high_capacity = ((media->ocr & OCR_HCS) == OCR_HCS);
+	media->rca = 0;
+	return 0;
+}
+
+static int32_t mmc_send_ext_csd(MmcCtrlr *ctrlr, unsigned char *ext_csd)
+{
+	int32_t rv;
+	/* Get the Card Status Register */
+	MmcCommand cmd;
+	cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.cmdarg = 0;
+	cmd.flags = 0;
+
+	MmcData data;
+	data.dest = (char *)ext_csd;
+	data.blocks = 1;
+	data.blocksize = 512;
+	data.flags = MMC_DATA_READ;
+
+	rv = mmc_send_cmd(ctrlr, &cmd, &data);
+
+	if (!rv && __mmc_trace) {
+		int32_t i, size;
+
+		size = data.blocks * data.blocksize;
+		mmc_trace("\t%p ext_csd:", ctrlr);
+		for (i = 0; i < size; i++) {
+			if (!(i % 32))
+			    printk(BIOS_DEBUG,"\n");
+			printk(BIOS_DEBUG," %2.2x", ext_csd[i]);
+		}
+		printk(BIOS_DEBUG,"\n");
+	}
+	return rv;
+}
+
+static int32_t mmc_switch(MmcMedia *media, uint8_t set, uint8_t index,
+		      uint8_t value)
+{
+	MmcCommand cmd;
+	cmd.cmdidx = MMC_CMD_SWITCH;
+	cmd.resp_type = MMC_RSP_R1b;
+	cmd.cmdarg = ((MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+			   (index << 16) | (value << 8));
+	cmd.flags = 0;
+
+	int32_t ret = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+
+	/* Waiting for the ready status */
+	mmc_send_status(media, MMC_IO_RETRIES);
+	return ret;
+
+}
+
+static void mmc_set_bus_width(MmcCtrlr *ctrlr, uint32_t width)
+{
+	ctrlr->bus_width = width;
+	ctrlr->set_ios(ctrlr);
+}
+
+static void mmc_set_timing(MmcCtrlr *ctrlr, uint32_t timing)
+{
+	ctrlr->timing = timing;
+	ctrlr->set_ios(ctrlr);
+}
+
+static int mmc_change_freq(MmcMedia *media)
+{
+	char cardtype;
+	int32_t err;
+	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, ext_csd, 512);
+
+	media->caps = 0;
+
+	/* Only version 4 supports high-speed */
+	if (media->version < MMC_VERSION_4)
+		return 0;
+
+	err = mmc_send_ext_csd(media->ctrlr, ext_csd);
+	if (err)
+		return err;
+
+	if (media->ctrlr->caps & MMC_MODE_HS_200MHz)
+		cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0x1f;
+	else
+		cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf;
+
+	if (cardtype & MMC_HS_200MHZ) {
+		/* Switch to 8-bit since HS200 only support 8-bit bus width */
+		err = mmc_switch(media, EXT_CSD_CMD_SET_NORMAL,
+			 EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8);
+		if (err)
+			return err;
+
+		/* Switch to HS200 */
+		err = mmc_switch(media, EXT_CSD_CMD_SET_NORMAL,
+			 EXT_CSD_HS_TIMING, 0x2);
+		if (err)
+			return err;
+
+		mmc_set_timing(media->ctrlr, MMC_TIMING_MMC_HS200);
+
+		/* Adjust Host Bus Wisth to 8-bit */
+		mmc_set_bus_width(media->ctrlr, 8);
+		media->caps |= EXT_CSD_BUS_WIDTH_8;
+	} else {
+		err = mmc_switch(media, EXT_CSD_CMD_SET_NORMAL,
+			 EXT_CSD_HS_TIMING, 1);
+
+		if (!err)
+			mmc_set_timing(media->ctrlr, MMC_TIMING_MMC_HS);
+	}
+
+	if (err)
+		return err;
+
+	/* Now check to see that it worked */
+	err = mmc_send_ext_csd(media->ctrlr, ext_csd);
+	if (err)
+		return err;
+
+	/* No high-speed support */
+	if (!ext_csd[EXT_CSD_HS_TIMING])
+		return 0;
+
+	/* High Speed is set, there are types: HS200, 52MHz, 26MHz */
+	if (cardtype & MMC_HS_200MHZ)
+		media->caps |= (MMC_MODE_HS_200MHz
+			| MMC_MODE_HS_52MHz | MMC_MODE_HS);
+	else if (cardtype & MMC_HS_52MHZ)
+		media->caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+	else
+		media->caps |= MMC_MODE_HS;
+	return 0;
+}
+
+static int32_t sd_switch(MmcCtrlr *ctrlr, int32_t mode, int32_t group, uint8_t value,
+		     uint8_t *resp)
+{
+	/* Switch the frequency */
+	MmcCommand cmd;
+	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.cmdarg = (mode << 31) | (0xffffff & ~(0xf << (group * 4))) |
+		     (value << (group * 4));
+	cmd.flags = 0;
+
+	MmcData data;
+	data.dest = (char *)resp;
+	data.blocksize = 64;
+	data.blocks = 1;
+	data.flags = MMC_DATA_READ;
+
+	return mmc_send_cmd(ctrlr, &cmd, &data);
+}
+
+static int32_t sd_change_freq(MmcMedia *media)
+{
+	int32_t err, timeout;
+	MmcCommand cmd;
+	MmcData data;
+	ALLOC_CACHE_ALIGN_BUFFER(uint32_t, scr, 2);
+	ALLOC_CACHE_ALIGN_BUFFER(uint32_t, switch_status, 16);
+
+	media->caps = 0;
+
+	/* Read the SCR to find out if this card supports higher speeds */
+	cmd.cmdidx = MMC_CMD_APP_CMD;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.cmdarg = media->rca << 16;
+	cmd.flags = 0;
+
+	err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+	if (err)
+		return err;
+
+	mmc_debug("%s: before SD_CMD_APP_SEND_SCR\n", __func__);
+	cmd.cmdidx = SD_CMD_APP_SEND_SCR;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.cmdarg = 0;
+	cmd.flags = 0;
+
+	timeout = 3;
+	while (timeout--) {
+		data.dest = (char *)scr;
+		data.blocksize = 8;
+		data.blocks = 1;
+		data.flags = MMC_DATA_READ;
+		err = mmc_send_cmd(media->ctrlr, &cmd, &data);
+		if (!err)
+			break;
+	}
+	if (err) {
+		mmc_error("%s: return err (%d).\n", __func__, err);
+		return err;
+	}
+	mmc_debug("%s: end SD_CMD_APP_SEND_SCR\n", __func__);
+
+	media->scr[0] = be32toh(scr[0]);
+	media->scr[1] = be32toh(scr[1]);
+
+	switch ((media->scr[0] >> 24) & 0xf) {
+		case 0:
+			media->version = SD_VERSION_1_0;
+			break;
+		case 1:
+			media->version = SD_VERSION_1_10;
+			break;
+		case 2:
+			media->version = SD_VERSION_2;
+			break;
+		default:
+			media->version = SD_VERSION_1_0;
+			break;
+	}
+
+	if (media->scr[0] & SD_DATA_4BIT)
+		media->caps |= MMC_MODE_4BIT;
+
+	/* Version 1.0 doesn't support switching */
+	if (media->version == SD_VERSION_1_0)
+		return 0;
+
+	timeout = 4;
+	while (timeout--) {
+		err = sd_switch(media->ctrlr, SD_SWITCH_CHECK, 0, 1,
+				(uint8_t *)switch_status);
+		if (err)
+			return err;
+
+		/* The high-speed function is busy.  Try again */
+		if (!(ntohl(switch_status[7]) & SD_HIGHSPEED_BUSY))
+			break;
+	}
+
+	/* If high-speed isn't supported, we return */
+	if (!(ntohl(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
+		return 0;
+
+	/*
+	 * If the host doesn't support SD_HIGHSPEED, do not switch card to
+	 * HIGHSPEED mode even if the card support SD_HIGHSPPED.
+	 * This can avoid furthur problem when the card runs in different
+	 * mode between the host.
+	 */
+	if (!((media->ctrlr->caps & MMC_MODE_HS_52MHz) &&
+		(media->ctrlr->caps & MMC_MODE_HS)))
+		return 0;
+
+	err = sd_switch(media->ctrlr, SD_SWITCH_SWITCH, 0, 1,
+			(uint8_t *)switch_status);
+	if (err)
+		return err;
+
+	if ((ntohl(switch_status[4]) & 0x0f000000) == 0x01000000) {
+		media->caps |= MMC_MODE_HS;
+		mmc_set_timing(media->ctrlr, MMC_TIMING_SD_HS);
+	}
+	return 0;
+}
+
+static void mmc_set_clock(MmcCtrlr *ctrlr, uint32_t clock)
+{
+	clock = MIN(clock, ctrlr->f_max);
+	clock = MAX(clock, ctrlr->f_min);
+
+	ctrlr->bus_hz = clock;
+	ctrlr->set_ios(ctrlr);
+}
+
+static uint32_t mmc_calculate_transfer_speed(uint32_t csd0)
+{
+	uint32_t mult, freq;
+
+	/* frequency bases, divided by 10 to be nice to platforms without
+	 * floating point */
+	static const int32_t fbase[] = {
+		10000,
+		100000,
+		1000000,
+		10000000,
+	};
+	/* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice
+	 * to platforms without floating point. */
+	static const int32_t multipliers[] = {
+		0,  // reserved
+		10,
+		12,
+		13,
+		15,
+		20,
+		25,
+		30,
+		35,
+		40,
+		45,
+		50,
+		55,
+		60,
+		70,
+		80,
+	};
+
+	/* divide frequency by 10, since the mults are 10x bigger */
+	freq = fbase[csd0 & 0x7];
+	mult = multipliers[(csd0 >> 3) & 0xf];
+	return freq * mult;
+}
+
+static int32_t mmc_startup(MmcMedia *media)
+{
+	int32_t err, width;
+	uint64_t cmult, csize, capacity;
+	uint32_t clock = MMC_CLOCK_DEFAULT_MHZ;
+
+	MmcCommand cmd;
+	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, ext_csd, EXT_CSD_SIZE);
+	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, test_csd, EXT_CSD_SIZE);
+
+	/* Put the Card in Identify Mode */
+	cmd.cmdidx = MMC_CMD_ALL_SEND_CID;
+	cmd.resp_type = MMC_RSP_R2;
+	cmd.cmdarg = 0;
+	cmd.flags = 0;
+	err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+	if (err)
+		return err;
+	memcpy(media->cid, cmd.response, sizeof(media->cid));
+
+	/*
+	 * For MMC cards, set the Relative Address.
+	 * For SD cards, get the Relatvie Address.
+	 * This also puts the cards into Standby State
+	 */
+	cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
+	cmd.cmdarg = media->rca << 16;
+	cmd.resp_type = MMC_RSP_R6;
+	cmd.flags = 0;
+	err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+	if (err)
+		return err;
+	if (IS_SD(media))
+		media->rca = (cmd.response[0] >> 16) & 0xffff;
+
+	/* Get the Card-Specific Data */
+	cmd.cmdidx = MMC_CMD_SEND_CSD;
+	cmd.resp_type = MMC_RSP_R2;
+	cmd.cmdarg = media->rca << 16;
+	cmd.flags = 0;
+	err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+
+	/* Waiting for the ready status */
+	mmc_send_status(media, MMC_IO_RETRIES);
+	if (err)
+		return err;
+
+	memcpy(media->csd, cmd.response, sizeof(media->csd));
+	if (media->version == MMC_VERSION_UNKNOWN) {
+		int32_t version = extract_uint32_bits(media->csd, 2, 4);
+		switch (version) {
+			case 0:
+				media->version = MMC_VERSION_1_2;
+				break;
+			case 1:
+				media->version = MMC_VERSION_1_4;
+				break;
+			case 2:
+				media->version = MMC_VERSION_2_2;
+				break;
+			case 3:
+				media->version = MMC_VERSION_3;
+				break;
+			case 4:
+				media->version = MMC_VERSION_4;
+				break;
+			default:
+				media->version = MMC_VERSION_1_2;
+				break;
+		}
+	}
+
+	media->tran_speed = mmc_calculate_transfer_speed(media->csd[0]);
+	media->read_bl_len = 1 << extract_uint32_bits(media->csd, 44, 4);
+
+	if (IS_SD(media))
+		media->write_bl_len = media->read_bl_len;
+	else
+		media->write_bl_len =
+			1 << extract_uint32_bits(media->csd, 102, 4);
+
+	if (media->high_capacity) {
+		cmult = 8;
+		csize = extract_uint32_bits(media->csd, 58, 22);
+
+	} else {
+		csize = extract_uint32_bits(media->csd, 54, 12);
+		cmult = extract_uint32_bits(media->csd, 78, 3);
+	}
+
+	media->capacity = (csize + 1) << (cmult + 2);
+	media->capacity *= media->read_bl_len;
+
+	if (media->read_bl_len > 512)
+		media->read_bl_len = 512;
+
+	if (media->write_bl_len > 512)
+		media->write_bl_len = 512;
+
+	mmc_debug("mmc media info: version=%#x, tran_speed=%d\n",
+	      media->version, (int32_t)media->tran_speed);
+
+	/* Select the card, and put it into Transfer Mode */
+	cmd.cmdidx = MMC_CMD_SELECT_CARD;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.cmdarg = media->rca << 16;
+	cmd.flags = 0;
+	err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+
+	if (err)
+		return err;
+
+	if (!IS_SD(media) && (media->version >= MMC_VERSION_4)) {
+		/* check  ext_csd version and capacity */
+		err = mmc_send_ext_csd(media->ctrlr, ext_csd);
+		if (!err & (ext_csd[EXT_CSD_REV] >= 2)) {
+			/* According to the JEDEC Standard, the value of
+			 * ext_csd's capacity is valid if the value is more
+			 * than 2GB */
+			// TODO(hungte) Replace by letohl().
+			capacity = (ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
+				    ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
+				    ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
+				    ext_csd[EXT_CSD_SEC_CNT + 3] << 24);
+			capacity *= 512;
+
+			if ((capacity >> 20) > 2 * 1024)
+				media->capacity = capacity;
+		}
+	}
+
+	if (IS_SD(media))
+		err = sd_change_freq(media);
+	else
+		err = mmc_change_freq(media);
+	if (err)
+		return err;
+
+	/* Restrict card's capabilities by what the host can do */
+	media->caps &= media->ctrlr->caps;
+
+	if (IS_SD(media)) {
+		if (media->caps & MMC_MODE_4BIT) {
+			cmd.cmdidx = MMC_CMD_APP_CMD;
+			cmd.resp_type = MMC_RSP_R1;
+			cmd.cmdarg = media->rca << 16;
+			cmd.flags = 0;
+
+			err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+			if (err)
+				return err;
+
+			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
+			cmd.resp_type = MMC_RSP_R1;
+			cmd.cmdarg = 2;
+			cmd.flags = 0;
+			err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+			if (err)
+				return err;
+
+			mmc_set_bus_width(media->ctrlr, 4);
+		}
+
+		if (media->caps & MMC_MODE_HS)
+			clock = MMC_CLOCK_50MHZ;
+		else
+			clock = MMC_CLOCK_25MHZ;
+	} else {
+		for (width = EXT_CSD_BUS_WIDTH_8; width >= 0; width--) {
+			/* If HS200 is switched, Bus Width has been 8-bit */
+			if (media->caps & MMC_MODE_HS_200MHz)
+				break;
+
+			/* Set the card to use 4 bit*/
+			err = mmc_switch(media, EXT_CSD_CMD_SET_NORMAL,
+					 EXT_CSD_BUS_WIDTH, width);
+			if (err)
+				continue;
+
+			if (!width) {
+				mmc_set_bus_width(media->ctrlr, 1);
+				break;
+			} else
+				mmc_set_bus_width(media->ctrlr, 4 * width);
+
+			err = mmc_send_ext_csd(media->ctrlr, test_csd);
+			if (!err &&
+			    (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] ==
+			    test_csd[EXT_CSD_PARTITIONING_SUPPORT]) &&
+			    (ext_csd[EXT_CSD_ERASE_GROUP_DEF] ==
+			    test_csd[EXT_CSD_ERASE_GROUP_DEF]) &&
+			    (ext_csd[EXT_CSD_REV] ==
+			    test_csd[EXT_CSD_REV]) &&
+			    (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
+			    test_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
+			    memcmp(&ext_csd[EXT_CSD_SEC_CNT],
+				   &test_csd[EXT_CSD_SEC_CNT], 4) == 0) {
+				media->caps |= width;
+				break;
+			}
+		}
+
+		if (media->caps & MMC_MODE_HS) {
+			if (media->caps & MMC_MODE_HS_200MHz)
+				clock = MMC_CLOCK_200MHZ;
+			else if (media->caps & MMC_MODE_HS_52MHz)
+				clock = MMC_CLOCK_52MHZ;
+			else
+				clock = MMC_CLOCK_26MHZ;
+		}
+	}
+	mmc_set_clock(media->ctrlr, clock);
+
+	printk(BIOS_DEBUG,"Man %06x Snr %u ",
+	       media->cid[0] >> 24,
+	       (((media->cid[2] & 0xffff) << 16) |
+		((media->cid[3] >> 16) & 0xffff)));
+	printk(BIOS_DEBUG,"Product %c%c%c%c", media->cid[0] & 0xff,
+	       (media->cid[1] >> 24), (media->cid[1] >> 16) & 0xff,
+	       (media->cid[1] >> 8) & 0xff);
+	if (!IS_SD(media)) /* eMMC product string is longer */
+		printk(BIOS_DEBUG,"%c%c", media->cid[1] & 0xff,
+		       (media->cid[2] >> 24) & 0xff);
+	printk(BIOS_DEBUG," Revision %d.%d\n", (media->cid[2] >> 20) & 0xf,
+	       (media->cid[2] >> 16) & 0xf);
+
+	/* Check whether to use HC erase group size or not. */
+	if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x1)
+		media->erase_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
+			512 * KiB;
+	else
+		media->erase_size = (extract_uint32_bits(media->csd, 81, 5)
+				     + 1) *
+			(extract_uint32_bits(media->csd, 86, 5) + 1);
+
+	media->trim_mult = ext_csd[EXT_CSD_TRIM_MULT];
+
+	return 0;
+}
+
+static int32_t mmc_send_if_cond(MmcMedia *media)
+{
+	MmcCommand cmd;
+	cmd.cmdidx = SD_CMD_SEND_IF_COND;
+	// Set if host supports voltages between 2.7 and 3.6 V.
+	cmd.cmdarg = ((media->ctrlr->voltages & 0xff8000) != 0) << 8 | 0xaa;
+	cmd.resp_type = MMC_RSP_R7;
+	cmd.flags = 0;
+	int32_t err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+	if (err)
+		return err;
+
+	if ((cmd.response[0] & 0xff) != 0xaa)
+		return MMC_UNUSABLE_ERR;
+	else
+		media->version = SD_VERSION_2;
+	return 0;
+}
+
+int32_t mmc_setup_media(MmcCtrlr *ctrlr,int removable)
+{
+	int32_t err;
+	MmcMedia *media = malloc(sizeof(*media));
+	memset(media,0,sizeof(*media));
+
+	media->ctrlr = ctrlr;
+
+	mmc_set_bus_width(ctrlr, 1);
+	mmc_set_clock(ctrlr, 1);
+
+	/* Reset the Card */
+	err = mmc_go_idle(media,removable);
+	if (err) {
+		free(media);
+		return err;
+	}
+
+	/* Test for SD version 2 */
+	err = mmc_send_if_cond(media);
+
+	/* Get SD card operating condition */
+	err = sd_send_op_cond(media);
+
+	/* If the command timed out, we check for an MMC card */
+	if (err == MMC_TIMEOUT) {
+		err = mmc_send_op_cond(media,removable);
+
+		if (err && err != MMC_IN_PROGRESS) {
+			mmc_error("Card did not respond to voltage select!\n");
+			free(media);
+			return MMC_UNUSABLE_ERR;
+		}
+	}
+
+	if (err && err != MMC_IN_PROGRESS) {
+		free(media);
+		return err;
+	}
+
+	if (err == MMC_IN_PROGRESS)
+		err = mmc_complete_op_cond(media);
+
+	if (!err) {
+		err = mmc_startup(media);
+		if (!err) {
+			ctrlr->media = media;
+			return 0;
+		}
+	}
+
+	free(media);
+	return err;
+}
diff --git a/src/drivers/storage/mmc.h b/src/drivers/storage/mmc.h
new file mode 100644
index 0000000..54c26f2
--- /dev/null
+++ b/src/drivers/storage/mmc.h
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2008,2010 Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc.  All rights reserved.
+ *
+ * Based (loosely) on the Linux code
+ *
+ * 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.
+ */
+
+#ifndef __DRIVERS_STORAGE_MMC_H__
+#define __DRIVERS_STORAGE_MMC_H__
+
+#include "storage_common.h"
+
+#define SD_VERSION_SD		0x20000
+#define SD_VERSION_2		(SD_VERSION_SD | 0x20)
+#define SD_VERSION_1_0		(SD_VERSION_SD | 0x10)
+#define SD_VERSION_1_10		(SD_VERSION_SD | 0x1a)
+#define MMC_VERSION_MMC		0x10000
+#define MMC_VERSION_UNKNOWN	(MMC_VERSION_MMC)
+#define MMC_VERSION_1_2		(MMC_VERSION_MMC | 0x12)
+#define MMC_VERSION_1_4		(MMC_VERSION_MMC | 0x14)
+#define MMC_VERSION_2_2		(MMC_VERSION_MMC | 0x22)
+#define MMC_VERSION_3		(MMC_VERSION_MMC | 0x30)
+#define MMC_VERSION_4		(MMC_VERSION_MMC | 0x40)
+
+#define MMC_MODE_HS		0x001
+#define MMC_MODE_HS_52MHz	0x010
+#define MMC_MODE_HS_200MHz	0x020
+#define MMC_MODE_HS400		0x040
+#define MMC_MODE_HS400ES	0x080
+#define MMC_MODE_1V8_VDD	0x100
+#define MMC_MODE_4BIT		0x200
+#define MMC_MODE_8BIT		0x400
+#define MMC_MODE_SPI		0x800
+#define MMC_MODE_HC		0x1000
+#define MMC_AUTO_CMD12		0x2000
+
+#define SD_DATA_4BIT		0x00040000
+
+#define IS_SD(x)		(x->version & SD_VERSION_SD)
+
+#define MMC_DATA_READ		1
+#define MMC_DATA_WRITE		2
+
+#define MMC_SUPPORT_ERR		-15 /* No support feature */
+#define MMC_NO_CARD_ERR		-16 /* No SD/MMC card inserted */
+#define MMC_UNUSABLE_ERR	-17 /* Unusable Card */
+#define MMC_COMM_ERR		-18 /* Communications Error */
+#define MMC_TIMEOUT		-19
+#define MMC_IN_PROGRESS		-20 /* operation is in progress */
+#define MMC_INVALID_ERR		-21 /* A catch all case. */
+
+#define MMC_CMD_GO_IDLE_STATE		0
+#define MMC_CMD_SEND_OP_COND		1
+#define MMC_CMD_ALL_SEND_CID		2
+#define MMC_CMD_SET_RELATIVE_ADDR	3
+#define MMC_CMD_SET_DSR			4
+#define MMC_CMD_SWITCH			6
+#define MMC_CMD_SELECT_CARD		7
+#define MMC_CMD_SEND_EXT_CSD		8
+#define MMC_CMD_SEND_CSD		9
+#define MMC_CMD_SEND_CID		10
+#define MMC_CMD_STOP_TRANSMISSION	12
+#define MMC_CMD_SEND_STATUS		13
+#define MMC_CMD_SET_BLOCKLEN		16
+#define MMC_CMD_READ_SINGLE_BLOCK	17
+#define MMC_CMD_READ_MULTIPLE_BLOCK	18
+#define MMC_CMD_WRITE_SINGLE_BLOCK	24
+#define MMC_CMD_WRITE_MULTIPLE_BLOCK	25
+#define MMC_CMD_ERASE_GROUP_START	35
+#define MMC_CMD_ERASE_GROUP_END		36
+#define MMC_CMD_ERASE			38
+#define MMC_CMD_APP_CMD			55
+#define MMC_CMD_SPI_READ_OCR		58
+#define MMC_CMD_SPI_CRC_ON_OFF		59
+
+#define MMC_TRIM_ARG			0x1
+#define MMC_SECURE_ERASE_ARG		0x80000000
+
+#define SD_CMD_SEND_RELATIVE_ADDR	3
+#define SD_CMD_SWITCH_FUNC		6
+#define SD_CMD_SEND_IF_COND		8
+
+#define SD_CMD_APP_SET_BUS_WIDTH	6
+#define SD_CMD_ERASE_WR_BLK_START	32
+#define SD_CMD_ERASE_WR_BLK_END		33
+#define SD_CMD_APP_SEND_OP_COND		41
+#define SD_CMD_APP_SEND_SCR		51
+
+/* SCR definitions in different words */
+#define SD_HIGHSPEED_BUSY	0x00020000
+#define SD_HIGHSPEED_SUPPORTED	0x00020000
+
+#define MMC_HS_TIMING		0x00000100
+#define MMC_HS_52MHZ		0x2
+#define MMC_HS_200MHZ		0x10
+#define MMC_HS400		0x40
+
+#define OCR_BUSY		0x80000000
+#define OCR_HCS			0x40000000
+#define OCR_VOLTAGE_MASK	0x007FFF80
+#define OCR_ACCESS_MODE		0x60000000
+
+#define SECURE_ERASE		0x80000000
+
+#define MMC_STATUS_MASK		(~0x0206BF7F)
+#define MMC_STATUS_RDY_FOR_DATA (1 << 8)
+#define MMC_STATUS_CURR_STATE	(0xf << 9)
+#define MMC_STATUS_ERROR	(1 << 19)
+
+#define MMC_VDD_165_195		0x00000080	/* VDD voltage 1.65 - 1.95 */
+#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_VDD_165_195_SHIFT   7
+
+#define MMC_SWITCH_MODE_CMD_SET		0x00 /* Change the command set */
+#define MMC_SWITCH_MODE_SET_BITS	0x01 /* Set bits in EXT_CSD byte
+						addressed by index which are
+						1 in value field */
+#define MMC_SWITCH_MODE_CLEAR_BITS	0x02 /* Clear bits in EXT_CSD byte
+						addressed by index, which are
+						1 in value field */
+#define MMC_SWITCH_MODE_WRITE_BYTE	0x03 /* Set target byte to value */
+
+#define SD_SWITCH_CHECK		0
+#define SD_SWITCH_SWITCH	1
+
+/*
+ * EXT_CSD fields
+ */
+#define EXT_CSD_PARTITIONING_SUPPORT	160	/* RO */
+#define EXT_CSD_ERASE_GROUP_DEF		175	/* R/W */
+#define EXT_CSD_PART_CONF		179	/* R/W */
+#define EXT_CSD_BUS_WIDTH		183	/* R/W */
+#define EXT_CSD_STROBE_SUPPORT		184	/* RO */
+#define EXT_CSD_HS_TIMING		185	/* R/W */
+#define EXT_CSD_REV			192	/* RO */
+#define EXT_CSD_CARD_TYPE		196	/* RO */
+#define EXT_CSD_SEC_CNT			212	/* RO, 4 bytes */
+#define EXT_CSD_HC_ERASE_GRP_SIZE	224	/* RO */
+#define EXT_CSD_TRIM_MULT		232     /* RO */
+
+/*
+ * EXT_CSD field definitions
+ */
+
+#define EXT_CSD_CMD_SET_NORMAL		(1 << 0)
+#define EXT_CSD_CMD_SET_SECURE		(1 << 1)
+#define EXT_CSD_CMD_SET_CPSECURE	(1 << 2)
+
+#define EXT_CSD_CARD_TYPE_26	(1 << 0)	/* Card can run at 26MHz */
+#define EXT_CSD_CARD_TYPE_52	(1 << 1)	/* Card can run at 52MHz */
+
+#define EXT_CSD_BUS_WIDTH_1	0	/* Card is in 1 bit mode */
+#define EXT_CSD_BUS_WIDTH_4	1	/* Card is in 4 bit mode */
+#define EXT_CSD_BUS_WIDTH_8	2	/* Card is in 8 bit mode */
+#define EXT_CSD_DDR_BUS_WIDTH_4	5	/* Card is in 4 bit DDR mode */
+#define EXT_CSD_DDR_BUS_WIDTH_8	6	/* Card is in 8 bit DDR mode */
+#define EXT_CSD_BUS_WIDTH_STROBE (1<<7)	/* Enhanced strobe mode */
+
+#define EXT_CSD_TIMING_BC	0	/* Backwards compatility */
+#define EXT_CSD_TIMING_HS	1	/* High speed */
+#define EXT_CSD_TIMING_HS200	2	/* HS200 */
+#define EXT_CSD_TIMING_HS400	3	/* HS400 */
+
+#define R1_ILLEGAL_COMMAND		(1 << 22)
+#define R1_APP_CMD			(1 << 5)
+
+#define MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_136	(1 << 1)		/* 136 bit response */
+#define MMC_RSP_CRC	(1 << 2)		/* expect valid crc */
+#define MMC_RSP_BUSY	(1 << 3)		/* card may send busy */
+#define MMC_RSP_OPCODE	(1 << 4)		/* response contains opcode */
+
+#define MMC_RSP_NONE	(0)
+#define MMC_RSP_R1	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R1b	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE| \
+			MMC_RSP_BUSY)
+#define MMC_RSP_R2	(MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
+#define MMC_RSP_R3	(MMC_RSP_PRESENT)
+#define MMC_RSP_R4	(MMC_RSP_PRESENT)
+#define MMC_RSP_R5	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R6	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R7	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+
+#define MMC_INIT_TIMEOUT_US_MS	1000
+#define MMC_IO_RETRIES	(1000)
+#define MMC_CLOCK_20MHZ (20000000)
+#define MMC_CLOCK_25MHZ (25000000)
+#define MMC_CLOCK_26MHZ (26000000)
+#define MMC_CLOCK_50MHZ (50000000)
+#define MMC_CLOCK_52MHZ (52000000)
+#define MMC_CLOCK_200MHZ (200000000)
+#define MMC_CLOCK_DEFAULT_MHZ	(MMC_CLOCK_20MHZ)
+
+#define EXT_CSD_SIZE	(512)
+
+typedef struct MmcCommand {
+	uint16_t cmdidx;
+	uint32_t resp_type;
+	uint32_t cmdarg;
+	uint32_t response[4];
+	uint32_t flags;
+} MmcCommand;
+
+typedef struct MmcData {
+	union {
+		char *dest;
+		const char *src;
+	};
+	uint32_t flags;
+	uint32_t blocks;
+	uint32_t blocksize;
+} MmcData;
+
+struct MmcMedia;
+typedef struct MmcMedia MmcMedia;
+
+typedef struct MmcCtrlr {
+	MmcMedia *media;
+
+	uint32_t voltages;
+	uint32_t f_min;
+	uint32_t f_max;
+	uint32_t bus_width;
+	uint32_t bus_hz;
+	uint32_t caps;
+	uint32_t b_max;
+	uint32_t timing;
+
+#define MMC_TIMING_LEGACY	0
+#define MMC_TIMING_MMC_HS	1
+#define MMC_TIMING_SD_HS	2
+#define MMC_TIMING_UHS_SDR12	3
+#define MMC_TIMING_UHS_SDR25	4
+#define MMC_TIMING_UHS_SDR50	5
+#define MMC_TIMING_UHS_SDR104	6
+#define MMC_TIMING_UHS_DDR50	7
+#define MMC_TIMING_MMC_DDR52	8
+#define MMC_TIMING_MMC_HS200	9
+#define MMC_TIMING_MMC_HS400	10
+
+	/*
+	 * Some eMMC devices do not support iterative OCR setting, they need
+	 * to be programmed with the required/expected value. This field is
+	 * used to set the value for those devices.
+	 */
+	uint32_t hardcoded_voltage;
+
+	int (*send_cmd)(struct MmcCtrlr *me, MmcCommand *cmd, MmcData *data);
+	void (*set_ios)(struct MmcCtrlr *me);
+} MmcCtrlr;
+
+typedef struct MmcMedia {
+	MmcCtrlr *ctrlr;
+
+	uint32_t caps;
+	uint32_t version;
+	uint32_t read_bl_len;
+	uint32_t write_bl_len;
+	uint64_t capacity;
+	int high_capacity;
+	uint32_t tran_speed;
+	/* Erase size in terms of block length. */
+	uint32_t erase_size;
+	/* Trim operation multiplier for determining timeout. */
+	uint32_t trim_mult;
+
+	uint32_t ocr;
+	uint16_t rca;
+	uint32_t scr[2];
+	uint32_t csd[4];
+	uint32_t cid[4];
+
+	uint32_t op_cond_response; // The response byte from the last op_cond
+} MmcMedia;
+
+int mmc_busy_wait_io(volatile uint32_t *address, uint32_t *output,
+		     uint32_t io_mask, uint32_t timeout_ms);
+int mmc_busy_wait_io_until(volatile uint32_t *address, uint32_t *output,
+			   uint32_t io_mask, uint32_t timeout_ms);
+uint32_t mmc_write(MmcMedia *media, uint32_t start, lba_t block_count,
+                          const void *src);
+int32_t mmc_read(MmcMedia *media, void *dest, uint32_t start,
+                    lba_t block_count);
+int32_t mmc_set_blocklen(MmcCtrlr *ctrlr, int32_t len);
+
+int mmc_setup_media(MmcCtrlr *ctrlr,int removable);
+
+// Debug functions.
+extern int __mmc_debug, __mmc_trace;
+#define mmc_debug(format...) \
+		while (__mmc_debug) { printk(BIOS_DEBUG,"mmc: " format); break; }
+#define mmc_trace(format...) \
+		while (__mmc_trace) { printk(BIOS_DEBUG,format); break; }
+#define mmc_error(format...) printk(BIOS_DEBUG,"mmc: ERROR: " format)
+
+#endif /* __DRIVERS_STORAGE_MMC_H__ */
diff --git a/src/drivers/storage/pci_sdhci.c b/src/drivers/storage/pci_sdhci.c
new file mode 100644
index 0000000..baf2588
--- /dev/null
+++ b/src/drivers/storage/pci_sdhci.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <device/pci_def.h>
+#include <device/pci_ops.h>
+#include <string.h>
+
+#include "storage_common.h"
+#include "sdhci.h"
+
+
+typedef struct {
+	SdhciHost sdhci_host;
+	struct device* sdhci_dev;
+	char dev_name[0x20];
+} PciSdhciHost;
+
+PciSdhciHost *gHostPtr;
+
+/* Discover the register file address of the PCI SDHCI device. */
+static int attach_device(SdhciHost *host)
+{
+	PciSdhciHost *pci_host;
+	uint32_t addr;
+
+	pci_host = container_of(host, PciSdhciHost, sdhci_host);
+	addr = pci_read_config32(pci_host->sdhci_dev, PCI_BASE_ADDRESS_0);
+
+	if (addr == ((uint32_t)~0)) {
+		printk(BIOS_DEBUG,"%s: Error: %s not found\n",
+		       __func__, pci_host->dev_name);
+		return -1;
+	}
+
+	host->ioaddr = (void *) (addr & ~0xf);
+
+	return 0;
+}
+
+/* Initialize an HDHCI port */
+SdhciHost *new_pci_sdhci_host(struct device* dev, int platform_info,
+			      int clock_min, int clock_max)
+{
+	PciSdhciHost *host;
+	int removable = platform_info & SDHCI_PLATFORM_REMOVABLE;
+
+	host = malloc(sizeof(*host));
+	memset(host,0,sizeof(*host));
+
+	gHostPtr = host;
+
+	host->sdhci_dev = dev;
+	host->sdhci_host.quirks = SDHCI_QUIRK_NO_HISPD_BIT |
+		SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER;
+
+	if (platform_info & SDHCI_PLATFORM_NO_EMMC_HS200)
+		host->sdhci_host.quirks |= SDHCI_QUIRK_NO_EMMC_HS200;
+
+	if (platform_info & SDHCI_PLATFORM_EMMC_1V8_POWER)
+		host->sdhci_host.quirks |= SDHCI_QUIRK_EMMC_1V8_POWER;
+
+	host->sdhci_host.attach = attach_device;
+	host->sdhci_host.clock_f_min = clock_min;
+	host->sdhci_host.clock_f_max = clock_max;
+	host->sdhci_host.removable = removable;
+
+	if (!removable)
+		/*
+		 * The value translates to 'block access mode, supporting
+		 * 1.7..1.95 and 2.7..3.6 voltage ranges, which is typical for
+		 * eMMC devices.
+		 */
+		host->sdhci_host.mmc_ctrlr.hardcoded_voltage = 0x40ff8080;
+
+	/*
+	 * Need to program host->ioaddr for SD/MMC read/write operation
+	 */
+	attach_device(&host->sdhci_host);
+
+	add_sdhci(&host->sdhci_host);
+
+	return &host->sdhci_host;
+}
+
+SdhciHost* sdhci_get_host_ptr( void )
+{
+	return &gHostPtr->sdhci_host;
+}
+
diff --git a/src/drivers/storage/sdhci.c b/src/drivers/storage/sdhci.c
new file mode 100644
index 0000000..54cc54d
--- /dev/null
+++ b/src/drivers/storage/sdhci.c
@@ -0,0 +1,721 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen at marvell.com>
+ *
+ * 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.
+ *
+ * Back ported to the 8xx platform (from the 8260 platform) by
+ * Murray.Jensen at cmst.csiro.au, 27-Jan-01.
+ */
+
+#include "mmc.h"
+#include "sdhci.h"
+
+#include <delay.h>
+#include <assert.h>
+#include <endian.h>
+#include <stdint.h>
+#include <timer.h>
+#include <string.h>
+
+static void sdhci_reset(SdhciHost *host, u8 mask)
+{
+	unsigned long timeout;
+        struct mono_time current, end;
+
+	/* Wait max 100 ms */
+	timeout = 100;
+
+	timer_monotonic_get(&current);
+	end = current;
+	mono_time_add_msecs(&end, timeout);
+
+
+	sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+	while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
+		timer_monotonic_get(&current);
+		if (mono_time_after(&current, &end)) {
+			printk(BIOS_DEBUG,"Reset 0x%x never completed.\n", (int)mask);
+			return;
+		}
+		udelay(1000);
+	}
+}
+
+static void sdhci_cmd_done(SdhciHost *host, MmcCommand *cmd)
+{
+	int i;
+	if (cmd->resp_type & MMC_RSP_136) {
+		/* CRC is stripped so we need to do some shifting. */
+		for (i = 0; i < 4; i++) {
+			cmd->response[i] = sdhci_readl(host,
+					SDHCI_RESPONSE + (3-i)*4) << 8;
+			if (i != 3)
+				cmd->response[i] |= sdhci_readb(host,
+						SDHCI_RESPONSE + (3-i)*4-1);
+		}
+	} else {
+		cmd->response[0] = sdhci_readl(host, SDHCI_RESPONSE);
+	}
+}
+
+static void sdhci_transfer_pio(SdhciHost *host, MmcData *data)
+{
+	int i;
+	char *offs;
+	for (i = 0; i < data->blocksize; i += 4) {
+		offs = data->dest + i;
+		if (data->flags == MMC_DATA_READ)
+			*(u32 *)offs = sdhci_readl(host, SDHCI_BUFFER);
+		else
+			sdhci_writel(host, *(u32 *)offs, SDHCI_BUFFER);
+	}
+}
+
+static int sdhci_transfer_data(SdhciHost *host, MmcData *data,
+			       unsigned int start_addr)
+{
+	unsigned int stat, rdy, mask, timeout, block = 0;
+
+	timeout = 1000000;
+	rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL;
+	mask = SDHCI_DATA_AVAILABLE | SDHCI_SPACE_AVAILABLE;
+	do {
+		stat = sdhci_readl(host, SDHCI_INT_STATUS);
+		if (stat & SDHCI_INT_ERROR) {
+			printk(BIOS_DEBUG,"Error detected in status(0x%X)!\n", stat);
+			return -1;
+		}
+		if (stat & rdy) {
+			if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & mask))
+				continue;
+			sdhci_writel(host, rdy, SDHCI_INT_STATUS);
+			sdhci_transfer_pio(host, data);
+			data->dest += data->blocksize;
+			if (++block >= data->blocks)
+				break;
+		}
+		if (timeout-- > 0)
+			udelay(10);
+		else {
+			printk(BIOS_DEBUG,"Transfer data timeout\n");
+			return -1;
+		}
+	} while (!(stat & SDHCI_INT_DATA_END));
+	return 0;
+}
+
+static void sdhci_alloc_adma_descs(SdhciHost *host, u32 need_descriptors)
+{
+	if (host->adma_descs) {
+		if (host->adma_desc_count < need_descriptors) {
+			/* Previously allocated array is too small */
+			free(host->adma_descs);
+			host->adma_desc_count = 0;
+			host->adma_descs = NULL;
+		}
+	}
+
+	if (!host->adma_descs) {
+		host->adma_descs = malloc(need_descriptors *
+					      sizeof(*host->adma_descs));
+		if (host->adma_descs == NULL)
+			die("fail to malloc adma_descs\n");
+		host->adma_desc_count = need_descriptors;
+	}
+
+	memset(host->adma_descs, 0, sizeof(*host->adma_descs) *
+	       need_descriptors);
+}
+
+static void sdhci_alloc_adma64_descs(SdhciHost *host, u32 need_descriptors)
+{
+	if (host->adma64_descs) {
+		if (host->adma_desc_count < need_descriptors) {
+			/* Previously allocated array is too small */
+			free(host->adma64_descs);
+			host->adma_desc_count = 0;
+			host->adma64_descs = NULL;
+		}
+	}
+
+	if (!host->adma64_descs) {
+		host->adma64_descs = malloc(need_descriptors *
+					   sizeof(*host->adma64_descs));
+		if (host->adma64_descs == NULL)
+			die("fail to malloc adma64_descs\n");
+
+		host->adma_desc_count = need_descriptors;
+	}
+
+	memset(host->adma64_descs, 0, sizeof(*host->adma64_descs) *
+	       need_descriptors);
+}
+
+static int sdhci_setup_adma(SdhciHost *host, MmcData *data)
+{
+	int i, togo, need_descriptors;
+	char *buffer_data;
+	u16 attributes;
+
+	togo = data->blocks * data->blocksize;
+	if (!togo) {
+		printk(BIOS_DEBUG,"%s: MmcData corrupted: %d blocks of %d bytes\n",
+		       __func__, data->blocks, data->blocksize);
+		return -1;
+	}
+
+	need_descriptors = 1 +  togo / SDHCI_MAX_PER_DESCRIPTOR;
+
+	if (host->dma64)
+		sdhci_alloc_adma64_descs(host, need_descriptors);
+	else
+		sdhci_alloc_adma_descs(host, need_descriptors);
+
+	buffer_data = data->dest;
+
+	/* Now set up the descriptor chain. */
+	for (i = 0; togo; i++) {
+		unsigned desc_length;
+
+		if (togo < SDHCI_MAX_PER_DESCRIPTOR)
+			desc_length = togo;
+		else
+			desc_length = SDHCI_MAX_PER_DESCRIPTOR;
+		togo -= desc_length;
+
+		attributes = SDHCI_ADMA_VALID | SDHCI_ACT_TRAN;
+		if (togo == 0)
+			attributes |= SDHCI_ADMA_END;
+
+		if (host->dma64) {
+			host->adma64_descs[i].addr = (uintptr_t) buffer_data;
+			host->adma64_descs[i].addr_hi = 0;
+			host->adma64_descs[i].length = desc_length;
+			host->adma64_descs[i].attributes = attributes;
+
+		} else {
+			host->adma_descs[i].addr = (uintptr_t) buffer_data;
+			host->adma_descs[i].length = desc_length;
+			host->adma_descs[i].attributes = attributes;
+		}
+
+		buffer_data += desc_length;
+	}
+
+	if (host->dma64)
+		sdhci_writel(host, (uintptr_t) host->adma64_descs,
+			     SDHCI_ADMA_ADDRESS);
+	else
+		sdhci_writel(host, (uintptr_t) host->adma_descs,
+			     SDHCI_ADMA_ADDRESS);
+
+	return 0;
+}
+
+static int sdhci_complete_adma(SdhciHost *host, MmcCommand *cmd)
+{
+	int retry;
+	u32 stat = 0, mask;
+
+	mask = SDHCI_INT_RESPONSE | SDHCI_INT_ERROR;
+
+	retry = 10000; /* Command should be done in way less than 10 ms. */
+	while (--retry) {
+		stat = sdhci_readl(host, SDHCI_INT_STATUS);
+		if (stat & mask)
+			break;
+		udelay(1);
+	}
+
+	sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
+
+	if (retry && !(stat & SDHCI_INT_ERROR)) {
+		/* Command OK, let's wait for data transfer completion. */
+		mask = SDHCI_INT_DATA_END |
+			SDHCI_INT_ERROR | SDHCI_INT_ADMA_ERROR;
+
+		/* Transfer should take 10 seconds tops. */
+		retry = 10 * 1000 * 1000;
+		while (--retry) {
+			stat = sdhci_readl(host, SDHCI_INT_STATUS);
+			if (stat & mask)
+				break;
+			udelay(1);
+		}
+
+		sdhci_writel(host, stat, SDHCI_INT_STATUS);
+		if (retry && !(stat & SDHCI_INT_ERROR)) {
+			sdhci_cmd_done(host, cmd);
+			return 0;
+		}
+	}
+
+	printk(BIOS_DEBUG,"%s: transfer error, stat %#x, adma error %#x, retry %d\n",
+	       __func__, stat, sdhci_readl(host, SDHCI_ADMA_ERROR), retry);
+
+	sdhci_reset(host, SDHCI_RESET_CMD);
+	sdhci_reset(host, SDHCI_RESET_DATA);
+
+	if (stat & SDHCI_INT_TIMEOUT)
+		return MMC_TIMEOUT;
+	else
+		return MMC_COMM_ERR;
+}
+
+static int sdhci_send_command_bounced(MmcCtrlr *mmc_ctrl, MmcCommand *cmd,
+				      MmcData *data)
+{
+	unsigned int stat = 0;
+	int ret = 0;
+	u32 mask, flags;
+	unsigned int timeout, start_addr = 0;
+	struct mono_time current, end;
+	SdhciHost *host = container_of(mmc_ctrl, SdhciHost, mmc_ctrlr);
+
+	/* Wait max 1 s */
+	timeout = 1000;
+
+	sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
+	mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT;
+
+	/* We shouldn't wait for data inihibit for stop commands, even
+	   though they might use busy signaling */
+	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+		mask &= ~SDHCI_DATA_INHIBIT;
+
+	while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
+		if (timeout == 0) {
+			printk(BIOS_DEBUG,"Controller never released inhibit bit(s), "
+			       "present state %#8.8x.\n",
+			       sdhci_readl(host, SDHCI_PRESENT_STATE));
+			return MMC_COMM_ERR;
+		}
+		timeout--;
+		udelay(1000);
+	}
+
+	mask = SDHCI_INT_RESPONSE;
+	if (!(cmd->resp_type & MMC_RSP_PRESENT))
+		flags = SDHCI_CMD_RESP_NONE;
+	else if (cmd->resp_type & MMC_RSP_136)
+		flags = SDHCI_CMD_RESP_LONG;
+	else if (cmd->resp_type & MMC_RSP_BUSY) {
+		flags = SDHCI_CMD_RESP_SHORT_BUSY;
+		mask |= SDHCI_INT_DATA_END;
+	} else
+		flags = SDHCI_CMD_RESP_SHORT;
+
+	if (cmd->resp_type & MMC_RSP_CRC)
+		flags |= SDHCI_CMD_CRC;
+	if (cmd->resp_type & MMC_RSP_OPCODE)
+		flags |= SDHCI_CMD_INDEX;
+	if (data)
+		flags |= SDHCI_CMD_DATA;
+
+	/* Set Transfer mode regarding to data flag */
+	if (data) {
+		u16 mode = 0;
+
+		sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
+						    data->blocksize),
+			     SDHCI_BLOCK_SIZE);
+
+		if (data->flags == MMC_DATA_READ)
+			mode |= SDHCI_TRNS_READ;
+
+		if (data->blocks > 1)
+			mode |= SDHCI_TRNS_BLK_CNT_EN |
+				SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
+
+		sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
+
+		if (host->host_caps & MMC_AUTO_CMD12) {
+			if (sdhci_setup_adma(host, data))
+				return -1;
+
+			mode |= SDHCI_TRNS_DMA;
+		}
+		sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
+	}
+
+	sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT);
+	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND);
+
+	if (data && (host->host_caps & MMC_AUTO_CMD12))
+		return sdhci_complete_adma(host, cmd);
+
+        timer_monotonic_get(&current);
+        end = current;
+        mono_time_add_msecs(&end, 2550);
+
+	do {
+		stat = sdhci_readl(host, SDHCI_INT_STATUS);
+		if (stat & SDHCI_INT_ERROR)
+			break;
+		timer_monotonic_get(&current);
+		/* Apply max timeout for R1b-type CMD defined in eMMC ext_csd
+		   except for erase ones */
+		if (mono_time_after(&current, &end)) {
+			if (host->quirks & SDHCI_QUIRK_BROKEN_R1B)
+				return 0;
+			else {
+				printk(BIOS_DEBUG,"Timeout for status update!\n");
+				return MMC_TIMEOUT;
+			}
+		}
+	} while ((stat & mask) != mask);
+
+	if ((stat & (SDHCI_INT_ERROR | mask)) == mask) {
+		sdhci_cmd_done(host, cmd);
+		sdhci_writel(host, mask, SDHCI_INT_STATUS);
+	} else
+		ret = -1;
+
+	if (!ret && data)
+		ret = sdhci_transfer_data(host, data, start_addr);
+
+	if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD)
+		udelay(1000);
+
+	stat = sdhci_readl(host, SDHCI_INT_STATUS);
+	sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
+
+	if (!ret)
+		return 0;
+
+	sdhci_reset(host, SDHCI_RESET_CMD);
+	sdhci_reset(host, SDHCI_RESET_DATA);
+	if (stat & SDHCI_INT_TIMEOUT)
+		return MMC_TIMEOUT;
+	else
+		return MMC_COMM_ERR;
+}
+
+static int sdhci_send_command(MmcCtrlr *mmc_ctrl, MmcCommand *cmd,
+			      MmcData *data)
+{
+	void *buf;
+	size_t len;
+	int ret;
+
+	if (data) {
+		if (data->flags & MMC_DATA_READ) {
+			buf = data->dest;
+		} else {
+			buf = (void *)data->src;
+		}
+		len = data->blocks * data->blocksize;
+	}
+
+	ret = sdhci_send_command_bounced(mmc_ctrl, cmd, data);
+
+	return ret;
+}
+
+static int sdhci_set_clock(SdhciHost *host, unsigned int clock)
+{
+	unsigned int div, clk, timeout;
+
+	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+	if (clock == 0)
+		return 0;
+
+	if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) {
+		/* Version 3.00 divisors must be a multiple of 2. */
+		if (host->clock_base <= clock)
+			div = 1;
+		else {
+			for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
+				if ((host->clock_base / div) <= clock)
+					break;
+			}
+		}
+	} else {
+		/* Version 2.00 divisors must be a power of 2. */
+		for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) {
+			if ((host->clock_base / div) <= clock)
+				break;
+		}
+	}
+	div >>= 1;
+
+	if (host->set_clock)
+		host->set_clock(host, div);
+
+	if (clock == MMC_CLOCK_200MHZ) {
+		sdhci_writew(host, SDHCI_CTRL_UHS_SDR104
+			| SDHCI_CTRL_VDD_180 | SDHCI_CTRL_DRV_TYPE_A
+			, SDHCI_HOST_CONTROL2);
+	}
+	clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
+	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
+		<< SDHCI_DIVIDER_HI_SHIFT;
+	clk |= SDHCI_CLOCK_INT_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	/* Wait max 20 ms */
+	timeout = 20;
+	while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+		& SDHCI_CLOCK_INT_STABLE)) {
+		if (timeout == 0) {
+			printk(BIOS_DEBUG,"Internal clock never stabilised.\n");
+			return -1;
+		}
+		timeout--;
+		udelay(1000);
+	}
+
+	clk |= SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+	return 0;
+}
+
+/* Find leftmost set bit in a 32 bit integer */
+static int fls(u32 x)
+{
+	int r = 32;
+
+	if (!x)
+		return 0;
+	if (!(x & 0xffff0000u)) {
+		x <<= 16;
+		r -= 16;
+	}
+	if (!(x & 0xff000000u)) {
+		x <<= 8;
+		r -= 8;
+	}
+	if (!(x & 0xf0000000u)) {
+		x <<= 4;
+		r -= 4;
+	}
+	if (!(x & 0xc0000000u)) {
+		x <<= 2;
+		r -= 2;
+	}
+	if (!(x & 0x80000000u)) {
+		x <<= 1;
+		r -= 1;
+	}
+	return r;
+}
+
+static void sdhci_set_power(SdhciHost *host, unsigned short power)
+{
+	u8 pwr = 0;
+
+	if (power != (unsigned short)-1) {
+		switch (1 << power) {
+		case MMC_VDD_165_195:
+			pwr = SDHCI_POWER_180;
+			break;
+		case MMC_VDD_29_30:
+		case MMC_VDD_30_31:
+			pwr = SDHCI_POWER_300;
+			break;
+		case MMC_VDD_32_33:
+		case MMC_VDD_33_34:
+			pwr = SDHCI_POWER_330;
+			break;
+		}
+	}
+
+	if (pwr == 0) {
+		sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+		return;
+	}
+
+	if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
+		sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+
+	pwr |= SDHCI_POWER_ON;
+
+	sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+}
+
+static void sdhci_set_ios(MmcCtrlr *mmc_ctrlr)
+{
+	u32 ctrl;
+	SdhciHost *host = container_of(mmc_ctrlr,
+				       SdhciHost, mmc_ctrlr);
+	if (host->set_control_reg)
+		host->set_control_reg(host);
+	if (mmc_ctrlr->bus_hz != host->clock)
+		sdhci_set_clock(host, mmc_ctrlr->bus_hz);
+	/* Switch to 1.8 volt for HS200 */
+	if (mmc_ctrlr->caps & MMC_MODE_1V8_VDD)
+		if (mmc_ctrlr->bus_hz == MMC_CLOCK_200MHZ)
+			sdhci_set_power(host, MMC_VDD_165_195_SHIFT);
+	/* Set bus width */
+	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+	if (mmc_ctrlr->bus_width == 8) {
+		ctrl &= ~SDHCI_CTRL_4BITBUS;
+		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)
+			ctrl |= SDHCI_CTRL_8BITBUS;
+	} else {
+		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)
+			ctrl &= ~SDHCI_CTRL_8BITBUS;
+		if (mmc_ctrlr->bus_width == 4)
+			ctrl |= SDHCI_CTRL_4BITBUS;
+		else
+			ctrl &= ~SDHCI_CTRL_4BITBUS;
+	}
+	if (mmc_ctrlr->bus_hz > 26000000)
+		ctrl |= SDHCI_CTRL_HISPD;
+	else
+		ctrl &= ~SDHCI_CTRL_HISPD;
+
+	if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)
+		ctrl &= ~SDHCI_CTRL_HISPD;
+
+	if (host->host_caps & MMC_AUTO_CMD12) {
+		ctrl &= ~SDHCI_CTRL_DMA_MASK;
+		if (host->dma64)
+			ctrl |= SDHCI_CTRL_ADMA64;
+		else
+			ctrl |= SDHCI_CTRL_ADMA32;
+	}
+	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+}
+
+/* Prepare SDHCI controller to be initialized */
+static int sdhci_pre_init(SdhciHost *host)
+{
+	unsigned int caps;
+
+	if (host->attach) {
+		int rv = host->attach(host);
+		if (rv)
+			return rv;
+	}
+
+	host->version = sdhci_readw(host, SDHCI_HOST_VERSION) & 0xff;
+
+	caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+
+	if (caps & SDHCI_CAN_DO_ADMA2)
+		host->host_caps |= MMC_AUTO_CMD12;
+
+	/* get base clock frequency from CAP register */
+	if (!(host->quirks & SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN)) {
+		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)
+			host->clock_base = (caps & SDHCI_CLOCK_V3_BASE_MASK)
+				>> SDHCI_CLOCK_BASE_SHIFT;
+		else
+			host->clock_base = (caps & SDHCI_CLOCK_BASE_MASK)
+				>> SDHCI_CLOCK_BASE_SHIFT;
+	}
+
+	if (host->clock_base == 0) {
+		printk(BIOS_DEBUG,"Hardware doesn't specify base clock frequency\n");
+		return -1;
+	}
+
+	host->clock_base *= 1000000;
+
+	if (host->clock_f_max)
+		host->mmc_ctrlr.f_max = host->clock_f_max;
+	else
+		host->mmc_ctrlr.f_max = host->clock_base;
+
+	if (host->clock_f_min) {
+		host->mmc_ctrlr.f_min = host->clock_f_min;
+	} else {
+		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)
+			host->mmc_ctrlr.f_min =
+				host->clock_base / SDHCI_MAX_DIV_SPEC_300;
+		else
+			host->mmc_ctrlr.f_min =
+				host->clock_base / SDHCI_MAX_DIV_SPEC_200;
+	}
+
+	if (caps & SDHCI_CAN_VDD_330)
+		host->mmc_ctrlr.voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+	if (caps & SDHCI_CAN_VDD_300)
+		host->mmc_ctrlr.voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+	if (caps & SDHCI_CAN_VDD_180)
+		host->mmc_ctrlr.voltages |= MMC_VDD_165_195;
+
+	if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE)
+		host->mmc_ctrlr.voltages |= host->voltages;
+
+	if (host->quirks & SDHCI_QUIRK_NO_EMMC_HS200)
+		host->mmc_ctrlr.caps = MMC_MODE_HS | MMC_MODE_HS_52MHz |
+			MMC_MODE_4BIT | MMC_MODE_HC;
+	else
+		host->mmc_ctrlr.caps = MMC_MODE_HS | MMC_MODE_HS_52MHz |
+			MMC_MODE_4BIT | MMC_MODE_HC | MMC_MODE_HS_200MHz;
+
+	if (host->quirks & SDHCI_QUIRK_EMMC_1V8_POWER)
+		host->mmc_ctrlr.caps |= MMC_MODE_1V8_VDD;
+
+	if (caps & SDHCI_CAN_DO_8BIT)
+		host->mmc_ctrlr.caps |= MMC_MODE_8BIT;
+	if (host->host_caps)
+		host->mmc_ctrlr.caps |= host->host_caps;
+	if (caps & SDHCI_CAN_64BIT)
+		host->dma64 = 1;
+
+	sdhci_reset(host, SDHCI_RESET_ALL);
+
+	return 0;
+}
+
+int sdhci_init(SdhciHost *host)
+{
+	int rv = sdhci_pre_init(host);
+
+	if (rv)
+		return rv; /* The error has been already reported */
+
+	sdhci_set_power(host, fls(host->mmc_ctrlr.voltages) - 1);
+
+	if (host->quirks & SDHCI_QUIRK_NO_CD) {
+		unsigned int status;
+
+		sdhci_writel(host, SDHCI_CTRL_CD_TEST_INS | SDHCI_CTRL_CD_TEST,
+			SDHCI_HOST_CONTROL);
+
+		status = sdhci_readl(host, SDHCI_PRESENT_STATE);
+		while ((!(status & SDHCI_CARD_PRESENT)) ||
+		    (!(status & SDHCI_CARD_STATE_STABLE)) ||
+		    (!(status & SDHCI_CARD_DETECT_PIN_LEVEL)))
+			status = sdhci_readl(host, SDHCI_PRESENT_STATE);
+	}
+
+	/* Enable only interrupts served by the SD controller */
+	sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK,
+		     SDHCI_INT_ENABLE);
+	/* Mask all sdhci interrupt sources */
+	sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE);
+
+	/* Set timeout to maximum, shouldn't happen if everything's right. */
+	sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
+
+	udelay(10000);
+	return 0;
+}
+
+void add_sdhci(SdhciHost *host)
+{
+	host->mmc_ctrlr.send_cmd = &sdhci_send_command;
+	host->mmc_ctrlr.set_ios = &sdhci_set_ios;
+
+	/* TODO(vbendeb): check if SDHCI spec allows to retrieve this value. */
+	host->mmc_ctrlr.b_max = 65535;
+}
+
+
diff --git a/src/drivers/storage/sdhci.h b/src/drivers/storage/sdhci.h
new file mode 100644
index 0000000..c44edad
--- /dev/null
+++ b/src/drivers/storage/sdhci.h
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen at marvell.com>
+ *
+ * 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.
+ *
+ * Back ported to the 8xx platform (from the 8260 platform) by
+ * Murray.Jensen at cmst.csiro.au, 27-Jan-01.
+ */
+#ifndef __DRIVER_STORAGE_SDHCI_H__
+#define __DRIVER_STORAGE_SDHCI_H__
+
+#include "mmc.h"
+#include <device/pci_ops.h>
+
+/*
+ * Controller registers
+ */
+
+#define SDHCI_DMA_ADDRESS	0x00
+
+#define SDHCI_BLOCK_SIZE	0x04
+#define  SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+#define SDHCI_BLOCK_COUNT	0x06
+
+#define SDHCI_ARGUMENT		0x08
+
+#define SDHCI_TRANSFER_MODE	0x0C
+#define  SDHCI_TRNS_DMA		0x01
+#define  SDHCI_TRNS_BLK_CNT_EN	0x02
+#define  SDHCI_TRNS_ACMD12	0x04
+#define  SDHCI_TRNS_READ	0x10
+#define  SDHCI_TRNS_MULTI	0x20
+
+#define SDHCI_COMMAND		0x0E
+#define  SDHCI_CMD_RESP_MASK	0x03
+#define  SDHCI_CMD_CRC		0x08
+#define  SDHCI_CMD_INDEX	0x10
+#define  SDHCI_CMD_DATA		0x20
+#define  SDHCI_CMD_ABORTCMD	0xC0
+
+#define  SDHCI_CMD_RESP_NONE	0x00
+#define  SDHCI_CMD_RESP_LONG	0x01
+#define  SDHCI_CMD_RESP_SHORT	0x02
+#define  SDHCI_CMD_RESP_SHORT_BUSY 0x03
+
+#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
+#define SDHCI_GET_CMD(c) ((c>>8) & 0x3f)
+
+#define SDHCI_RESPONSE		0x10
+
+#define SDHCI_BUFFER		0x20
+
+#define SDHCI_PRESENT_STATE	0x24
+#define  SDHCI_CMD_INHIBIT	0x00000001
+#define  SDHCI_DATA_INHIBIT	0x00000002
+#define  SDHCI_DOING_WRITE	0x00000100
+#define  SDHCI_DOING_READ	0x00000200
+#define  SDHCI_SPACE_AVAILABLE	0x00000400
+#define  SDHCI_DATA_AVAILABLE	0x00000800
+#define  SDHCI_CARD_PRESENT	0x00010000
+#define  SDHCI_CARD_STATE_STABLE	0x00020000
+#define  SDHCI_CARD_DETECT_PIN_LEVEL	0x00040000
+#define  SDHCI_WRITE_PROTECT	0x00080000
+
+#define SDHCI_HOST_CONTROL	0x28
+#define  SDHCI_CTRL_LED		0x01
+#define  SDHCI_CTRL_4BITBUS	0x02
+#define  SDHCI_CTRL_HISPD	0x04
+#define  SDHCI_CTRL_DMA_MASK	0x18
+#define   SDHCI_CTRL_SDMA	0x00
+#define   SDHCI_CTRL_ADMA1	0x08
+#define   SDHCI_CTRL_ADMA32	0x10
+#define   SDHCI_CTRL_ADMA64	0x18
+#define  SDHCI_CTRL_8BITBUS	0x20
+#define  SDHCI_CTRL_CD_TEST_INS	0x40
+#define  SDHCI_CTRL_CD_TEST	0x80
+
+#define SDHCI_POWER_CONTROL	0x29
+#define  SDHCI_POWER_ON		0x01
+#define  SDHCI_POWER_180	0x0A
+#define  SDHCI_POWER_300	0x0C
+#define  SDHCI_POWER_330	0x0E
+
+#define SDHCI_BLOCK_GAP_CONTROL	0x2A
+
+#define SDHCI_WAKE_UP_CONTROL	0x2B
+#define  SDHCI_WAKE_ON_INT	0x01
+#define  SDHCI_WAKE_ON_INSERT	0x02
+#define  SDHCI_WAKE_ON_REMOVE	0x04
+
+#define SDHCI_CLOCK_CONTROL	0x2C
+#define  SDHCI_DIVIDER_SHIFT	8
+#define  SDHCI_DIVIDER_HI_SHIFT	6
+#define  SDHCI_DIV_MASK	0xFF
+#define  SDHCI_DIV_MASK_LEN	8
+#define  SDHCI_DIV_HI_MASK	0x300
+#define  SDHCI_CLOCK_CARD_EN	0x0004
+#define  SDHCI_CLOCK_INT_STABLE	0x0002
+#define  SDHCI_CLOCK_INT_EN	0x0001
+
+#define SDHCI_TIMEOUT_CONTROL	0x2E
+
+#define SDHCI_SOFTWARE_RESET	0x2F
+#define  SDHCI_RESET_ALL	0x01
+#define  SDHCI_RESET_CMD	0x02
+#define  SDHCI_RESET_DATA	0x04
+
+#define SDHCI_INT_STATUS	0x30
+#define SDHCI_INT_ENABLE	0x34
+#define SDHCI_SIGNAL_ENABLE	0x38
+#define  SDHCI_INT_RESPONSE	0x00000001
+#define  SDHCI_INT_DATA_END	0x00000002
+#define  SDHCI_INT_DMA_END	0x00000008
+#define  SDHCI_INT_SPACE_AVAIL	0x00000010
+#define  SDHCI_INT_DATA_AVAIL	0x00000020
+#define  SDHCI_INT_CARD_INSERT	0x00000040
+#define  SDHCI_INT_CARD_REMOVE	0x00000080
+#define  SDHCI_INT_CARD_INT	0x00000100
+#define  SDHCI_INT_ERROR	0x00008000
+#define  SDHCI_INT_TIMEOUT	0x00010000
+#define  SDHCI_INT_CRC		0x00020000
+#define  SDHCI_INT_END_BIT	0x00040000
+#define  SDHCI_INT_INDEX	0x00080000
+#define  SDHCI_INT_DATA_TIMEOUT	0x00100000
+#define  SDHCI_INT_DATA_CRC	0x00200000
+#define  SDHCI_INT_DATA_END_BIT	0x00400000
+#define  SDHCI_INT_BUS_POWER	0x00800000
+#define  SDHCI_INT_ACMD12ERR	0x01000000
+#define  SDHCI_INT_ADMA_ERROR	0x02000000
+
+#define  SDHCI_INT_NORMAL_MASK	0x00007FFF
+#define  SDHCI_INT_ERROR_MASK	0xFFFF8000
+
+#define  SDHCI_INT_CMD_MASK	(SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
+				 SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
+#define  SDHCI_INT_DATA_MASK	(SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
+				 SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
+				 SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
+				 SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR)
+#define SDHCI_INT_ALL_MASK	((unsigned int)-1)
+
+#define SDHCI_ACMD12_ERR	0x3C
+
+#define SDHCI_HOST_CONTROL2             0x3E
+#define  SDHCI_CTRL_UHS_MASK            0x0007
+#define   SDHCI_CTRL_UHS_SDR12          0x0000
+#define   SDHCI_CTRL_UHS_SDR25          0x0001
+#define   SDHCI_CTRL_UHS_SDR50          0x0002
+#define   SDHCI_CTRL_UHS_SDR104         0x0003
+#define   SDHCI_CTRL_UHS_DDR50          0x0004
+#define   SDHCI_CTRL_HS_SDR200          0x0005 /* reserved value in SDIO spec */
+#define  SDHCI_CTRL_VDD_180             0x0008
+#define  SDHCI_CTRL_DRV_TYPE_MASK       0x0030
+#define   SDHCI_CTRL_DRV_TYPE_B         0x0000
+#define   SDHCI_CTRL_DRV_TYPE_A         0x0010
+#define   SDHCI_CTRL_DRV_TYPE_C         0x0020
+#define   SDHCI_CTRL_DRV_TYPE_D         0x0030
+#define  SDHCI_CTRL_EXEC_TUNING         0x0040
+#define  SDHCI_CTRL_TUNED_CLK           0x0080
+#define  SDHCI_CTRL_PRESET_VAL_ENABLE   0x8000
+
+#define SDHCI_CAPABILITIES	0x40
+#define  SDHCI_TIMEOUT_CLK_MASK	0x0000003F
+#define  SDHCI_TIMEOUT_CLK_SHIFT 0
+#define  SDHCI_TIMEOUT_CLK_UNIT	0x00000080
+#define  SDHCI_CLOCK_BASE_MASK	0x00003F00
+#define  SDHCI_CLOCK_V3_BASE_MASK	0x0000FF00
+#define  SDHCI_CLOCK_BASE_SHIFT	8
+#define  SDHCI_MAX_BLOCK_MASK	0x00030000
+#define  SDHCI_MAX_BLOCK_SHIFT  16
+#define  SDHCI_CAN_DO_8BIT	0x00040000
+#define  SDHCI_CAN_DO_ADMA2	0x00080000
+#define  SDHCI_CAN_DO_ADMA1	0x00100000
+#define  SDHCI_CAN_DO_HISPD	0x00200000
+#define  SDHCI_CAN_DO_SDMA	0x00400000
+#define  SDHCI_CAN_VDD_330	0x01000000
+#define  SDHCI_CAN_VDD_300	0x02000000
+#define  SDHCI_CAN_VDD_180	0x04000000
+#define  SDHCI_CAN_64BIT	0x10000000
+
+#define SDHCI_CAPABILITIES_1	0x44
+
+#define SDHCI_MAX_CURRENT	0x48
+
+/* 4C-4F reserved for more max current */
+
+#define SDHCI_SET_ACMD12_ERROR	0x50
+#define SDHCI_SET_INT_ERROR	0x52
+
+#define SDHCI_ADMA_ERROR	0x54
+
+/* 55-57 reserved */
+
+#define SDHCI_ADMA_ADDRESS	0x58
+
+/* 60-FB reserved */
+
+#define SDHCI_SLOT_INT_STATUS	0xFC
+
+#define SDHCI_HOST_VERSION	0xFE
+#define  SDHCI_VENDOR_VER_MASK	0xFF00
+#define  SDHCI_VENDOR_VER_SHIFT	8
+#define  SDHCI_SPEC_VER_MASK	0x00FF
+#define  SDHCI_SPEC_VER_SHIFT	0
+#define   SDHCI_SPEC_100	0
+#define   SDHCI_SPEC_200	1
+#define   SDHCI_SPEC_300	2
+
+/*
+ * End of controller registers.
+ */
+
+#define SDHCI_MAX_DIV_SPEC_200	256
+#define SDHCI_MAX_DIV_SPEC_300	2046
+
+/*
+* platform_infos
+*/
+#define SDHCI_PLATFORM_REMOVABLE	(1 << 0)
+#define SDHCI_PLATFORM_NO_EMMC_HS200	(1 << 1)
+#define SDHCI_PLATFORM_EMMC_1V8_POWER	(1 << 2)
+#define SDHCI_PLATFORM_NO_CLK_BASE	(1 << 3)
+/*
+ * quirks
+ */
+#define SDHCI_QUIRK_32BIT_DMA_ADDR	(1 << 0)
+#define SDHCI_QUIRK_REG32_RW		(1 << 1)
+#define SDHCI_QUIRK_BROKEN_R1B		(1 << 2)
+#define SDHCI_QUIRK_NO_HISPD_BIT	(1 << 3)
+#define SDHCI_QUIRK_BROKEN_VOLTAGE	(1 << 4)
+#define SDHCI_QUIRK_NO_CD		(1 << 5)
+#define SDHCI_QUIRK_WAIT_SEND_CMD	(1 << 6)
+#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1 << 7)
+#define SDHCI_QUIRK_NO_EMMC_HS200	(1 << 8)
+#define SDHCI_QUIRK_EMMC_1V8_POWER	(1 << 9)
+#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1 << 10)
+
+/*
+ * Host SDMA buffer boundary. Valid values from 4K to 512K in powers of 2.
+ */
+#define SDHCI_DEFAULT_BOUNDARY_SIZE	(512 * 1024)
+#define SDHCI_DEFAULT_BOUNDARY_ARG	(7)
+
+/* ADMA packet descriptor */
+typedef struct {
+	u16     attributes;
+	u16     length;
+	u32     addr;
+} SdhciAdma;
+
+typedef struct {
+	u16     attributes;
+	u16     length;
+	u32     addr;
+	u32     addr_hi;
+} SdhciAdma64;
+
+#define SDHCI_MAX_PER_DESCRIPTOR 0x10000
+
+/* ADMA descriptor attributes */
+#define SDHCI_ADMA_VALID (1 << 0)
+#define SDHCI_ADMA_END (1 << 1)
+#define SDHCI_ADMA_INT (1 << 2)
+#define SDHCI_ACT_NOP (0 << 4)
+#define SDHCI_ACT_TRAN (2 << 4)
+#define SDHCI_ACT_LINK (3 << 4)
+
+typedef struct sdhci_host SdhciHost;
+
+struct sdhci_host {
+	MmcCtrlr mmc_ctrlr;
+	char *name;
+	void *ioaddr;
+
+	int initialized;
+	unsigned quirks;
+	unsigned host_caps;
+	unsigned version;
+	unsigned clock; /* current, min and max interface clocks */
+	unsigned clock_f_min;
+	unsigned clock_f_max;
+	unsigned clock_base; /* controller base clock */
+	int removable;
+	unsigned voltages;
+
+	/*
+	 * Dynamically allocated array of ADMA descriptors to use for data
+	 * transfers
+	 */
+	SdhciAdma *adma_descs;
+	SdhciAdma64 *adma64_descs;
+	/* select 32bit or 64bit ADMA operations */
+	unsigned dma64;
+
+	/* Number of ADMA descriptors currently in the array. */
+	int adma_desc_count;
+
+	int (*attach)(SdhciHost *host);
+	void (*set_control_reg)(SdhciHost *host);
+	void (*set_clock)(SdhciHost *host, unsigned int div);
+};
+
+
+static inline void sdhci_writel(SdhciHost *host, u32 val, int reg)
+{
+	writel(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writew(SdhciHost *host, u16 val, int reg)
+{
+	writew(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writeb(SdhciHost *host, u8 val, int reg)
+{
+	writeb(val, host->ioaddr + reg);
+}
+static inline u32 sdhci_readl(SdhciHost *host, int reg)
+{
+	return readl(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(SdhciHost *host, int reg)
+{
+	return readw(host->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(SdhciHost *host, int reg)
+{
+	return readb(host->ioaddr + reg);
+}
+
+void add_sdhci(SdhciHost *host);
+int sdhci_init(SdhciHost *host);
+SdhciHost* sdhci_get_host_ptr( void );
+
+/* Add SDHCI controller from PCI */
+SdhciHost *new_pci_sdhci_host(struct device *dev,
+			      int platform_info,
+			      int clock_min,
+			      int clock_max);
+
+/* Add SDHCI controller with memory address */
+SdhciHost *new_mem_sdhci_host(void *ioaddr,
+			      int platform_info,
+			      int clock_min,
+			      int clock_max,
+			      int clock_base);
+
+#endif
diff --git a/src/drivers/storage/storage_common.h b/src/drivers/storage/storage_common.h
new file mode 100644
index 0000000..f4beae7
--- /dev/null
+++ b/src/drivers/storage/storage_common.h
@@ -0,0 +1,28 @@
+#ifndef __COMMON_STORAGE_H__
+#define __COMMON_STORAGE_H__
+
+#include <stdint.h>
+
+#define readb(_a) (*(volatile const unsigned char *) (_a))
+#define readw(_a) (*(volatile const unsigned short *) (_a))
+#define readl(_a) (*(volatile const unsigned int *) (_a))
+
+#define writeb(_v, _a) (*(volatile unsigned char *) (_a) = (_v))
+#define writew(_v, _a) (*(volatile unsigned short *) (_a) = (_v))
+#define writel(_v, _a) (*(volatile unsigned int *) (_a) = (_v))
+
+#define DMA_MINALIGN (64)
+#define ROUND(a,b) (((a) + (b) - 1) & ~((b) - 1))
+#define ALLOC_CACHE_ALIGN_BUFFER(type, name, size)                   \
+        char __##name[ROUND(size * sizeof(type), DMA_MINALIGN) +     \
+                      DMA_MINALIGN - 1];                             \
+        type *name = (type *) ALIGN((uintptr_t)__##name, DMA_MINALIGN)
+
+/* NOOPs mirroring ARM's cache API, since x86 devices usually cache snoop */
+#define dcache_invalidate_by_mva(addr, len)
+#define dcache_clean_invalidate_by_mva(addr, len)
+
+typedef u32 pcidev_t;
+typedef uint64_t lba_t;
+
+#endif /*__COMMON_STORAGE_H__ */



More information about the coreboot-gerrit mailing list