Merge pull request #1480 from sandrine-bailleux-arm/topics/sb/debug-macros

Always compile debug macros
diff --git a/docs/plat/rpi3.rst b/docs/plat/rpi3.rst
index 5ac9085..0ba564d 100644
--- a/docs/plat/rpi3.rst
+++ b/docs/plat/rpi3.rst
@@ -196,29 +196,19 @@
 
     CROSS_COMPILE=aarch64-linux-gnu- make PLAT=rpi3             \
     RPI3_BL33_IN_AARCH32=1                                      \
-    BL33=../rpi3-arm-tf-bootstrap/aarch32/el2-bootstrap.bin     \
-    all fip
+    BL33=../rpi3-arm-tf-bootstrap/aarch32/el2-bootstrap.bin
 
 For a AArch64 kernel, use this other command line:
 
 .. code:: shell
 
     CROSS_COMPILE=aarch64-linux-gnu- make PLAT=rpi3             \
-    BL33=../rpi3-arm-tf-bootstrap/aarch64/el2-bootstrap.bin     \
-    all fip
-
-Then, join BL1 and the FIP with the following instructions (replace ``release``
-by ``debug`` if you set the build option ``DEBUG=1``):
-
-.. code:: shell
-
-    cp build/rpi3/release/bl1.bin bl1.pad.bin
-    truncate --size=131072 bl1.pad.bin
-    cat bl1.pad.bin build/rpi3/release/fip.bin > armstub8.bin
+    BL33=../rpi3-arm-tf-bootstrap/aarch64/el2-bootstrap.bin
 
-The resulting file, ``armstub8.bin``, contains BL1 and the FIP in the place they
-need to be for TF-A to boot correctly. Now, follow the instructions in
-`Setup SD card`_.
+The build system concatenates BL1 and the FIP so that the addresses match the
+ones in the memory map. The resulting file is ``armstub8.bin``, located in the
+build folder (e.g. ``build/rpi3/debug/armstub8.bin``). Now, follow the
+instructions in `Setup SD card`_.
 
 The following build options are supported:
 
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
new file mode 100644
index 0000000..8fe3239
--- /dev/null
+++ b/drivers/mmc/mmc.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/* Define a simple and generic interface to access eMMC and SD-card devices. */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <mmc.h>
+#include <stdbool.h>
+#include <string.h>
+#include <utils.h>
+
+#define MMC_DEFAULT_MAX_RETRIES		5
+#define SEND_OP_COND_MAX_RETRIES	100
+
+#define MULT_BY_512K_SHIFT		19
+
+static const struct mmc_ops *ops;
+static unsigned int mmc_ocr_value;
+static struct mmc_csd_emmc mmc_csd;
+static unsigned char mmc_ext_csd[512] __aligned(4);
+static unsigned int mmc_flags;
+static struct mmc_device_info *mmc_dev_info;
+static unsigned int rca;
+
+static const unsigned char tran_speed_base[16] = {
+	0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80
+};
+
+static const unsigned char sd_tran_speed_base[16] = {
+	0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
+};
+
+static bool is_cmd23_enabled(void)
+{
+	return ((mmc_flags & MMC_FLAG_CMD23) != 0U);
+}
+
+static int mmc_send_cmd(unsigned int idx, unsigned int arg,
+			unsigned int r_type, unsigned int *r_data)
+{
+	struct mmc_cmd cmd;
+	int ret;
+
+	zeromem(&cmd, sizeof(struct mmc_cmd));
+
+	cmd.cmd_idx = idx;
+	cmd.cmd_arg = arg;
+	cmd.resp_type = r_type;
+
+	ret = ops->send_cmd(&cmd);
+
+	if ((ret == 0) && (r_data != NULL)) {
+		int i;
+
+		for (i = 0; i < 4; i++) {
+			*r_data = cmd.resp_data[i];
+			r_data++;
+		}
+	}
+
+	if (ret != 0) {
+		VERBOSE("Send command %u error: %d\n", idx, ret);
+	}
+
+	return ret;
+}
+
+static int mmc_device_state(void)
+{
+	int retries = MMC_DEFAULT_MAX_RETRIES;
+	unsigned int resp_data[4];
+
+	do {
+		int ret;
+
+		if (retries == 0) {
+			ERROR("CMD13 failed after %d retries\n",
+			      MMC_DEFAULT_MAX_RETRIES);
+			return -EIO;
+		}
+
+		ret = mmc_send_cmd(MMC_CMD(13), rca << RCA_SHIFT_OFFSET,
+				   MMC_RESPONSE_R(1), &resp_data[0]);
+		if (ret != 0) {
+			return ret;
+		}
+
+		if ((resp_data[0] & STATUS_SWITCH_ERROR) != 0U) {
+			return -EIO;
+		}
+
+		retries--;
+	} while ((resp_data[0] & STATUS_READY_FOR_DATA) == 0U);
+
+	return MMC_GET_STATE(resp_data[0]);
+}
+
+static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
+{
+	int ret;
+
+	ret = mmc_send_cmd(MMC_CMD(6),
+			   EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
+			   EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL,
+			   0, NULL);
+	if (ret != 0) {
+		return ret;
+	}
+
+	do {
+		ret = mmc_device_state();
+		if (ret < 0) {
+			return ret;
+		}
+	} while (ret == MMC_STATE_PRG);
+
+	return 0;
+}
+
+static int mmc_sd_switch(unsigned int bus_width)
+{
+	int ret;
+	int retries = MMC_DEFAULT_MAX_RETRIES;
+	unsigned int scr[2] = { 0 };
+	unsigned int bus_width_arg = 0;
+
+	ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr));
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* CMD55: Application Specific Command */
+	ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
+			   MMC_RESPONSE_R(1), NULL);
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* ACMD51: SEND_SCR */
+	do {
+		ret = mmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R(1), NULL);
+		if ((ret != 0) && (retries == 0)) {
+			ERROR("ACMD51 failed after %d retries (ret=%d)\n",
+			      MMC_DEFAULT_MAX_RETRIES, ret);
+			return ret;
+		}
+
+		retries--;
+	} while (ret != 0);
+
+	ret = ops->read(0, (uintptr_t)&scr, sizeof(scr));
+	if (ret != 0) {
+		return ret;
+	}
+
+	if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) &&
+	    (bus_width == MMC_BUS_WIDTH_4)) {
+		bus_width_arg = 2;
+	}
+
+	/* CMD55: Application Specific Command */
+	ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
+			   MMC_RESPONSE_R(1), NULL);
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* ACMD6: SET_BUS_WIDTH */
+	ret = mmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R(1), NULL);
+	if (ret != 0) {
+		return ret;
+	}
+
+	do {
+		ret = mmc_device_state();
+		if (ret < 0) {
+			return ret;
+		}
+	} while (ret == MMC_STATE_PRG);
+
+	return 0;
+}
+
+static int mmc_set_ios(unsigned int clk, unsigned int bus_width)
+{
+	int ret;
+	unsigned int width = bus_width;
+
+	if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) {
+		if (width == MMC_BUS_WIDTH_8) {
+			WARN("Wrong bus config for SD-card, force to 4\n");
+			width = MMC_BUS_WIDTH_4;
+		}
+		ret = mmc_sd_switch(width);
+		if (ret != 0) {
+			return ret;
+		}
+	} else if (mmc_csd.spec_vers == 4U) {
+		ret = mmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH,
+				      (unsigned int)width);
+		if (ret != 0) {
+			return ret;
+		}
+	} else {
+		VERBOSE("Wrong MMC type or spec version\n");
+	}
+
+	return ops->set_ios(clk, width);
+}
+
+static int mmc_fill_device_info(void)
+{
+	unsigned long long c_size;
+	unsigned int speed_idx;
+	unsigned int nb_blocks;
+	unsigned int freq_unit;
+	int ret;
+	struct mmc_csd_sd_v2 *csd_sd_v2;
+
+	switch (mmc_dev_info->mmc_dev_type) {
+	case MMC_IS_EMMC:
+		mmc_dev_info->block_size = MMC_BLOCK_SIZE;
+
+		ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd,
+				   sizeof(mmc_ext_csd));
+		if (ret != 0) {
+			return ret;
+		}
+
+		/* MMC CMD8: SEND_EXT_CSD */
+		ret = mmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R(1), NULL);
+		if (ret != 0) {
+			return ret;
+		}
+
+		ret = ops->read(0, (uintptr_t)&mmc_ext_csd,
+				sizeof(mmc_ext_csd));
+		if (ret != 0) {
+			return ret;
+		}
+
+		nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) |
+			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) |
+			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) |
+			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24);
+
+		mmc_dev_info->device_size = (unsigned long long)nb_blocks *
+			mmc_dev_info->block_size;
+
+		break;
+
+	case MMC_IS_SD:
+		/*
+		 * Use the same mmc_csd struct, as required fields here
+		 * (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC.
+		 */
+		mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len);
+
+		c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) |
+			 (unsigned long long)mmc_csd.c_size_low;
+		assert(c_size != 0xFFFU);
+
+		mmc_dev_info->device_size = (c_size + 1U) *
+					    BIT_64(mmc_csd.c_size_mult + 2U) *
+					    mmc_dev_info->block_size;
+
+		break;
+
+	case MMC_IS_SD_HC:
+		assert(mmc_csd.csd_structure == 1U);
+
+		mmc_dev_info->block_size = MMC_BLOCK_SIZE;
+
+		/* Need to use mmc_csd_sd_v2 struct */
+		csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd;
+		c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) |
+			 (unsigned long long)csd_sd_v2->c_size_low;
+
+		mmc_dev_info->device_size = (c_size + 1U) << MULT_BY_512K_SHIFT;
+
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret != 0) {
+		return ret;
+	}
+
+	speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >>
+			 CSD_TRAN_SPEED_MULT_SHIFT;
+
+	assert(speed_idx > 0U);
+
+	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
+		mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx];
+	} else {
+		mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx];
+	}
+
+	freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK;
+	while (freq_unit != 0U) {
+		mmc_dev_info->max_bus_freq *= 10U;
+		--freq_unit;
+	}
+
+	mmc_dev_info->max_bus_freq *= 10000U;
+
+	return 0;
+}
+
+static int sd_send_op_cond(void)
+{
+	int retries = SEND_OP_COND_MAX_RETRIES;
+	unsigned int resp_data[4];
+
+	do {
+		int ret;
+
+		if (retries == 0) {
+			ERROR("ACMD41 failed after %d retries\n",
+			      SEND_OP_COND_MAX_RETRIES);
+			return -EIO;
+		}
+
+		/* CMD55: Application Specific Command */
+		ret = mmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R(1), NULL);
+		if (ret != 0) {
+			return ret;
+		}
+
+		/* ACMD41: SD_SEND_OP_COND */
+		ret = mmc_send_cmd(MMC_ACMD(41), OCR_HCS, MMC_RESPONSE_R(3),
+				   &resp_data[0]);
+		if (ret != 0) {
+			return ret;
+		}
+
+		retries--;
+	} while ((resp_data[0] & OCR_POWERUP) == 0U);
+
+	mmc_ocr_value = resp_data[0];
+
+	if ((mmc_ocr_value & OCR_HCS) != 0U) {
+		mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC;
+	} else {
+		mmc_dev_info->mmc_dev_type = MMC_IS_SD;
+	}
+
+	return 0;
+}
+
+static int mmc_send_op_cond(void)
+{
+	int ret;
+	int retries = SEND_OP_COND_MAX_RETRIES;
+	unsigned int resp_data[4];
+
+	/* CMD0: reset to IDLE */
+	ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL);
+	if (ret != 0) {
+		return ret;
+	}
+
+	do {
+		if (retries == 0) {
+			ERROR("CMD1 failed after %d retries\n",
+			      SEND_OP_COND_MAX_RETRIES);
+			return -EIO;
+		}
+
+		/* CMD1: get OCR register (SEND_OP_COND) */
+		ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE |
+				   OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7,
+				   MMC_RESPONSE_R(3), &resp_data[0]);
+		if (ret != 0) {
+			return ret;
+		}
+
+		retries--;
+	} while ((resp_data[0] & OCR_POWERUP) == 0U);
+
+	mmc_ocr_value = resp_data[0];
+
+	return 0;
+}
+
+static int mmc_enumerate(unsigned int clk, unsigned int bus_width)
+{
+	int ret;
+	unsigned int resp_data[4];
+
+	ops->init();
+
+	/* CMD0: reset to IDLE */
+	ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL);
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* CMD8: Send Interface Condition Command */
+	ret = mmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN,
+			   MMC_RESPONSE_R(7), &resp_data[0]);
+
+	if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) {
+		ret = sd_send_op_cond();
+	} else {
+		ret = mmc_send_op_cond();
+	}
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* CMD2: Card Identification */
+	ret = mmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R(2), NULL);
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* CMD3: Set Relative Address */
+	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
+		rca = MMC_FIX_RCA;
+		ret = mmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET,
+				   MMC_RESPONSE_R(1), NULL);
+		if (ret != 0) {
+			return ret;
+		}
+	} else {
+		ret = mmc_send_cmd(MMC_CMD(3), 0,
+				   MMC_RESPONSE_R(6), &resp_data[0]);
+		if (ret != 0) {
+			return ret;
+		}
+
+		rca = (resp_data[0] & 0xFFFF0000U) >> 16;
+	}
+
+	/* CMD9: CSD Register */
+	ret = mmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET,
+			   MMC_RESPONSE_R(2), &resp_data[0]);
+	if (ret != 0) {
+		return ret;
+	}
+
+	memcpy(&mmc_csd, &resp_data, sizeof(resp_data));
+
+	/* CMD7: Select Card */
+	ret = mmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET,
+			   MMC_RESPONSE_R(1), NULL);
+	if (ret != 0) {
+		return ret;
+	}
+
+	do {
+		ret = mmc_device_state();
+		if (ret < 0) {
+			return ret;
+		}
+	} while (ret != MMC_STATE_TRAN);
+
+	ret = mmc_fill_device_info();
+	if (ret != 0) {
+		return ret;
+	}
+
+	return mmc_set_ios(clk, bus_width);
+}
+
+size_t mmc_read_blocks(unsigned int lba, uintptr_t buf, size_t size)
+{
+	int ret;
+	unsigned int cmd_idx, cmd_arg;
+
+	assert((ops != NULL) &&
+	       (ops->read != NULL) &&
+	       (size != 0U) &&
+	       ((size & MMC_BLOCK_MASK) == 0U));
+
+	ret = ops->prepare(lba, buf, size);
+	if (ret != 0) {
+		return 0;
+	}
+
+	if (is_cmd23_enabled()) {
+		/* Set block count */
+		ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
+				   MMC_RESPONSE_R(1), NULL);
+		if (ret != 0) {
+			return 0;
+		}
+
+		cmd_idx = MMC_CMD(18);
+	} else {
+		if (size > MMC_BLOCK_SIZE) {
+			cmd_idx = MMC_CMD(18);
+		} else {
+			cmd_idx = MMC_CMD(17);
+		}
+	}
+
+	if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) &&
+	    (mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) {
+		cmd_arg = lba * MMC_BLOCK_SIZE;
+	} else {
+		cmd_arg = lba;
+	}
+
+	ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R(1), NULL);
+	if (ret != 0) {
+		return 0;
+	}
+
+	ret = ops->read(lba, buf, size);
+	if (ret != 0) {
+		return 0;
+	}
+
+	/* Wait buffer empty */
+	do {
+		ret = mmc_device_state();
+		if (ret < 0) {
+			return 0;
+		}
+	} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA));
+
+	if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
+		ret = mmc_send_cmd(MMC_CMD(12), 0, 0, NULL);
+		if (ret != 0) {
+			return 0;
+		}
+	}
+
+	return size;
+}
+
+size_t mmc_write_blocks(unsigned int lba, const uintptr_t buf, size_t size)
+{
+	int ret;
+	unsigned int cmd_idx, cmd_arg;
+
+	assert((ops != NULL) &&
+	       (ops->write != NULL) &&
+	       (size != 0U) &&
+	       ((buf & MMC_BLOCK_MASK) == 0U) &&
+	       ((size & MMC_BLOCK_MASK) == 0U));
+
+	ret = ops->prepare(lba, buf, size);
+	if (ret != 0) {
+		return 0;
+	}
+
+	if (is_cmd23_enabled()) {
+		/* Set block count */
+		ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
+				   MMC_RESPONSE_R(1), NULL);
+		if (ret != 0) {
+			return 0;
+		}
+
+		cmd_idx = MMC_CMD(25);
+	} else {
+		if (size > MMC_BLOCK_SIZE) {
+			cmd_idx = MMC_CMD(25);
+		} else {
+			cmd_idx = MMC_CMD(24);
+		}
+	}
+
+	if ((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) {
+		cmd_arg = lba * MMC_BLOCK_SIZE;
+	} else {
+		cmd_arg = lba;
+	}
+
+	ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R(1), NULL);
+	if (ret != 0) {
+		return 0;
+	}
+
+	ret = ops->write(lba, buf, size);
+	if (ret != 0) {
+		return 0;
+	}
+
+	/* Wait buffer empty */
+	do {
+		ret = mmc_device_state();
+		if (ret < 0) {
+			return 0;
+		}
+	} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_RCV));
+
+	if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
+		ret = mmc_send_cmd(MMC_CMD(12), 0, 0, NULL);
+		if (ret != 0) {
+			return 0;
+		}
+	}
+
+	return size;
+}
+
+size_t mmc_erase_blocks(unsigned int lba, size_t size)
+{
+	int ret;
+
+	assert(ops != NULL);
+	assert((size != 0U) && ((size & MMC_BLOCK_MASK) == 0U));
+
+	ret = mmc_send_cmd(MMC_CMD(35), lba, MMC_RESPONSE_R(1), NULL);
+	if (ret != 0) {
+		return 0;
+	}
+
+	ret = mmc_send_cmd(MMC_CMD(36), lba + (size / MMC_BLOCK_SIZE) - 1U,
+			   MMC_RESPONSE_R(1), NULL);
+	if (ret != 0) {
+		return 0;
+	}
+
+	ret = mmc_send_cmd(MMC_CMD(38), lba, MMC_RESPONSE_R(0x1B), NULL);
+	if (ret != 0) {
+		return 0;
+	}
+
+	do {
+		ret = mmc_device_state();
+		if (ret < 0) {
+			return 0;
+		}
+	} while (ret != MMC_STATE_TRAN);
+
+	return size;
+}
+
+static inline void mmc_rpmb_enable(void)
+{
+	mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
+			PART_CFG_BOOT_PARTITION1_ENABLE |
+			PART_CFG_PARTITION1_ACCESS);
+}
+
+static inline void mmc_rpmb_disable(void)
+{
+	mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
+			PART_CFG_BOOT_PARTITION1_ENABLE);
+}
+
+size_t mmc_rpmb_read_blocks(unsigned int lba, uintptr_t buf, size_t size)
+{
+	size_t size_read;
+
+	mmc_rpmb_enable();
+	size_read = mmc_read_blocks(lba, buf, size);
+	mmc_rpmb_disable();
+
+	return size_read;
+}
+
+size_t mmc_rpmb_write_blocks(unsigned int lba, const uintptr_t buf, size_t size)
+{
+	size_t size_written;
+
+	mmc_rpmb_enable();
+	size_written = mmc_write_blocks(lba, buf, size);
+	mmc_rpmb_disable();
+
+	return size_written;
+}
+
+size_t mmc_rpmb_erase_blocks(unsigned int lba, size_t size)
+{
+	size_t size_erased;
+
+	mmc_rpmb_enable();
+	size_erased = mmc_erase_blocks(lba, size);
+	mmc_rpmb_disable();
+
+	return size_erased;
+}
+
+int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk,
+	     unsigned int width, unsigned int flags,
+	     struct mmc_device_info *device_info)
+{
+	assert((ops_ptr != NULL) &&
+	       (ops_ptr->init != NULL) &&
+	       (ops_ptr->send_cmd != NULL) &&
+	       (ops_ptr->set_ios != NULL) &&
+	       (ops_ptr->prepare != NULL) &&
+	       (ops_ptr->read != NULL) &&
+	       (ops_ptr->write != NULL) &&
+	       (device_info != NULL) &&
+	       (clk != 0) &&
+	       ((width == MMC_BUS_WIDTH_1) ||
+		(width == MMC_BUS_WIDTH_4) ||
+		(width == MMC_BUS_WIDTH_8) ||
+		(width == MMC_BUS_WIDTH_DDR_4) ||
+		(width == MMC_BUS_WIDTH_DDR_8)));
+
+	ops = ops_ptr;
+	mmc_flags = flags;
+	mmc_dev_info = device_info;
+
+	return mmc_enumerate(clk, width);
+}
diff --git a/include/drivers/mmc.h b/include/drivers/mmc.h
new file mode 100644
index 0000000..65f4bbd
--- /dev/null
+++ b/include/drivers/mmc.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __MMC_H__
+#define __MMC_H__
+
+#include <stdint.h>
+#include <utils_def.h>
+
+#define MMC_BLOCK_SIZE			U(512)
+#define MMC_BLOCK_MASK			(MMC_BLOCK_SIZE - U(1))
+#define MMC_BOOT_CLK_RATE		(400 * 1000)
+
+#define MMC_CMD(_x)			U(_x)
+
+#define MMC_ACMD(_x)			U(_x)
+
+#define OCR_POWERUP			BIT(31)
+#define OCR_HCS				BIT(30)
+#define OCR_BYTE_MODE			(U(0) << 29)
+#define OCR_SECTOR_MODE			(U(2) << 29)
+#define OCR_ACCESS_MODE_MASK		(U(3) << 29)
+#define OCR_3_5_3_6			BIT(23)
+#define OCR_3_4_3_5			BIT(22)
+#define OCR_3_3_3_4			BIT(21)
+#define OCR_3_2_3_3			BIT(20)
+#define OCR_3_1_3_2			BIT(19)
+#define OCR_3_0_3_1			BIT(18)
+#define OCR_2_9_3_0			BIT(17)
+#define OCR_2_8_2_9			BIT(16)
+#define OCR_2_7_2_8			BIT(15)
+#define OCR_VDD_MIN_2V7			GENMASK(23, 15)
+#define OCR_VDD_MIN_2V0			GENMASK(14, 8)
+#define OCR_VDD_MIN_1V7			BIT(7)
+
+#define MMC_RESPONSE_R(_x)		U(_x)
+
+/* Value randomly chosen for eMMC RCA, it should be > 1 */
+#define MMC_FIX_RCA			6
+#define RCA_SHIFT_OFFSET		16
+
+#define CMD_EXTCSD_PARTITION_CONFIG	179
+#define CMD_EXTCSD_BUS_WIDTH		183
+#define CMD_EXTCSD_HS_TIMING		185
+#define CMD_EXTCSD_SEC_CNT		212
+
+#define PART_CFG_BOOT_PARTITION1_ENABLE	(U(1) << 3)
+#define PART_CFG_PARTITION1_ACCESS	(U(1) << 0)
+
+/* Values in EXT CSD register */
+#define MMC_BUS_WIDTH_1			U(0)
+#define MMC_BUS_WIDTH_4			U(1)
+#define MMC_BUS_WIDTH_8			U(2)
+#define MMC_BUS_WIDTH_DDR_4		U(5)
+#define MMC_BUS_WIDTH_DDR_8		U(6)
+#define MMC_BOOT_MODE_BACKWARD		(U(0) << 3)
+#define MMC_BOOT_MODE_HS_TIMING		(U(1) << 3)
+#define MMC_BOOT_MODE_DDR		(U(2) << 3)
+
+#define EXTCSD_SET_CMD			(U(0) << 24)
+#define EXTCSD_SET_BITS			(U(1) << 24)
+#define EXTCSD_CLR_BITS			(U(2) << 24)
+#define EXTCSD_WRITE_BYTES		(U(3) << 24)
+#define EXTCSD_CMD(x)			(((x) & 0xff) << 16)
+#define EXTCSD_VALUE(x)			(((x) & 0xff) << 8)
+#define EXTCSD_CMD_SET_NORMAL		U(1)
+
+#define CSD_TRAN_SPEED_UNIT_MASK	GENMASK(2, 0)
+#define CSD_TRAN_SPEED_MULT_MASK	GENMASK(6, 3)
+#define CSD_TRAN_SPEED_MULT_SHIFT	3
+
+#define STATUS_CURRENT_STATE(x)		(((x) & 0xf) << 9)
+#define STATUS_READY_FOR_DATA		BIT(8)
+#define STATUS_SWITCH_ERROR		BIT(7)
+#define MMC_GET_STATE(x)		(((x) >> 9) & 0xf)
+#define MMC_STATE_IDLE			0
+#define MMC_STATE_READY			1
+#define MMC_STATE_IDENT			2
+#define MMC_STATE_STBY			3
+#define MMC_STATE_TRAN			4
+#define MMC_STATE_DATA			5
+#define MMC_STATE_RCV			6
+#define MMC_STATE_PRG			7
+#define MMC_STATE_DIS			8
+#define MMC_STATE_BTST			9
+#define MMC_STATE_SLP			10
+
+#define MMC_FLAG_CMD23			(U(1) << 0)
+
+#define CMD8_CHECK_PATTERN		U(0xAA)
+#define VHS_2_7_3_6_V			BIT(8)
+
+#define SD_SCR_BUS_WIDTH_1		BIT(8)
+#define SD_SCR_BUS_WIDTH_4		BIT(10)
+
+struct mmc_cmd {
+	unsigned int	cmd_idx;
+	unsigned int	cmd_arg;
+	unsigned int	resp_type;
+	unsigned int	resp_data[4];
+};
+
+struct mmc_ops {
+	void (*init)(void);
+	int (*send_cmd)(struct mmc_cmd *cmd);
+	int (*set_ios)(unsigned int clk, unsigned int width);
+	int (*prepare)(int lba, uintptr_t buf, size_t size);
+	int (*read)(int lba, uintptr_t buf, size_t size);
+	int (*write)(int lba, const uintptr_t buf, size_t size);
+};
+
+struct mmc_csd_emmc {
+	unsigned int		not_used:		1;
+	unsigned int		crc:			7;
+	unsigned int		ecc:			2;
+	unsigned int		file_format:		2;
+	unsigned int		tmp_write_protect:	1;
+	unsigned int		perm_write_protect:	1;
+	unsigned int		copy:			1;
+	unsigned int		file_format_grp:	1;
+
+	unsigned int		reserved_1:		5;
+	unsigned int		write_bl_partial:	1;
+	unsigned int		write_bl_len:		4;
+	unsigned int		r2w_factor:		3;
+	unsigned int		default_ecc:		2;
+	unsigned int		wp_grp_enable:		1;
+
+	unsigned int		wp_grp_size:		5;
+	unsigned int		erase_grp_mult:		5;
+	unsigned int		erase_grp_size:		5;
+	unsigned int		c_size_mult:		3;
+	unsigned int		vdd_w_curr_max:		3;
+	unsigned int		vdd_w_curr_min:		3;
+	unsigned int		vdd_r_curr_max:		3;
+	unsigned int		vdd_r_curr_min:		3;
+	unsigned int		c_size_low:		2;
+
+	unsigned int		c_size_high:		10;
+	unsigned int		reserved_2:		2;
+	unsigned int		dsr_imp:		1;
+	unsigned int		read_blk_misalign:	1;
+	unsigned int		write_blk_misalign:	1;
+	unsigned int		read_bl_partial:	1;
+	unsigned int		read_bl_len:		4;
+	unsigned int		ccc:			12;
+
+	unsigned int		tran_speed:		8;
+	unsigned int		nsac:			8;
+	unsigned int		taac:			8;
+	unsigned int		reserved_3:		2;
+	unsigned int		spec_vers:		4;
+	unsigned int		csd_structure:		2;
+};
+
+struct mmc_csd_sd_v2 {
+	unsigned int		not_used:		1;
+	unsigned int		crc:			7;
+	unsigned int		reserved_1:		2;
+	unsigned int		file_format:		2;
+	unsigned int		tmp_write_protect:	1;
+	unsigned int		perm_write_protect:	1;
+	unsigned int		copy:			1;
+	unsigned int		file_format_grp:	1;
+
+	unsigned int		reserved_2:		5;
+	unsigned int		write_bl_partial:	1;
+	unsigned int		write_bl_len:		4;
+	unsigned int		r2w_factor:		3;
+	unsigned int		reserved_3:		2;
+	unsigned int		wp_grp_enable:		1;
+
+	unsigned int		wp_grp_size:		7;
+	unsigned int		sector_size:		7;
+	unsigned int		erase_block_en:		1;
+	unsigned int		reserved_4:		1;
+	unsigned int		c_size_low:		16;
+
+	unsigned int		c_size_high:		6;
+	unsigned int		reserved_5:		6;
+	unsigned int		dsr_imp:		1;
+	unsigned int		read_blk_misalign:	1;
+	unsigned int		write_blk_misalign:	1;
+	unsigned int		read_bl_partial:	1;
+	unsigned int		read_bl_len:		4;
+	unsigned int		ccc:			12;
+
+	unsigned int		tran_speed:		8;
+	unsigned int		nsac:			8;
+	unsigned int		taac:			8;
+	unsigned int		reserved_6:		6;
+	unsigned int		csd_structure:		2;
+};
+
+enum mmc_device_type {
+	MMC_IS_EMMC,
+	MMC_IS_SD,
+	MMC_IS_SD_HC,
+};
+
+struct mmc_device_info {
+	unsigned long long	device_size;	/* Size of device in bytes */
+	unsigned int		block_size;	/* Block size in bytes */
+	unsigned int		max_bus_freq;	/* Max bus freq in Hz */
+	enum mmc_device_type	mmc_dev_type;	/* Type of MMC */
+};
+
+size_t mmc_read_blocks(unsigned int lba, uintptr_t buf, size_t size);
+size_t mmc_write_blocks(unsigned int lba, const uintptr_t buf, size_t size);
+size_t mmc_erase_blocks(unsigned int lba, size_t size);
+size_t mmc_rpmb_read_blocks(unsigned int lba, uintptr_t buf, size_t size);
+size_t mmc_rpmb_write_blocks(unsigned int lba, const uintptr_t buf,
+			     size_t size);
+size_t mmc_rpmb_erase_blocks(unsigned int lba, size_t size);
+int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk,
+	     unsigned int width, unsigned int flags,
+	     struct mmc_device_info *device_info);
+
+#endif	/* __MMC_H__ */
diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h
index 2d40630..42bbf38 100644
--- a/include/plat/arm/common/plat_arm.h
+++ b/include/plat/arm/common/plat_arm.h
@@ -171,7 +171,6 @@
 int arm_validate_ns_entrypoint(uintptr_t entrypoint);
 void arm_system_pwr_domain_save(void);
 void arm_system_pwr_domain_resume(void);
-void arm_program_trusted_mailbox(uintptr_t address);
 int arm_psci_read_mem_protect(int *enabled);
 int arm_nor_psci_write_mem_protect(int val);
 void arm_nor_psci_do_static_mem_protect(void);
@@ -250,6 +249,7 @@
 void plat_arm_interconnect_init(void);
 void plat_arm_interconnect_enter_coherency(void);
 void plat_arm_interconnect_exit_coherency(void);
+void plat_arm_program_trusted_mailbox(uintptr_t address);
 
 #if ARM_PLAT_MT
 unsigned int plat_arm_get_cpu_pe_count(u_register_t mpidr);
diff --git a/plat/arm/common/arm_bl1_setup.c b/plat/arm/common/arm_bl1_setup.c
index 1c900b7..c9b1a68 100644
--- a/plat/arm/common/arm_bl1_setup.c
+++ b/plat/arm/common/arm_bl1_setup.c
@@ -144,7 +144,7 @@
 	 * in order to release secondary CPUs from their holding pen and make
 	 * them jump there.
 	 */
-	arm_program_trusted_mailbox(ep_info->pc);
+	plat_arm_program_trusted_mailbox(ep_info->pc);
 	dsbsy();
 	sev();
 #endif
diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk
index 5330847..67b574d 100644
--- a/plat/arm/common/arm_common.mk
+++ b/plat/arm/common/arm_common.mk
@@ -166,7 +166,7 @@
 				plat/arm/common/arm_err.c			\
 				plat/arm/common/arm_io_storage.c
 ifdef EL3_PAYLOAD_BASE
-# Need the arm_program_trusted_mailbox() function to release secondary CPUs from
+# Need the plat_arm_program_trusted_mailbox() function to release secondary CPUs from
 # their holding pen
 BL1_SOURCES		+=	plat/arm/common/arm_pm.c
 endif
diff --git a/plat/arm/common/arm_pm.c b/plat/arm/common/arm_pm.c
index 4fdb10a..d0350d6 100644
--- a/plat/arm/common/arm_pm.c
+++ b/plat/arm/common/arm_pm.c
@@ -14,8 +14,9 @@
 #include <platform_def.h>
 #include <psci.h>
 
-/* Allow ARM Standard platforms to override this function */
+/* Allow ARM Standard platforms to override these functions */
 #pragma weak plat_arm_psci_override_pm_ops
+#pragma weak plat_arm_program_trusted_mailbox
 
 #if ARM_RECOM_STATE_ID_ENC
 extern unsigned int arm_pm_idle_states[];
@@ -189,11 +190,11 @@
 }
 
 /*******************************************************************************
- * Private function to program the mailbox for a cpu before it is released
+ * ARM platform function to program the mailbox for a cpu before it is released
  * from reset. This function assumes that the Trusted mail box base is within
  * the ARM_SHARED_RAM region
  ******************************************************************************/
-void arm_program_trusted_mailbox(uintptr_t address)
+void plat_arm_program_trusted_mailbox(uintptr_t address)
 {
 	uintptr_t *mailbox = (void *) PLAT_ARM_TRUSTED_MAILBOX_BASE;
 
@@ -218,6 +219,6 @@
 	*psci_ops = plat_arm_psci_override_pm_ops(&plat_arm_psci_pm_ops);
 
 	/* Setup mailbox with entry point. */
-	arm_program_trusted_mailbox(sec_entrypoint);
+	plat_arm_program_trusted_mailbox(sec_entrypoint);
 	return 0;
 }
diff --git a/plat/arm/css/common/css_common.mk b/plat/arm/css/common/css_common.mk
index 72d5527..29dd01d 100644
--- a/plat/arm/css/common/css_common.mk
+++ b/plat/arm/css/common/css_common.mk
@@ -32,6 +32,7 @@
 				plat/arm/css/drivers/scpi/css_scpi.c
 else
 BL31_SOURCES		+=	plat/arm/css/drivers/scp/css_pm_scmi.c		\
+				plat/arm/css/drivers/scmi/scmi_ap_core_proto.c	\
 				plat/arm/css/drivers/scmi/scmi_common.c		\
 				plat/arm/css/drivers/scmi/scmi_pwr_dmn_proto.c	\
 				plat/arm/css/drivers/scmi/scmi_sys_pwr_proto.c	\
diff --git a/plat/arm/css/drivers/scmi/scmi.h b/plat/arm/css/drivers/scmi/scmi.h
index cf9ef5e..723fd06 100644
--- a/plat/arm/css/drivers/scmi/scmi.h
+++ b/plat/arm/css/drivers/scmi/scmi.h
@@ -12,6 +12,7 @@
 #include <stdint.h>
 
 /* Supported SCMI Protocol Versions */
+#define SCMI_AP_CORE_PROTO_VER			MAKE_SCMI_VERSION(1, 0)
 #define SCMI_PWR_DMN_PROTO_VER			MAKE_SCMI_VERSION(1, 0)
 #define SCMI_SYS_PWR_PROTO_VER			MAKE_SCMI_VERSION(1, 0)
 
@@ -29,6 +30,8 @@
 /* SCMI Protocol identifiers */
 #define SCMI_PWR_DMN_PROTO_ID			0x11
 #define SCMI_SYS_PWR_PROTO_ID			0x12
+/* The AP core protocol is a CSS platform-specific extension */
+#define SCMI_AP_CORE_PROTO_ID			0x90
 
 /* Mandatory messages IDs for all SCMI protocols */
 #define SCMI_PROTO_VERSION_MSG			0x0
@@ -43,6 +46,10 @@
 #define SCMI_SYS_PWR_STATE_SET_MSG		0x3
 #define SCMI_SYS_PWR_STATE_GET_MSG		0x4
 
+/* SCMI AP core protocol message IDs */
+#define SCMI_AP_CORE_RESET_ADDR_SET_MSG		0x3
+#define SCMI_AP_CORE_RESET_ADDR_GET_MSG		0x4
+
 /* Helper macros for system power management protocol commands */
 
 /*
@@ -73,6 +80,13 @@
 #define SCMI_SYS_PWR_POWER_UP			0x3
 #define SCMI_SYS_PWR_SUSPEND			0x4
 
+/*
+ * Macros to describe the bit-fields of the `attribute` of AP core protocol
+ * AP_CORE_RESET_ADDR set/get messages.
+ */
+#define SCMI_AP_CORE_LOCK_ATTR_SHIFT		0x0
+#define SCMI_AP_CORE_LOCK_ATTR			(1U << SCMI_AP_CORE_LOCK_ATTR_SHIFT)
+
 /* SCMI Error code definitions */
 #define SCMI_E_QUEUED			1
 #define SCMI_E_SUCCESS			0
@@ -133,4 +147,8 @@
 int scmi_sys_pwr_state_set(void *p, uint32_t flags, uint32_t system_state);
 int scmi_sys_pwr_state_get(void *p, uint32_t *system_state);
 
+/* SCMI AP core configuration protocol commands. */
+int scmi_ap_core_set_reset_addr(void *p, uint64_t reset_addr, uint32_t attr);
+int scmi_ap_core_get_reset_addr(void *p, uint64_t *reset_addr, uint32_t *attr);
+
 #endif	/* __CSS_SCMI_H__ */
diff --git a/plat/arm/css/drivers/scmi/scmi_ap_core_proto.c b/plat/arm/css/drivers/scmi/scmi_ap_core_proto.c
new file mode 100644
index 0000000..1438cba
--- /dev/null
+++ b/plat/arm/css/drivers/scmi/scmi_ap_core_proto.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include "scmi.h"
+#include "scmi_private.h"
+
+/*
+ * API to set the SCMI AP core reset address and attributes
+ */
+int scmi_ap_core_set_reset_addr(void *p, uint64_t reset_addr, uint32_t attr)
+{
+	mailbox_mem_t *mbx_mem;
+	int token = 0, ret;
+	scmi_channel_t *ch = (scmi_channel_t *)p;
+
+	validate_scmi_channel(ch);
+
+	scmi_get_channel(ch);
+
+	mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+	mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_AP_CORE_PROTO_ID,
+			SCMI_AP_CORE_RESET_ADDR_SET_MSG, token);
+	mbx_mem->len = SCMI_AP_CORE_RESET_ADDR_SET_MSG_LEN;
+	mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+	SCMI_PAYLOAD_ARG3(mbx_mem->payload, reset_addr & 0xffffffff,
+		reset_addr >> 32, attr);
+
+	scmi_send_sync_command(ch);
+
+	/* Get the return values */
+	SCMI_PAYLOAD_RET_VAL1(mbx_mem->payload, ret);
+	assert(mbx_mem->len == SCMI_AP_CORE_RESET_ADDR_SET_RESP_LEN);
+	assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
+
+	scmi_put_channel(ch);
+
+	return ret;
+}
+
+/*
+ * API to get the SCMI AP core reset address and attributes
+ */
+int scmi_ap_core_get_reset_addr(void *p, uint64_t *reset_addr, uint32_t *attr)
+{
+	mailbox_mem_t *mbx_mem;
+	int token = 0, ret;
+	scmi_channel_t *ch = (scmi_channel_t *)p;
+	uint32_t lo_addr, hi_addr;
+
+	validate_scmi_channel(ch);
+
+	scmi_get_channel(ch);
+
+	mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+	mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_AP_CORE_PROTO_ID,
+			SCMI_AP_CORE_RESET_ADDR_GET_MSG, token);
+	mbx_mem->len = SCMI_AP_CORE_RESET_ADDR_GET_MSG_LEN;
+	mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+
+	scmi_send_sync_command(ch);
+
+	/* Get the return values */
+	SCMI_PAYLOAD_RET_VAL4(mbx_mem->payload, ret, lo_addr, hi_addr, *attr);
+	*reset_addr = lo_addr | (uint64_t)hi_addr << 32;
+	assert(mbx_mem->len == SCMI_AP_CORE_RESET_ADDR_GET_RESP_LEN);
+	assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header));
+
+	scmi_put_channel(ch);
+
+	return ret;
+}
diff --git a/plat/arm/css/drivers/scmi/scmi_private.h b/plat/arm/css/drivers/scmi/scmi_private.h
index 67fe748..39bc8cc 100644
--- a/plat/arm/css/drivers/scmi/scmi_private.h
+++ b/plat/arm/css/drivers/scmi/scmi_private.h
@@ -18,6 +18,12 @@
 #define SCMI_PROTO_MSG_ATTR_MSG_LEN		8
 #define SCMI_PROTO_MSG_ATTR_RESP_LEN		12
 
+#define SCMI_AP_CORE_RESET_ADDR_SET_MSG_LEN	16
+#define SCMI_AP_CORE_RESET_ADDR_SET_RESP_LEN	8
+
+#define SCMI_AP_CORE_RESET_ADDR_GET_MSG_LEN	4
+#define SCMI_AP_CORE_RESET_ADDR_GET_RESP_LEN	20
+
 #define SCMI_PWR_STATE_SET_MSG_LEN		16
 #define SCMI_PWR_STATE_SET_RESP_LEN		8
 
@@ -113,6 +119,11 @@
 		(val3) = mmio_read_32((uintptr_t)&payld_arr[2]);	\
 	} while (0)
 
+#define SCMI_PAYLOAD_RET_VAL4(payld_arr, val1, val2, val3, val4)	do {	\
+		SCMI_PAYLOAD_RET_VAL3(payld_arr, val1, val2, val3);		\
+		(val4) = mmio_read_32((uintptr_t)&payld_arr[3]);		\
+	} while (0)
+
 /*
  * Private data structure for representing the mailbox memory layout. Refer
  * the SCMI specification for more details.
diff --git a/plat/arm/css/drivers/scp/css_pm_scmi.c b/plat/arm/css/drivers/scp/css_pm_scmi.c
index 715bf98..7032267 100644
--- a/plat/arm/css/drivers/scp/css_pm_scmi.c
+++ b/plat/arm/css/drivers/scp/css_pm_scmi.c
@@ -306,6 +306,28 @@
 		.ring_doorbell = &mhu_ring_doorbell,
 };
 
+static int scmi_ap_core_init(scmi_channel_t *ch)
+{
+#if PROGRAMMABLE_RESET_ADDRESS
+	uint32_t version;
+	int ret;
+
+	ret = scmi_proto_version(ch, SCMI_AP_CORE_PROTO_ID, &version);
+	if (ret != SCMI_E_SUCCESS) {
+		WARN("SCMI AP core protocol version message failed\n");
+		return -1;
+	}
+
+	if (!is_scmi_version_compatible(SCMI_AP_CORE_PROTO_VER, version)) {
+		WARN("SCMI AP core protocol version 0x%x incompatible with driver version 0x%x\n",
+			version, SCMI_AP_CORE_PROTO_VER);
+		return -1;
+	}
+	INFO("SCMI AP core protocol version 0x%x detected\n", version);
+#endif
+	return 0;
+}
+
 void plat_arm_pwrc_setup(void)
 {
 	channel.info = &plat_css_scmi_plat_info;
@@ -315,6 +337,10 @@
 		ERROR("SCMI Initialization failed\n");
 		panic();
 	}
+	if (scmi_ap_core_init(&channel) < 0) {
+		ERROR("SCMI AP core protocol initialization failed\n");
+		panic();
+	}
 }
 
 /******************************************************************************
@@ -386,3 +412,18 @@
 	 */
 	return 0;
 }
+
+#if PROGRAMMABLE_RESET_ADDRESS
+void plat_arm_program_trusted_mailbox(uintptr_t address)
+{
+	int ret;
+
+	assert(scmi_handle);
+	ret = scmi_ap_core_set_reset_addr(scmi_handle, address,
+		SCMI_AP_CORE_LOCK_ATTR);
+	if (ret != SCMI_E_SUCCESS) {
+		ERROR("CSS: Failed to program reset address: %d\n", ret);
+		panic();
+	}
+}
+#endif
diff --git a/plat/rpi3/platform.mk b/plat/rpi3/platform.mk
index df19705..5990f27 100644
--- a/plat/rpi3/platform.mk
+++ b/plat/rpi3/platform.mk
@@ -20,7 +20,8 @@
 				plat/common/aarch64/platform_mp_stack.S	\
 				plat/rpi3/aarch64/plat_helpers.S	\
 				plat/rpi3/rpi3_bl1_setup.c		\
-				plat/rpi3/rpi3_io_storage.c
+				plat/rpi3/rpi3_io_storage.c		\
+				plat/rpi3/rpi3_mbox.c
 
 BL2_SOURCES		+=	common/desc_image_load.c		\
 				drivers/io/io_fip.c			\
@@ -54,6 +55,26 @@
     TF_CFLAGS_aarch64	+=	-mtune=cortex-a53
 endif
 
+# Platform Makefile target
+# ------------------------
+
+RPI3_BL1_PAD_BIN	:=	${BUILD_PLAT}/bl1_pad.bin
+RPI3_ARMSTUB8_BIN	:=	${BUILD_PLAT}/armstub8.bin
+
+# Add new default target when compiling this platform
+all: armstub
+
+# This target concatenates BL1 and the FIP so that the base addresses match the
+# ones defined in the memory map
+armstub: bl1 fip
+	@echo "  CAT     $@"
+	${Q}cp ${BUILD_PLAT}/bl1.bin ${RPI3_BL1_PAD_BIN}
+	${Q}truncate --size=131072 ${RPI3_BL1_PAD_BIN}
+	${Q}cat ${RPI3_BL1_PAD_BIN} ${BUILD_PLAT}/fip.bin > ${RPI3_ARMSTUB8_BIN}
+	@${ECHO_BLANK_LINE}
+	@echo "Built $@ successfully"
+	@${ECHO_BLANK_LINE}
+
 # Build config flags
 # ------------------
 
diff --git a/plat/rpi3/rpi3_bl1_setup.c b/plat/rpi3/rpi3_bl1_setup.c
index c98715b..39bb332 100644
--- a/plat/rpi3/rpi3_bl1_setup.c
+++ b/plat/rpi3/rpi3_bl1_setup.c
@@ -7,6 +7,7 @@
 #include <arch.h>
 #include <arch_helpers.h>
 #include <bl_common.h>
+#include <debug.h>
 #include <platform_def.h>
 #include <xlat_mmu_helpers.h>
 #include <xlat_tables_defs.h>
@@ -56,6 +57,39 @@
 
 void bl1_platform_setup(void)
 {
+	uint32_t __unused rev;
+	int __unused rc;
+
+	rc = rpi3_vc_hardware_get_board_revision(&rev);
+
+	if (rc == 0) {
+		const char __unused *model, __unused *info;
+
+		switch (rev) {
+		case 0xA02082:
+			model = "Raspberry Pi 3 Model B";
+			info = "(1GB, Sony, UK)";
+			break;
+		case 0xA22082:
+			model = "Raspberry Pi 3 Model B";
+			info = "(1GB, Embest, China)";
+			break;
+		case 0xA020D3:
+			model = "Raspberry Pi 3 Model B+";
+			info = "(1GB, Sony, UK)";
+			break;
+		default:
+			model = "Unknown";
+			info = "(Unknown)";
+			ERROR("rpi3: Unknown board revision 0x%08x\n", rev);
+			break;
+		}
+
+		NOTICE("rpi3: Detected: %s %s [0x%08x]\n", model, info, rev);
+	} else {
+		ERROR("rpi3: Unable to detect board revision\n");
+	}
+
 	/* Initialise the IO layer and register platform IO devices */
 	plat_rpi3_io_setup();
 }
diff --git a/plat/rpi3/rpi3_hw.h b/plat/rpi3/rpi3_hw.h
index 70272e0..a83a0ad 100644
--- a/plat/rpi3/rpi3_hw.h
+++ b/plat/rpi3/rpi3_hw.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -17,11 +17,25 @@
 #define RPI3_IO_SIZE			ULL(0x01000000)
 
 /*
- * Serial port (called 'Mini UART' in the BCM docucmentation).
+ * ARM <-> VideoCore mailboxes
  */
-#define RPI3_IO_MINI_UART_OFFSET	ULL(0x00215040)
-#define RPI3_MINI_UART_BASE		(RPI3_IO_BASE + RPI3_IO_MINI_UART_OFFSET)
-#define RPI3_MINI_UART_CLK_IN_HZ	ULL(500000000)
+#define RPI3_MBOX_OFFSET		ULL(0x0000B880)
+#define RPI3_MBOX_BASE			(RPI3_IO_BASE + RPI3_MBOX_OFFSET)
+/* VideoCore -> ARM */
+#define RPI3_MBOX0_READ_OFFSET		ULL(0x00000000)
+#define RPI3_MBOX0_PEEK_OFFSET		ULL(0x00000010)
+#define RPI3_MBOX0_SENDER_OFFSET	ULL(0x00000014)
+#define RPI3_MBOX0_STATUS_OFFSET	ULL(0x00000018)
+#define RPI3_MBOX0_CONFIG_OFFSET	ULL(0x0000001C)
+/* ARM -> VideoCore */
+#define RPI3_MBOX1_WRITE_OFFSET		ULL(0x00000020)
+#define RPI3_MBOX1_PEEK_OFFSET		ULL(0x00000030)
+#define RPI3_MBOX1_SENDER_OFFSET	ULL(0x00000034)
+#define RPI3_MBOX1_STATUS_OFFSET	ULL(0x00000038)
+#define RPI3_MBOX1_CONFIG_OFFSET	ULL(0x0000003C)
+/* Mailbox status constants */
+#define RPI3_MBOX_STATUS_FULL_MASK	U(0x80000000) /* Set if full */
+#define RPI3_MBOX_STATUS_EMPTY_MASK	U(0x40000000) /* Set if empty */
 
 /*
  * Power management, reset controller, watchdog.
@@ -30,11 +44,26 @@
 #define RPI3_PM_BASE			(RPI3_IO_BASE + RPI3_IO_PM_OFFSET)
 /* Registers on top of RPI3_PM_BASE. */
 #define RPI3_PM_RSTC_OFFSET		ULL(0x0000001C)
+#define RPI3_PM_RSTS_OFFSET		ULL(0x00000020)
 #define RPI3_PM_WDOG_OFFSET		ULL(0x00000024)
 /* Watchdog constants */
-#define RPI3_PM_PASSWORD		ULL(0x5A000000)
-#define RPI3_PM_RSTC_WRCFG_MASK		ULL(0x00000030)
-#define RPI3_PM_RSTC_WRCFG_FULL_RESET	ULL(0x00000020)
+#define RPI3_PM_PASSWORD		U(0x5A000000)
+#define RPI3_PM_RSTC_WRCFG_MASK		U(0x00000030)
+#define RPI3_PM_RSTC_WRCFG_FULL_RESET	U(0x00000020)
+/*
+ * The RSTS register is used by the VideoCore firmware when booting the
+ * Raspberry Pi to know which partition to boot from. The partition value is
+ * formed by bits 0, 2, 4, 6, 8 and 10. Partition 63 is used by said firmware
+ * to indicate halt.
+ */
+#define RPI3_PM_RSTS_WRCFG_HALT		U(0x00000555)
+
+/*
+ * Serial port (called 'Mini UART' in the BCM docucmentation).
+ */
+#define RPI3_IO_MINI_UART_OFFSET	ULL(0x00215040)
+#define RPI3_MINI_UART_BASE		(RPI3_IO_BASE + RPI3_IO_MINI_UART_OFFSET)
+#define RPI3_MINI_UART_CLK_IN_HZ	ULL(500000000)
 
 /*
  * Local interrupt controller
diff --git a/plat/rpi3/rpi3_mbox.c b/plat/rpi3/rpi3_mbox.c
new file mode 100644
index 0000000..77e17af
--- /dev/null
+++ b/plat/rpi3/rpi3_mbox.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <arch_helpers.h>
+#include <debug.h>
+#include <mmio.h>
+#include <platform_def.h>
+
+#include "rpi3_hw.h"
+
+/* This struct must be aligned to 16 bytes */
+typedef struct __packed __aligned(16) rpi3_mbox_request {
+	uint32_t	size; /* Buffer size in bytes */
+	uint32_t	code; /* Request/response code */
+	uint32_t	tags[0];
+} rpi3_mbox_request_t;
+
+#define RPI3_MBOX_BUFFER_SIZE		U(256)
+static uint8_t __aligned(16) rpi3_mbox_buffer[RPI3_MBOX_BUFFER_SIZE];
+
+/* Constants to perform a request/check the status of a request. */
+#define RPI3_MBOX_PROCESS_REQUEST	U(0x00000000)
+#define RPI3_MBOX_REQUEST_SUCCESSFUL	U(0x80000000)
+#define RPI3_MBOX_REQUEST_ERROR		U(0x80000001)
+
+/* Command constants */
+#define RPI3_TAG_HARDWARE_GET_BOARD_REVISION	U(0x00010002)
+#define RPI3_TAG_END				U(0x00000000)
+
+#define RPI3_TAG_REQUEST		U(0x00000000)
+#define RPI3_TAG_IS_RESPONSE		U(0x80000000) /* Set if response */
+#define RPI3_TAG_RESPONSE_LENGTH_MASK	U(0x7FFFFFFF)
+
+#define RPI3_CHANNEL_ARM_TO_VC		U(0x8)
+#define RPI3_CHANNEL_MASK		U(0xF)
+
+#define RPI3_MAILBOX_MAX_RETRIES	U(1000000)
+
+/*******************************************************************************
+ * Helpers to send requests to the VideoCore using the mailboxes.
+ ******************************************************************************/
+static void rpi3_vc_mailbox_request_send(void)
+{
+	uint32_t st, data;
+	uintptr_t resp_addr, addr;
+	unsigned int retries;
+
+	/* This is the location of the request buffer */
+	addr = (uintptr_t) &rpi3_mbox_buffer;
+
+	/* Make sure that the changes are seen by the VideoCore */
+	flush_dcache_range(addr, RPI3_MBOX_BUFFER_SIZE);
+
+	/* Wait until the outbound mailbox is empty */
+	retries = 0U;
+
+	do {
+		st = mmio_read_32(RPI3_MBOX_BASE + RPI3_MBOX1_STATUS_OFFSET);
+
+		retries++;
+		if (retries == RPI3_MAILBOX_MAX_RETRIES) {
+			ERROR("rpi3: mbox: Send request timeout\n");
+			return;
+		}
+
+	} while ((st & RPI3_MBOX_STATUS_EMPTY_MASK) == 0U);
+
+	/* Send base address of this message to start request */
+	mmio_write_32(RPI3_MBOX_BASE + RPI3_MBOX1_WRITE_OFFSET,
+		      RPI3_CHANNEL_ARM_TO_VC | (uint32_t) addr);
+
+	/* Wait until the inbound mailbox isn't empty */
+	retries = 0U;
+
+	do {
+		st = mmio_read_32(RPI3_MBOX_BASE + RPI3_MBOX0_STATUS_OFFSET);
+
+		retries++;
+		if (retries == RPI3_MAILBOX_MAX_RETRIES) {
+			ERROR("rpi3: mbox: Receive response timeout\n");
+			return;
+		}
+
+	} while ((st & RPI3_MBOX_STATUS_EMPTY_MASK) != 0U);
+
+	/* Get location and channel */
+	data = mmio_read_32(RPI3_MBOX_BASE + RPI3_MBOX0_READ_OFFSET);
+
+	if ((data & RPI3_CHANNEL_MASK) != RPI3_CHANNEL_ARM_TO_VC) {
+		ERROR("rpi3: mbox: Wrong channel: 0x%08x\n", data);
+		panic();
+	}
+
+	resp_addr = (uintptr_t)(data & ~RPI3_CHANNEL_MASK);
+	if (addr != resp_addr) {
+		ERROR("rpi3: mbox: Unexpected address: 0x%08x\n", data);
+		panic();
+	}
+
+	/* Make sure that the data seen by the CPU is up to date */
+	inv_dcache_range(addr, RPI3_MBOX_BUFFER_SIZE);
+}
+
+/*******************************************************************************
+ * Request board revision. Returns the revision and 0 on success, -1 on error.
+ ******************************************************************************/
+int rpi3_vc_hardware_get_board_revision(uint32_t *revision)
+{
+	uint32_t tag_request_size = sizeof(uint32_t);
+	rpi3_mbox_request_t *req = (rpi3_mbox_request_t *) rpi3_mbox_buffer;
+
+	assert(revision != NULL);
+
+	VERBOSE("rpi3: mbox: Sending request at %p\n", (void *)req);
+
+	req->size = sizeof(rpi3_mbox_buffer);
+	req->code = RPI3_MBOX_PROCESS_REQUEST;
+
+	req->tags[0] = RPI3_TAG_HARDWARE_GET_BOARD_REVISION;
+	req->tags[1] = tag_request_size; /* Space available for the response */
+	req->tags[2] = RPI3_TAG_REQUEST;
+	req->tags[3] = 0; /* Placeholder for the response */
+
+	req->tags[4] = RPI3_TAG_END;
+
+	rpi3_vc_mailbox_request_send();
+
+	if (req->code != RPI3_MBOX_REQUEST_SUCCESSFUL) {
+		ERROR("rpi3: mbox: Code = 0x%08x\n", req->code);
+		return -1;
+	}
+
+	if (req->tags[2] != (RPI3_TAG_IS_RESPONSE | tag_request_size)) {
+		ERROR("rpi3: mbox: get board revision failed (0x%08x)\n",
+		      req->tags[2]);
+		return -1;
+	}
+
+	*revision = req->tags[3];
+
+	return 0;
+}
diff --git a/plat/rpi3/rpi3_pm.c b/plat/rpi3/rpi3_pm.c
index 1d067fb..9694858 100644
--- a/plat/rpi3/rpi3_pm.c
+++ b/plat/rpi3/rpi3_pm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -150,41 +150,61 @@
 }
 
 /*******************************************************************************
- * Platform handler to reboot the system
+ * Platform handlers for system reset and system off.
  ******************************************************************************/
-#define RESET_TIMEOUT	10
 
-static void __dead2 rpi3_system_reset(void)
-{
-	/* Setup watchdog for reset */
+/* 10 ticks (Watchdog timer = Timer clock / 16) */
+#define RESET_TIMEOUT	U(10)
 
-	static const uintptr_t base = RPI3_PM_BASE;
+static void __dead2 rpi3_watchdog_reset(void)
+{
 	uint32_t rstc;
 
-	INFO("rpi3: PSCI System Reset: invoking watchdog reset\n");
-
 	console_flush();
 
-	rstc = mmio_read_32(base + RPI3_PM_RSTC_OFFSET);
-	rstc &= ~RPI3_PM_RSTC_WRCFG_MASK;
-	rstc |= RPI3_PM_RSTC_WRCFG_FULL_RESET;
-
-	dmbst();
+	dsbsy();
+	isb();
 
-	/*
-	 * Watchdog timer = Timer clock / 16
-	 * Password (31:16) | Value (11:0)
-	 */
-	mmio_write_32(base + RPI3_PM_WDOG_OFFSET,
+	mmio_write_32(RPI3_PM_BASE + RPI3_PM_WDOG_OFFSET,
 		      RPI3_PM_PASSWORD | RESET_TIMEOUT);
-	mmio_write_32(base + RPI3_PM_RSTC_OFFSET,
-		      RPI3_PM_PASSWORD | rstc);
+
+	rstc = mmio_read_32(RPI3_PM_BASE + RPI3_PM_RSTC_OFFSET);
+	rstc &= ~RPI3_PM_RSTC_WRCFG_MASK;
+	rstc |= RPI3_PM_PASSWORD | RPI3_PM_RSTC_WRCFG_FULL_RESET;
+	mmio_write_32(RPI3_PM_BASE + RPI3_PM_RSTC_OFFSET, rstc);
 
 	for (;;) {
 		wfi();
 	}
 }
 
+static void __dead2 rpi3_system_reset(void)
+{
+	INFO("rpi3: PSCI_SYSTEM_RESET: Invoking watchdog reset\n");
+
+	rpi3_watchdog_reset();
+}
+
+static void __dead2 rpi3_system_off(void)
+{
+	uint32_t rsts;
+
+	INFO("rpi3: PSCI_SYSTEM_OFF: Invoking watchdog reset\n");
+
+	/*
+	 * This function doesn't actually make the Raspberry Pi turn itself off,
+	 * the hardware doesn't allow it. It simply reboots it and the RSTS
+	 * value tells the bootcode.bin firmware not to continue the regular
+	 * bootflow and to stay in a low power mode.
+	 */
+
+	rsts = mmio_read_32(RPI3_PM_BASE + RPI3_PM_RSTS_OFFSET);
+	rsts |= RPI3_PM_PASSWORD | RPI3_PM_RSTS_WRCFG_HALT;
+	mmio_write_32(RPI3_PM_BASE + RPI3_PM_RSTS_OFFSET, rsts);
+
+	rpi3_watchdog_reset();
+}
+
 /*******************************************************************************
  * Platform handlers and setup function.
  ******************************************************************************/
@@ -192,6 +212,7 @@
 	.cpu_standby = rpi3_cpu_standby,
 	.pwr_domain_on = rpi3_pwr_domain_on,
 	.pwr_domain_on_finish = rpi3_pwr_domain_on_finish,
+	.system_off = rpi3_system_off,
 	.system_reset = rpi3_system_reset,
 	.validate_power_state = rpi3_validate_power_state,
 };
diff --git a/plat/rpi3/rpi3_private.h b/plat/rpi3/rpi3_private.h
index a9fbfe4..9d1744e 100644
--- a/plat/rpi3/rpi3_private.h
+++ b/plat/rpi3/rpi3_private.h
@@ -33,4 +33,7 @@
 /* IO storage utility functions */
 void plat_rpi3_io_setup(void);
 
+/* VideoCore firmware commands */
+int rpi3_vc_hardware_get_board_revision(uint32_t *revision);
+
 #endif /*__RPI3_PRIVATE_H__ */