[coreboot-gerrit] Change in coreboot[master]: drivers/storage: Add SD/MMC/eMMC support

Lee Leahy (Code Review) gerrit at coreboot.org
Fri Apr 7 21:42:15 CEST 2017


Lee Leahy has uploaded a new change for review. ( https://review.coreboot.org/19208 )

Change subject: drivers/storage: Add SD/MMC/eMMC support
......................................................................

drivers/storage: Add SD/MMC/eMMC support

drivers/storage: Add depthcharge SD/EMMC driver

Copy the SD/MMC driver from depthcharge revision
eb583fa82e697a97cb13a2906fcda6e2470c7623 into coreboot.

Removed #include "config.h" from mmc.c to allow the lint tests to pass.

TEST=Does not build

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

include/device: Move mmc.h and sdhci.h into include/device

Move include files from drivers/storage into include/device.
Rename mmc.h to storage.h.

TEST=None

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Get SD/MMC driver to build

Add the Kconfig and Makefile and make necessary edits to get the code to
build.

TEST=Build for reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Support PCI SD/MMC controllers

Add support to initialize a PCI controller.

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Fix formatting issues detected by checkpatch

Fix the following errors and warnings detected by checkpatch.pl:

ERROR: code indent should use tabs where possible
ERROR: switch and case should be at the same indent
ERROR: spaces prohibited around that '->' (ctx:VxW)
ERROR: space required after that ',' (ctx:VxV)
WARNING: line over 80 characters
WARNING: please, no spaces at the start of a lineWARNING: please, no spaces at the start of a line
WARNING: Prefer 'unsigned int' to bare use of 'unsigned'
WARNING: suspect code indent for conditional statements (24, 28)

These issues should not change the binary.

TEST=None

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Fix data flow issues detected by checkpatch

Fix the following error and warnings detected by checkpatch.pl:

ERROR: trailing statements should be on next line
WARNING: else is not generally useful after a break or return
WARNING: long udelay - prefer mdelay; see arch/arm/include/asm/delay.h

These change are expected to change the binary.

The following errors detected by checkpatch are false positives:
ERROR: need consistent spacing around '*' (ctx:WxV)
ERROR: Macros with complex values should be enclosed in parentheses

TEST=None

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

include/device: Fix MMC voltage mask

Add the missing voltage (MMC_VDD_35_36) into the voltage mask.

TEST=None

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Rename mmc_* debug macros to sd_mmc_*

Rename the mmc_debug, mmc_trace and mmc_error to sd_mmc_debug,
sd_mmc_trace and sd_mmc_error.  Replace printf with sd_mmc_error.

TEST=Build and run on galileo gen2

Signed-off-by: Lee Leahy <leroy.p.leahy at intel.com>

drivers/storage: Add sdhc_* debug macros

Add sdhc_debug, sdhc_trace and sd_error macros.
Add Kconfig values to enable debugging and Trace.

TEST=Build and run on Galileo Gen2

Signed-off-by: Lee Leahy <leroy.p.leahy at intel.com>

drivers/storage: Add tracing support to SDHCI driver

Add tracing and debug support to the SDHCI driver
* Replace printf and printk with sd_error
* Display the interrupt status for timeout errors
* Add tracing to display the commands and responses
* Add debug to display high level state of the SD/MMC device and bus

TEST=Build and run on Galileo Gen2

Signed-off-by: Lee Leahy <leroy.p.leahy at intel.com>

drivers/storage: Allow SOC to override more controller features

Instead of converting from SDHCI_PLATFORM_* to SDHCI_QUIRK_* to
MMC_MODE_*, call the SOC back with the SdhciHost and MmcCtrlr structures
to allow the necessary updates to support the controller quirks.

TEST=Build for reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Split out ADMA support

On x86 sysems DMA is only supported to RAM, early stages must do
programed I/O to send/receive data from the SD/MMC device.  Use Kconfig
values to enable DMA during early boot stages.

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Move 1V8 support into soc routine

Eliminate SDHCI_PLATFORM_EMMC_1V8_POWER and SDHCI_QUIRK_EMMC_1V8_POWER
and instead let the platform set MMC_MODE_1V8_VDD in
host->mmc_ctrlr.caps.

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Move HS400 support into soc routine

Eliminate SDHCI_PLATFORM_SUPPORTS_HS400ES and
SDHCI_QUIRK_SUPPORTS_HS400ES and instead let the platform set
MMC_MODE_HS400ES and possibly MMC_MODE_HS400.

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Rework clock handling

* Eliminate SDHCI_PLATFORM_NO_CLK_BASE and
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN
* Move the clock values into the controller structure
* Use bus_hz as actual bus frequency
* Platform overrides clock frequencies using soc_sdhci_quirks
* Use MIN and MAX to insure clock value is in the supported range
* Remove loop for computing divisor for Version 3 controllers
* Only stop and restart the clock when the clock frequency changes
* Display clock values after the call to soc_sdhci_quirks

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Switch to using a common symbol for the controller

Change the various controller references to use mmc_ctrlr for the
controller.

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Update the voltage handling

Update the voltage handling:
* Eliminate SDHCI_QUIRK_BROKEN_VOLTAGE
* Eliminate hardcoded_voltage
* Platforms adjust the voltages in soc_sdhci_quirks
* Only change the power when necessary

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Update modes of operation

Update the modes of operation:
* Eliminate SDHCI_PLATFORM_NO_EMMC_HS200 and SDHCI_QUIRK_NO_EMMC_HS200
* Platform can use soc_sdhci_quirks to update operating modes

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Move DMA fields into MmcCtrlr

Update DMA support:
* Move controller structures into sdhci.h
* Move ADMA structures and fields into MmcCtrlr

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Update bus width support

Rework the bus width support:
* Simplify the bus width logic
* Display bus width changes when they happen

BRANCH=none
BUG=None
TEST=None

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Change MMC_TIMING_* to BUS_TIMING_*

rename MMC_TIMING_* to BUS_TIMING_* and simplify its use by replacing an
if chain with an array.

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Rename MMC_MODE_ to DRVR_CAP

Rename MMC_MODE_* to DRVR_CAP_* since these values change the behavior
of the driver and not all of them are MMC specific.

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Move quirks into caps

Use a single field to handle driver quirks and capabilities:

* Migrate host->quirk bits into mmc_ctrlr->caps
* Rename SDHCI_QUIRK_* to DRVR_CAP_*
* Rename SDHCI_PALTFORM_REMOVABLE to DRVR_CAP_REMOVABLE
* No need to pass in platform_info
* Host sets quirks and removable bit by setting the corresponding
DRVR_CAP_* bit in soc_sdhci_quirks

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Associate removeable with the controller

The platform construction determines whether a SD/MMC device is
removable or embedded.  Migrate the removable flag from the host to the
controller.  This enables the mainboard code to indicate if the SD/MMC
device is socketed or soldered onto the board.

TEST=Build and run on Galileo Gen2

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Statically allocate MmcMedia

Prepare for use in earlier stages.  Statically allocate MmcMedia instead
of using dynamic allocation.  Rework sdhci_update to make it media
independent.

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Eliminate the SdhciHost structure

Replace the SdhciHost structure with the MmcCtrlr structure.

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

driver/storage: Support other controller types

Split the code to allow for other SD/MMC controllers by using three data
structures:
* SdhciCtrlr - SDHCI controller specific data, devices/sdhci.h
* SdMmcCtrlr - Controller independent data, devices/sd_mmc_ctrlr.h
* MmcMedia - eMMC/MMC card specific data, devices/mmc.h

The specific changes:
* Move controller independent structures and definitions into
sd_mmc_ctrlr.h
* Pass MmcMedia as first parameter to all external mmc routines
* Use ctrlr to reference the SdMmcCtrlr data structure in all sources
* Use sdhci_ctrlr to reference the SdhciCtrlr data structure in the
SDHCI driver
* Move the controller independent fields into SdMmcCtrlr
* Remove the media field from the controller structures
* MmcMedia data structure now points to generic controller (SdMmcCtrlr)
* SdMmcCtrlr structure at beginning of SdhciCtrlr structure to allow
recast
* Removed mmc_busy_wait_io and mmc_busy_wait_io_until from mmc.c

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Split out erase and write support

Split out erase and write support:
* Move erase support into card_erase.c
* Move write support into card_write.c
* Add Kconfig values to enable erase and write support during bootblock
& verstage

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Remove typedefs and bumppy case

Update the code to be more consistent with the coreboot coding style:

* Remove the typedefs from the code
* Replace MmcCommand with struct mmc_command
* Replace MmcData with struct mmc_data
* Replace SdMmcCtrlr with struct sd_mmc_ctrlr
* Replace MmcMedia with struct flash_media
* Replace lba_t with uint64_t
* Replace SdhciAdma with struct sdhci_adma
* Replace SdhciAdma64 with struct sdhci_adma64
* Replace SdhciCtrlr with struct sdhci_ctrlr
* Replace pcidev_t with uint32_t

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Only expose calling APIs

Migrate driver specific constants, routines and data structures into the
storage directory.

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Split UP mmc.c

Divide up mmc.c into the following modules:
* mmc.c - MMC specific code
* sd.c - SD specific code
* sd_mmc.c - Common MMC and SD initialization code, may support SDIO
             in the future.
* storage.c - Storage card/device specific code

TEST=Build and run on reef and Galileo Gen2

Signed-off-by: Lee Leahy <leroy.p.leahy at intel.com>

drivers/storage: Update debug and error messages

Update the error messages:
* Remove prefix
* Make them type independent
* Remove a couple debug messages

TEST=Build and run on reef

Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>

drivers/storage: Add partition support

Acknowledge that embedded MMC devices support a physical breakup of the
storage device into hardware enforced partitions.  Enable the caller to
select the desired partition to be used for read/write/erase operations.

Capacity is now partition based.  Replace block_count with the capacity
array.  Since read and write sizes may be different, validate the access
using the capacity values.

TEST=Build and run on reef

Change-Id: I9b5f9db1e27833e4ce4a97ad4f5ef3a46f64f2a2
Signed-off-by: Lee Leahy <Leroy.P.Leahy at intel.com>
---
A src/drivers/storage/Kconfig
A src/drivers/storage/Makefile.inc
A src/drivers/storage/bouncebuf.c
A src/drivers/storage/bouncebuf.h
A src/drivers/storage/mmc.c
A src/drivers/storage/mmc.h
A src/drivers/storage/pci_sdhci.c
A src/drivers/storage/sd.c
A src/drivers/storage/sd_mmc.c
A src/drivers/storage/sd_mmc.h
A src/drivers/storage/sdhci.c
A src/drivers/storage/sdhci.h
A src/drivers/storage/sdhci_adma.c
A src/drivers/storage/storage.c
A src/drivers/storage/storage.h
A src/drivers/storage/storage_erase.c
A src/drivers/storage/storage_write.c
A src/include/device/sd_mmc_ctrlr.h
A src/include/device/sdhci.h
A src/include/device/storage.h
20 files changed, 4,115 insertions(+), 0 deletions(-)


  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/08/19208/1

diff --git a/src/drivers/storage/Kconfig b/src/drivers/storage/Kconfig
new file mode 100644
index 0000000..e7fa3f3
--- /dev/null
+++ b/src/drivers/storage/Kconfig
@@ -0,0 +1,109 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2017 Intel Corp.
+##
+## 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; version 2 of the License.
+##
+## 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.
+##
+
+config DRIVERS_STORAGE
+	bool
+	default n
+
+if DRIVERS_STORAGE
+
+config DRIVERS_STORAGE_MMC
+	bool "Enable MultiMediaCard (MMC) and eMMC device support"
+	default n
+
+config DRIVERS_STORAGE_SD
+	bool "Enable Secure Digital (SD) memory card support"
+	default n
+
+config STORAGE_ERASE
+	bool "Support SD/MMC erase operations"
+	default n
+	help
+	  Select to enable SD/MMC erase oprations
+
+config STORAGE_EARLY_ERASE
+	bool "Enable erase operations in bootblock and verstage"
+	default n
+	depends on STORAGE_ERASE
+
+config STORAGE_WRITE
+	bool "Support SD/MMC write operations"
+	default n
+	help
+	  Select to enable SD/MMC write oprations
+
+config STORAGE_EARLY_WRITE
+	bool "Enable write operations in bootblock and verstage"
+	default n
+	depends on STORAGE_WRITE
+
+config DRIVERS_STORAGE_SD
+	bool "Enable Secure Digital (SD) memory card support"
+	default n
+
+config SD_MMC_DEBUG
+	bool "Debug SD/MMC card/devices operations"
+	default n
+	help
+	  Display overview of SD/MMC card/device operations
+
+config SD_MMC_TRACE
+	bool "Trace SD/MMC card/device operations"
+	default n
+	help
+	  Display details of SD/MMC card/device operations
+
+config SDHC_DEBUG
+	bool "Debug SD/MMC controller settings"
+	default n
+	help
+	  Display clock speed and bus width settings
+
+config SDHC_TRACE
+	bool "Trace SD/MMC controller operations"
+	default n
+	help
+	  Display the operations performed by the SD/MMC controller
+
+config SDHCI_CONTROLLER
+	bool "Support SD host controller"
+	default n
+
+if SDHCI_CONTROLLER
+
+config SDHCI_ADMA_IN_BOOTBLOCK
+	bool
+	default n
+	help
+	  Determine if bootblock is able to use ADMA2 or ADMA64
+
+config SDHCI_ADMA_IN_ROMSTAGE
+	bool
+	default n
+	help
+	  Determine if romstage is able to use ADMA2 or ADMA64
+
+config SDHCI_ADMA_IN_VERSTAGE
+	bool
+	default n
+	help
+	  Determine if verstage is able to use ADMA2 or ADMA64
+
+config SDHCI_BOUNCE_BUFFER
+	bool "Use DMA bounce buffer for SD/MMC controller"
+	default n
+
+endif # SDHCI_CONTROLLER
+endif # DRIVERS_STORAGE
diff --git a/src/drivers/storage/Makefile.inc b/src/drivers/storage/Makefile.inc
new file mode 100644
index 0000000..d77602e
--- /dev/null
+++ b/src/drivers/storage/Makefile.inc
@@ -0,0 +1,102 @@
+#
+# This file is part of the coreboot project.
+#
+# Copyright (C) 2017 Intel Corporation.
+#
+# 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; version 2 of the License.
+#
+# 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.
+#
+
+ifeq ($(CONFIG_DRIVERS_STORAGE),y)
+
+bootblock-y += sd_mmc.c
+bootblock-y += storage.c
+
+verstage-y += sd_mmc.c
+verstage-y += storage.c
+
+romstage-y += sd_mmc.c
+romstage-y += storage.c
+
+postcar-y += sd_mmc.c
+postcar-y += storage.c
+
+ramstage-y += sd_mmc.c
+ramstage-y += storage.c
+
+# Determine the type of controller being used
+ifeq ($(CONFIG_SDHCI_CONTROLLER),y)
+bootblock-y += pci_sdhci.c
+bootblock-y += sdhci.c
+bootblock-$(CONFIG_SDHCI_ADMA_IN_BOOTBLOCK) += sdhci_adma.c
+
+verstage-y += pci_sdhci.c
+verstage-y += sdhci.c
+verstage-$(CONFIG_SDHCI_ADMA_IN_VERSTAGE) += sdhci_adma.c
+
+romstage-y += pci_sdhci.c
+romstage-y += sdhci.c
+romstage-$(CONFIG_SDHCI_ADMA_IN_ROMSTAGE) += sdhci_adma.c
+
+postcar-y += pci_sdhci.c
+postcar-y += sdhci.c
+postcar-y += sdhci_adma.c
+
+ramstage-y += pci_sdhci.c
+ramstage-y += sdhci.c
+ramstage-y += sdhci_adma.c
+
+# Determine if the bounce buffer is necessary
+ifeq ($(CONFIG_SDHCI_BOUNCE_BUFFER),y)
+bootblock-y += bouncebuf.c
+verstage-y += bouncebuf.c
+romstage-y += bouncebuf.c
+postcar-y += bouncebuf.c
+ramstage-y += bouncebuf.c
+endif # CONFIG_SDHCI_BOUNCE_BUFFER
+
+endif # CONFIG_SDHCI_CONTROLLER
+
+# Determine if MultiMediaCards or embedded MMC devices are supported
+ifeq ($(CONFIG_DRIVERS_STORAGE_MMC),y)
+bootblock-y += mmc.c
+verstage-y += mmc.c
+romstage-y += mmc.c
+postcar-y += mmc.c
+ramstage-y += mmc.c
+endif # CONFIG_DRIVERS_STORAGE_MMC
+
+# Determine if Secure Digital cards are supported
+ifeq ($(CONFIG_DRIVERS_STORAGE_SD),y)
+bootblock-y += sd.c
+verstage-y += sd.c
+romstage-y += sd.c
+postcar-y += sd.c
+ramstage-y += sd.c
+endif # CONFIG_DRIVERS_STORAGE_SD
+
+# Determine if erase operations are supported
+ifeq ($(CONFIG_STORAGE_ERASE),y)
+bootblock-$(CONFIG_STORAGE_EARLY_ERASE) += storage_erase.c
+verstage-$(CONFIG_STORAGE_EARLY_ERASE) += storage_erase.c
+romstage-y += storage_erase.c
+postcar-y += storage_erase.c
+ramstage-y += storage_erase.c
+endif # CONFIG_STORAGE_ERASE
+
+# Determine if write operations are supported
+ifeq ($(CONFIG_STORAGE_WRITE),y)
+bootblock-$(CONFIG_STORAGE_EARLY_WRITE) += storage_write.c
+verstage-$(CONFIG_STORAGE_EARLY_WRITE) += storage_write.c
+romstage-y += storage_write.c
+postcar-y += storage_write.c
+ramstage-y += storage_write.c
+endif # CONFIG_STORAGE_WRITE
+
+endif # CONFIG_DRIVERS_STORAGE
diff --git a/src/drivers/storage/bouncebuf.c b/src/drivers/storage/bouncebuf.c
new file mode 100644
index 0000000..f1193b8
--- /dev/null
+++ b/src/drivers/storage/bouncebuf.c
@@ -0,0 +1,97 @@
+/*
+ * Generic bounce buffer implementation
+ *
+ * Copyright (C) 2012 Marek Vasut <marex at denx.de>
+ * Copyright 2013 Google Inc.  All rights reserved.
+ *
+ * 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 <arch/cache.h>
+#include <stdlib2.h>
+#include <string.h>
+#include "bouncebuf.h"
+#include "storage.h"
+
+// TODO(hungte) Deprecate this file after we've shown the drivers using this
+// file work, and get migrated into better code to handle non-cached and aligned
+// memory.
+
+static int addr_aligned(struct bounce_buffer *state)
+{
+	const uint32_t align_mask = ARCH_DMA_MINALIGN - 1;
+
+	// Check if start is aligned
+	if ((uintptr_t)state->user_buffer & align_mask) {
+		sdhc_debug("Unaligned buffer address %p\n", state->user_buffer);
+		return 0;
+	}
+
+	// Check if length is aligned
+	if (state->len != state->len_aligned) {
+		sdhc_debug("Unaligned buffer length %zd\n", state->len);
+		return 0;
+	}
+
+	// Aligned
+	return 1;
+}
+
+int bounce_buffer_start(struct bounce_buffer *state, void *data,
+			size_t len, unsigned int flags)
+{
+	state->user_buffer = data;
+	state->bounce_buffer = data;
+	state->len = len;
+	state->len_aligned = ROUND(len, ARCH_DMA_MINALIGN);
+	state->flags = flags;
+
+	if (!addr_aligned(state)) {
+		state->bounce_buffer = memalign(ARCH_DMA_MINALIGN,
+						state->len_aligned);
+		if (!state->bounce_buffer)
+			return -1;
+
+		if (state->flags & GEN_BB_READ)
+			memcpy(state->bounce_buffer, state->user_buffer,
+				state->len);
+	}
+
+	/*
+	 * Flush data to RAM so DMA reads can pick it up,
+	 * and any CPU writebacks don't race with DMA writes
+	 */
+	dcache_clean_invalidate_by_mva(state->bounce_buffer,
+				       state->len_aligned);
+	return 0;
+}
+
+int bounce_buffer_stop(struct bounce_buffer *state)
+{
+	if (state->flags & GEN_BB_WRITE) {
+		// Invalidate cache so that CPU can see any newly DMA'd data
+		dcache_invalidate_by_mva(state->bounce_buffer,
+					 state->len_aligned);
+	}
+
+	if (state->bounce_buffer == state->user_buffer)
+		return 0;
+
+	if (state->flags & GEN_BB_WRITE)
+		memcpy(state->user_buffer, state->bounce_buffer, state->len);
+
+	free(state->bounce_buffer);
+
+	return 0;
+}
diff --git a/src/drivers/storage/bouncebuf.h b/src/drivers/storage/bouncebuf.h
new file mode 100644
index 0000000..a81fa80
--- /dev/null
+++ b/src/drivers/storage/bouncebuf.h
@@ -0,0 +1,99 @@
+/*
+ * Generic bounce buffer implementation
+ *
+ * Copyright (C) 2012 Marek Vasut <marex at denx.de>
+ * Copyright 2013 Google Inc.  All rights reserved.
+ *
+ * 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_BOUNCEBUF_H__
+#define __DRIVERS_STORAGE_BOUNCEBUF_H__
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/*
+ * GEN_BB_READ -- Data are read from the buffer eg. by DMA hardware.
+ * The source buffer is copied into the bounce buffer (if unaligned, otherwise
+ * the source buffer is used directly) upon start() call, then the operation
+ * requiring the aligned transfer happens, then the bounce buffer is lost upon
+ * stop() call.
+ */
+#define GEN_BB_READ	(1 << 0)
+/*
+ * GEN_BB_WRITE -- Data are written into the buffer eg. by DMA hardware.
+ * The source buffer starts in an undefined state upon start() call, then the
+ * operation requiring the aligned transfer happens, then the bounce buffer is
+ * copied into the destination buffer (if unaligned, otherwise destination
+ * buffer is used directly) upon stop() call.
+ */
+#define GEN_BB_WRITE	(1 << 1)
+/*
+ * GEN_BB_RW -- Data are read and written into the buffer eg. by DMA hardware.
+ * The source buffer is copied into the bounce buffer (if unaligned, otherwise
+ * the source buffer is used directly) upon start() call, then the  operation
+ * requiring the aligned transfer happens, then the bounce buffer is  copied
+ * into the destination buffer (if unaligned, otherwise destination buffer is
+ * used directly) upon stop() call.
+ */
+#define GEN_BB_RW	(GEN_BB_READ | GEN_BB_WRITE)
+
+struct bounce_buffer {
+	/* Copy of data parameter passed to start() */
+	void *user_buffer;
+	/*
+	 * DMA-aligned buffer. This field is always set to the value that
+	 * should be used for DMA; either equal to .user_buffer, or to a
+	 * freshly allocated aligned buffer.
+	 */
+	void *bounce_buffer;
+	/* Copy of len parameter passed to start() */
+	size_t len;
+	/* DMA-aligned buffer length */
+	size_t len_aligned;
+	/* Copy of flags parameter passed to start() */
+	unsigned int flags;
+};
+
+/**
+ * bounce_buffer_start() -- Start the bounce buffer session
+ * state:	stores state passed between bounce_buffer_{start,stop}
+ * data:	pointer to buffer to be aligned
+ * len:		length of the buffer
+ * flags:	flags describing the transaction, see above.
+ */
+int bounce_buffer_start(struct bounce_buffer *state, void *data,
+			size_t len, unsigned int flags);
+/**
+ * bounce_buffer_stop() -- Finish the bounce buffer session
+ * state:	stores state passed between bounce_buffer_{start,stop}
+ */
+int bounce_buffer_stop(struct bounce_buffer *state);
+
+// TODO(hungte) Eliminate the alignment stuff below and replace them with a
+// better and centralized way to handler non-cache/aligned memory.
+// Helper macros for alignment.
+#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)
+#ifndef ARCH_DMA_MINALIGN
+#define ARCH_DMA_MINALIGN (DMA_MINALIGN)
+#endif
+
+#endif // __DRIVERS_STORAGE_BOUNCEBUF_H__
diff --git a/src/drivers/storage/mmc.c b/src/drivers/storage/mmc.c
new file mode 100644
index 0000000..cb63e46
--- /dev/null
+++ b/src/drivers/storage/mmc.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc.  All rights reserved.
+ * Copyrigit 2017 Intel Corporation
+ *
+ * MultiMediaCard (MMC) and eMMC specific support code
+ * This code is controller independent
+ *
+ * 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 <delay.h>
+#include <device/storage.h>
+#include <endian.h>
+#include "sd_mmc.h"
+#include <stdlib2.h>
+#include "mmc.h"
+#include "sd_mmc.h"
+#include "storage.h"
+#include <string.h>
+#include <timer.h>
+
+/* We pass in the cmd since otherwise the init seems to fail */
+static int mmc_send_op_cond_iter(struct storage_media *media,
+	struct mmc_command *cmd, int use_arg)
+{
+	cmd->cmdidx = MMC_CMD_SEND_OP_COND;
+	cmd->resp_type = CARD_RSP_R3;
+
+	/* Set the controller's operating conditions */
+	if (use_arg) {
+		uint32_t mask = media->op_cond_response &
+			(OCR_VOLTAGE_MASK | OCR_ACCESS_MODE);
+		cmd->cmdarg = media->ctrlr->voltages & mask;
+
+		/* Always request high capacity if supported by the
+		 * controller
+		 */
+		if (media->ctrlr->caps & DRVR_CAP_HC)
+			cmd->cmdarg |= OCR_HCS;
+	}
+	cmd->flags = 0;
+	int err = mmc_send_cmd(media->ctrlr, cmd, NULL);
+	if (err)
+		return err;
+
+	media->op_cond_response = cmd->response[0];
+	return 0;
+}
+
+int mmc_send_op_cond(struct storage_media *media)
+{
+	struct mmc_command cmd;
+	int max_iters = 2;
+
+	/* Ask the card for its operating conditions */
+	cmd.cmdarg = 0;
+	for (int i = 0; i < max_iters; i++) {
+		int 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 CARD_IN_PROGRESS;
+}
+
+int mmc_complete_op_cond(struct storage_media *media)
+{
+	struct mmc_command cmd;
+	struct stopwatch sw;
+
+	stopwatch_init_msecs_expire(&sw, MMC_INIT_TIMEOUT_US_MS);
+	while (1) {
+		// CMD1 queries whether initialization is done.
+		int 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.
+		if (stopwatch_expired(&sw))
+			return CARD_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;
+}
+
+int mmc_send_ext_csd(struct sd_mmc_ctrlr *ctrlr, unsigned char *ext_csd)
+{
+	int rv;
+	/* Get the Card Status Register */
+	struct mmc_command cmd;
+	cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
+	cmd.resp_type = CARD_RSP_R1;
+	cmd.cmdarg = 0;
+	cmd.flags = 0;
+
+	struct mmc_data data;
+	data.dest = (char *)ext_csd;
+	data.blocks = 1;
+	data.blocksize = 512;
+	data.flags = DATA_FLAG_READ;
+
+	rv = mmc_send_cmd(ctrlr, &cmd, &data);
+
+	if (!rv && IS_ENABLED(CONFIG_SD_MMC_TRACE)) {
+		int i, size;
+
+		size = data.blocks * data.blocksize;
+		sd_mmc_trace("\t%p ext_csd:", ctrlr);
+		for (i = 0; i < size; i++) {
+			if (!(i % 32))
+				sd_mmc_trace("\n");
+			sd_mmc_trace(" %2.2x", ext_csd[i]);
+		}
+		sd_mmc_trace("\n");
+	}
+	return rv;
+}
+
+static int mmc_switch(struct storage_media *media, uint8_t set, uint8_t index,
+		      uint8_t value)
+{
+	struct mmc_command cmd;
+	cmd.cmdidx = MMC_CMD_SWITCH;
+	cmd.resp_type = CARD_RSP_R1b;
+	cmd.cmdarg = ((MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+			   (index << 16) | (value << 8));
+	cmd.flags = 0;
+
+	int ret = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+
+	/* Waiting for the ready status */
+	sd_mmc_send_status(media, SD_MMC_IO_RETRIES);
+	return ret;
+
+}
+
+static void mmc_recalculate_clock(struct storage_media *media)
+{
+	uint32_t clock;
+
+	clock = CLOCK_26MHZ;
+	if (media->caps & DRVR_CAP_HS) {
+		if ((media->caps & DRVR_CAP_HS_200MHz) ||
+		    (media->caps & DRVR_CAP_HS400ES))
+			clock = CLOCK_200MHZ;
+		else if (media->caps & DRVR_CAP_HS_52MHz)
+			clock = CLOCK_52MHZ;
+	}
+	SET_CLOCK(media->ctrlr, clock);
+}
+
+static int mmc_select_hs(struct storage_media *media)
+{
+	int ret;
+
+	/* Switch the MMC device into high speed mode */
+	ret = mmc_switch(media, EXT_CSD_CMD_SET_NORMAL,
+		 EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS);
+	if (ret) {
+		sd_mmc_error("Timing switch to high speed failed\n");
+		return ret;
+	}
+	sdhc_debug("SDHCI switched MMC to high speed\n");
+
+	/* Increase the controller clock speed */
+	SET_TIMING(media->ctrlr, BUS_TIMING_MMC_HS);
+	media->caps |= DRVR_CAP_HS_52MHz | DRVR_CAP_HS;
+	mmc_recalculate_clock(media);
+	ret = sd_mmc_send_status(media, SD_MMC_IO_RETRIES);
+	return ret;
+}
+
+static int mmc_select_hs400es(struct storage_media *media)
+{
+	int ret;
+
+	/* Switch the MMC device into high speed mode */
+	ret = mmc_select_hs(media);
+	if (ret)
+		return ret;
+
+	/* Switch MMC device to 8-bit DDR with strobe */
+	ret = mmc_switch(media, EXT_CSD_CMD_SET_NORMAL,
+			 EXT_CSD_BUS_WIDTH,
+			 EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE);
+	if (ret) {
+		sd_mmc_error("Switching bus width for HS400ES failed\n");
+		return ret;
+	}
+	sdhc_debug("SDHCI switched MMC to 8-bit DDR\n");
+
+	/* Set controller to 8-bit mode */
+	SET_BUS_WIDTH(media->ctrlr, 8);
+	media->caps |= EXT_CSD_BUS_WIDTH_8;
+
+	/* Switch MMC device to HS400 */
+	ret = mmc_switch(media, EXT_CSD_CMD_SET_NORMAL,
+			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400);
+	if (ret) {
+		sd_mmc_error("Switch to HS400ES failed\n");
+		return ret;
+	}
+
+	/* Set controller to 200 MHz and use receive strobe */
+	SET_TIMING(media->ctrlr, BUS_TIMING_MMC_HS400ES);
+	media->caps |= DRVR_CAP_HS400ES | DRVR_CAP_HS_52MHz | DRVR_CAP_HS;
+	mmc_recalculate_clock(media);
+	ret = sd_mmc_send_status(media, SD_MMC_IO_RETRIES);
+	return ret;
+}
+
+static int mmc_select_hs200(struct storage_media *media)
+{
+	int ret;
+
+	/* Switch the MMC device to 8-bit SDR */
+	ret = mmc_switch(media, EXT_CSD_CMD_SET_NORMAL,
+		 EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8);
+	if (ret) {
+		sd_mmc_error("Switching bus width for HS200 failed\n");
+		return ret;
+	}
+
+	/* Set controller to 8-bit mode */
+	SET_BUS_WIDTH(media->ctrlr, 8);
+	media->caps |= EXT_CSD_BUS_WIDTH_8;
+
+	/* Switch to HS200 */
+	ret = mmc_switch(media, EXT_CSD_CMD_SET_NORMAL,
+		 EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200);
+
+	if (ret) {
+		sd_mmc_error("Switch to HS200 failed\n");
+		return ret;
+	}
+	sdhc_debug("SDHCI switched MMC to 8-bit SDR\n");
+
+	/* Set controller to 200 MHz */
+	SET_TIMING(media->ctrlr, BUS_TIMING_MMC_HS200);
+	media->caps |= DRVR_CAP_HS_200MHz | DRVR_CAP_HS_52MHz | DRVR_CAP_HS;
+	mmc_recalculate_clock(media);
+	return ret;
+}
+
+int mmc_change_freq(struct storage_media *media)
+{
+	int 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 & DRVR_CAP_HS400ES) &&
+	    (ext_csd[EXT_CSD_CARD_TYPE] & MMC_HS400) &&
+	    ext_csd[EXT_CSD_STROBE_SUPPORT])
+		err = mmc_select_hs400es(media);
+	else if ((media->ctrlr->caps & DRVR_CAP_HS_200MHz) &&
+		 (ext_csd[EXT_CSD_CARD_TYPE] & MMC_HS_200MHZ))
+		err = mmc_select_hs200(media);
+	else
+		err = mmc_select_hs(media);
+
+	return err;
+}
+
+int mmc_set_bus_width(struct storage_media *media)
+{
+	int err;
+	int width;
+
+	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, ext_csd, EXT_CSD_SIZE);
+	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, test_csd, EXT_CSD_SIZE);
+
+	/* Set the bus width */
+	err = 0;
+	for (width = EXT_CSD_BUS_WIDTH_8; width >= 0; width--) {
+		/* If HS200 is switched, Bus Width has been 8-bit */
+		if ((media->caps & DRVR_CAP_HS_200MHz) ||
+		    (media->caps & DRVR_CAP_HS400ES))
+			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) {
+			SET_BUS_WIDTH(media->ctrlr, 1);
+			break;
+		}
+		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;
+		}
+	}
+	return err;
+}
+
+int mmc_update_capacity(struct storage_media *media)
+{
+	uint64_t capacity;
+	int err;
+	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, ext_csd, EXT_CSD_SIZE);
+	uint32_t erase_size;
+	uint32_t hc_erase_size;
+	uint64_t hc_wp_size;
+
+	if (media->version < MMC_VERSION_4)
+		return 0;
+
+	/* check  ext_csd version and capacity */
+	err = mmc_send_ext_csd(media->ctrlr, ext_csd);
+	if (err)
+		return err;
+
+	if (ext_csd[EXT_CSD_REV] < 2)
+		return 0;
+	/* Check whether to use HC erase group size or not. */
+	hc_erase_size = ext_csd[224] * 512 * KiB;
+	if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x1)
+		erase_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
+			* 512 * KiB;
+	else
+		erase_size = (sd_mmc_extract_uint32_bits(media->csd,
+			81, 5) + 1) *
+			(sd_mmc_extract_uint32_bits(media->csd, 86, 5)
+			+ 1);
+	media->erase_blocks = erase_size / media->write_bl_len;
+	media->trim_mult = ext_csd[EXT_CSD_TRIM_MULT];
+
+	/* Determine the eMMC device information */
+	media->partition_config = ext_csd[EXT_CSD_PART_CONF]
+		& EXT_CSD_PART_ACCESS_MASK;
+
+	/* Determine the user partition size
+	 *
+	 * According to the JEDEC Standard, the value of
+	 * ext_csd's capacity is valid if the value is
+	 * more than 2GB
+	 */
+	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[MMC_PARTITION_USER] = capacity;
+
+	/* Determine the boot parition sizes */
+	capacity = ext_csd[EXT_CSD_BOOT_SIZE_MULT] * 128 * KiB;
+	media->capacity[MMC_PARTITION_BOOT_1] = capacity;
+	media->capacity[MMC_PARTITION_BOOT_2] = capacity;
+
+	/* Determine the RPMB size */
+	hc_wp_size = ext_csd[EXT_CSD_HC_WP_GRP_SIZE] * hc_erase_size;
+	capacity = 128 * KiB * ext_csd[EXT_CSD_RPMB_SIZE_MULT];
+	media->capacity[MMC_PARTITION_RPMB] = capacity;
+
+	/* Determine the general partition sizes */
+	capacity = (ext_csd[EXT_CSD_GP_SIZE_MULT_GP0 + 2] << 16)
+		| (ext_csd[EXT_CSD_GP_SIZE_MULT_GP0 + 1] << 8)
+		| ext_csd[EXT_CSD_GP_SIZE_MULT_GP0];
+	capacity *= hc_wp_size;
+	media->capacity[MMC_PARTITION_GP1] = capacity;
+
+	capacity = (ext_csd[EXT_CSD_GP_SIZE_MULT_GP1 + 2] << 16)
+		| (ext_csd[EXT_CSD_GP_SIZE_MULT_GP1 + 1] << 8)
+		| ext_csd[EXT_CSD_GP_SIZE_MULT_GP1];
+	capacity *= hc_wp_size;
+	media->capacity[MMC_PARTITION_GP2] = capacity;
+
+	capacity = (ext_csd[EXT_CSD_GP_SIZE_MULT_GP2 + 2] << 16)
+		| (ext_csd[EXT_CSD_GP_SIZE_MULT_GP2 + 1] << 8)
+		| ext_csd[EXT_CSD_GP_SIZE_MULT_GP2];
+	capacity *= hc_wp_size;
+	media->capacity[MMC_PARTITION_GP3] = capacity;
+
+	capacity = (ext_csd[EXT_CSD_GP_SIZE_MULT_GP3 + 2] << 16)
+		| (ext_csd[EXT_CSD_GP_SIZE_MULT_GP3 + 1] << 8)
+		| ext_csd[EXT_CSD_GP_SIZE_MULT_GP3];
+	capacity *= hc_wp_size;
+	media->capacity[MMC_PARTITION_GP4] = capacity;
+	return 0;
+}
+
+int mmc_set_partition(struct storage_media *media,
+	unsigned int partition_number)
+{
+	uint8_t partition_config;
+
+	/* Validate the partition number */
+	if ((partition_number > MMC_PARTITION_GP4)
+		|| (!media->capacity[partition_number]))
+		return -1;
+
+	/* Update the partition register */
+	partition_config = media->partition_config;
+	partition_config &= EXT_CSD_PART_ACCESS_MASK;
+	partition_config |= partition_number;
+
+	/* Select the new partition */
+	struct mmc_command cmd;
+	cmd.cmdidx = MMC_CMD_SWITCH;
+	cmd.resp_type = CARD_RSP_R1b;
+	cmd.cmdarg = ((MMC_SWITCH_MODE_WRITE_BYTE << 24)
+		| (EXT_CSD_PART_CONF << 16) | (partition_config << 8));
+	cmd.flags = 0;
+
+	int ret = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+
+	/* Waiting for the ready status */
+	sd_mmc_send_status(media, SD_MMC_IO_RETRIES);
+
+	/* Update the partition number */
+	if (!ret)
+		media->partition_config = partition_config;
+
+	return ret;
+}
+
+const char *mmc_partition_name(struct storage_media *media,
+	unsigned int partition_number)
+{
+	static const char * const partition_name[8] = {
+		"User",		/* 0 */
+		"Boot 1",	/* 1 */
+		"Boot 2",	/* 2 */
+		"RPMB",		/* 3 */
+		"GP 1",		/* 4 */
+		"GP 2",		/* 5 */
+		"GP 3",		/* 6 */
+		"GP 4"		/* 7 */
+	};
+
+	if (partition_number >= ARRAY_SIZE(partition_name))
+		return "";
+	return partition_name[partition_number];
+}
diff --git a/src/drivers/storage/mmc.h b/src/drivers/storage/mmc.h
new file mode 100644
index 0000000..455948b
--- /dev/null
+++ b/src/drivers/storage/mmc.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2008,2010 Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc.  All rights reserved.
+ * Copyrigit 2017 Intel Corporation
+ *
+ * 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 __DRIVER_STORAGE_MMC_H__
+#define __DRIVER_STORAGE_MMC_H__
+
+#include <device/sd_mmc_ctrlr.h>
+
+#define MMC_HS_TIMING		0x00000100
+#define MMC_HS_52MHZ		0x2
+#define MMC_HS_200MHZ		0x10
+#define MMC_HS400		0x40
+
+#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_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 R1_ILLEGAL_COMMAND		(1 << 22)
+#define R1_APP_CMD			(1 << 5)
+
+#define MMC_INIT_TIMEOUT_US	(1000 * 1000)
+#define MMC_INIT_TIMEOUT_US_MS	1000
+
+int storage_block_setup_media(struct storage_media *media,
+	struct sd_mmc_ctrlr *ctrlr);
+
+#endif /* __DRIVER_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..1622788
--- /dev/null
+++ b/src/drivers/storage/pci_sdhci.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 Google Inc.
+ * Copyrigit 2017 Intel Corporation
+ *
+ * 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 <rules.h>
+#if ENV_RAMSTAGE
+#define __SIMPLE_DEVICE__		1
+#endif
+
+#include <assert.h>
+#include <console/console.h>
+#include <device/pci.h>
+#include <device/sdhci.h>
+#include "sd_mmc.h"
+#include "storage.h"
+#include <string.h>
+
+/* Initialize an SDHCI port */
+int sdhci_controller_init(struct sdhci_ctrlr *sdhci_ctrlr, void *ioaddr)
+{
+	memset(sdhci_ctrlr, 0, sizeof(*sdhci_ctrlr));
+	sdhci_ctrlr->ioaddr = ioaddr;
+	return add_sdhci(sdhci_ctrlr);
+}
+
+struct sd_mmc_ctrlr *new_mem_sdhci_controller(void *ioaddr)
+{
+	struct sdhci_ctrlr *sdhci_ctrlr;
+
+	sdhci_ctrlr = malloc(sizeof(*sdhci_ctrlr));
+	if (sdhci_ctrlr == NULL)
+		return NULL;
+
+	if (sdhci_controller_init(sdhci_ctrlr, ioaddr)) {
+		free(sdhci_ctrlr);
+		sdhci_ctrlr = NULL;
+	}
+	return &sdhci_ctrlr->sd_mmc_ctrlr;
+}
+
+struct sd_mmc_ctrlr *new_pci_sdhci_controller(uint32_t dev)
+{
+	uint32_t addr;
+
+	addr = pci_read_config32(dev, PCI_BASE_ADDRESS_0);
+	if (addr == ((uint32_t)~0)) {
+		sdhc_error("Error: PCI SDHCI not found\n");
+		return NULL;
+	}
+
+	addr &= ~0xf;
+	return new_mem_sdhci_controller((void *)addr);
+}
diff --git a/src/drivers/storage/sd.c b/src/drivers/storage/sd.c
new file mode 100644
index 0000000..2c042e9
--- /dev/null
+++ b/src/drivers/storage/sd.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc.  All rights reserved.
+ * Copyrigit 2017 Intel Corporation
+ *
+ * Secure Digital (SD) card specific support code
+ * This code is controller independent
+ *
+ * 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 <delay.h>
+#include <device/sd_mmc_ctrlr.h>
+#include <device/storage.h>
+#include <endian.h>
+#include "sd_mmc.h"
+#include "storage.h"
+#include <string.h>
+#include <timer.h>
+
+int sd_send_if_cond(struct storage_media *media)
+{
+sd_mmc_trace("sd_send_if_cond\n");
+	struct mmc_command cmd;
+	cmd.cmdidx = SD_CMD_SEND_IF_COND;
+	/* Set if controller supports voltages between 2.7 and 3.6 V. */
+	cmd.cmdarg = ((media->ctrlr->voltages & 0xff8000) != 0) << 8 | 0xaa;
+	cmd.resp_type = CARD_RSP_R7;
+	cmd.flags = 0;
+	int err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+	if (err)
+		return err;
+
+	if ((cmd.response[0] & 0xff) != 0xaa)
+		return CARD_UNUSABLE_ERR;
+	media->version = SD_VERSION_2;
+	return 0;
+}
+
+int sd_send_op_cond(struct storage_media *media)
+{
+sd_mmc_trace("sd_send_op_cond\n");
+	int err;
+	struct mmc_command cmd;
+
+	int tries = SD_MMC_IO_RETRIES;
+	while (tries--) {
+		cmd.cmdidx = MMC_CMD_APP_CMD;
+		cmd.resp_type = CARD_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 = CARD_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 CARD_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;
+}
+
+static int sd_switch(struct sd_mmc_ctrlr *ctrlr, int mode, int group,
+	uint8_t value, uint8_t *resp)
+{
+sd_mmc_trace("sd_switch\n");
+	/* Switch the frequency */
+	struct mmc_command cmd;
+	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
+	cmd.resp_type = CARD_RSP_R1;
+	cmd.cmdarg = (mode << 31) | (0xffffff & ~(0xf << (group * 4))) |
+		     (value << (group * 4));
+	cmd.flags = 0;
+
+	struct mmc_data data;
+	data.dest = (char *)resp;
+	data.blocksize = 64;
+	data.blocks = 1;
+	data.flags = DATA_FLAG_READ;
+
+	return mmc_send_cmd(ctrlr, &cmd, &data);
+}
+
+static void sd_recalculate_clock(struct storage_media *media)
+{
+sd_mmc_trace("sd_recalculate_clock\n");
+	uint32_t clock = 1;
+
+	if (media->caps & DRVR_CAP_HS)
+		clock = CLOCK_50MHZ;
+	else
+		clock = CLOCK_25MHZ;
+	SET_CLOCK(media->ctrlr, clock);
+}
+
+int sd_change_freq(struct storage_media *media)
+{
+sd_mmc_trace("sd_change_freq\n");
+	int err, timeout;
+	struct mmc_command cmd;
+	struct mmc_data 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 = CARD_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_SEND_SCR;
+	cmd.resp_type = CARD_RSP_R1;
+	cmd.cmdarg = 0;
+	cmd.flags = 0;
+
+	timeout = 3;
+	while (timeout--) {
+		data.dest = (char *)scr;
+		data.blocksize = 8;
+		data.blocks = 1;
+		data.flags = DATA_FLAG_READ;
+		err = mmc_send_cmd(media->ctrlr, &cmd, &data);
+		if (!err)
+			break;
+	}
+	if (err) {
+		sd_mmc_error("%s returning %d\n", __func__, err);
+		return err;
+	}
+
+	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 |= DRVR_CAP_4BIT;
+
+	/* Version 1.0 doesn't support switching */
+	if (media->version == SD_VERSION_1_0)
+		goto out;
+
+	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))
+		goto out;
+
+	/*
+	 * If the controller doesn't support SD_HIGHSPEED, do not switch the
+	 * card to HIGHSPEED mode even if the card support SD_HIGHSPPED.
+	 * This can avoid a further problem when the card runs in different
+	 * mode than the controller.
+	 */
+	if (!((media->ctrlr->caps & DRVR_CAP_HS_52MHz) &&
+		(media->ctrlr->caps & DRVR_CAP_HS)))
+		goto out;
+
+	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 |= DRVR_CAP_HS;
+		SET_TIMING(media->ctrlr, BUS_TIMING_SD_HS);
+	}
+
+out:
+	sd_recalculate_clock(media);
+	return 0;
+}
+
+int sd_set_bus_width(struct storage_media *media)
+{
+sd_mmc_trace("sd_set_bus_width\n");
+	int err;
+	struct mmc_command cmd;
+
+	if (media->caps & DRVR_CAP_4BIT) {
+		cmd.cmdidx = MMC_CMD_APP_CMD;
+		cmd.resp_type = CARD_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 = CARD_RSP_R1;
+		cmd.cmdarg = 2;
+		cmd.flags = 0;
+		err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+		if (err)
+			return err;
+
+		SET_BUS_WIDTH(media->ctrlr, 4);
+	}
+	return 0;
+}
+
+
+int sd_set_partition(struct storage_media *media,
+	unsigned int partition_number)
+{
+	/* Validate the partition number */
+	if (partition_number)
+		return -1;
+
+	/* Update the partition number */
+	media->partition_config = partition_number;
+	return 0;
+}
+
+const char *sd_partition_name(struct storage_media *media,
+	unsigned int partition_number)
+{
+	return "";
+}
diff --git a/src/drivers/storage/sd_mmc.c b/src/drivers/storage/sd_mmc.c
new file mode 100644
index 0000000..3e37a85
--- /dev/null
+++ b/src/drivers/storage/sd_mmc.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc.  All rights reserved.
+ * Copyrigit 2017 Intel Corporation
+ *
+ * MultiMediaCard (MMC), eMMC and Secure Digital (SD) common initialization
+ * code which brings the card into the standby state.  This code is controller
+ * independent.
+ *
+ * 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 <delay.h>
+#include <device/storage.h>
+#include <endian.h>
+#include "mmc.h"
+#include "sd_mmc.h"
+#include "storage.h"
+#include <string.h>
+#include <timer.h>
+
+uint64_t sd_mmc_extract_uint32_bits(const uint32_t *array, int start, int count)
+{
+sd_mmc_trace("sd_mmc_extract_uint32_bits\n");
+	int 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 uint32_t sd_mmc_calculate_transfer_speed(uint32_t csd0)
+{
+sd_mmc_trace("sd_mmc_calculate_transfer_speed\n");
+	uint32_t mult, freq;
+
+	/* frequency bases, divided by 10 to be nice to platforms without
+	 * floating point */
+	static const int fbase[] = {
+		10000,
+		100000,
+		1000000,
+		10000000,
+	};
+	/* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice
+	 * to platforms without floating point. */
+	static const int 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;
+}
+
+int mmc_send_cmd(struct sd_mmc_ctrlr *ctrlr, struct mmc_command *cmd,
+	struct mmc_data *data)
+{
+sd_mmc_trace("mmc_send_cmd\n");
+	int ret = -1, retries = 2;
+
+	sd_mmc_trace("CMD_SEND:%d %p\n", cmd->cmdidx, ctrlr);
+	sd_mmc_trace("\tARG\t\t\t %#8.8x\n", cmd->cmdarg);
+	sd_mmc_trace("\tFLAG\t\t\t %d\n", cmd->flags);
+	if (data) {
+		sd_mmc_trace("\t%s %d block(s) of %d bytes (%p)\n",
+			  data->flags == DATA_FLAG_READ ? "READ" : "WRITE",
+			  data->blocks,
+			  data->blocksize,
+			  data->dest);
+	}
+
+	while (retries--) {
+		ret = ctrlr->send_cmd(ctrlr, cmd, data);
+
+		switch (cmd->resp_type) {
+		case CARD_RSP_NONE:
+			sd_mmc_trace("\tCARD_RSP_NONE\n");
+			break;
+
+		case CARD_RSP_R1:
+			sd_mmc_trace("\tCARD_RSP_R1,5,6,7 \t %#8.8x\n",
+				  cmd->response[0]);
+			break;
+
+		case CARD_RSP_R1b:
+			sd_mmc_trace("\tCARD_RSP_R1b\t\t %#8.8x\n",
+				  cmd->response[0]);
+			break;
+
+		case CARD_RSP_R2:
+			sd_mmc_trace("\tCARD_RSP_R2\t\t %#8.8x\n",
+				  cmd->response[0]);
+			sd_mmc_trace("\t          \t\t %#8.8x\n",
+				  cmd->response[1]);
+			sd_mmc_trace("\t          \t\t %#8.8x\n",
+				  cmd->response[2]);
+			sd_mmc_trace("\t          \t\t %#8.8x\n",
+				  cmd->response[3]);
+			break;
+
+		case CARD_RSP_R3:
+			sd_mmc_trace("\tCARD_RSP_R3,4\t\t %#8.8x\n",
+				  cmd->response[0]);
+			break;
+
+		default:
+			sd_mmc_trace("\tERROR MMC rsp not supported\n");
+			break;
+		}
+		sd_mmc_trace("\trv:\t\t\t %d\n", ret);
+
+		/* Retry failed commands, bail out otherwise.  */
+		if (!ret)
+			break;
+	}
+	return ret;
+}
+
+static int sd_mmc_go_idle(struct storage_media *media)
+{
+sd_mmc_trace("sd_mmc_go_idle\n");
+	int removable;
+
+	// Some cards can't accept idle commands without delay.
+	removable = media->ctrlr->caps & DRVR_CAP_REMOVABLE;
+	if (removable)
+		mdelay(1);
+
+	struct mmc_command cmd;
+	cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
+	cmd.cmdarg = 0;
+	cmd.resp_type = CARD_RSP_NONE;
+	cmd.flags = 0;
+
+	int 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;
+}
+
+int sd_mmc_send_status(struct storage_media *media, ssize_t tries)
+{
+sd_mmc_trace("sd_mmc_send_status\n");
+	struct mmc_command cmd;
+	cmd.cmdidx = MMC_CMD_SEND_STATUS;
+	cmd.resp_type = CARD_RSP_R1;
+	cmd.cmdarg = media->rca << 16;
+	cmd.flags = 0;
+
+	while (tries--) {
+		int 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) {
+			sd_mmc_error("Status Error: %#8.8x\n", cmd.response[0]);
+			return CARD_COMM_ERR;
+		}
+
+		udelay(100);
+	}
+
+	sd_mmc_trace("CURR STATE:%d\n",
+		  (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9);
+
+	if (tries < 0) {
+		sd_mmc_error("Timeout waiting card ready\n");
+		return CARD_TIMEOUT;
+	}
+	return 0;
+}
+
+int sd_mmc_set_blocklen(struct sd_mmc_ctrlr *ctrlr, int len)
+{
+sd_mmc_trace("sd_mmc_set_blocklen\n");
+	struct mmc_command cmd;
+	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
+	cmd.resp_type = CARD_RSP_R1;
+	cmd.cmdarg = len;
+	cmd.flags = 0;
+
+	return mmc_send_cmd(ctrlr, &cmd, NULL);
+}
+
+int sd_mmc_enter_standby(struct storage_media *media)
+{
+sd_mmc_trace("mmc_startup\n");
+	struct mmc_command cmd;
+	struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
+	int err;
+
+	SET_BUS_WIDTH(ctrlr, 1);
+	SET_CLOCK(ctrlr, 1);
+
+	/* Reset the Card */
+	err = sd_mmc_go_idle(media);
+	if (err)
+		return err;
+
+	/* Test for SD version 2 */
+	err = CARD_TIMEOUT;
+	if (IS_ENABLED(CONFIG_DRIVERS_STORAGE_SD)) {
+		err = sd_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 (IS_ENABLED(CONFIG_DRIVERS_STORAGE_MMC) && (err == CARD_TIMEOUT)) {
+		/* Some cards seem to need this */
+		sd_mmc_go_idle(media);
+
+		err = mmc_send_op_cond(media);
+		if (err == CARD_IN_PROGRESS)
+			err = mmc_complete_op_cond(media);
+	}
+
+	if (err) {
+		sd_mmc_error(
+			"Card did not respond to voltage select!\n");
+		return CARD_UNUSABLE_ERR;
+	}
+
+	/* Put the Card in Identify Mode */
+	cmd.cmdidx = MMC_CMD_ALL_SEND_CID;
+	cmd.resp_type = CARD_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 = CARD_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 = CARD_RSP_R2;
+	cmd.cmdarg = media->rca << 16;
+	cmd.flags = 0;
+	err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+
+	/* Waiting for the ready status */
+	sd_mmc_send_status(media, SD_MMC_IO_RETRIES);
+	if (err)
+		return err;
+
+	memcpy(media->csd, cmd.response, sizeof(media->csd));
+	if (media->version == MMC_VERSION_UNKNOWN) {
+		int version = sd_mmc_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 = sd_mmc_calculate_transfer_speed(media->csd[0]);
+
+	/* Determine the read and write block lengths */
+	media->read_bl_len = 1 << sd_mmc_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 << sd_mmc_extract_uint32_bits(media->csd, 102, 4);
+
+	sd_mmc_debug("mmc media info: version=%#x, tran_speed=%d\n",
+	      media->version, (int)media->tran_speed);
+
+	return 0;
+}
diff --git a/src/drivers/storage/sd_mmc.h b/src/drivers/storage/sd_mmc.h
new file mode 100644
index 0000000..4df6228
--- /dev/null
+++ b/src/drivers/storage/sd_mmc.h
@@ -0,0 +1,95 @@
+/*
+ * Copyrigit 2017 Intel Corporation
+ *
+ * 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_SD_MMC_H__
+#define __DRIVERS_STORAGE_SD_MMC_H__
+
+#include <device/sd_mmc_ctrlr.h>
+#include <device/storage.h>
+#include <stddef.h>
+
+#define SD_MMC_IO_RETRIES	1000
+
+#define IS_SD(x)		(x->version & SD_VERSION_SD)
+
+#define SET_BUS_WIDTH(ctrlr, width)		\
+	do {					\
+		ctrlr->bus_width = width;	\
+		ctrlr->set_ios(ctrlr);		\
+	} while (0)
+
+#define SET_CLOCK(ctrlr, clock_hz)		\
+	do {					\
+		ctrlr->request_hz = clock_hz;	\
+		ctrlr->set_ios(ctrlr);		\
+	} while (0)
+
+#define SET_TIMING(ctrlr, timing_value)		\
+	do {					\
+		ctrlr->timing = timing_value;	\
+		ctrlr->set_ios(ctrlr);		\
+	} while (0)
+
+/* Common support routines */
+int sd_mmc_enter_standby(struct storage_media *media);
+uint64_t sd_mmc_extract_uint32_bits(const uint32_t *array, int start,
+	int count);
+int sd_mmc_send_status(struct storage_media *media, ssize_t tries);
+int sd_mmc_set_blocklen(struct sd_mmc_ctrlr *ctrlr, int len);
+
+/* MMC support routines */
+int mmc_complete_op_cond(struct storage_media *media);
+const char *mmc_partition_name(struct storage_media *media,
+	unsigned int partition_number);
+int mmc_send_ext_csd(struct sd_mmc_ctrlr *ctrlr, unsigned char *ext_csd);
+int mmc_send_op_cond(struct storage_media *media);
+int mmc_set_partition(struct storage_media *media,
+	unsigned int partition_number);
+int mmc_update_capacity(struct storage_media *media);
+
+/* SD card support routines */
+int sd_change_freq(struct storage_media *media);
+const char *sd_partition_name(struct storage_media *media,
+	unsigned int partition_number);
+int sd_send_if_cond(struct storage_media *media);
+int sd_send_op_cond(struct storage_media *media);
+int sd_set_partition(struct storage_media *media,
+	unsigned int partition_number);
+int sd_set_bus_width(struct storage_media *media);
+
+int mmc_change_freq(struct storage_media *media);
+int mmc_send_cmd(struct sd_mmc_ctrlr *ctrlr, struct mmc_command *cmd,
+	struct mmc_data *data);
+int mmc_send_status(struct storage_media *media, ssize_t tries);
+int mmc_set_bus_width(struct storage_media *media);
+
+/* Controller debug functions */
+#define sdhc_debug(format...) \
+		if (IS_ENABLED(CONFIG_SDHC_DEBUG))	\
+			printk(BIOS_DEBUG, format);
+#define sdhc_trace(format...) \
+		if (IS_ENABLED(CONFIG_SDHC_TRACE))	\
+			printk(BIOS_DEBUG, format);
+#define sdhc_error(format...) printk(BIOS_ERR, "ERROR: " format)
+
+/* Card/device debug functions */
+#define sd_mmc_debug(format...) \
+		if (IS_ENABLED(CONFIG_SD_MMC_DEBUG))	\
+			printk(BIOS_DEBUG, format);
+#define sd_mmc_trace(format...) \
+		if (IS_ENABLED(CONFIG_SD_MMC_TRACE))	\
+			printk(BIOS_DEBUG, format);
+#define sd_mmc_error(format...) printk(BIOS_ERR, "ERROR: " format)
+
+#endif /* __DRIVERS_STORAGE_SD_MMC_H__ */
diff --git a/src/drivers/storage/sdhci.c b/src/drivers/storage/sdhci.c
new file mode 100644
index 0000000..b241575
--- /dev/null
+++ b/src/drivers/storage/sdhci.c
@@ -0,0 +1,735 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen at marvell.com>
+ *
+ * Copyrigit 2017 Intel Corporation
+ *
+ * Secure Digital (SD) Host Controller interface specific 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.
+ *
+ * Back ported to the 8xx platform (from the 8260 platform) by
+ * Murray.Jensen at cmst.csiro.au, 27-Jan-01.
+ */
+
+#include <assert.h>
+#include "bouncebuf.h"
+#include <delay.h>
+#include <device/sd_mmc_ctrlr.h>
+#include <device/sdhci.h>
+#include <device/storage.h>
+#include <endian.h>
+#include <stdlib2.h>
+#include "sdhci.h"
+#include "sd_mmc.h"
+#include "storage.h"
+#include <string.h>
+#include <timer.h>
+
+#define DMA_AVAILABLE	((CONFIG_SDHCI_ADMA_IN_BOOTBLOCK && ENV_BOOTBLOCK) \
+			|| (CONFIG_SDHCI_ADMA_IN_VERSTAGE && ENV_VERSTAGE) \
+			|| (CONFIG_SDHCI_ADMA_IN_ROMSTAGE && ENV_ROMSTAGE) \
+			|| ENV_POSTCAR || ENV_RAMSTAGE)
+
+__attribute__((weak)) void *dma_malloc(size_t length_in_bytes)
+{
+	return malloc(length_in_bytes);
+}
+
+void sdhci_reset(struct sdhci_ctrlr *sdhci_ctrlr, u8 mask)
+{
+	struct stopwatch sw;
+
+	/* Wait max 100 ms */
+	stopwatch_init_msecs_expire(&sw, 100);
+
+	sdhci_writeb(sdhci_ctrlr, mask, SDHCI_SOFTWARE_RESET);
+	while (sdhci_readb(sdhci_ctrlr, SDHCI_SOFTWARE_RESET) & mask) {
+		if (stopwatch_expired(&sw)) {
+			sdhc_error("Reset 0x%x never completed.\n", (int)mask);
+			return;
+		}
+		udelay(1000);
+	}
+}
+
+void sdhci_cmd_done(struct sdhci_ctrlr *sdhci_ctrlr, struct mmc_command *cmd)
+{
+	int i;
+	if (cmd->resp_type & CARD_RSP_136) {
+		/* CRC is stripped so we need to do some shifting. */
+		for (i = 0; i < 4; i++) {
+			cmd->response[i] = sdhci_readl(sdhci_ctrlr,
+					SDHCI_RESPONSE + (3-i)*4) << 8;
+			if (i != 3)
+				cmd->response[i] |= sdhci_readb(sdhci_ctrlr,
+						SDHCI_RESPONSE + (3-i)*4-1);
+		}
+		sdhc_trace("Response: 0x%08x.%08x.%08x.%08x\n",
+			cmd->response[3], cmd->response[2], cmd->response[1],
+			cmd->response[0]);
+	} else {
+		cmd->response[0] = sdhci_readl(sdhci_ctrlr, SDHCI_RESPONSE);
+		sdhc_trace("Response: 0x%08x\n", cmd->response[0]);
+	}
+}
+
+static void sdhci_transfer_pio(struct sdhci_ctrlr *sdhci_ctrlr,
+	struct mmc_data *data)
+{
+	int i;
+	char *offs;
+	for (i = 0; i < data->blocksize; i += 4) {
+		offs = data->dest + i;
+		if (data->flags == DATA_FLAG_READ)
+			*(u32 *)offs = sdhci_readl(sdhci_ctrlr, SDHCI_BUFFER);
+		else
+			sdhci_writel(sdhci_ctrlr, *(u32 *)offs, SDHCI_BUFFER);
+	}
+}
+
+static int sdhci_transfer_data(struct sdhci_ctrlr *sdhci_ctrlr,
+	struct mmc_data *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(sdhci_ctrlr, SDHCI_INT_STATUS);
+		if (stat & SDHCI_INT_ERROR) {
+			sdhc_error("Error detected in status(0x%X)!\n", stat);
+			return -1;
+		}
+		if (stat & rdy) {
+			if (!(sdhci_readl(sdhci_ctrlr, SDHCI_PRESENT_STATE)
+				& mask))
+				continue;
+			sdhci_writel(sdhci_ctrlr, rdy, SDHCI_INT_STATUS);
+			sdhci_transfer_pio(sdhci_ctrlr, data);
+			data->dest += data->blocksize;
+			if (++block >= data->blocks)
+				break;
+		}
+		if (timeout-- > 0)
+			udelay(10);
+		else {
+			sdhc_error("Transfer data timeout\n");
+			return -1;
+		}
+	} while (!(stat & SDHCI_INT_DATA_END));
+	return 0;
+}
+
+static int sdhci_send_command_bounced(struct sd_mmc_ctrlr *ctrlr,
+	struct mmc_command *cmd, struct mmc_data *data,
+	struct bounce_buffer *bbstate)
+{
+	struct sdhci_ctrlr *sdhci_ctrlr = (struct sdhci_ctrlr *)ctrlr;
+	unsigned int stat = 0;
+	int ret = 0;
+	u32 mask, flags;
+	unsigned int timeout, start_addr = 0;
+	struct stopwatch sw;
+
+	/* Wait max 1 s */
+	timeout = 1000;
+
+	sdhci_writel(sdhci_ctrlr, 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->flags & CMD_FLAG_IGNORE_INHIBIT)
+		mask &= ~SDHCI_DATA_INHIBIT;
+
+	while (sdhci_readl(sdhci_ctrlr, SDHCI_PRESENT_STATE) & mask) {
+		if (timeout == 0) {
+			sdhc_trace("Cmd: %2d, Arg: 0x%08x, not sent\n",
+					cmd->cmdidx, cmd->cmdarg);
+			sdhc_error("Controller never released inhibit bit(s), "
+			       "present state %#8.8x.\n",
+			       sdhci_readl(sdhci_ctrlr, SDHCI_PRESENT_STATE));
+			return CARD_COMM_ERR;
+		}
+		timeout--;
+		udelay(1000);
+	}
+
+	mask = SDHCI_INT_RESPONSE;
+	if (!(cmd->resp_type & CARD_RSP_PRESENT))
+		flags = SDHCI_CMD_RESP_NONE;
+	else if (cmd->resp_type & CARD_RSP_136)
+		flags = SDHCI_CMD_RESP_LONG;
+	else if (cmd->resp_type & CARD_RSP_BUSY) {
+		flags = SDHCI_CMD_RESP_SHORT_BUSY;
+		mask |= SDHCI_INT_DATA_END;
+	} else
+		flags = SDHCI_CMD_RESP_SHORT;
+
+	if (cmd->resp_type & CARD_RSP_CRC)
+		flags |= SDHCI_CMD_CRC;
+	if (cmd->resp_type & CARD_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(sdhci_ctrlr,
+			SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
+			data->blocksize), SDHCI_BLOCK_SIZE);
+
+		if (data->flags == DATA_FLAG_READ)
+			mode |= SDHCI_TRNS_READ;
+
+		if (data->blocks > 1)
+			mode |= SDHCI_TRNS_BLK_CNT_EN |
+				SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
+
+		sdhci_writew(sdhci_ctrlr, data->blocks, SDHCI_BLOCK_COUNT);
+
+		if (DMA_AVAILABLE) {
+			if (ctrlr->caps & DRVR_CAP_AUTO_CMD12) {
+				if (sdhci_setup_adma(sdhci_ctrlr, data))
+					return -1;
+				mode |= SDHCI_TRNS_DMA;
+			}
+		}
+		sdhci_writew(sdhci_ctrlr, mode, SDHCI_TRANSFER_MODE);
+	}
+
+	sdhc_trace("Cmd: %2d, Arg: 0x%08x\n", cmd->cmdidx, cmd->cmdarg);
+	sdhci_writel(sdhci_ctrlr, cmd->cmdarg, SDHCI_ARGUMENT);
+	sdhci_writew(sdhci_ctrlr, SDHCI_MAKE_CMD(cmd->cmdidx, flags),
+		SDHCI_COMMAND);
+
+	if (DMA_AVAILABLE) {
+		if (data && (ctrlr->caps & DRVR_CAP_AUTO_CMD12))
+			return sdhci_complete_adma(sdhci_ctrlr, cmd);
+	}
+
+	stopwatch_init_msecs_expire(&sw, 2550);
+	do {
+		stat = sdhci_readl(sdhci_ctrlr, SDHCI_INT_STATUS);
+		if (stat & SDHCI_INT_ERROR) {
+			sdhc_trace("Error - IntStatus: 0x%08x\n", stat);
+			break;
+		}
+
+		/* Apply max timeout for R1b-type CMD defined in eMMC ext_csd
+		   except for erase ones */
+		if (stopwatch_expired(&sw)) {
+			if (ctrlr->caps & DRVR_CAP_BROKEN_R1B)
+				return 0;
+			sdhc_error("Timeout for status update!  IntStatus: 0x%08x\n",
+					stat);
+			return CARD_TIMEOUT;
+		}
+	} while ((stat & mask) != mask);
+
+	if ((stat & (SDHCI_INT_ERROR | mask)) == mask) {
+		sdhci_cmd_done(sdhci_ctrlr, cmd);
+		sdhci_writel(sdhci_ctrlr, mask, SDHCI_INT_STATUS);
+	} else
+		ret = -1;
+
+	if (!ret && data)
+		ret = sdhci_transfer_data(sdhci_ctrlr, data, start_addr);
+
+	if (ctrlr->caps & DRVR_CAP_WAIT_SEND_CMD)
+		udelay(1000);
+
+	stat = sdhci_readl(sdhci_ctrlr, SDHCI_INT_STATUS);
+	sdhci_writel(sdhci_ctrlr, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
+
+	if (!ret)
+		return 0;
+
+	sdhci_reset(sdhci_ctrlr, SDHCI_RESET_CMD);
+	sdhci_reset(sdhci_ctrlr, SDHCI_RESET_DATA);
+	if (stat & SDHCI_INT_TIMEOUT)
+		return CARD_TIMEOUT;
+	return CARD_COMM_ERR;
+}
+
+static int sdhci_send_command(struct sd_mmc_ctrlr *ctrlr,
+	struct mmc_command *cmd, struct mmc_data *data)
+{
+	void *buf;
+	unsigned int bbflags;
+	size_t len;
+	struct bounce_buffer *bbstate = NULL;
+	struct bounce_buffer bbstate_val;
+	int ret;
+
+	if (IS_ENABLED(CONFIG_SDHCI_BOUNCE_BUFFER) && data) {
+		if (data->flags & DATA_FLAG_READ) {
+			buf = data->dest;
+			bbflags = GEN_BB_WRITE;
+		} else {
+			buf = (void *)data->src;
+			bbflags = GEN_BB_READ;
+		}
+		len = data->blocks * data->blocksize;
+
+		/*
+		 * on some platform(like rk3399 etc) need to worry about
+		 * cache coherency, so check the buffer, if not dma
+		 * coherent, use bounce_buffer to do DMA management.
+		 */
+		if (!dma_coherent(buf)) {
+			bbstate = &bbstate_val;
+			if (bounce_buffer_start(bbstate, buf, len, bbflags)) {
+				sdhc_error(
+					"ERROR: Failed to get bounce buffer.\n");
+				return -1;
+			}
+		}
+	}
+
+	ret = sdhci_send_command_bounced(ctrlr, cmd, data, bbstate);
+
+	if (IS_ENABLED(CONFIG_SDHCI_BOUNCE_BUFFER) && bbstate)
+		bounce_buffer_stop(bbstate);
+
+	return ret;
+}
+
+static int sdhci_set_clock(struct sdhci_ctrlr *sdhci_ctrlr, unsigned int clock)
+{
+	struct sd_mmc_ctrlr *ctrlr = &sdhci_ctrlr->sd_mmc_ctrlr;
+	unsigned int actual, div, clk, timeout;
+
+	/* Turn off the clock if requested */
+	actual = clock;
+	if (actual == 0) {
+		sdhci_writew(sdhci_ctrlr, 0, SDHCI_CLOCK_CONTROL);
+		sdhc_debug("SDHCI bus clock: Off\n");
+		return 0;
+	}
+
+	/* Compute the divisor for the new clock frequency */
+	actual = MIN(actual, ctrlr->f_max);
+	actual = MAX(actual, ctrlr->f_min);
+	if (ctrlr->clock_base <= actual)
+		div = 0;
+	else {
+		/* Version 3.00 divisors must be a multiple of 2. */
+		if ((ctrlr->version & SDHCI_SPEC_VER_MASK)
+			>= SDHCI_SPEC_300) {
+			div = MIN(((ctrlr->clock_base + actual - 1)
+				/ actual), SDHCI_MAX_DIV_SPEC_300);
+			actual = ctrlr->clock_base / div;
+			div += 1;
+		} else {
+			/* Version 2.00 divisors must be a power of 2. */
+			for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) {
+				if ((ctrlr->clock_base / div) <= actual)
+					break;
+			}
+			actual = ctrlr->clock_base / div;
+		}
+		div >>= 1;
+	}
+
+	/* Set the new clock frequency */
+	if (actual != ctrlr->bus_hz) {
+		/* Turn off the clock */
+		sdhci_writew(sdhci_ctrlr, 0, SDHCI_CLOCK_CONTROL);
+
+		/* Set the new clock frequency */
+		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(sdhci_ctrlr, clk, SDHCI_CLOCK_CONTROL);
+
+		/* Display the requested clock frequency */
+		sdhc_debug("SDHCI bus clock: %d.%03d MHz\n",
+				actual / 1000000,
+				(actual / 1000) % 1000);
+
+		/* Wait max 20 ms */
+		timeout = 20;
+		while (!((clk = sdhci_readw(sdhci_ctrlr, SDHCI_CLOCK_CONTROL))
+			& SDHCI_CLOCK_INT_STABLE)) {
+			if (timeout == 0) {
+				sdhc_error("Internal clock never stabilised.\n");
+				return -1;
+			}
+			timeout--;
+			udelay(1000);
+		}
+
+		clk |= SDHCI_CLOCK_CARD_EN;
+		sdhci_writew(sdhci_ctrlr, clk, SDHCI_CLOCK_CONTROL);
+		ctrlr->bus_hz = actual;
+	}
+	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(struct sdhci_ctrlr *sdhci_ctrlr,
+	unsigned short power)
+{
+	struct sd_mmc_ctrlr *ctrlr = &sdhci_ctrlr->sd_mmc_ctrlr;
+	u8 pwr = 0;
+	u8 pwr_ctrl;
+	const char *voltage;
+
+	if (power != (unsigned short)-1) {
+		switch (1 << power) {
+		case MMC_VDD_165_195:
+			voltage = "1.8";
+			pwr = SDHCI_POWER_180;
+			break;
+		case MMC_VDD_29_30:
+		case MMC_VDD_30_31:
+			voltage = "3.0";
+			pwr = SDHCI_POWER_300;
+			break;
+		case MMC_VDD_32_33:
+		case MMC_VDD_33_34:
+			voltage = "3.3";
+			pwr = SDHCI_POWER_330;
+			break;
+		}
+	}
+
+	/* Determine the power state */
+	pwr_ctrl = sdhci_readb(sdhci_ctrlr, SDHCI_POWER_CONTROL);
+	if (pwr == 0) {
+		if (pwr_ctrl & SDHCI_POWER_ON)
+			sdhc_debug("SDHCI voltage: Off\n");
+		sdhci_writeb(sdhci_ctrlr, 0, SDHCI_POWER_CONTROL);
+		return;
+	}
+
+	/* Determine if the power has changed */
+	if (pwr_ctrl != (pwr | SDHCI_POWER_ON)) {
+		sdhc_debug("SDHCI voltage: %s Volts\n", voltage);
+
+		/* Select the voltage */
+		if (ctrlr->caps & DRVR_CAP_NO_SIMULT_VDD_AND_POWER)
+			sdhci_writeb(sdhci_ctrlr, pwr, SDHCI_POWER_CONTROL);
+
+		/* Apply power to the SD/MMC device */
+		pwr |= SDHCI_POWER_ON;
+		sdhci_writeb(sdhci_ctrlr, pwr, SDHCI_POWER_CONTROL);
+	}
+}
+
+const u16 speed_driver_voltage[] = {
+	0, /*  0: BUS_TIMING_LEGACY */
+	0, /*  1: BUS_TIMING_MMC_HS */
+	0, /*  2: BUS_TIMING_SD_HS */
+	SDHCI_CTRL_UHS_SDR12 | SDHCI_CTRL_VDD_180, /* 3: BUS_TIMING_UHS_SDR12 */
+	SDHCI_CTRL_UHS_SDR25 | SDHCI_CTRL_VDD_180, /* 4: BUS_TIMING_UHS_SDR25 */
+	SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180, /* 5: BUS_TIMING_UHS_SDR50 */
+	/*  6: BUS_TIMING_UHS_SDR104 */
+	SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_DRV_TYPE_A | SDHCI_CTRL_VDD_180,
+	SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180, /* 7: BUS_TIMING_UHS_DDR50 */
+	SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180, /* 8: BUS_TIMING_MMC_DDR52 */
+	/*  9: BUS_TIMING_MMC_HS200 */
+	SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_DRV_TYPE_A | SDHCI_CTRL_VDD_180,
+	/* 10: BUS_TIMING_MMC_HS400 */
+	SDHCI_CTRL_HS400 | SDHCI_CTRL_DRV_TYPE_A | SDHCI_CTRL_VDD_180,
+	/* 11: BUS_TIMING_MMC_HS400ES */
+	SDHCI_CTRL_HS400 | SDHCI_CTRL_DRV_TYPE_A | SDHCI_CTRL_VDD_180
+};
+
+static void sdhci_set_uhs_signaling(struct sdhci_ctrlr *sdhci_ctrlr,
+	uint32_t timing)
+{
+	u16 ctrl_2;
+
+	/* Select bus speed mode, driver and VDD 1.8 volt support */
+	ctrl_2 = sdhci_readw(sdhci_ctrlr, SDHCI_HOST_CONTROL2);
+	ctrl_2 &= ~(SDHCI_CTRL_UHS_MASK | SDHCI_CTRL_DRV_TYPE_MASK
+		| SDHCI_CTRL_VDD_180);
+	if (timing < ARRAY_SIZE(speed_driver_voltage))
+		ctrl_2 |= speed_driver_voltage[timing];
+	sdhci_writew(sdhci_ctrlr, ctrl_2, SDHCI_HOST_CONTROL2);
+}
+
+static void sdhci_set_ios(struct sd_mmc_ctrlr *ctrlr)
+{
+	struct sdhci_ctrlr *sdhci_ctrlr = (struct sdhci_ctrlr *)ctrlr;
+	u32 ctrl;
+	u32 previous_ctrl;
+	u32 bus_width;
+	int version;
+
+	if (ctrlr->set_control_reg)
+		ctrlr->set_control_reg(ctrlr);
+
+	/* Set the clock frequency */
+	if (ctrlr->bus_hz != ctrlr->request_hz)
+		sdhci_set_clock(sdhci_ctrlr, ctrlr->request_hz);
+
+	/* Switch to 1.8 volt for HS200 */
+	if (ctrlr->caps & DRVR_CAP_1V8_VDD)
+		if (ctrlr->bus_hz == CLOCK_200MHZ)
+			sdhci_set_power(sdhci_ctrlr, MMC_VDD_165_195_SHIFT);
+
+	/* Determine the new bus width */
+	bus_width = 1;
+	ctrl = sdhci_readb(sdhci_ctrlr, SDHCI_HOST_CONTROL);
+	previous_ctrl = ctrl;
+	ctrl &= ~SDHCI_CTRL_4BITBUS;
+	version = ctrlr->version & SDHCI_SPEC_VER_MASK;
+	if (version >= SDHCI_SPEC_300)
+		ctrl &= ~SDHCI_CTRL_8BITBUS;
+
+	if ((ctrlr->bus_width == 8) && (version >= SDHCI_SPEC_300)) {
+		ctrl |= SDHCI_CTRL_8BITBUS;
+		bus_width = 8;
+	} else if (ctrlr->bus_width == 4) {
+		ctrl |= SDHCI_CTRL_4BITBUS;
+		bus_width = 4;
+	}
+
+	if (!(ctrlr->timing == BUS_TIMING_LEGACY) &&
+	    !(ctrlr->caps & DRVR_CAP_NO_HISPD_BIT))
+		ctrl |= SDHCI_CTRL_HISPD;
+	else
+		ctrl &= ~SDHCI_CTRL_HISPD;
+
+	sdhci_set_uhs_signaling(sdhci_ctrlr, ctrlr->timing);
+
+	if (DMA_AVAILABLE) {
+		if (ctrlr->caps & DRVR_CAP_AUTO_CMD12) {
+			ctrl &= ~SDHCI_CTRL_DMA_MASK;
+			if (sdhci_ctrlr->dma64)
+				ctrl |= SDHCI_CTRL_ADMA64;
+			else
+				ctrl |= SDHCI_CTRL_ADMA32;
+		}
+	}
+
+	/* Set the new bus width */
+	if (IS_ENABLED(CONFIG_SDHC_DEBUG)
+		&& ((ctrl ^ previous_ctrl) & (SDHCI_CTRL_4BITBUS
+		| ((version >= SDHCI_SPEC_300) ? SDHCI_CTRL_8BITBUS : 0))))
+		sdhc_debug("SDHCI bus width: %d bit%s\n", bus_width,
+			(bus_width != 1) ? "s" : "");
+	sdhci_writeb(sdhci_ctrlr, ctrl, SDHCI_HOST_CONTROL);
+}
+
+/* Prepare SDHCI controller to be initialized */
+static int sdhci_pre_init(struct sdhci_ctrlr *sdhci_ctrlr)
+{
+	struct sd_mmc_ctrlr *ctrlr = &sdhci_ctrlr->sd_mmc_ctrlr;
+	unsigned int caps, caps_1;
+
+	/* Get controller version and capabilities */
+	ctrlr->version = sdhci_readw(sdhci_ctrlr, SDHCI_HOST_VERSION) & 0xff;
+	caps = sdhci_readl(sdhci_ctrlr, SDHCI_CAPABILITIES);
+	caps_1 = sdhci_readl(sdhci_ctrlr, SDHCI_CAPABILITIES_1);
+
+	/* Determine the supported voltages */
+	if (caps & SDHCI_CAN_VDD_330)
+		ctrlr->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+	if (caps & SDHCI_CAN_VDD_300)
+		ctrlr->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+	if (caps & SDHCI_CAN_VDD_180)
+		ctrlr->voltages |= MMC_VDD_165_195;
+
+	/* Get the controller's base clock frequency */
+	if ((ctrlr->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)
+		ctrlr->clock_base = (caps & SDHCI_CLOCK_V3_BASE_MASK)
+			>> SDHCI_CLOCK_BASE_SHIFT;
+	else
+		ctrlr->clock_base = (caps & SDHCI_CLOCK_BASE_MASK)
+			>> SDHCI_CLOCK_BASE_SHIFT;
+	ctrlr->clock_base *= 1000000;
+	ctrlr->f_max = ctrlr->clock_base;
+
+	/* Determine the controller's clock frequency range */
+	ctrlr->f_min = 0;
+	if ((ctrlr->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)
+		ctrlr->f_min =
+			ctrlr->clock_base / SDHCI_MAX_DIV_SPEC_300;
+	else
+		ctrlr->f_min =
+			ctrlr->clock_base / SDHCI_MAX_DIV_SPEC_200;
+
+	/* Determine the controller's modes of operation */
+	ctrlr->caps |= DRVR_CAP_HS_200MHz | DRVR_CAP_HS_52MHz | DRVR_CAP_HS;
+	if (caps_1 & SDHCI_SUPPORT_HS400)
+		ctrlr->caps |= DRVR_CAP_HS400ES;
+
+	/* Determine the bus widths the controller supports */
+	ctrlr->caps |= DRVR_CAP_4BIT;
+	if (caps & SDHCI_CAN_DO_8BIT)
+		ctrlr->caps |= DRVR_CAP_8BIT;
+
+	/* Determine the controller's DMA support */
+	if (caps & SDHCI_CAN_DO_ADMA2)
+		ctrlr->caps |= DRVR_CAP_AUTO_CMD12;
+	if (DMA_AVAILABLE && (caps & SDHCI_CAN_64BIT))
+		sdhci_ctrlr->dma64 = 1;
+
+	/* Specify the modes that the driver stack supports */
+	ctrlr->caps |= DRVR_CAP_HC;
+
+	/* Let the SOC adjust the configuration to handle controller quirks */
+	soc_sd_mmc_controller_quirks(&sdhci_ctrlr->sd_mmc_ctrlr);
+	if (ctrlr->clock_base == 0) {
+		sdhc_error("Hardware doesn't specify base clock frequency\n");
+		return -1;
+	}
+	if (!ctrlr->f_max)
+		ctrlr->f_max = ctrlr->clock_base;
+
+	/* Display the results */
+	sdhc_trace("0x%08x: ctrlr->caps\n", ctrlr->caps);
+	sdhc_trace("%d.%03d: ctrlr->clock_base\n",
+		ctrlr->clock_base / 1000000,
+		(ctrlr->clock_base / 1000) % 1000);
+	sdhc_trace("%d.%03d: ctrlr->f_max\n",
+		ctrlr->f_max / 1000000,
+		(ctrlr->f_max / 1000) % 1000);
+	sdhc_trace("%d.%03d: ctrlr->f_min\n",
+		ctrlr->f_min / 1000000,
+		(ctrlr->f_min / 1000) % 1000);
+	sdhc_trace("%d.%03d: ctrlr->clock_base\n",
+		ctrlr->clock_base / 1000000,
+		(ctrlr->clock_base / 1000) % 1000);
+	sdhc_trace("0x%08x: ctrlr->voltages\n", ctrlr->voltages);
+
+	sdhci_reset(sdhci_ctrlr, SDHCI_RESET_ALL);
+
+	return 0;
+}
+
+__attribute__((weak)) void soc_sd_mmc_controller_quirks(struct sd_mmc_ctrlr
+	*ctrlr)
+{
+}
+
+static int sdhci_init(struct sdhci_ctrlr *sdhci_ctrlr)
+{
+	struct sd_mmc_ctrlr *ctrlr = &sdhci_ctrlr->sd_mmc_ctrlr;
+	int rv;
+
+	/* Only initialize the controller upon reset or card insertion */
+	if (ctrlr->initialized)
+		return 0;
+
+	sdhc_debug("SDHCI Controller Base Address: 0x%p\n",
+			sdhci_ctrlr->ioaddr);
+
+	rv = sdhci_pre_init(sdhci_ctrlr);
+	if (rv)
+		return rv; /* The error has been already reported */
+
+	sdhci_set_power(sdhci_ctrlr, fls(ctrlr->voltages) - 1);
+
+	if (ctrlr->caps & DRVR_CAP_NO_CD) {
+		unsigned int status;
+
+		sdhci_writel(sdhci_ctrlr, SDHCI_CTRL_CD_TEST_INS
+			| SDHCI_CTRL_CD_TEST, SDHCI_HOST_CONTROL);
+
+		status = sdhci_readl(sdhci_ctrlr, SDHCI_PRESENT_STATE);
+		while ((!(status & SDHCI_CARD_PRESENT)) ||
+		    (!(status & SDHCI_CARD_STATE_STABLE)) ||
+		    (!(status & SDHCI_CARD_DETECT_PIN_LEVEL)))
+			status = sdhci_readl(sdhci_ctrlr, SDHCI_PRESENT_STATE);
+	}
+
+	/* Enable only interrupts served by the SD controller */
+	sdhci_writel(sdhci_ctrlr, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK,
+		     SDHCI_INT_ENABLE);
+	/* Mask all sdhci interrupt sources */
+	sdhci_writel(sdhci_ctrlr, 0x0, SDHCI_SIGNAL_ENABLE);
+
+	/* Set timeout to maximum, shouldn't happen if everything's right. */
+	sdhci_writeb(sdhci_ctrlr, 0xe, SDHCI_TIMEOUT_CONTROL);
+
+	mdelay(10);
+	ctrlr->initialized = 1;
+	return 0;
+}
+
+static int sdhci_update(struct sdhci_ctrlr *sdhci_ctrlr)
+{
+	struct sd_mmc_ctrlr *ctrlr = &sdhci_ctrlr->sd_mmc_ctrlr;
+
+	if (ctrlr->caps & DRVR_CAP_REMOVABLE) {
+		int present = (sdhci_readl(sdhci_ctrlr, SDHCI_PRESENT_STATE) &
+			       SDHCI_CARD_PRESENT) != 0;
+
+		if (!present) {
+			/* A card was present indicate the controller needs
+			 * initialization on the next call.
+			 */
+			ctrlr->initialized = 0;
+			return 0;
+		}
+	}
+
+	/* A card is present, get it ready. */
+	if (sdhci_init(sdhci_ctrlr))
+		return -1;
+	return 0;
+}
+
+int add_sdhci(struct sdhci_ctrlr *sdhci_ctrlr)
+{
+	struct sd_mmc_ctrlr *ctrlr = &sdhci_ctrlr->sd_mmc_ctrlr;
+
+	ctrlr->send_cmd = &sdhci_send_command;
+	ctrlr->set_ios = &sdhci_set_ios;
+
+	/* TODO(vbendeb): check if SDHCI spec allows to retrieve this value. */
+	ctrlr->b_max = 65535;
+
+	/* Initialize the SDHC controller */
+	return sdhci_update(sdhci_ctrlr);
+}
diff --git a/src/drivers/storage/sdhci.h b/src/drivers/storage/sdhci.h
new file mode 100644
index 0000000..81356cf
--- /dev/null
+++ b/src/drivers/storage/sdhci.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen at marvell.com>
+ *
+ * Copyrigit 2017 Intel Corporation
+ *
+ * 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 __DRIVERS_STORAGE_SDHCI_H__
+#define __DRIVERS_STORAGE_SDHCI_H__
+
+#include <arch/io.h>
+#include <device/sd_mmc_ctrlr.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_HS400		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_SUPPORT_HS400	0x80000000
+
+#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
+
+/*
+ * Controller 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)
+
+#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)
+
+/* Driver specific capabilities */
+#define DRVR_CAP_1V8_VDD			0x00010000
+#define DRVR_CAP_32BIT_DMA_ADDR			0x00020000
+#define DRVR_CAP_8BIT				0x00040000
+#define DRVR_CAP_BROKEN_R1B			0x00080000
+#define DRVR_CAP_HS400				0x00100000
+#define DRVR_CAP_NO_CD				0x00200000
+#define DRVR_CAP_NO_HISPD_BIT			0x00400000
+#define DRVR_CAP_NO_SIMULT_VDD_AND_POWER	0x00800000
+#define DRVR_CAP_REG32_RW			0x01000000
+#define DRVR_CAP_SPI				0x02000000
+#define DRVR_CAP_WAIT_SEND_CMD			0x04000000
+
+static inline void sdhci_writel(struct sdhci_ctrlr *sdhci_ctrlr, u32 val,
+	int reg)
+{
+	write32(sdhci_ctrlr->ioaddr + reg, val);
+}
+
+static inline void sdhci_writew(struct sdhci_ctrlr *sdhci_ctrlr, u16 val,
+	int reg)
+{
+	write16(sdhci_ctrlr->ioaddr + reg, val);
+}
+
+static inline void sdhci_writeb(struct sdhci_ctrlr *sdhci_ctrlr, u8 val,
+	int reg)
+{
+	write8(sdhci_ctrlr->ioaddr + reg, val);
+}
+static inline u32 sdhci_readl(struct sdhci_ctrlr *sdhci_ctrlr, int reg)
+{
+	return read32(sdhci_ctrlr->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(struct sdhci_ctrlr *sdhci_ctrlr, int reg)
+{
+	return read16(sdhci_ctrlr->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(struct sdhci_ctrlr *sdhci_ctrlr, int reg)
+{
+	return read8(sdhci_ctrlr->ioaddr + reg);
+}
+
+void sdhci_reset(struct sdhci_ctrlr *sdhci_ctrlr, u8 mask);
+void sdhci_cmd_done(struct sdhci_ctrlr *sdhci_ctrlr, struct mmc_command *cmd);
+int sdhci_setup_adma(struct sdhci_ctrlr *sdhci_ctrlr, struct mmc_data *data);
+int sdhci_complete_adma(struct sdhci_ctrlr *sdhci_ctrlr,
+	struct mmc_command *cmd);
+
+#endif /* __DRIVERS_STORAGE_SDHCI_H__ */
diff --git a/src/drivers/storage/sdhci_adma.c b/src/drivers/storage/sdhci_adma.c
new file mode 100644
index 0000000..f5b99ae
--- /dev/null
+++ b/src/drivers/storage/sdhci_adma.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen at marvell.com>
+ *
+ * Copyrigit 2017 Intel Corporation
+ *
+ * Secure Digital (SD) Host Controller interface DMA support 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.
+ *
+ * Back ported to the 8xx platform (from the 8260 platform) by
+ * Murray.Jensen at cmst.csiro.au, 27-Jan-01.
+ */
+
+#include <assert.h>
+#include <delay.h>
+#include <device/sdhci.h>
+#include <device/storage.h>
+#include <endian.h>
+#include "sdhci.h"
+#include "sd_mmc.h"
+#include "storage.h"
+#include <string.h>
+#include <timer.h>
+
+static void sdhci_alloc_adma_descs(struct sdhci_ctrlr *sdhci_ctrlr,
+	u32 need_descriptors)
+{
+	if (sdhci_ctrlr->adma_descs) {
+		if (sdhci_ctrlr->adma_desc_count < need_descriptors) {
+			/* Previously allocated array is too small */
+			free(sdhci_ctrlr->adma_descs);
+			sdhci_ctrlr->adma_desc_count = 0;
+			sdhci_ctrlr->adma_descs = NULL;
+		}
+	}
+
+	/* use dma_malloc() to make sure we get the coherent/uncached memory */
+	if (!sdhci_ctrlr->adma_descs) {
+		sdhci_ctrlr->adma_descs = malloc(need_descriptors
+			* sizeof(*sdhci_ctrlr->adma_descs));
+		if (sdhci_ctrlr->adma_descs == NULL)
+			die("fail to malloc adma_descs\n");
+		sdhci_ctrlr->adma_desc_count = need_descriptors;
+	}
+
+	memset(sdhci_ctrlr->adma_descs, 0, sizeof(*sdhci_ctrlr->adma_descs)
+		* need_descriptors);
+}
+
+static void sdhci_alloc_adma64_descs(struct sdhci_ctrlr *sdhci_ctrlr,
+	u32 need_descriptors)
+{
+	if (sdhci_ctrlr->adma64_descs) {
+		if (sdhci_ctrlr->adma_desc_count < need_descriptors) {
+			/* Previously allocated array is too small */
+			free(sdhci_ctrlr->adma64_descs);
+			sdhci_ctrlr->adma_desc_count = 0;
+			sdhci_ctrlr->adma64_descs = NULL;
+		}
+	}
+
+	/* use dma_malloc() to make sure we get the coherent/uncached memory */
+	if (!sdhci_ctrlr->adma64_descs) {
+		sdhci_ctrlr->adma64_descs = malloc(need_descriptors
+			* sizeof(*sdhci_ctrlr->adma64_descs));
+		if (sdhci_ctrlr->adma64_descs == NULL)
+			die("fail to malloc adma64_descs\n");
+
+		sdhci_ctrlr->adma_desc_count = need_descriptors;
+	}
+
+	memset(sdhci_ctrlr->adma64_descs, 0, sizeof(*sdhci_ctrlr->adma64_descs)
+		* need_descriptors);
+}
+
+int sdhci_setup_adma(struct sdhci_ctrlr *sdhci_ctrlr, struct mmc_data *data)
+{
+	int i, togo, need_descriptors;
+	char *buffer_data;
+	u16 attributes;
+
+	togo = data->blocks * data->blocksize;
+	if (!togo) {
+		sdhc_error("%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 (sdhci_ctrlr->dma64)
+		sdhci_alloc_adma64_descs(sdhci_ctrlr, need_descriptors);
+	else
+		sdhci_alloc_adma_descs(sdhci_ctrlr, need_descriptors);
+	buffer_data = data->dest;
+
+	/* Now set up the descriptor chain. */
+	for (i = 0; togo; i++) {
+		unsigned int 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 (sdhci_ctrlr->dma64) {
+			sdhci_ctrlr->adma64_descs[i].addr =
+				(uintptr_t)buffer_data;
+			sdhci_ctrlr->adma64_descs[i].addr_hi = 0;
+			sdhci_ctrlr->adma64_descs[i].length = desc_length;
+			sdhci_ctrlr->adma64_descs[i].attributes = attributes;
+
+		} else {
+			sdhci_ctrlr->adma_descs[i].addr =
+				(uintptr_t)buffer_data;
+			sdhci_ctrlr->adma_descs[i].length = desc_length;
+			sdhci_ctrlr->adma_descs[i].attributes = attributes;
+		}
+
+		buffer_data += desc_length;
+	}
+
+	if (sdhci_ctrlr->dma64)
+		sdhci_writel(sdhci_ctrlr, (uintptr_t) sdhci_ctrlr->adma64_descs,
+			     SDHCI_ADMA_ADDRESS);
+	else
+		sdhci_writel(sdhci_ctrlr, (uintptr_t) sdhci_ctrlr->adma_descs,
+			     SDHCI_ADMA_ADDRESS);
+
+	return 0;
+}
+
+int sdhci_complete_adma(struct sdhci_ctrlr *sdhci_ctrlr,
+	struct mmc_command *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(sdhci_ctrlr, SDHCI_INT_STATUS);
+		if (stat & mask)
+			break;
+		udelay(1);
+	}
+
+	sdhci_writel(sdhci_ctrlr, 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(sdhci_ctrlr, SDHCI_INT_STATUS);
+			if (stat & mask)
+				break;
+			udelay(1);
+		}
+
+		sdhci_writel(sdhci_ctrlr, stat, SDHCI_INT_STATUS);
+		if (retry && !(stat & SDHCI_INT_ERROR)) {
+			sdhci_cmd_done(sdhci_ctrlr, cmd);
+			return 0;
+		}
+	}
+
+	sdhc_error("%s: transfer error, stat %#x, adma error %#x, retry %d\n",
+	       __func__, stat, sdhci_readl(sdhci_ctrlr, SDHCI_ADMA_ERROR),
+		retry);
+
+	sdhci_reset(sdhci_ctrlr, SDHCI_RESET_CMD);
+	sdhci_reset(sdhci_ctrlr, SDHCI_RESET_DATA);
+
+	if (stat & SDHCI_INT_TIMEOUT)
+		return CARD_TIMEOUT;
+	return CARD_COMM_ERR;
+}
diff --git a/src/drivers/storage/storage.c b/src/drivers/storage/storage.c
new file mode 100644
index 0000000..8a837f6
--- /dev/null
+++ b/src/drivers/storage/storage.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc.  All rights reserved.
+ * Copyrigit 2017 Intel Corporation
+ *
+ * MultiMediaCard (MMC), eMMC and Secure Digital (SD) common code which
+ * transitions the card from the standby state to the transfer state.  The
+ * common code supports read operations, erase and write operations are in
+ * a separate modules.  This code is controller independent.
+ *
+ * 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 <commonlib/helpers.h>
+#include <delay.h>
+#include <device/storage.h>
+#include <endian.h>
+#include "sd_mmc.h"
+#include "storage.h"
+#include <string.h>
+#include <timer.h>
+
+#define DECIMAL_CAPACITY_MULTIPLIER	1000ULL
+#define HEX_CAPACITY_MULTIPLIER		1024ULL
+
+static const char * const hex_unit_name[] = {
+	"TiB", "GiB", "MiB", "KiB", "B"
+};
+static const char * const decimal_unit_name[] = {
+	"TB", "GB", "MB", "KB", "B"
+};
+
+static const uint64_t decimal_capacity_table[] = {
+	/* TB */
+	DECIMAL_CAPACITY_MULTIPLIER * DECIMAL_CAPACITY_MULTIPLIER
+		* DECIMAL_CAPACITY_MULTIPLIER * DECIMAL_CAPACITY_MULTIPLIER,
+	/* GB */
+	DECIMAL_CAPACITY_MULTIPLIER * DECIMAL_CAPACITY_MULTIPLIER
+		* DECIMAL_CAPACITY_MULTIPLIER,
+	/* MB */
+	DECIMAL_CAPACITY_MULTIPLIER * DECIMAL_CAPACITY_MULTIPLIER,
+	/* KB */
+	DECIMAL_CAPACITY_MULTIPLIER,
+	/* B */
+	1
+};
+
+static const uint64_t hex_capacity_table[] = {
+	/* TiB */
+	HEX_CAPACITY_MULTIPLIER * HEX_CAPACITY_MULTIPLIER
+		* HEX_CAPACITY_MULTIPLIER * HEX_CAPACITY_MULTIPLIER,
+	/* GiB */
+	HEX_CAPACITY_MULTIPLIER * HEX_CAPACITY_MULTIPLIER
+		* HEX_CAPACITY_MULTIPLIER,
+	/* MiB */
+	HEX_CAPACITY_MULTIPLIER * HEX_CAPACITY_MULTIPLIER,
+	/* KiB */
+	HEX_CAPACITY_MULTIPLIER,
+	/* B */
+	1
+};
+
+static void display_capacity(struct storage_media *media, int partition_number)
+{
+	uint64_t capacity;
+	uint64_t decimal_divisor;
+	const char *decimal_units;
+	uint64_t hex_divisor;
+	const char *hex_units;
+	int index;
+	const char *name;
+	const char *separator;
+
+	/* Get the partition name */
+	capacity = media->capacity[partition_number];
+	name = storage_partition_name(media, partition_number);
+	separator = "";
+	if (IS_ENABLED(CONFIG_DRIVERS_STORAGE_MMC) && !IS_SD(media))
+		separator = ": ";
+
+	/* Determine the decimal divisor for the capacity */
+	ASSERT(ARRAY_SIZE(decimal_capacity_table)
+		== ARRAY_SIZE(decimal_unit_name));
+	for (index = 0; index < ARRAY_SIZE(decimal_capacity_table); index++) {
+		if (capacity >= decimal_capacity_table[index])
+			break;
+	}
+	decimal_divisor = decimal_capacity_table[index];
+	decimal_units = decimal_unit_name[index];
+
+	/* Determine the hex divisor for the capacity */
+	ASSERT(ARRAY_SIZE(hex_capacity_table) == ARRAY_SIZE(hex_unit_name));
+	for (index = 0; index < ARRAY_SIZE(hex_capacity_table); index++) {
+		if (capacity >= hex_capacity_table[index])
+			break;
+	}
+	hex_divisor = hex_capacity_table[index];
+	hex_units = hex_unit_name[index];
+
+	/* Display the capacity */
+	sdhc_debug("%3lld.%03lld %sytes (%3lld.%03lld %sytes)%s%s\n",
+		capacity / decimal_divisor,
+		(capacity / (decimal_divisor / 1000)) % 1000,
+		decimal_units,
+		capacity / hex_divisor,
+		((capacity / (hex_divisor / 1024)) * 1000 / 1024) % 1000,
+		hex_units,
+		separator,
+		name);
+}
+
+int storage_startup(struct storage_media *media)
+{
+sd_mmc_trace("storage_startup\n");
+	int err;
+	uint64_t capacity;
+	uint64_t cmult, csize;
+	struct mmc_command cmd;
+	int partition_number;
+
+	/* Determine the storage capacity */
+	if (media->high_capacity) {
+		cmult = 8;
+		csize = sd_mmc_extract_uint32_bits(media->csd, 58, 22);
+	} else {
+		csize = sd_mmc_extract_uint32_bits(media->csd, 54, 12);
+		cmult = sd_mmc_extract_uint32_bits(media->csd, 78, 3);
+	}
+	capacity = (csize + 1) << (cmult + 2);
+	capacity *= media->read_bl_len;
+	media->capacity[0] = capacity;
+
+	/* Limit the block size to 512 bytes */
+	if (media->read_bl_len > 512)
+		media->read_bl_len = 512;
+	if (media->write_bl_len > 512)
+		media->write_bl_len = 512;
+
+	/* Get the erase size in blocks */
+	media->erase_blocks =
+		(sd_mmc_extract_uint32_bits(media->csd, 47, 3) + 1)
+		* (sd_mmc_extract_uint32_bits(media->csd, 42, 5) + 1);
+
+	/* Select the card, and put it into Transfer Mode */
+	cmd.cmdidx = MMC_CMD_SELECT_CARD;
+	cmd.resp_type = CARD_RSP_R1;
+	cmd.cmdarg = media->rca << 16;
+	cmd.flags = 0;
+	err = mmc_send_cmd(media->ctrlr, &cmd, NULL);
+	if (err)
+		return err;
+
+	/* Increase the bus frequency */
+	if (IS_ENABLED(CONFIG_DRIVERS_STORAGE_SD) && IS_SD(media))
+		err = sd_change_freq(media);
+	else if (IS_ENABLED(CONFIG_DRIVERS_STORAGE_MMC)) {
+		err = mmc_change_freq(media);
+		if (!err)
+			mmc_update_capacity(media);
+	}
+	if (err)
+		return err;
+
+	/* Restrict card's capabilities by what the controller can do */
+	media->caps &= media->ctrlr->caps;
+
+	/* Increase the bus width if possible */
+	if (IS_ENABLED(CONFIG_DRIVERS_STORAGE_SD) && IS_SD(media))
+		err = sd_set_bus_width(media);
+	else if (IS_ENABLED(CONFIG_DRIVERS_STORAGE_MMC))
+		err = mmc_set_bus_width(media);
+	if (err)
+		return err;
+
+	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);
+
+	/* Display the partition capacities */
+	if (IS_ENABLED(CONFIG_SDHC_DEBUG)) {
+		for (partition_number = 0; partition_number
+			< ARRAY_SIZE(media->capacity); partition_number++) {
+			if (!media->capacity[partition_number])
+				continue;
+			display_capacity(media, partition_number);
+		}
+	}
+	return 0;
+}
+
+int storage_setup_media(struct storage_media *media, struct sd_mmc_ctrlr *ctrlr)
+{
+sd_mmc_trace("storage_setup_media\n");
+	int err;
+
+	memset(media, 0, sizeof(*media));
+	media->ctrlr = ctrlr;
+
+	err = sd_mmc_enter_standby(media);
+	if (err)
+		return err;
+	return storage_startup(media);
+}
+
+static int storage_read(struct storage_media *media, void *dest, uint32_t start,
+	uint32_t block_count)
+{
+sd_mmc_trace("mmc_read\n");
+
+	struct mmc_command cmd;
+	cmd.resp_type = CARD_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;
+
+	struct mmc_data data;
+	data.dest = dest;
+	data.blocks = block_count;
+	data.blocksize = media->read_bl_len;
+	data.flags = DATA_FLAG_READ;
+
+	if (mmc_send_cmd(media->ctrlr, &cmd, &data))
+		return 0;
+
+	if ((block_count > 1) && !(media->ctrlr->caps
+		& DRVR_CAP_AUTO_CMD12)) {
+		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+		cmd.cmdarg = 0;
+		cmd.resp_type = CARD_RSP_R1b;
+		cmd.flags = CMD_FLAG_IGNORE_INHIBIT;
+		if (mmc_send_cmd(media->ctrlr, &cmd, NULL)) {
+			sd_mmc_error("Failed to send stop cmd\n");
+			return 0;
+		}
+
+		/* Waiting for the ready status */
+		sd_mmc_send_status(media, SD_MMC_IO_RETRIES);
+	}
+
+	return block_count;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BlockDevice utilities and callbacks
+
+int storage_block_setup(struct storage_media *media, uint64_t start,
+	uint64_t count, int is_read)
+{
+sd_mmc_trace("block_mmc_setup\n");
+	struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
+	int partition_number;
+
+	if (count == 0)
+		return 0;
+
+	uint32_t bl_len = is_read ? media->read_bl_len :
+		media->write_bl_len;
+
+	/* Validate the block range */
+	partition_number = media->partition_config & EXT_CSD_PART_ACCESS_MASK;
+	if (((start * bl_len) > media->capacity[partition_number])
+		|| (((start + count) * bl_len) >
+		media->capacity[partition_number])) {
+		sd_mmc_error("Block range exceeds device capacity\n");
+		return 0;
+	}
+
+	/*
+	 * CMD16 only applies to single data rate mode, and block
+	 * length for double data rate is always 512 bytes.
+	 */
+	if ((ctrlr->timing == BUS_TIMING_UHS_DDR50) ||
+	    (ctrlr->timing == BUS_TIMING_MMC_DDR52) ||
+	    (ctrlr->timing == BUS_TIMING_MMC_HS400) ||
+	    (ctrlr->timing == BUS_TIMING_MMC_HS400ES))
+		return 1;
+	if (sd_mmc_set_blocklen(ctrlr, bl_len))
+		return 0;
+
+	return 1;
+}
+
+uint64_t storage_block_read(struct storage_media *media, uint64_t start,
+	uint64_t count, void *buffer)
+{
+sd_mmc_trace("block_mmc_read\n");
+	uint8_t *dest = (uint8_t *)buffer;
+
+	if (storage_block_setup(media, start, count, 1) == 0)
+		return 0;
+
+	uint64_t todo = count;
+	struct sd_mmc_ctrlr *ctrlr = media->ctrlr;;
+	do {
+		uint32_t cur = (uint32_t)MIN(todo, ctrlr->b_max);
+		if (storage_read(media, dest, start, cur) != cur)
+			return 0;
+		todo -= cur;
+		sd_mmc_debug("%s: Got %d blocks, more %d (total %d) to go.\n",
+			  __func__, (int)cur, (int)todo, (int)count);
+		start += cur;
+		dest += cur * media->read_bl_len;
+	} while (todo > 0);
+	return count;
+}
+
+int storage_set_partition(struct storage_media *media,
+	unsigned int partition_number)
+{
+	int err;
+
+	/* Select the partition */
+	if (IS_ENABLED(CONFIG_DRIVERS_STORAGE_SD) && IS_SD(media))
+		err = sd_set_partition(media, partition_number);
+	else if (IS_ENABLED(CONFIG_DRIVERS_STORAGE_MMC))
+		err = mmc_set_partition(media, partition_number);
+	if (err)
+		sd_mmc_error("Invalid partition number!\n");
+	return err;
+}
+
+const char *storage_partition_name(struct storage_media *media,
+	unsigned int partition_number)
+{
+	const char *name;
+
+	/* Get the partition name */
+	name = NULL;
+	if (IS_ENABLED(CONFIG_DRIVERS_STORAGE_SD) && IS_SD(media))
+		name = sd_partition_name(media, partition_number);
+	else if (IS_ENABLED(CONFIG_DRIVERS_STORAGE_MMC))
+		name = mmc_partition_name(media, partition_number);
+	return name;
+}
+
+unsigned int storage_get_current_partition(struct storage_media *media)
+{
+	return media->partition_config & EXT_CSD_PART_ACCESS_MASK;
+}
diff --git a/src/drivers/storage/storage.h b/src/drivers/storage/storage.h
new file mode 100644
index 0000000..0bd669e
--- /dev/null
+++ b/src/drivers/storage/storage.h
@@ -0,0 +1,37 @@
+/*
+ * Copyrigit 2017 Intel Corporation
+ *
+ * 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_STORAGE_H__
+#define __DRIVERS_STORAGE_STORAGE_H__
+
+#include <stdint.h>
+#include <device/storage.h>
+
+#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)
+
+/* Storage support routines */
+int storage_startup(struct storage_media *media);
+int storage_block_setup(struct storage_media *media, uint64_t start,
+	uint64_t count, int is_read);
+
+#endif /* __DRIVERS_STORAGE_STORAGE_H__ */
diff --git a/src/drivers/storage/storage_erase.c b/src/drivers/storage/storage_erase.c
new file mode 100644
index 0000000..40f7a73
--- /dev/null
+++ b/src/drivers/storage/storage_erase.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc.  All rights reserved.
+ * Copyrigit 2017 Intel Corporation
+ *
+ * MultiMediaCard (MMC), eMMC and Secure Digital (SD) erase support code.
+ * This code is controller independent.
+ *
+ * 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 <console/console.h>
+#include "sd_mmc.h"
+#include "storage.h"
+
+uint64_t storage_block_erase(struct storage_media *media, uint64_t start,
+	uint64_t count)
+{
+sd_mmc_trace("block_mmc_erase\n");
+	struct mmc_command cmd;
+
+	if (storage_block_setup(media, start, count, 0) == 0)
+		return 0;
+
+	struct sd_mmc_ctrlr *ctrlr = media->ctrlr;;
+
+	cmd.cmdidx = MMC_CMD_ERASE_GROUP_START;
+	cmd.resp_type = CARD_RSP_R1;
+	cmd.cmdarg = start;
+	cmd.flags = 0;
+
+	if (mmc_send_cmd(ctrlr, &cmd, NULL))
+		return 0;
+
+	cmd.cmdidx = MMC_CMD_ERASE_GROUP_END;
+	cmd.cmdarg = start + count - 1;
+	cmd.resp_type = CARD_RSP_R1;
+	cmd.flags = 0;
+
+	if (mmc_send_cmd(ctrlr, &cmd, NULL))
+		return 0;
+
+	cmd.cmdidx = MMC_CMD_ERASE;
+	cmd.cmdarg = MMC_TRIM_ARG;	/* just unmap blocks */
+	cmd.resp_type = CARD_RSP_R1;
+	cmd.flags = 0;
+
+	if (mmc_send_cmd(ctrlr, &cmd, NULL))
+		return 0;
+
+	size_t erase_blocks;
+	/*
+	 * Timeout for TRIM operation on one erase group is defined as:
+	 * TRIM timeout = 300ms x TRIM_MULT
+	 *
+	 * This timeout is expressed in units of 100us to sd_mmc_send_status.
+	 *
+	 * Hence, timeout_per_erase_block = TRIM timeout * 1000us/100us;
+	 */
+	size_t timeout_per_erase_block = (media->trim_mult * 300) * 10;
+	int err = 0;
+
+	erase_blocks = ALIGN_UP(count, media->erase_blocks)
+		/ media->erase_blocks;
+
+	while (erase_blocks) {
+		/*
+		 * To avoid overflow of timeout value, loop in calls to
+		 * sd_mmc_send_status for erase_blocks number of times.
+		 */
+		err = sd_mmc_send_status(media, timeout_per_erase_block);
+
+		/* Send status successful, erase action complete. */
+		if (err == 0)
+			break;
+
+		erase_blocks--;
+	}
+
+	/* Total timeout done. Still status not successful. */
+	if (err) {
+		sd_mmc_error("TRIM operation not successful within timeout.\n");
+		return 0;
+	}
+
+	return count;
+}
diff --git a/src/drivers/storage/storage_write.c b/src/drivers/storage/storage_write.c
new file mode 100644
index 0000000..c2dfe42
--- /dev/null
+++ b/src/drivers/storage/storage_write.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc.  All rights reserved.
+ * Copyrigit 2017 Intel Corporation
+ *
+ * MultiMediaCard (MMC), eMMC and Secure Digital (SD) write support code.
+ * This code is controller independent.
+ *
+ * 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 <console/console.h>
+#include "sd_mmc.h"
+#include "storage.h"
+#include <string.h>
+
+static uint32_t storage_write(struct storage_media *media, uint32_t start,
+	uint64_t block_count, const void *src)
+{
+sd_mmc_trace("mmc_write\n");
+	struct mmc_command cmd;
+	cmd.resp_type = CARD_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;
+
+	struct mmc_data data;
+	data.src = src;
+	data.blocks = block_count;
+	data.blocksize = media->write_bl_len;
+	data.flags = DATA_FLAG_WRITE;
+
+	if (mmc_send_cmd(media->ctrlr, &cmd, &data)) {
+		sd_mmc_error("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
+		& DRVR_CAP_AUTO_CMD12)) {
+		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+		cmd.cmdarg = 0;
+		cmd.resp_type = CARD_RSP_R1b;
+		cmd.flags = CMD_FLAG_IGNORE_INHIBIT;
+		if (mmc_send_cmd(media->ctrlr, &cmd, NULL)) {
+			sd_mmc_error("Failed to send stop cmd\n");
+			return 0;
+		}
+
+		/* Waiting for the ready status */
+		sd_mmc_send_status(media, SD_MMC_IO_RETRIES);
+	}
+
+	return block_count;
+}
+
+uint64_t storage_block_write(struct storage_media *media, uint64_t start,
+	uint64_t count, const void *buffer)
+{
+sd_mmc_trace("block_mmc_write\n");
+	const uint8_t *src = (const uint8_t *)buffer;
+
+	if (storage_block_setup(media, start, count, 0) == 0)
+		return 0;
+
+	uint64_t todo = count;
+	struct sd_mmc_ctrlr *ctrlr = media->ctrlr;;
+	do {
+		uint64_t cur = MIN(todo, ctrlr->b_max);
+		if (storage_write(media, start, cur, src) != cur)
+			return 0;
+		todo -= cur;
+		start += cur;
+		src += cur * media->write_bl_len;
+	} while (todo > 0);
+	return count;
+}
+
+uint64_t storage_block_fill_write(struct storage_media *media, uint64_t start,
+	uint64_t count, uint32_t fill_pattern)
+{
+sd_mmc_trace("block_mmc_fill_write\n");
+	if (storage_block_setup(media, start, count, 0) == 0)
+		return 0;
+
+	struct sd_mmc_ctrlr *ctrlr = media->ctrlr;;
+	uint64_t block_size = media->write_bl_len;
+	/*
+	 * We allocate max 4 MiB buffer on heap and set it to fill_pattern and
+	 * perform mmc_write operation using this 4MiB buffer until requested
+	 * size on disk is written by the fill byte.
+	 *
+	 * 4MiB was chosen after repeating several experiments with the max
+	 * buffer size to be used. Using 1 lba i.e. block_size buffer results in
+	 * very large fill_write time. On the other hand, choosing 4MiB, 8MiB or
+	 * even 128 Mib resulted in similar write times. With 2MiB, the
+	 * fill_write time increased by several seconds. So, 4MiB was chosen as
+	 * the default max buffer size.
+	 */
+	uint64_t heap_lba = (4 * MiB) / block_size;
+	/*
+	 * Actual allocated buffer size is minimum of three entities:
+	 * 1) 4MiB equivalent in lba
+	 * 2) count: Number of lbas to overwrite
+	 * 3) ctrlr->b_max: Max lbas that the block device allows write
+	 * operation on at a time.
+	 */
+	uint64_t buffer_lba = MIN(MIN(heap_lba, count), ctrlr->b_max);
+
+	uint64_t buffer_bytes = buffer_lba * block_size;
+	uint64_t buffer_words = buffer_bytes / sizeof(uint32_t);
+	uint32_t *buffer = malloc(buffer_bytes);
+	uint32_t *ptr = buffer;
+
+	for ( ; buffer_words ; buffer_words--)
+		*ptr++ = fill_pattern;
+
+	uint64_t todo = count;
+	int ret = 0;
+
+	do {
+		uint64_t curr_lba = MIN(buffer_lba, todo);
+
+		if (storage_write(media, start, curr_lba, buffer) != curr_lba)
+			goto cleanup;
+		todo -= curr_lba;
+		start += curr_lba;
+	} while (todo > 0);
+
+	ret = count;
+
+cleanup:
+	free(buffer);
+	return ret;
+}
diff --git a/src/include/device/sd_mmc_ctrlr.h b/src/include/device/sd_mmc_ctrlr.h
new file mode 100644
index 0000000..923e519
--- /dev/null
+++ b/src/include/device/sd_mmc_ctrlr.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen at marvell.com>
+ *
+ * Copyrigit 2017 Intel Corporation
+ *
+ * Controller independent definitions
+ *
+ * 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 __DEVICE_SD_MMC_CTRLR_H__
+#define __DEVICE_SD_MMC_CTRLR_H__
+
+#include <stdint.h>
+
+/* Error values returned by the storage drivers */
+#define CARD_UNUSABLE_ERR	-17 /* Unusable Card */
+#define CARD_COMM_ERR		-18 /* Communications Error */
+#define CARD_TIMEOUT		-19
+#define CARD_IN_PROGRESS	-20 /* operation is in progress */
+
+struct mmc_command {
+	uint16_t cmdidx;
+
+/* Common commands */
+#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_DSR			4
+#define MMC_CMD_SELECT_CARD		7
+#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_APP_CMD			55
+
+/* MMC specific commands */
+#define MMC_CMD_SET_RELATIVE_ADDR	3
+#define MMC_CMD_SWITCH			6
+#define MMC_CMD_SEND_EXT_CSD		8
+#define MMC_CMD_ERASE_GROUP_START	35
+#define MMC_CMD_ERASE_GROUP_END		36
+#define MMC_CMD_ERASE			38
+#define MMC_CMD_SPI_READ_OCR		58
+#define MMC_CMD_SPI_CRC_ON_OFF		59
+
+/* SD specific commands */
+#define SD_CMD_SEND_RELATIVE_ADDR	3
+#define SD_CMD_SWITCH_FUNC		6
+#define SD_CMD_SEND_IF_COND		8
+#define SD_CMD_ERASE_WR_BLK_START	32
+#define SD_CMD_ERASE_WR_BLK_END		33
+
+/* SD specific APP commands */
+#define SD_CMD_APP_SET_BUS_WIDTH	6
+#define SD_CMD_APP_SEND_OP_COND		41
+#define SD_CMD_APP_SEND_SCR		51
+
+	uint32_t resp_type;
+
+#define CARD_RSP_PRESENT (1 << 0)
+#define CARD_RSP_136	(1 << 1)		/* 136 bit response */
+#define CARD_RSP_CRC	(1 << 2)		/* expect valid crc */
+#define CARD_RSP_BUSY	(1 << 3)		/* card may send busy */
+#define CARD_RSP_OPCODE	(1 << 4)		/* response contains opcode */
+
+#define CARD_RSP_NONE	(0)
+#define CARD_RSP_R1	(CARD_RSP_PRESENT|CARD_RSP_CRC|CARD_RSP_OPCODE)
+#define CARD_RSP_R1b	(CARD_RSP_PRESENT|CARD_RSP_CRC|CARD_RSP_OPCODE| \
+			CARD_RSP_BUSY)
+#define CARD_RSP_R2	(CARD_RSP_PRESENT|CARD_RSP_136|CARD_RSP_CRC)
+#define CARD_RSP_R3	(CARD_RSP_PRESENT)
+#define CARD_RSP_R4	(CARD_RSP_PRESENT)
+#define CARD_RSP_R5	(CARD_RSP_PRESENT|CARD_RSP_CRC|CARD_RSP_OPCODE)
+#define CARD_RSP_R6	(CARD_RSP_PRESENT|CARD_RSP_CRC|CARD_RSP_OPCODE)
+#define CARD_RSP_R7	(CARD_RSP_PRESENT|CARD_RSP_CRC|CARD_RSP_OPCODE)
+
+	uint32_t cmdarg;
+
+#define MMC_TRIM_ARG			0x1
+#define MMC_SECURE_ERASE_ARG		0x80000000
+
+	uint32_t response[4];
+	uint32_t flags;
+
+#define CMD_FLAG_IGNORE_INHIBIT	1
+};
+
+#define SD_SWITCH_CHECK		0
+#define SD_SWITCH_SWITCH	1
+
+#define SD_DATA_4BIT		0x00040000
+
+/* SCR definitions in different words */
+#define SD_HIGHSPEED_BUSY	0x00020000
+#define SD_HIGHSPEED_SUPPORTED	0x00020000
+
+struct mmc_data {
+	union {
+		char *dest;
+		const char *src;
+	};
+	uint32_t flags;
+
+#define DATA_FLAG_READ		1
+#define DATA_FLAG_WRITE		2
+
+	uint32_t blocks;
+	uint32_t blocksize;
+};
+
+struct sd_mmc_ctrlr {
+	int (*send_cmd)(struct sd_mmc_ctrlr *sdhc_ctrlr,
+		struct mmc_command *cmd, struct mmc_data *data);
+	void (*set_ios)(struct sd_mmc_ctrlr *sdhc_ctrlr);
+	void (*set_control_reg)(struct sd_mmc_ctrlr *sdhc_ctrlr);
+	int initialized;
+	unsigned int version;
+	uint32_t voltages;
+
+#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
+
+	uint32_t clock_base;	/* Controller's base clock */
+	uint32_t f_min;
+	uint32_t f_max;
+	uint32_t request_hz;	/* Desired clock frequency */
+	uint32_t bus_hz;	/* Actual bus clock frequency */
+
+#define CLOCK_KHZ		1000
+#define CLOCK_MHZ		(1000 * CLOCK_KHZ)
+#define CLOCK_20MHZ		(20 * CLOCK_MHZ)
+#define CLOCK_25MHZ		(25 * CLOCK_MHZ)
+#define CLOCK_26MHZ		(26 * CLOCK_MHZ)
+#define CLOCK_50MHZ		(50 * CLOCK_MHZ)
+#define CLOCK_52MHZ		(52 * CLOCK_MHZ)
+#define CLOCK_200MHZ		(200 * CLOCK_MHZ)
+
+	uint32_t bus_width;
+	uint32_t caps;
+
+/* Generic controller & driver capabilities.  Controller specific capabilities
+ * start at 0x00010000
+ */
+#define DRVR_CAP_4BIT				0x00000001
+#define DRVR_CAP_AUTO_CMD12			0x00000002
+#define DRVR_CAP_HC				0x00000004
+#define DRVR_CAP_HS				0x00000008
+#define DRVR_CAP_HS_52MHz			0x00000010
+#define DRVR_CAP_HS_200MHz			0x00000020
+#define DRVR_CAP_HS400ES			0x00000040
+#define DRVR_CAP_REMOVABLE			0x00000080
+
+	uint32_t b_max;
+	uint32_t timing;
+
+#define BUS_TIMING_LEGACY	0
+#define BUS_TIMING_MMC_HS	1
+#define BUS_TIMING_SD_HS	2
+#define BUS_TIMING_UHS_SDR12	3
+#define BUS_TIMING_UHS_SDR25	4
+#define BUS_TIMING_UHS_SDR50	5
+#define BUS_TIMING_UHS_SDR104	6
+#define BUS_TIMING_UHS_DDR50	7
+#define BUS_TIMING_MMC_DDR52	8
+#define BUS_TIMING_MMC_HS200	9
+#define BUS_TIMING_MMC_HS400	10
+#define BUS_TIMING_MMC_HS400ES	11
+};
+
+/* SOC specific routine to override ctrlr->caps and .voltages
+ *
+ * Set/clear the necessary DRVR_CAP_xxx bits in ctrlr->caps to specify the
+ * controllers capabilities and driver workarounds.
+ *
+ * Set/clear the necessary MMC_VDD_xxx bits in ctrlr->voltages to specify the
+ * controllers power support.
+ */
+void soc_sd_mmc_controller_quirks(struct sd_mmc_ctrlr *ctrlr);
+
+#endif /* __DEVICE_SD_MMC_CTRLR_H__ */
diff --git a/src/include/device/sdhci.h b/src/include/device/sdhci.h
new file mode 100644
index 0000000..5e8509e
--- /dev/null
+++ b/src/include/device/sdhci.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen at marvell.com>
+ *
+ * Copyrigit 2017 Intel Corporation
+ *
+ * SD host controller specific definitions
+ *
+ * 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 __DEVICE_SDHCI_H__
+#define __DEVICE_SDHCI_H__
+
+#include <device/sd_mmc_ctrlr.h>
+
+/* Driver specific capabilities */
+#define DRVR_CAP_1V8_VDD			0x00010000
+#define DRVR_CAP_32BIT_DMA_ADDR			0x00020000
+#define DRVR_CAP_8BIT				0x00040000
+#define DRVR_CAP_BROKEN_R1B			0x00080000
+#define DRVR_CAP_HS400				0x00100000
+#define DRVR_CAP_NO_CD				0x00200000
+#define DRVR_CAP_NO_HISPD_BIT			0x00400000
+#define DRVR_CAP_NO_SIMULT_VDD_AND_POWER	0x00800000
+#define DRVR_CAP_REG32_RW			0x01000000
+#define DRVR_CAP_SPI				0x02000000
+#define DRVR_CAP_WAIT_SEND_CMD			0x04000000
+
+/* ADMA packet descriptor */
+struct sdhci_adma{
+	u16     attributes;
+	u16     length;
+	u32     addr;
+};
+
+struct sdhci_adma64 {
+	u16     attributes;
+	u16     length;
+	u32     addr;
+	u32     addr_hi;
+};
+
+struct sdhci_ctrlr {
+	struct sd_mmc_ctrlr sd_mmc_ctrlr;
+	void *ioaddr;
+	uint32_t b_max;
+
+	/*
+	 * Dynamically allocated array of ADMA descriptors to use for data
+	 * transfers
+	 */
+	struct sdhci_adma *adma_descs;
+	struct sdhci_adma64 *adma64_descs;
+	/* select 32bit or 64bit ADMA operations */
+	unsigned int dma64;
+
+	/* Number of ADMA descriptors currently in the array. */
+	int adma_desc_count;
+};
+
+int add_sdhci(struct sdhci_ctrlr *sdhci_ctrlr);
+int sdhci_controller_init(struct sdhci_ctrlr *sdhci_ctrlr, void *ioaddr);
+
+/* Add SDHCI controller from PCI */
+struct sd_mmc_ctrlr *new_pci_sdhci_controller(uint32_t dev);
+
+/* Add SDHCI controller with memory address */
+struct sd_mmc_ctrlr *new_mem_sdhci_controller(void *ioaddr);
+
+#endif /* __DEVICE_SDHCI_H__ */
diff --git a/src/include/device/storage.h b/src/include/device/storage.h
new file mode 100644
index 0000000..3cf118f
--- /dev/null
+++ b/src/include/device/storage.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2008,2010 Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc.  All rights reserved.
+ * Copyrigit 2017 Intel Corporation
+ *
+ * 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 __DEVICE_STORAGE_H__
+#define __DEVICE_STORAGE_H__
+
+#include <device/sd_mmc_ctrlr.h>
+
+/*
+ * EXT_CSD fields
+ */
+#define EXT_CSD_GP_SIZE_MULT_GP0	143	/* RO */
+#define EXT_CSD_GP_SIZE_MULT_GP1	146	/* RO */
+#define EXT_CSD_GP_SIZE_MULT_GP2	149	/* RO */
+#define EXT_CSD_GP_SIZE_MULT_GP3	152	/* RO */
+#define EXT_CSD_PARTITIONING_SUPPORT	160	/* RO */
+#define EXT_CSD_RPMB_SIZE_MULT		168	/* 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_WP_GRP_SIZE		221	/* RO */
+#define EXT_CSD_HC_ERASE_GRP_SIZE	224	/* RO */
+#define EXT_CSD_BOOT_SIZE_MULT		226	/* 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 EXT_CSD_SIZE			512
+
+/* 179: EXT_CSD_PART_CONF */
+#define EXT_CSD_PART_ACCESS_MASK	7	/* Partition access mask */
+
+struct storage_media {
+	uint64_t capacity[8];		/* Partition capacity in bytes */
+	struct sd_mmc_ctrlr *ctrlr;
+
+#define MMC_PARTITION_USER		0
+#define MMC_PARTITION_BOOT_1		1
+#define MMC_PARTITION_BOOT_2		2
+#define MMC_PARTITION_RPMB		3
+#define MMC_PARTITION_GP1		4
+#define MMC_PARTITION_GP2		5
+#define MMC_PARTITION_GP3		6
+#define MMC_PARTITION_GP4		7
+
+	uint32_t caps;
+	uint32_t version;
+
+#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)
+
+	uint32_t read_bl_len;
+	uint32_t write_bl_len;
+	int high_capacity;
+	uint32_t tran_speed;
+	/* Erase size in terms of block length. */
+	uint32_t erase_blocks;
+	/* Trim operation multiplier for determining timeout. */
+	uint32_t trim_mult;
+
+	uint32_t ocr;
+
+#define OCR_BUSY		0x80000000
+#define OCR_HCS			0x40000000
+#define OCR_VOLTAGE_MASK	0x00FFFF80
+#define OCR_ACCESS_MODE		0x60000000
+
+	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
+
+	uint8_t partition_config;	/* Duplicate of EXT_CSD_PART_CONF */
+};
+
+uint64_t storage_block_erase(struct storage_media *media, uint64_t start,
+	uint64_t count);
+uint64_t storage_block_fill_write(struct storage_media *media, uint64_t start,
+	uint64_t count, uint32_t fill_pattern);
+uint64_t storage_block_read(struct storage_media *media, uint64_t start,
+	uint64_t count, void *buffer);
+uint64_t storage_block_write(struct storage_media *media, uint64_t start,
+	uint64_t count, const void *buffer);
+
+unsigned int storage_get_current_partition(struct storage_media *media);
+const char *storage_partition_name(struct storage_media *media,
+	unsigned int partition_number);
+int storage_setup_media(struct storage_media *media,
+	struct sd_mmc_ctrlr *ctrlr);
+
+int storage_set_partition(struct storage_media *media,
+	unsigned int partition_number);
+
+#endif /* __DEVICE_STORAGE_H__ */

-- 
To view, visit https://review.coreboot.org/19208
To unsubscribe, visit https://review.coreboot.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I9b5f9db1e27833e4ce4a97ad4f5ef3a46f64f2a2
Gerrit-PatchSet: 1
Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Owner: Lee Leahy <leroy.p.leahy at intel.com>



More information about the coreboot-gerrit mailing list