summaryrefslogtreecommitdiff
path: root/drivers/mmc/mmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/mmc.c')
-rw-r--r--drivers/mmc/mmc.c271
1 files changed, 239 insertions, 32 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 2590f1bcce..a492bbb41f 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -524,48 +524,70 @@ static int sd_send_op_cond(struct mmc *mmc)
return 0;
}
-static int mmc_send_op_cond(struct mmc *mmc)
+/* We pass in the cmd since otherwise the init seems to fail */
+static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd,
+ int use_arg)
{
- int timeout = 10000;
- struct mmc_cmd cmd;
int err;
+ cmd->cmdidx = MMC_CMD_SEND_OP_COND;
+ cmd->resp_type = MMC_RSP_R3;
+ cmd->cmdarg = 0;
+ if (use_arg && !mmc_host_is_spi(mmc)) {
+ cmd->cmdarg =
+ (mmc->voltages &
+ (mmc->op_cond_response & OCR_VOLTAGE_MASK)) |
+ (mmc->op_cond_response & OCR_ACCESS_MODE);
+
+ if (mmc->host_caps & MMC_MODE_HC)
+ cmd->cmdarg |= OCR_HCS;
+ }
+ err = mmc_send_cmd(mmc, cmd, NULL);
+ if (err)
+ return err;
+ mmc->op_cond_response = cmd->response[0];
+ return 0;
+}
+
+int mmc_send_op_cond(struct mmc *mmc)
+{
+ struct mmc_cmd cmd;
+ int err, i;
+
/* Some cards seem to need this */
mmc_go_idle(mmc);
/* Asking to the card its capabilities */
- cmd.cmdidx = MMC_CMD_SEND_OP_COND;
- cmd.resp_type = MMC_RSP_R3;
- cmd.cmdarg = 0;
-
- err = mmc_send_cmd(mmc, &cmd, NULL);
+ mmc->op_cond_pending = 1;
+ for (i = 0; i < 2; i++) {
+ err = mmc_send_op_cond_iter(mmc, &cmd, i != 0);
+ if (err)
+ return err;
- if (err)
- return err;
+ /* exit if not busy (flag seems to be inverted) */
+ if (mmc->op_cond_response & OCR_BUSY)
+ return 0;
+ }
+ return IN_PROGRESS;
+}
- udelay(1000);
+int mmc_complete_op_cond(struct mmc *mmc)
+{
+ struct mmc_cmd cmd;
+ int timeout = 1000;
+ uint start;
+ int err;
+ mmc->op_cond_pending = 0;
+ start = get_timer(0);
do {
- cmd.cmdidx = MMC_CMD_SEND_OP_COND;
- cmd.resp_type = MMC_RSP_R3;
- cmd.cmdarg = (mmc_host_is_spi(mmc) ? 0 :
- (mmc->voltages &
- (cmd.response[0] & OCR_VOLTAGE_MASK)) |
- (cmd.response[0] & OCR_ACCESS_MODE));
-
- if (mmc->host_caps & MMC_MODE_HC)
- cmd.cmdarg |= OCR_HCS;
-
- err = mmc_send_cmd(mmc, &cmd, NULL);
-
+ err = mmc_send_op_cond_iter(mmc, &cmd, 1);
if (err)
return err;
-
- udelay(1000);
- } while (!(cmd.response[0] & OCR_BUSY) && timeout--);
-
- if (timeout <= 0)
- return UNUSABLE_ERR;
+ if (get_timer(start) > timeout)
+ return UNUSABLE_ERR;
+ udelay(100);
+ } while (!(mmc->op_cond_response & OCR_BUSY));
if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
@@ -1274,7 +1296,7 @@ block_dev_desc_t *mmc_get_dev(int dev)
}
#endif
-int mmc_init(struct mmc *mmc)
+int mmc_start_init(struct mmc *mmc)
{
int err;
@@ -1314,17 +1336,48 @@ int mmc_init(struct mmc *mmc)
if (err == TIMEOUT) {
err = mmc_send_op_cond(mmc);
- if (err) {
+ if (err && err != IN_PROGRESS) {
printf("Card did not respond to voltage select!\n");
return UNUSABLE_ERR;
}
}
- err = mmc_startup(mmc);
+ if (err == IN_PROGRESS)
+ mmc->init_in_progress = 1;
+
+ return err;
+}
+
+static int mmc_complete_init(struct mmc *mmc)
+{
+ int err = 0;
+
+ if (mmc->op_cond_pending)
+ err = mmc_complete_op_cond(mmc);
+
+ if (!err)
+ err = mmc_startup(mmc);
if (err)
mmc->has_init = 0;
else
mmc->has_init = 1;
+ mmc->init_in_progress = 0;
+ return err;
+}
+
+int mmc_init(struct mmc *mmc)
+{
+ int err = IN_PROGRESS;
+ unsigned start = get_timer(0);
+
+ if (mmc->has_init)
+ return 0;
+ if (!mmc->init_in_progress)
+ err = mmc_start_init(mmc);
+
+ if (!err || err == IN_PROGRESS)
+ err = mmc_complete_init(mmc);
+ debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
return err;
}
@@ -1362,6 +1415,25 @@ int get_mmc_num(void)
return cur_dev_num;
}
+void mmc_set_preinit(struct mmc *mmc, int preinit)
+{
+ mmc->preinit = preinit;
+}
+
+static void do_preinit(void)
+{
+ struct mmc *m;
+ struct list_head *entry;
+
+ list_for_each(entry, &mmc_devices) {
+ m = list_entry(entry, struct mmc, link);
+
+ if (m->preinit)
+ mmc_start_init(m);
+ }
+}
+
+
int mmc_initialize(bd_t *bis)
{
INIT_LIST_HEAD (&mmc_devices);
@@ -1372,5 +1444,140 @@ int mmc_initialize(bd_t *bis)
print_mmc_devices(',');
+ do_preinit();
+ return 0;
+}
+
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+/*
+ * This function changes the size of boot partition and the size of rpmb
+ * partition present on EMMC devices.
+ *
+ * Input Parameters:
+ * struct *mmc: pointer for the mmc device strcuture
+ * bootsize: size of boot partition
+ * rpmbsize: size of rpmb partition
+ *
+ * Returns 0 on success.
+ */
+
+int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize,
+ unsigned long rpmbsize)
+{
+ int err;
+ struct mmc_cmd cmd;
+
+ /* Only use this command for raw EMMC moviNAND. Enter backdoor mode */
+ cmd.cmdidx = MMC_CMD_RES_MAN;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.cmdarg = MMC_CMD62_ARG1;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ debug("mmc_boot_partition_size_change: Error1 = %d\n", err);
+ return err;
+ }
+
+ /* Boot partition changing mode */
+ cmd.cmdidx = MMC_CMD_RES_MAN;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.cmdarg = MMC_CMD62_ARG2;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ debug("mmc_boot_partition_size_change: Error2 = %d\n", err);
+ return err;
+ }
+ /* boot partition size is multiple of 128KB */
+ bootsize = (bootsize * 1024) / 128;
+
+ /* Arg: boot partition size */
+ cmd.cmdidx = MMC_CMD_RES_MAN;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.cmdarg = bootsize;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ debug("mmc_boot_partition_size_change: Error3 = %d\n", err);
+ return err;
+ }
+ /* RPMB partition size is multiple of 128KB */
+ rpmbsize = (rpmbsize * 1024) / 128;
+ /* Arg: RPMB partition size */
+ cmd.cmdidx = MMC_CMD_RES_MAN;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.cmdarg = rpmbsize;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ debug("mmc_boot_partition_size_change: Error4 = %d\n", err);
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * 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)
+ *
+ * Returns 0 on success.
+ */
+int mmc_boot_part_access(struct mmc *mmc, u8 ack, u8 part_num, u8 access)
+{
+ 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_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);
+ }
+ return err;
+ }
+
+ if (access) {
+ /* 4bit transfer mode at booting time. */
+ cmd.cmdidx = MMC_CMD_SWITCH;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (EXT_CSD_BOOT_BUS_WIDTH << 16) |
+ ((1 << 0) << 8);
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ debug("mmc boot partition#%d open fail:Error2 = %d\n",
+ part_num, err);
+ return err;
+ }
+ }
return 0;
}
+#endif