#include <config.h>
#include <common.h>
#include <command.h>
+#include <errno.h>
#include <mmc.h>
#include <part.h>
#include <malloc.h>
#include <div64.h>
#include "mmc_private.h"
-/* Set block count limit because of 16 bit register limit on some hardware*/
-#ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT
-#define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535
-#endif
-
static struct list_head mmc_devices;
static int cur_dev_num = -1;
-int __weak board_mmc_getwp(struct mmc *mmc)
+__weak int board_mmc_getwp(struct mmc *mmc)
{
return -1;
}
wp = board_mmc_getwp(mmc);
if (wp < 0) {
- if (mmc->getwp)
- wp = mmc->getwp(mmc);
+ if (mmc->cfg->ops->getwp)
+ wp = mmc->cfg->ops->getwp(mmc);
else
wp = 0;
}
return wp;
}
-int __board_mmc_getcd(struct mmc *mmc) {
+__weak int board_mmc_getcd(struct mmc *mmc)
+{
return -1;
}
-int board_mmc_getcd(struct mmc *mmc)__attribute__((weak,
- alias("__board_mmc_getcd")));
-
int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
int ret;
printf("CMD_SEND:%d\n", cmd->cmdidx);
printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg);
- ret = mmc->send_cmd(mmc, cmd, data);
+ ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
switch (cmd->resp_type) {
case MMC_RSP_NONE:
printf("\t\tMMC_RSP_NONE\n");
break;
}
#else
- ret = mmc->send_cmd(mmc, cmd, data);
+ ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
#endif
return ret;
}
#endif
return TIMEOUT;
}
+ if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR)
+ return SWITCH_ERR;
return 0;
}
{
struct mmc_cmd cmd;
+ if (mmc->card_caps & MMC_MODE_DDR_52MHz)
+ return 0;
+
cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = len;
return 0;
do {
- cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo;
+ cur = (blocks_todo > mmc->cfg->b_max) ?
+ mmc->cfg->b_max : blocks_todo;
if(mmc_read_blocks(mmc, dst, start, cur) != cur)
return 0;
blocks_todo -= cur;
* specified.
*/
cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 :
- (mmc->voltages & 0xff8000);
+ (mmc->cfg->voltages & 0xff8000);
if (mmc->version == SD_VERSION_2)
cmd.cmdarg |= OCR_HCS;
cmd->cmdarg = 0;
if (use_arg && !mmc_host_is_spi(mmc)) {
cmd->cmdarg =
- (mmc->voltages &
+ (mmc->cfg->voltages &
(mmc->op_cond_response & OCR_VOLTAGE_MASK)) |
(mmc->op_cond_response & OCR_ACCESS_MODE);
- if (mmc->host_caps & MMC_MODE_HC)
+ if (mmc->cfg->host_caps & MMC_MODE_HC)
cmd->cmdarg |= OCR_HCS;
}
err = mmc_send_cmd(mmc, cmd, NULL);
return 0;
}
-int mmc_send_op_cond(struct mmc *mmc)
+static int mmc_send_op_cond(struct mmc *mmc)
{
struct mmc_cmd cmd;
int err, i;
return IN_PROGRESS;
}
-int mmc_complete_op_cond(struct mmc *mmc)
+static int mmc_complete_op_cond(struct mmc *mmc)
{
struct mmc_cmd cmd;
int timeout = 1000;
mmc->ocr = cmd.response[0];
mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
- mmc->rca = 0;
+ mmc->rca = 1;
return 0;
}
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
if (err)
- return err;
+ return err == SWITCH_ERR ? 0 : err;
/* Now check to see that it worked */
err = mmc_send_ext_csd(mmc, ext_csd);
return 0;
/* High Speed is set, there are two types: 52MHz and 26MHz */
- if (cardtype & MMC_HS_52MHZ)
+ if (cardtype & EXT_CSD_CARD_TYPE_52) {
+ if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
+ mmc->card_caps |= MMC_MODE_DDR_52MHz;
mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
- else
+ } else {
mmc->card_caps |= MMC_MODE_HS;
+ }
return 0;
}
return 0;
}
+int mmc_select_hwpart(int dev_num, int hwpart)
+{
+ struct mmc *mmc = find_mmc_device(dev_num);
+ int ret;
+
+ if (!mmc)
+ return -ENODEV;
+
+ if (mmc->part_num == hwpart)
+ return 0;
+
+ if (mmc->part_config == MMCPART_NOAVAILABLE) {
+ printf("Card doesn't support part_switch\n");
+ return -EMEDIUMTYPE;
+ }
+
+ ret = mmc_switch_part(dev_num, hwpart);
+ if (ret)
+ return ret;
+
+ mmc->part_num = hwpart;
+
+ return 0;
+}
+
+
int mmc_switch_part(int dev_num, unsigned int part_num)
{
struct mmc *mmc = find_mmc_device(dev_num);
ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
(mmc->part_config & ~PART_ACCESS_MASK)
| (part_num & PART_ACCESS_MASK));
- if (ret)
- return ret;
- return mmc_set_capacity(mmc, part_num);
+ /*
+ * Set the capacity if the switch succeeded or was intended
+ * to return to representing the raw device.
+ */
+ if ((ret == 0) || ((ret == -ENODEV) && (part_num == 0)))
+ ret = mmc_set_capacity(mmc, part_num);
+
+ return ret;
}
int mmc_getcd(struct mmc *mmc)
cd = board_mmc_getcd(mmc);
if (cd < 0) {
- if (mmc->getcd)
- cd = mmc->getcd(mmc);
+ if (mmc->cfg->ops->getcd)
+ cd = mmc->cfg->ops->getcd(mmc);
else
cd = 1;
}
* This can avoid furthur problem when the card runs in different
* mode between the host.
*/
- if (!((mmc->host_caps & MMC_MODE_HS_52MHz) &&
- (mmc->host_caps & MMC_MODE_HS)))
+ if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) &&
+ (mmc->cfg->host_caps & MMC_MODE_HS)))
return 0;
err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
static void mmc_set_ios(struct mmc *mmc)
{
- mmc->set_ios(mmc);
+ if (mmc->cfg->ops->set_ios)
+ mmc->cfg->ops->set_ios(mmc);
}
void mmc_set_clock(struct mmc *mmc, uint clock)
{
- if (clock > mmc->f_max)
- clock = mmc->f_max;
+ if (clock > mmc->cfg->f_max)
+ clock = mmc->cfg->f_max;
- if (clock < mmc->f_min)
- clock = mmc->f_min;
+ if (clock < mmc->cfg->f_min)
+ clock = mmc->cfg->f_min;
mmc->clock = clock;
mmc->tran_speed = freq * mult;
+ mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1);
mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
if (IS_SD(mmc))
if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
+ if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) {
+ cmd.cmdidx = MMC_CMD_SET_DSR;
+ cmd.cmdarg = (mmc->dsr & 0xffff) << 16;
+ cmd.resp_type = MMC_RSP_NONE;
+ if (mmc_send_cmd(mmc, &cmd, NULL))
+ printf("MMC: SET_DSR failed\n");
+ }
+
/* Select the card, and put it into Transfer Mode */
if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
cmd.cmdidx = MMC_CMD_SELECT_CARD;
}
/*
- * Check whether GROUP_DEF is set, if yes, read out
- * group size from ext_csd directly, or calculate
- * the group size from the csd value.
+ * Host needs to enable ERASE_GRP_DEF bit if device is
+ * partitioned. This bit will be lost every time after a reset
+ * or power off. This will affect erase size.
*/
- if (ext_csd[EXT_CSD_ERASE_GROUP_DEF]) {
+ if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
+ (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) {
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_ERASE_GROUP_DEF, 1);
+
+ if (err)
+ return err;
+ else
+ ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
+
+ /* Read out group size from ext_csd */
mmc->erase_grp_size =
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
MMC_MAX_BLOCK_LEN * 1024;
} else {
+ /* Calculate the group size from the csd value. */
int erase_gsz, erase_gmul;
erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
return err;
/* Restrict card's capabilities by what the host can do */
- mmc->card_caps &= mmc->host_caps;
+ mmc->card_caps &= mmc->cfg->host_caps;
if (IS_SD(mmc)) {
if (mmc->card_caps & MMC_MODE_4BIT) {
/* An array of possible bus widths in order of preference */
static unsigned ext_csd_bits[] = {
+ EXT_CSD_DDR_BUS_WIDTH_8,
+ EXT_CSD_DDR_BUS_WIDTH_4,
EXT_CSD_BUS_WIDTH_8,
EXT_CSD_BUS_WIDTH_4,
EXT_CSD_BUS_WIDTH_1,
/* An array to map CSD bus widths to host cap bits */
static unsigned ext_to_hostcaps[] = {
+ [EXT_CSD_DDR_BUS_WIDTH_4] = MMC_MODE_DDR_52MHz,
+ [EXT_CSD_DDR_BUS_WIDTH_8] = MMC_MODE_DDR_52MHz,
[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT,
[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT,
};
/* An array to map chosen bus width to an integer */
static unsigned widths[] = {
- 8, 4, 1,
+ 8, 4, 8, 4, 1,
};
for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
* this bus width, if it's more than 1
*/
if (extw != EXT_CSD_BUS_WIDTH_1 &&
- !(mmc->host_caps & ext_to_hostcaps[extw]))
+ !(mmc->cfg->host_caps & ext_to_hostcaps[extw]))
continue;
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
mmc_set_bus_width(mmc, widths[idx]);
err = mmc_send_ext_csd(mmc, test_csd);
+ /* Only compare read only fields */
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_HC_WP_GRP_SIZE] \
+ == test_csd[EXT_CSD_HC_WP_GRP_SIZE] \
&& ext_csd[EXT_CSD_REV] \
== test_csd[EXT_CSD_REV]
&& ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] \
cmd.cmdidx = SD_CMD_SEND_IF_COND;
/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
- cmd.cmdarg = ((mmc->voltages & 0xff8000) != 0) << 8 | 0xaa;
+ cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa;
cmd.resp_type = MMC_RSP_R7;
err = mmc_send_cmd(mmc, &cmd, NULL);
return 0;
}
-int mmc_register(struct mmc *mmc)
+/* not used any more */
+int __deprecated mmc_register(struct mmc *mmc)
+{
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
+ printf("%s is deprecated! use mmc_create() instead.\n", __func__);
+#endif
+ return -1;
+}
+
+struct mmc *mmc_create(const struct mmc_config *cfg, void *priv)
{
+ struct mmc *mmc;
+
+ /* quick validation */
+ if (cfg == NULL || cfg->ops == NULL || cfg->ops->send_cmd == NULL ||
+ cfg->f_min == 0 || cfg->f_max == 0 || cfg->b_max == 0)
+ return NULL;
+
+ mmc = calloc(1, sizeof(*mmc));
+ if (mmc == NULL)
+ return NULL;
+
+ mmc->cfg = cfg;
+ mmc->priv = priv;
+
+ /* the following chunk was mmc_register() */
+
+ /* Setup dsr related values */
+ mmc->dsr_imp = 0;
+ mmc->dsr = 0xffffffff;
/* Setup the universal parts of the block interface just once */
mmc->block_dev.if_type = IF_TYPE_MMC;
mmc->block_dev.dev = cur_dev_num++;
mmc->block_dev.block_read = mmc_bread;
mmc->block_dev.block_write = mmc_bwrite;
mmc->block_dev.block_erase = mmc_berase;
- if (!mmc->b_max)
- mmc->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
- INIT_LIST_HEAD (&mmc->link);
+ /* setup initial part type */
+ mmc->block_dev.part_type = mmc->cfg->part_type;
- list_add_tail (&mmc->link, &mmc_devices);
+ INIT_LIST_HEAD(&mmc->link);
- return 0;
+ list_add_tail(&mmc->link, &mmc_devices);
+
+ return mmc;
+}
+
+void mmc_destroy(struct mmc *mmc)
+{
+ /* only freeing memory for now */
+ free(mmc);
}
#ifdef CONFIG_PARTITIONS
}
#endif
+/* board-specific MMC power initializations. */
+__weak void board_mmc_power_init(void)
+{
+}
+
int mmc_start_init(struct mmc *mmc)
{
int err;
- if (mmc_getcd(mmc) == 0) {
+ /* we pretend there's no card when init is NULL */
+ if (mmc_getcd(mmc) == 0 || mmc->cfg->ops->init == NULL) {
mmc->has_init = 0;
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("MMC: no card present\n");
if (mmc->has_init)
return 0;
- err = mmc->init(mmc);
+ board_mmc_power_init();
+
+ /* made sure it's not NULL earlier */
+ err = mmc->cfg->ops->init(mmc);
if (err)
return err;
int mmc_init(struct mmc *mmc)
{
int err = IN_PROGRESS;
- unsigned start = get_timer(0);
+ unsigned start;
if (mmc->has_init)
return 0;
+
+ start = get_timer(0);
+
if (!mmc->init_in_progress)
err = mmc_start_init(mmc);
return err;
}
-/*
- * CPU and board-specific MMC initializations. Aliased function
- * signals caller to move on
- */
-static int __def_mmc_init(bd_t *bis)
+int mmc_set_dsr(struct mmc *mmc, u16 val)
+{
+ mmc->dsr = val;
+ return 0;
+}
+
+/* CPU-specific MMC initializations */
+__weak int cpu_mmc_init(bd_t *bis)
{
return -1;
}
-int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
-int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
+/* board-specific MMC initializations. */
+__weak int board_mmc_init(bd_t *bis)
+{
+ return -1;
+}
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
list_for_each(entry, &mmc_devices) {
m = list_entry(entry, struct mmc, link);
- printf("%s: %d", m->name, m->block_dev.dev);
+ printf("%s: %d", m->cfg->name, m->block_dev.dev);
if (entry->next != &mmc_devices)
printf("%c ", separator);
}
/*
- * This function shall form and send the commands to open / close the
- * boot partition specified by user.
- *
- * Input Parameters:
- * ack: 0x0 - No boot acknowledge sent (default)
- * 0x1 - Boot acknowledge sent during boot operation
- * part_num: User selects boot data that will be sent to master
- * 0x0 - Device not boot enabled (default)
- * 0x1 - Boot partition 1 enabled for boot
- * 0x2 - Boot partition 2 enabled for boot
- * access: User selects partitions to access
- * 0x0 : No access to boot partition (default)
- * 0x1 : R/W boot partition 1
- * 0x2 : R/W boot partition 2
- * 0x3 : R/W Replay Protected Memory Block (RPMB)
+ * Modify EXT_CSD[177] which is BOOT_BUS_WIDTH
+ * based on the passed in values for BOOT_BUS_WIDTH, RESET_BOOT_BUS_WIDTH
+ * and BOOT_MODE.
*
* Returns 0 on success.
*/
-int mmc_boot_part_access(struct mmc *mmc, u8 ack, u8 part_num, u8 access)
+int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode)
{
int err;
- struct mmc_cmd cmd;
- /* Boot ack enable, boot partition enable , boot partition access */
- cmd.cmdidx = MMC_CMD_SWITCH;
- cmd.resp_type = MMC_RSP_R1b;
-
- cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
- (EXT_CSD_PART_CONF << 16) |
- ((EXT_CSD_BOOT_ACK(ack) |
- EXT_CSD_BOOT_PART_NUM(part_num) |
- EXT_CSD_PARTITION_ACCESS(access)) << 8);
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_BUS_WIDTH,
+ EXT_CSD_BOOT_BUS_WIDTH_MODE(mode) |
+ EXT_CSD_BOOT_BUS_WIDTH_RESET(reset) |
+ EXT_CSD_BOOT_BUS_WIDTH_WIDTH(width));
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err) {
- if (access) {
- debug("mmc boot partition#%d open fail:Error1 = %d\n",
- part_num, err);
- } else {
- debug("mmc boot partition#%d close fail:Error = %d\n",
- part_num, err);
- }
+ if (err)
return err;
- }
+ return 0;
+}
- if (access) {
- /* 4bit transfer mode at booting time. */
- cmd.cmdidx = MMC_CMD_SWITCH;
- cmd.resp_type = MMC_RSP_R1b;
+/*
+ * Modify EXT_CSD[179] which is PARTITION_CONFIG (formerly BOOT_CONFIG)
+ * based on the passed in values for BOOT_ACK, BOOT_PARTITION_ENABLE and
+ * PARTITION_ACCESS.
+ *
+ * Returns 0 on success.
+ */
+int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access)
+{
+ int err;
- cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
- (EXT_CSD_BOOT_BUS_WIDTH << 16) |
- ((1 << 0) << 8);
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
+ EXT_CSD_BOOT_ACK(ack) |
+ EXT_CSD_BOOT_PART_NUM(part_num) |
+ EXT_CSD_PARTITION_ACCESS(access));
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err) {
- debug("mmc boot partition#%d open fail:Error2 = %d\n",
- part_num, err);
- return err;
- }
- }
+ if (err)
+ return err;
return 0;
}
+
+/*
+ * Modify EXT_CSD[162] which is RST_n_FUNCTION based on the given value
+ * for enable. Note that this is a write-once field for non-zero values.
+ *
+ * Returns 0 on success.
+ */
+int mmc_set_rst_n_function(struct mmc *mmc, u8 enable)
+{
+ return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_RST_N_FUNCTION,
+ enable);
+}
#endif