Add MMC framework
authorYann Gautier <yann.gautier@st.com>
Tue, 3 Jul 2018 16:32:12 +0000 (18:32 +0200)
committerYann Gautier <yann.gautier@st.com>
Tue, 3 Jul 2018 16:32:12 +0000 (18:32 +0200)
This change is largely based on existing eMMC framework by Haojian Zhuang
(@hzhuang1).

The MMC framework supports both eMMC and SD card devices. It was
written as a new framework since breaking few eMMC framework APIs.

At card probe and after the reset to idle command (CMD0), a Send
Interface Condition Command is sent (CMD8) to distinguish between
eMMC and SD card devices. eMMC devices go through the same
sequence as in the former eMMC framework. Else the framework
uses commands dedicated to SD-cards for init or frequency switch.

A structure is created to share info with the driver. It stores:
- the MMC type (eMMC, SD or SD HC)
- the device size
- the max frequency supported by the device
- the block size: 512 for eMMC and SD-HC and read from CSD
 structure for older SD-cards

Restriction to align buffers on block size has been removed.
Cache maintenance was removed and is expected to be done in the platform
or device driver.

The MMC framework includes some MISRA compliance coding style
maybe not yet ported in the existing eMMC framework.

Fixes ARM-software/tf-issues#597

Signed-off-by: Yann Gautier <yann.gautier@st.com>
drivers/mmc/mmc.c [new file with mode: 0644]
include/drivers/mmc.h [new file with mode: 0644]

diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
new file mode 100644 (file)
index 0000000..8fe3239
--- /dev/null
@@ -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 (file)
index 0000000..65f4bbd
--- /dev/null
@@ -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__ */