summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin LUNDHOLM <martin.xa.lundholm@stericsson.com>2010-08-23 17:31:29 +0200
committerMichael BRANDT <michael.brandt@stericsson.com>2010-08-30 19:39:16 +0200
commit3a5b27b58258ff61dfae3167bc46fed0146ddffd (patch)
treef185f10ccb07c693998340343afc4a680949aea7
parent6e7432129841a6177b097c5d2388c6aa9ed257c3 (diff)
U-boot: MMC update.
MMC has been updated with several improvements. Primarily MMC performance has been improved by using assembler code for low level FIFO handling. Also some MMC functionality has been added, e.g. support for DDR and reliable write. Data and command delay times were incremented, otherwise hangups and timeouts were observed. Tested on HREF+ 1.1 V21 and HREF+ 1.1 V32 (Micron PoP). Following WP depends on this change (more reliable SD card write support): ST-Ericsson ID: WP264488 Change-Id: Ic92abffe1640aa9375b8d43a6b8522ca8296a368 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/4056 Reviewed-by: Michael BRANDT <michael.brandt@stericsson.com> Tested-by: Michael BRANDT <michael.brandt@stericsson.com>
-rw-r--r--board/st/u8500/Makefile4
-rw-r--r--board/st/u8500/mmc_fifo.S146
-rw-r--r--board/st/u8500/mmc_fifo.h45
-rw-r--r--board/st/u8500/mmc_host.c295
-rw-r--r--board/st/u8500/mmc_host.h9
-rw-r--r--board/st/u8500/mmc_utils.c14
-rw-r--r--common/cmd_mmc.c245
-rw-r--r--drivers/mmc/mmc.c170
-rw-r--r--include/mmc.h116
9 files changed, 790 insertions, 254 deletions
diff --git a/board/st/u8500/Makefile b/board/st/u8500/Makefile
index c151f72b5..3bd3a7a37 100644
--- a/board/st/u8500/Makefile
+++ b/board/st/u8500/Makefile
@@ -22,15 +22,17 @@
include $(TOPDIR)/config.mk
-CFLAGS += -D__RELEASE -D__STN_8500
LIB = $(obj)lib$(BOARD).a
COBJS := u8500.o flash.o gpio.o u8500_i2c.o mmc_host.o mmc_utils.o clock.o prcmu.o mcde_display.o mcde_hw.o ab8500vibra.o
+SOBJS := mmc_fifo.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
SOBJS := $(addprefix $(obj),$(SOBJS))
+$(obj)mmc_host.o: CFLAGS += -O2
+
$(LIB): $(obj).depend $(OBJS) $(SOBJS)
$(AR) $(ARFLAGS) $@ $(OBJS) $(SOBJS)
diff --git a/board/st/u8500/mmc_fifo.S b/board/st/u8500/mmc_fifo.S
new file mode 100644
index 000000000..44911a8d5
--- /dev/null
+++ b/board/st/u8500/mmc_fifo.S
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Martin Lundholm <martin.xa.lundholm@stericsson.com>
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include "mmc_fifo.h"
+#include "mmc_host.h"
+
+/*
+ * Function: mmc_fifo_read()
+ *
+ * int mmc_fifo_read(u32 *fifo, u32 *buf, unsigned int count, u32 *status_reg)
+ *
+ * Info: Reads data from an MMC (ARM PL180) FIFO
+ *
+ * Parameters:
+ * fifo - pointer to the first PL180 FIFO register
+ * buf - pointer to a read buffer (32-bit aligned)
+ * count - number of bytes to be read (32-bit aligned)
+ * status_reg - pointer to the PL180 status register
+ *
+ * Returns '0' if success and PL180 status on failure.
+ *
+ */
+
+ .globl mmc_fifo_read
+mmc_fifo_read:
+ push {r4-r10,lr}
+mmc_fifo_read_loop_32_1:
+ /* If count is <32B read word-wise */
+ cmp r2,#32
+ blo mmc_fifo_read_loop_4_1
+mmc_fifo_read_loop_32_2:
+ /* Load SDI_STA to r4 */
+ ldr r4,[r3]
+ /* Exit if SDI_STA_DCRCFAIL or SDI_STA_DTIMEOUT is set */
+ ands r5,r4,#(SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT)
+ bne mmc_fifo_read_fail
+ /* Wait until SDI_STA_RXFIFOBR is set */
+ tst r4,#SDI_STA_RXFIFOBR
+ beq mmc_fifo_read_loop_32_2
+ /* Load and store 8 words */
+ ldmia r0,{r4-r10,lr}
+ stmia r1!,{r4-r10,lr}
+ subs r2,r2,#32
+ b mmc_fifo_read_loop_32_1
+mmc_fifo_read_loop_4_1:
+ /* Read word wise */
+ cmp r2,#4
+ blo mmc_fifo_read_ok
+mmc_fifo_read_loop_4_2:
+ /* Load SDI_STA to r4 */
+ ldr r4,[r3]
+ /* Exit if SDI_STA_DCRCFAIL or SDI_STA_DTIMEOUT is set */
+ ands r5,r4,#(SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT)
+ bne mmc_fifo_read_fail
+ /* Wait until SDI_STA_RXDAVL is set */
+ tst r4,#SDI_STA_RXDAVL
+ beq mmc_fifo_read_loop_4_2
+ /* Load and store 1 word */
+ ldmia r0,{r4}
+ stmia r1!,{r4}
+ subs r2,r2,#4
+ b mmc_fifo_read_loop_4_1
+mmc_fifo_read_ok:
+ /* Wait until SDI_STA_DBCKEND and SDI_STA_DATAEND are set */
+ ldr r4,[r3]
+ ands r5,r4,#(SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT)
+ bne mmc_fifo_read_fail
+ and r5,r4,#(SDI_STA_DBCKEND | SDI_STA_DATAEND)
+ cmp r5,#(SDI_STA_DBCKEND | SDI_STA_DATAEND)
+ bne mmc_fifo_read_ok
+mmc_fifo_read_fail:
+ mov r0,r4
+ pop {r4-r10,pc}
+
+/*
+ * Function: mmc_fifo_write()
+ *
+ * int mmc_fifo_write(u32 *buf, u32 *fifo, unsigned int count, u32 *status_reg)
+ *
+ * Info: Writes data to an MMC (ARM PL180) FIFO
+ *
+ * Parameters:
+ * buf - pointer to a write buffer (32-bit aligned)
+ * fifo - pointer to the first PL180 FIFO register
+ * count - number of bytes to be written (32-bit aligned)
+ * status_reg - pointer to the PL180 status register
+ *
+ * Returns '0' if success and PL180 status on failure.
+ *
+ */
+
+ .globl mmc_fifo_write
+mmc_fifo_write:
+ push {r4-r10,lr}
+mmc_fifo_write_loop_32_1:
+ /* If count is <32B read word-wise */
+ cmp r2,#32
+ blo mmc_fifo_write_loop_4_1
+mmc_fifo_write_loop_32_2:
+ /* Load SDI_STA to r4 */
+ ldr r4,[r3]
+ /* Exit if SDI_STA_DCRCFAIL or SDI_STA_DTIMEOUT is set */
+ ands r5,r4,#(SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT)
+ bne mmc_fifo_write_fail
+ /* Wait until SDI_STA_TXFIFOBW is set */
+ tst r4,#SDI_STA_TXFIFOBW
+ beq mmc_fifo_write_loop_32_2
+ /* Load and store 8 words */
+ ldmia r0!,{r4-r10,lr}
+ stmia r1,{r4-r10,lr}
+ subs r2,r2,#32
+ b mmc_fifo_write_loop_32_1
+mmc_fifo_write_loop_4_1:
+ /* Read word wise */
+ cmp r2,#4
+ blo mmc_fifo_write_ok
+mmc_fifo_write_loop_4_2:
+ /* Load SDI_STA to r4 */
+ ldr r4,[r3]
+ /* Exit if SDI_STA_DCRCFAIL or SDI_STA_DTIMEOUT is set */
+ ands r5,r4,#(SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT)
+ bne mmc_fifo_write_fail
+ /* Wait until SDI_STA_TXFIFOBW is set */
+ tst r4,#SDI_STA_TXFIFOBW
+ beq mmc_fifo_write_loop_4_2
+ /* Load and store 1 word */
+ ldmia r0!,{r4}
+ stmia r1,{r4}
+ subs r2,r2,#4
+ b mmc_fifo_write_loop_4_1
+mmc_fifo_write_ok:
+ /* Wait until SDI_STA_DBCKEND and SDI_STA_DATAEND are set */
+ ldr r4,[r3]
+ ands r5,r4,#(SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT)
+ bne mmc_fifo_write_fail
+ and r5,r4,#(SDI_STA_DBCKEND | SDI_STA_DATAEND)
+ cmp r5,#(SDI_STA_DBCKEND | SDI_STA_DATAEND)
+ bne mmc_fifo_write_ok
+mmc_fifo_write_fail:
+ mov r0,r4
+ pop {r4-r10,pc}
diff --git a/board/st/u8500/mmc_fifo.h b/board/st/u8500/mmc_fifo.h
new file mode 100644
index 000000000..e2fb0a53b
--- /dev/null
+++ b/board/st/u8500/mmc_fifo.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Martin Lundholm <martin.xa.lundholm@stericsson.com>
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <common.h>
+
+#ifndef __ASSEMBLY__
+
+/*
+ * Function: mmc_fifo_read()
+ *
+ * Info: Reads data from an MMC (ARM PL180) FIFO
+ *
+ * Parameters:
+ * fifo - pointer to the first PL180 FIFO register
+ * buf - pointer to a read buffer (32-bit aligned)
+ * count - number of bytes to be read (32-bit aligned)
+ * status_reg - pointer to the PL180 status register
+ *
+ * Returns '0' if success and PL180 status on failure.
+ *
+ */
+int mmc_fifo_read(u32 *fifo, u32 *buf, unsigned int count, u32 *status_reg);
+
+/*
+ * Function: mmc_fifo_write()
+ *
+ * Info: Writes data to an MMC (ARM PL180) FIFO
+ *
+ * Parameters:
+ * buf - pointer to a write buffer (32-bit aligned)
+ * fifo - pointer to the first PL180 FIFO register
+ * count - number of bytes to be written (32-bit aligned)
+ * status_reg - pointer to the PL180 status register
+ *
+ * Returns '0' if success and PL180 status on failure.
+ *
+ */
+int mmc_fifo_write(u32 *buf, u32 *fifo, unsigned int count, u32 *status_reg);
+
+#endif /* __ASSEMBLY__ */
diff --git a/board/st/u8500/mmc_host.c b/board/st/u8500/mmc_host.c
index 9bf319504..df46eb02c 100644
--- a/board/st/u8500/mmc_host.c
+++ b/board/st/u8500/mmc_host.c
@@ -7,13 +7,20 @@
* License terms: GNU General Public License (GPL), version 2.
*/
-/*#define DEBUG*/
+/*
+ * There are two levels of debug printouts in this file. The macro DEBUG can be
+ * set to either DBG_LVL_INFO (1) or DBG_LVL_VERBOSE (2).
+ */
+#define DBG_LVL_INFO (1)
+#define DBG_LVL_VERBOSE (2)
#include "common.h"
#include <mmc.h>
-#include "gpio.h" /* two copies of gpio.h exists - why? */
+#include "gpio.h"
#include "mmc_host.h"
#include <malloc.h>
+#include <div64.h>
+#include "mmc_fifo.h"
struct mmc_host {
struct sdi_registers *base;
@@ -21,7 +28,7 @@ struct mmc_host {
/*
* wait_for_command_end() - waiting for the command completion
- * this function will wait till the command completion has happened or
+ * this function will wait until the command completion has happened or
* any error generated by reading the status register
*/
static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)
@@ -39,15 +46,15 @@ static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)
hoststatus = readl(&host->base->status) & statusmask;
while (!hoststatus);
- debug("SDI_ICR <= 0x%08X\n", statusmask);
+ debugX(DBG_LVL_VERBOSE, "SDI_ICR <= 0x%08X\n", statusmask);
writel(statusmask, &host->base->status_clear);
if (hoststatus & SDI_STA_CTIMEOUT) {
- debug("CMD%d time out\n", cmd->cmdidx);
+ debugX(DBG_LVL_VERBOSE, "CMD%d time out\n", cmd->cmdidx);
return TIMEOUT;
} else if ((hoststatus & SDI_STA_CCRCFAIL) &&
(cmd->flags & MMC_RSP_CRC)) {
- debug("CMD%d CRC error\n", cmd->cmdidx);
+ debugX(DBG_LVL_VERBOSE, "CMD%d CRC error\n", cmd->cmdidx);
return MMC_CMD_CRC_FAIL;
}
@@ -56,7 +63,8 @@ static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)
cmd->response[1] = readl(&host->base->response1);
cmd->response[2] = readl(&host->base->response2);
cmd->response[3] = readl(&host->base->response3);
- debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, "
+ debugX(DBG_LVL_VERBOSE,
+ "CMD%d response[0]:0x%08X, response[1]:0x%08X, "
"response[2]:0x%08X, response[3]:0x%08X\n",
cmd->cmdidx, cmd->response[0], cmd->response[1],
cmd->response[2], cmd->response[3]);
@@ -72,8 +80,10 @@ static int do_command(struct mmc *dev, struct mmc_cmd *cmd)
int result;
u32 sdi_cmd = 0;
struct mmc_host *host = dev->priv;
+ u32 lap = 0;
- debug("Request to do CMD%d on %s\n", cmd->cmdidx, dev->name);
+ debugX(DBG_LVL_VERBOSE, "Request to do CMD%d on %s\n", cmd->cmdidx,
+ dev->name);
sdi_cmd = (cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN;
@@ -83,13 +93,24 @@ static int do_command(struct mmc *dev, struct mmc_cmd *cmd)
sdi_cmd |= SDI_CMD_LONGRESP;
}
- debug("SDI_ARG <= 0x%08X\n", cmd->cmdarg);
+ debugX(DBG_LVL_VERBOSE, "SDI_ARG <= 0x%08X\n", cmd->cmdarg);
writel((u32)cmd->cmdarg, &host->base->argument);
udelay(COMMAND_REG_DELAY); /* DONT REMOVE */
- debug("SDI_CMD <= 0x%08X\n", sdi_cmd);
- writel(sdi_cmd, &host->base->command);
-
- result = wait_for_command_end(dev, cmd);
+ debugX(DBG_LVL_VERBOSE, "SDI_CMD <= 0x%08X\n", sdi_cmd);
+
+ /*
+ * It has been noticed that after a write operation some cards does
+ * not respond to a new command for a few milliseconds. So here we
+ * retry the command a couple of times if we get a timeout.
+ */
+ do {
+ writel(sdi_cmd, &host->base->command);
+ result = wait_for_command_end(dev, cmd);
+ if ((result != TIMEOUT) || (lap >= 10))
+ break;
+ udelay(1000);
+ lap++;
+ } while (1);
/* After CMD2 set RCA to a none zero value. */
if ((result == MMC_OK) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID))
@@ -98,7 +119,7 @@ static int do_command(struct mmc *dev, struct mmc_cmd *cmd)
/* After CMD3 open drain is switched off and push pull is used. */
if ((result == MMC_OK) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) {
u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD;
- debug("SDI_PWR <= 0x%08X\n", sdi_pwr);
+ debugX(DBG_LVL_VERBOSE, "SDI_PWR <= 0x%08X\n", sdi_pwr);
writel(sdi_pwr, &host->base->power);
}
@@ -121,76 +142,28 @@ static int convert_from_bytes_to_power_of_two(unsigned int x)
*/
static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize)
{
- u32 *tempbuff = dest;
- int i;
u64 xfercount = blkcount * blksize;
struct mmc_host *host = dev->priv;
u32 status;
- u32 status_err;
-
- debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
-
- status = readl(&host->base->status);
- status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT);
- while (!status_err &&
- (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32))) {
- if (status & SDI_STA_RXFIFOBR) {
- for (i = 0; i < SDI_FIFO_BURST_SIZE; i++)
- *(tempbuff + i) = readl(&host->base->fifo);
- tempbuff += SDI_FIFO_BURST_SIZE;
- xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32);
- }
- status = readl(&host->base->status);
- status_err = status &
- (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT);
- }
-
- if (status & SDI_STA_DTIMEOUT) {
- printf("Reading data timedout, xfercount:%llu,"
- "status:0x%08X\n", xfercount, status);
- return MMC_DATA_TIMEOUT;
- } else if (status & SDI_STA_DCRCFAIL) {
- printf("Reading data CRC error\n");
- return MMC_DATA_CRC_FAIL;
- }
- while ((!status_err) && (xfercount >= sizeof(u32))) {
- if (status & SDI_STA_RXDAVL) {
- *(tempbuff) = readl(&host->base->fifo);
- tempbuff++;
- xfercount -= sizeof(u32);
- }
- status = readl(&host->base->status);
- status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT);
- }
+ debugX(DBG_LVL_VERBOSE, "read_bytes: blkcount=%u blksize=%u\n",
+ blkcount, blksize);
- /* Wait for DBCKEND */
- status_err = status &
- (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND);
- while (!status_err) {
- status = readl(&host->base->status);
- status_err = status &
- (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND);
- }
+ status = mmc_fifo_read(&host->base->fifo, dest, xfercount,
+ &host->base->status);
- if (status & SDI_STA_DTIMEOUT) {
- printf("Reading data timedout, xfercount:%llu,"
- "status:0x%08X\n", xfercount, status);
- return MMC_DATA_TIMEOUT;
- } else if (status & SDI_STA_DCRCFAIL) {
- printf("Reading data CRC error\n");
- return MMC_DATA_CRC_FAIL;
+ if (status & (SDI_STA_DTIMEOUT | SDI_STA_DCRCFAIL)) {
+ printf("Reading data failed: status:0x%08X\n", status);
+ if (status & SDI_STA_DTIMEOUT)
+ return MMC_DATA_TIMEOUT;
+ else if (status & SDI_STA_DCRCFAIL)
+ return MMC_DATA_CRC_FAIL;
}
- debug("SDI_ICR <= 0x%08X\n", SDI_ICR_MASK);
+ debugX(DBG_LVL_VERBOSE, "SDI_ICR <= 0x%08X\n", SDI_ICR_MASK);
writel(SDI_ICR_MASK, &host->base->status_clear);
- debug("Reading loop ended\n");
-
- if (xfercount) {
- printf("Reading data error, xfercount:%llu",
- (unsigned long long) xfercount);
- return MMC_GENERAL_UNKNOWN_ERROR;
- }
+ debugX(DBG_LVL_VERBOSE, "Reading data completed status:0x%08X\n",
+ status);
return MMC_OK;
}
@@ -200,65 +173,33 @@ static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize)
*/
static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize)
{
- u32 *tempbuff = src;
- int i;
u64 xfercount = blkcount * blksize;
struct mmc_host *host = dev->priv;
u32 status;
- u32 status_err;
-
- debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
-
- status = readl(&host->base->status);
- status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT);
- while (!status_err && xfercount) {
- if (status & SDI_STA_TXFIFOBW) {
- if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) {
- for (i = 0; i < SDI_FIFO_BURST_SIZE; i++)
- writel(*(tempbuff + i),
- &host->base->fifo);
- tempbuff += SDI_FIFO_BURST_SIZE;
- xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32);
- } else {
- while (xfercount >= sizeof(u32)) {
- writel(*(tempbuff), &host->base->fifo);
- tempbuff++;
- xfercount -= sizeof(u32);
- }
- }
- }
- status = readl(&host->base->status);
- status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT);
- }
+ u32 status_busy;
- /* Wait for DBCKEND */
- status_err = status &
- (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND);
- while (!status_err) {
- status = readl(&host->base->status);
- status_err = status &
- (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND);
- }
+ debugX(DBG_LVL_VERBOSE, "write_bytes: blkcount=%u blksize=%u\n",
+ blkcount, blksize);
- if (status & SDI_STA_DTIMEOUT) {
- printf(
- "Writing data timedout, xfercount:%llu,status:0x%08X\n",
- xfercount, status);
- return MMC_DATA_TIMEOUT;
- } else if (status & SDI_STA_DCRCFAIL) {
- printf("Writing data CRC error\n");
- return MMC_DATA_CRC_FAIL;
+ status = mmc_fifo_write(src, &host->base->fifo, xfercount,
+ &host->base->status);
+
+ if (status & (SDI_STA_DTIMEOUT | SDI_STA_DCRCFAIL)) {
+ printf("Writing data failed: status=0x%08X\n", status);
+ if (status & SDI_STA_DTIMEOUT)
+ return MMC_DATA_TIMEOUT;
+ else if (status & SDI_STA_DCRCFAIL)
+ return MMC_DATA_CRC_FAIL;
}
+ /* Wait if busy */
+ status_busy = status & SDI_STA_CARDBUSY;
+ while (status_busy)
+ status_busy = readl(&host->base->status) & SDI_STA_CARDBUSY;
writel(SDI_ICR_MASK, &host->base->status_clear);
- debug("Writing data loop completed status:0x%08X\n", status);
-
- if (xfercount) {
- printf("Writing data error, xfercount:%llu",
- (unsigned long long) xfercount);
- return MMC_GENERAL_UNKNOWN_ERROR;
- }
+ debugX(DBG_LVL_VERBOSE, "Writing data completed status:0x%08X\n",
+ status);
return MMC_OK;
}
@@ -280,8 +221,13 @@ static int do_data_transfer(struct mmc *dev,
u32 data_ctrl = 0;
u32 data_len = (u32) (data->blocks * data->blocksize);
- debug("Request to do data xfer on %s\n", dev->name);
- debug("do_data_transfer(%u) start\n", data->blocks);
+ debugX(DBG_LVL_VERBOSE, "Request to do data xfer on %s\n", dev->name);
+ debugX(DBG_LVL_VERBOSE, "do_data_transfer(%u) start\n", data->blocks);
+
+#if (DEBUG >= DBG_LVL_INFO)
+ if (data->blocks > 1)
+ reset_timer();
+#endif
if (cpu_is_u8500v1() || u8500_is_earlydrop()) {
blksz = convert_from_bytes_to_power_of_two(data->blocksize);
@@ -290,20 +236,31 @@ static int do_data_transfer(struct mmc *dev,
blksz = data->blocksize;
data_ctrl |= (blksz << INDEX(SDI_DCTRL_DBLOCKSIZE_V2_MASK));
}
-
- data_ctrl |= SDI_DCTRL_DTEN;
-
- debug("SDI_DTIMER <= 0x%08X\n", SDI_DTIMER_DEFAULT);
- writel(SDI_DTIMER_DEFAULT, &host->base->datatimer);
- debug("SDI_DLEN <= 0x%08X\n", data_len);
+ data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE;
+ if (dev->ddr_en &&
+ ((cmd->cmdidx == MMC_CMD_READ_SINGLE_BLOCK) ||
+ (cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) ||
+ (cmd->cmdidx == MMC_CMD_WRITE_SINGLE_BLOCK) ||
+ (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK) ||
+ (cmd->cmdidx == MMC_CMD_SEND_EXT_CSD)))
+ data_ctrl |= SDI_DCTRL_DDR_MODE;
+
+#if (DEBUG >= DBG_LVL_VERBOSE)
+ if (data_ctrl & SDI_DCTRL_DDR_MODE)
+ printf("SDI_DCTRL_DDR_MODE\n");
+#endif
+
+ debugX(DBG_LVL_VERBOSE, "SDI_DTIMER <= 0x%08X\n", dev->data_timeout);
+ writel(dev->data_timeout, &host->base->datatimer);
+ debugX(DBG_LVL_VERBOSE, "SDI_DLEN <= 0x%08X\n", data_len);
writel(data_len, &host->base->datalength);
udelay(DATA_REG_DELAY); /* DONT REMOVE */
if (data->flags & (MMC_DATA_READ)) {
- debug("It is a read operation\n");
+ debugX(DBG_LVL_VERBOSE, "It is a read operation\n");
data_ctrl |= SDI_DCTRL_DTDIR_IN;
- debug("SDI_DCTRL <= 0x%08X\n", data_ctrl);
+ debugX(DBG_LVL_VERBOSE, "SDI_DCTRL <= 0x%08X\n", data_ctrl);
writel(data_ctrl, &host->base->datactrl);
error = do_command(dev, cmd);
@@ -315,13 +272,13 @@ static int do_data_transfer(struct mmc *dev,
(u32)data->blocks,
(u32)data->blocksize);
} else if (data->flags & (MMC_DATA_WRITE)) {
- debug("It is a write operation\n");
+ debugX(DBG_LVL_VERBOSE, "It is a write operation\n");
error = do_command(dev, cmd);
if (error)
return error;
- debug("SDI_DCTRL <= 0x%08X\n", data_ctrl);
+ debugX(DBG_LVL_VERBOSE, "SDI_DCTRL <= 0x%08X\n", data_ctrl);
writel(data_ctrl, &host->base->datactrl);
error = write_bytes(dev,
@@ -330,7 +287,24 @@ static int do_data_transfer(struct mmc *dev,
(u32)data->blocksize);
}
- debug("do_data_transfer() end\n");
+#if (DEBUG >= DBG_LVL_INFO)
+ if (data->blocks > 1) {
+ u32 transfer_time = (u32) get_timer_us();
+ u64 throughput = lldiv((u64) 1000 * 1000 * data->blocks *
+ data->blocksize, transfer_time);
+ printf("MMC %s: %u bytes in %u [us] => %llu [B/s] = "
+ "%llu [kB/s] = %llu.%02u [MB/s] = %llu [Mbits/s]\n",
+ (data->flags & (MMC_DATA_READ)) ? "read" : "write",
+ data->blocks * data->blocksize,
+ transfer_time,
+ throughput, throughput / 1024,
+ throughput / (1024 * 1024),
+ (u32)((100 * throughput) / (1024 * 1024) -
+ (throughput / (1024 * 1024)) * 100),
+ throughput * 8 / (1024 * 1024));
+ }
+#endif
+ debugX(DBG_LVL_VERBOSE, "do_data_transfer() end\n");
return error;
}
@@ -365,7 +339,7 @@ static int mmc_host_reset(struct mmc *dev)
struct mmc_host *host = dev->priv;
u32 sdi_u32 = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON;
- debug("SDI_PWR <= 0x%08X\n", sdi_u32);
+ debugX(DBG_LVL_VERBOSE, "SDI_PWR <= 0x%08X\n", sdi_u32);
writel(sdi_u32, &host->base->power);
return MMC_OK;
}
@@ -376,6 +350,8 @@ static int mmc_host_reset(struct mmc *dev)
*/
static int sd_host_reset(struct mmc *dev)
{
+ (void) dev; /* Parameter not used! */
+
return MMC_OK;
}
/*
@@ -396,7 +372,8 @@ static void host_set_ios(struct mmc *dev)
u32 clkdiv = 0;
u32 tmp_clock;
- debug("setting clock and bus width in the host:");
+ debugX(DBG_LVL_VERBOSE,
+ "setting clock and bus width in the host:");
if (dev->clock >= dev->f_max) {
clkdiv = 0;
dev->clock = dev->f_max;
@@ -437,7 +414,10 @@ static void host_set_ios(struct mmc *dev)
sdi_clkcr |= buswidth;
}
- debug("SDI_CLKCR <= 0x%08X\n", sdi_clkcr);
+
+ dev->data_timeout = MMC_DATA_TIMEOUT * dev->clock;
+
+ debugX(DBG_LVL_VERBOSE, "SDI_CLKCR <= 0x%08X\n", sdi_clkcr);
writel(sdi_clkcr, &host->base->clock);
udelay(CLK_CHANGE_DELAY);
}
@@ -466,7 +446,7 @@ static int host_poweroff(struct mmc *dev)
{
struct mmc_host *host = dev->priv;
u32 sdi_pwr = ~SDI_PWR_PWRCTRL_ON;
- debug("SDI_PWR <= 0x%08X\n", sdi_pwr);
+ debugX(DBG_LVL_VERBOSE, "SDI_PWR <= 0x%08X\n", sdi_pwr);
writel(sdi_pwr, &host->base->power);
return 0;
}
@@ -485,7 +465,7 @@ static int emmc_host_init(struct mmc *dev)
/* TODO: Investigate what is actually needed of the below. */
if (u8500_is_earlydrop()) {
- debug("configuring EMMC for ED\n");
+ debugX(DBG_LVL_VERBOSE, "configuring EMMC for ED\n");
/* Initialize the gpio alternate function for eMMC */
struct gpio_register *p_gpio_register =
(void *)IO_ADDRESS(CFG_GPIO_6_BASE);
@@ -502,7 +482,7 @@ static int emmc_host_init(struct mmc *dev)
host->base = (struct sdi_registers *)CFG_EMMC_BASE_ED;
} else {
- debug("configuring EMMC for V1\n");
+ debugX(DBG_LVL_VERBOSE, "configuring EMMC for V1\n");
/* enable the alternate function of PoP EMMC */
gpioerror = gpio_altfuncenable(GPIO_ALT_POP_EMMC, "EMMC");
if (gpioerror != GPIO_OK) {
@@ -515,15 +495,15 @@ static int emmc_host_init(struct mmc *dev)
}
sdi_u32 = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON;
- debug("SDI_PWR <= 0x%08X\n", sdi_u32);
+ debugX(DBG_LVL_VERBOSE, "SDI_PWR <= 0x%08X\n", sdi_u32);
writel(sdi_u32, &host->base->power);
/* setting clk freq less than 400KHz */
sdi_u32 = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN | SDI_CLKCR_HWFC_EN;
- debug("SDI_CLKCR <= 0x%08X\n", sdi_u32);
+ debugX(DBG_LVL_VERBOSE, "SDI_CLKCR <= 0x%08X\n", sdi_u32);
writel(sdi_u32, &host->base->clock);
udelay(CLK_CHANGE_DELAY);
sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK;
- debug("SDI_MASK0 <= 0x%08X\n", sdi_u32);
+ debugX(DBG_LVL_VERBOSE, "SDI_MASK0 <= 0x%08X\n", sdi_u32);
writel(sdi_u32, &host->base->mask0);
dev->clock = MCLK / (2 + SDI_CLKCR_CLKDIV_INIT);
sprintf(dev->name, "EMMC");
@@ -531,10 +511,11 @@ static int emmc_host_init(struct mmc *dev)
dev->set_ios = host_set_ios;
dev->init = mmc_host_reset;
dev->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_HS |
- MMC_MODE_HS_52MHz;
+ MMC_MODE_HS_52MHz /* | MMC_MODE_REL_WR | MMC_MODE_DDR */;
dev->voltages = VOLTAGE_WINDOW_MMC;
dev->f_min = dev->clock;
dev->f_max = MCLK / 2;
+ dev->ddr_en = 0;
return 0;
end:
@@ -573,23 +554,23 @@ static int mmc_host_init(struct mmc *dev)
host->base = (struct sdi_registers *)CFG_MMC_BASE;
sdi_u32 = 0xBF;
- debug("SDI_PWR <= 0x%08X\n", sdi_u32);
+ debugX(DBG_LVL_VERBOSE, "SDI_PWR <= 0x%08X\n", sdi_u32);
writel(sdi_u32, &host->base->power);
/* setting clk freq just less than 400KHz */
sdi_u32 = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN | SDI_CLKCR_HWFC_EN;
- debug("SDI_CLKCR <= 0x%08X\n", sdi_u32);
+ debugX(DBG_LVL_VERBOSE, "SDI_CLKCR <= 0x%08X\n", sdi_u32);
writel(sdi_u32, &host->base->clock);
udelay(CLK_CHANGE_DELAY);
sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK;
- debug("SDI_MASK0 <= 0x%08X\n", sdi_u32);
+ debugX(DBG_LVL_VERBOSE, "SDI_MASK0 <= 0x%08X\n", sdi_u32);
writel(sdi_u32, &host->base->mask0);
dev->clock = MCLK / (2 + SDI_CLKCR_CLKDIV_INIT);
sprintf(dev->name, "MMC");
dev->send_cmd = host_request;
dev->set_ios = host_set_ios;
dev->init = sd_host_reset;
- dev->host_caps = /*MMC_MODE_4BIT*/ 0; /* Some SD cards do not work in
- 4 bit mode! */
+ dev->host_caps = /* MMC_MODE_4BIT */ 0; /* Some SD cards do not work in
+ 4 bit mode! */
dev->voltages = VOLTAGE_WINDOW_SD;
dev->f_min = dev->clock;
dev->f_max = MCLK / 2;
@@ -610,7 +591,9 @@ int board_mmc_init(bd_t *bis)
int error;
struct mmc *dev;
- debug("mmc_host - board_mmc_init\n");
+ debugX(DBG_LVL_VERBOSE, "mmc_host - board_mmc_init\n");
+
+ (void) bis; /* Parameter not used! */
dev = alloc_mmc_struct();
if (!dev)
@@ -622,7 +605,7 @@ int board_mmc_init(bd_t *bis)
return -1;
}
mmc_register(dev);
- debug("registered emmc interface number is:%d\n",
+ debugX(DBG_LVL_VERBOSE, "registered emmc interface number is:%d\n",
dev->block_dev.dev);
dev = alloc_mmc_struct();
@@ -635,7 +618,7 @@ int board_mmc_init(bd_t *bis)
return -1;
}
mmc_register(dev);
- debug("registered mmc/sd interface number is:%d\n",
+ debugX(DBG_LVL_VERBOSE, "registered mmc/sd interface number is:%d\n",
dev->block_dev.dev);
return 0;
diff --git a/board/st/u8500/mmc_host.h b/board/st/u8500/mmc_host.h
index b33351f09..3b5e1c589 100644
--- a/board/st/u8500/mmc_host.h
+++ b/board/st/u8500/mmc_host.h
@@ -13,10 +13,11 @@
/* See SDI (SD card host interface) documentation in U8500 sw specification. */
#define COMMAND_REG_DELAY 300
-#define DATA_REG_DELAY 1000
+#define DATA_REG_DELAY 10000
#define CLK_CHANGE_DELAY 2000
#define MAX_ERROR_VALUE -65
+#ifndef __ASSEMBLY__
enum mmc_result {
/* MMC specific error defines */
MMC_CMD_CRC_FAIL = (MAX_ERROR_VALUE - 1),/* Command response received
@@ -95,6 +96,7 @@ enum mmc_result {
MMC_NO_MORE_FILTER_PENDING_EVENT = 5,
MMC_NO_PENDING_EVENT_ERROR = 7,
};
+#endif
#define MCLK (100*1000*1000)
@@ -185,7 +187,8 @@ enum mmc_result {
#define SDI_CMD_CE_ATACMD (0x00004000)
#define SDI_CMD_CBOOTMODEEN (0x00008000)
-#define SDI_DTIMER_DEFAULT (0xFFFF0000)
+/* MMC_DATA_TIMEOUT sets the data timeout in seconds */
+#define MMC_DATA_TIMEOUT (30)
/* SDI Status register bits */
@@ -261,6 +264,7 @@ enum mmc_result {
#define SDI_FIFO_BURST_SIZE (8)
+#ifndef __ASSEMBLY__
struct sdi_registers {
u32 power; /* 0x00*/
u32 clock; /* 0x04*/
@@ -293,5 +297,6 @@ struct sdi_registers {
u32 pcell_id2; /* 0xFF8*/
u32 pcell_id3; /* 0xFFC*/
};
+#endif
#endif
diff --git a/board/st/u8500/mmc_utils.c b/board/st/u8500/mmc_utils.c
index af5f0475d..b630e8adb 100644
--- a/board/st/u8500/mmc_utils.c
+++ b/board/st/u8500/mmc_utils.c
@@ -66,12 +66,12 @@ static struct partition partitions_v1[] = {
[0] = PART(0x83, 0x000A0000, 0x00004000), /* Kernel */
[1] = PART(0x83, 0x000A4000, 0x00080000), /* Root file system */
[2] = PART(0x83, 0x001FF800, 0x00000800), /* Modem parameters */
- [3] = {0},
+ [3] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
};
#undef PART
-int write_partition_block(void)
+int write_partition_block(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int err;
u32 offset = PIB_EMMC_ADDR;
@@ -79,6 +79,11 @@ int write_partition_block(void)
u8 emmc_existing_partition[512];
struct mmc *boot_dev = NULL;
+ (void) cmdtp; /* Parameter not used! */
+ (void) flag; /* Parameter not used! */
+ (void) argc; /* Parameter not used! */
+ (void) argv; /* Parameter not used! */
+
memset(mbr, 0, 0x1be);
if (u8500_is_earlydrop())
memcpy(mbr + 0x1be, partitions_ed, sizeof(partitions_ed));
@@ -136,6 +141,11 @@ static int mmc_read_cmd_file(cmd_tbl_t *cmdtp, int flag, int argc,
long sz;
char mmc_cmdbuffer[1024];
+ (void) cmdtp; /* Parameter not used! */
+ (void) flag; /* Parameter not used! */
+ (void) argc; /* Parameter not used! */
+ (void) argv; /* Parameter not used! */
+
sz = file_fat_read("command.txt", &mmc_cmdbuffer,
sizeof(mmc_cmdbuffer) - 1);
if (sz == -1) {
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index 0e3393b5f..66a4882fe 100644
--- a/common/cmd_mmc.c
+++ b/common/cmd_mmc.c
@@ -25,6 +25,8 @@
#include <command.h>
#include <mmc.h>
+#define READ_WRITE_TEST "rwr"
+
#ifndef CONFIG_GENERIC_MMC
static int curr_device = -1;
@@ -114,6 +116,135 @@ static void print_mmcinfo(struct mmc *mmc)
printf("Bus Width: %d-bit\n", mmc->bus_width);
}
+static void print_csd(struct mmc *mmc)
+{
+ printf("mmc->csd[0] = 0x%08X\n", mmc->csd[0]);
+ printf("mmc->csd[1] = 0x%08X\n", mmc->csd[1]);
+ printf("mmc->csd[2] = 0x%08X\n", mmc->csd[2]);
+ printf("mmc->csd[3] = 0x%08X\n", mmc->csd[3]);
+
+ printf("csd->csd_structure = 0x%02X\n", CSD_STRUCTURE(mmc->csd));
+ printf("csd->spec_vers = 0x%02X\n", CSD_SPEC_VERS(mmc->csd));
+ printf("csd->taac = 0x%02X\n", CSD_TAAC(mmc->csd));
+ printf("csd->nsac = 0x%02X\n", CSD_NSAC(mmc->csd));
+ printf("csd->tran_speed = 0x%02X\n", CSD_TRAN_SPEED(mmc->csd));
+ printf("csd->ccc = 0x%04X\n", CSD_CCC(mmc->csd));
+ printf("csd->read_bl_len = 0x%02X\n", CSD_READ_BL_LEN(mmc->csd));
+ printf("csd->read_bl_partial = 0x%02X\n",
+ CSD_READ_BL_PARTIAL(mmc->csd));
+ printf("csd->write_blk_misalign = 0x%02X\n",
+ CSD_WRITE_BLK_MISALIGN(mmc->csd));
+ printf("csd->read_blk_misalign = 0x%02X\n",
+ CSD_READ_BLK_MISALIGN(mmc->csd));
+ printf("csd->dsr_imp = 0x%02X\n", CSD_DSR_IMP(mmc->csd));
+ printf("csd->c_size = 0x%04X\n", CSD_C_SIZE(mmc->csd));
+ printf("csd->vdd_r_curr_min = 0x%04X\n", CSD_VDD_R_CURR_MIN(mmc->csd));
+ printf("csd->vdd_r_curr_max = 0x%04X\n", CSD_VDD_R_CURR_MAX(mmc->csd));
+ printf("csd->vdd_w_curr_min = 0x%04X\n", CSD_VDD_W_CURR_MIN(mmc->csd));
+ printf("csd->vdd_w_curr_max = 0x%04X\n", CSD_VDD_W_CURR_MAX(mmc->csd));
+ printf("csd->c_size_mult = 0x%04X\n", CSD_C_SIZE_MULT(mmc->csd));
+ printf("csd->erase_grp_size = 0x%04X\n", CSD_ERASE_GRP_SIZE(mmc->csd));
+ printf("csd->erase_grp_mult = 0x%04X\n", CSD_ERASE_GRP_MULT(mmc->csd));
+ printf("csd->wp_grp_size = 0x%04X\n", CSD_WP_GRP_SIZE(mmc->csd));
+ printf("csd->wp_grp_enable = 0x%02X\n", CSD_WP_GRP_ENABLE(mmc->csd));
+ printf("csd->default_ecc = 0x%02X\n", CSD_DEFAULT_ECC(mmc->csd));
+ printf("csd->r2w_factor = 0x%04X\n", CSD_R2W_FACTOR(mmc->csd));
+ printf("csd->write_bl_len = 0x%04X\n", CSD_WRITE_BL_LEN(mmc->csd));
+ printf("csd->write_bl_partial = 0x%02X\n",
+ CSD_WRITE_BL_PARTIAL(mmc->csd));
+ printf("csd->file_format_grp = 0x%02X\n",
+ CSD_FILE_FORMAT_GRP(mmc->csd));
+ printf("csd->copy = 0x%02X\n", CSD_COPY(mmc->csd));
+ printf("csd->perm_write_protect = 0x%02X\n",
+ CSD_PERM_WRITE_PROTECT(mmc->csd));
+ printf("csd->tmp_write_protect = 0x%02X\n",
+ CSD_TMP_WRITE_PROTECT(mmc->csd));
+ printf("csd->ecc = 0x%02X\n", CSD_ECC(mmc->csd));
+ printf("csd->crc = 0x%02X\n", CSD_CRC(mmc->csd));
+ printf("csd->one = 0x%02X\n", CSD_ONE(mmc->csd));
+}
+
+static void print_ext_csd(char *ext_csd)
+{
+ printf("ext_csd->s_cmd_set = 0x%02X\n", ext_csd[504]);
+ printf("ext_csd->hpl_features = 0x%02X\n", ext_csd[503]);
+ printf("ext_csd->bkops_support = 0x%02X\n", ext_csd[502]);
+ printf("ext_csd->bkops_status = 0x%02X\n", ext_csd[246]);
+ printf("ext_csd->correctly_prg_sectors_num = 0x%08X\n",
+ ext_csd[245] * (1 << 24) | ext_csd[244] * (1 << 16) |
+ ext_csd[243] * (1 << 8) | ext_csd[242]);
+ printf("ext_csd->ini_timeout_ap = 0x%02X\n", ext_csd[241]);
+ printf("ext_csd->pwr_cl_ddr_52_360 = 0x%02X\n", ext_csd[239]);
+ printf("ext_csd->pwr_cl_ddr_52_195 = 0x%02X\n", ext_csd[238]);
+ printf("ext_csd->min_perf_ddr_w_8_52 = 0x%02X\n", ext_csd[235]);
+ printf("ext_csd->min_perf_ddr_r_8_52 = 0x%02X\n", ext_csd[234]);
+ printf("ext_csd->trim_mult = 0x%02X\n", ext_csd[232]);
+ printf("ext_csd->sec_feature_support = 0x%02X\n", ext_csd[231]);
+ printf("ext_csd->sec_erase_mult = 0x%02X\n", ext_csd[230]);
+ printf("ext_csd->sec_trim_mult = 0x%02X\n", ext_csd[229]);
+ printf("ext_csd->boot_info = 0x%02X\n", ext_csd[228]);
+ printf("ext_csd->boot_size_mult = 0x%02X\n", ext_csd[226]);
+ printf("ext_csd->acc_size = 0x%02X\n", ext_csd[225]);
+ printf("ext_csd->hc_erase_gp_size = 0x%02X\n", ext_csd[224]);
+ printf("ext_csd->erase_timeout_mult = 0x%02X\n", ext_csd[223]);
+ printf("ext_csd->rel_wr_sec_c = 0x%02X\n", ext_csd[222]);
+ printf("ext_csd->hc_wp_grp_size = 0x%02X\n", ext_csd[221]);
+ printf("ext_csd->s_c_vcc = 0x%02X\n", ext_csd[220]);
+ printf("ext_csd->s_c_vccq = 0x%02X\n", ext_csd[219]);
+ printf("ext_csd->s_a_timeout = 0x%02X\n", ext_csd[217]);
+ printf("ext_csd->sec_count = 0x%08X\n", ext_csd[215] * (1 << 24) |
+ ext_csd[214] * (1 << 16)|ext_csd[213]*(1 << 8)|ext_csd[212]);
+ printf("ext_csd->min_perf_w_8_52 = 0x%02X\n", ext_csd[210]);
+ printf("ext_csd->min_perf_r_8_52 = 0x%02X\n", ext_csd[209]);
+ printf("ext_csd->min_perf_w_8_26_4_52 = 0x%02X\n", ext_csd[208]);
+ printf("ext_csd->min_perf_r_8_26_4_52 = 0x%02X\n", ext_csd[207]);
+ printf("ext_csd->min_perf_w_4_26 = 0x%02X\n", ext_csd[206]);
+ printf("ext_csd->min_perf_r_4_26 = 0x%02X\n", ext_csd[205]);
+ printf("ext_csd->pwr_cl_26_360 = 0x%02X\n", ext_csd[203]);
+ printf("ext_csd->pwr_cl_52_360 = 0x%02X\n", ext_csd[202]);
+ printf("ext_csd->pwr_cl_26_195 = 0x%02X\n", ext_csd[201]);
+ printf("ext_csd->pwr_cl_52_195 = 0x%02X\n", ext_csd[200]);
+ printf("ext_csd->partition_switch_time = 0x%02X\n", ext_csd[199]);
+ printf("ext_csd->out_of_interrupt_time = 0x%02X\n", ext_csd[198]);
+ printf("ext_csd->card_type = 0x%02X\n", ext_csd[196]);
+ printf("ext_csd->csd_structure = 0x%02X\n", ext_csd[194]);
+ printf("ext_csd->ext_csd_rev = 0x%02X\n", ext_csd[192]);
+ printf("ext_csd->cmd_set = 0x%02X\n", ext_csd[191]);
+ printf("ext_csd->cmd_set_rev = 0x%02X\n", ext_csd[189]);
+ printf("ext_csd->power_class = 0x%02X\n", ext_csd[187]);
+ printf("ext_csd->hs_timing = 0x%02X\n", ext_csd[185]);
+ printf("ext_csd->bus_width = 0x%02X\n", ext_csd[183]);
+ printf("ext_csd->erased_mem_cont = 0x%02X\n", ext_csd[181]);
+ printf("ext_csd->partition_config = 0x%02X\n", ext_csd[179]);
+ printf("ext_csd->boot_config_prot = 0x%02X\n", ext_csd[178]);
+ printf("ext_csd->boot_bus_width = 0x%02X\n", ext_csd[177]);
+ printf("ext_csd->erase_group_def = 0x%02X\n", ext_csd[175]);
+ printf("ext_csd->boot_wp = 0x%02X\n", ext_csd[173]);
+ printf("ext_csd->user_wp = 0x%02X\n", ext_csd[171]);
+ printf("ext_csd->fw_config = 0x%02X\n", ext_csd[169]);
+ printf("ext_csd->rpmb_size_mult = 0x%02X\n", ext_csd[168]);
+ printf("ext_csd->wr_rel_set = 0x%02X\n", ext_csd[167]);
+ printf("ext_csd->wr_rel_param = 0x%02X\n", ext_csd[166]);
+ printf("ext_csd->bkops_start = 0x%02X\n", ext_csd[164]);
+ printf("ext_csd->bkops_en = 0x%02X\n", ext_csd[163]);
+ printf("ext_csd->rst_n_function = 0x%02X\n", ext_csd[162]);
+ printf("ext_csd->hpi_mgmt = 0x%02X\n", ext_csd[161]);
+ printf("ext_csd->partitioning_support = 0x%02X\n", ext_csd[160]);
+ printf("ext_csd->max_en_size_mult = 0x%08X\n", ext_csd[159] * (1 << 16)
+ | ext_csd[158] * (1 << 8) | ext_csd[157]);
+ printf("ext_csd->partitions_attribute = 0x%02X\n", ext_csd[156]);
+ printf("ext_csd->partition_setting_completed = 0x%02X\n", ext_csd[155]);
+ printf("ext_csd->gp_size_mult = 0x%08X\n", ext_csd[146] * (1 << 24) |
+ ext_csd[145] * (1 << 16) | ext_csd[144] * (1 << 8) |
+ ext_csd[143]);
+ printf("ext_csd->enh_size_mult = 0x%08X\n", ext_csd[142] * (1 << 16) |
+ ext_csd[141]*(1 << 8) | ext_csd[140]);
+ printf("ext_csd->enh_start_addr = 0x%08X\n", ext_csd[139] * (1 << 24) |
+ ext_csd[138] * (1 << 16) | ext_csd[137] * (1 << 8) |
+ ext_csd[136]);
+ printf("ext_csd->sec_bad_blk_mgmnt = 0x%02X\n", ext_csd[134]);
+}
+
int do_mmcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
struct mmc *mmc;
@@ -130,13 +261,37 @@ int do_mmcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
mmc_init(mmc);
print_mmcinfo(mmc);
+
+ if ((argc > 2) && (strcmp(argv[2], "csd") == 0))
+ print_csd(mmc);
+
+ /* Print Ext CSD */
+ if ((argc > 2) && (strcmp(argv[2], "ext_csd") == 0)) {
+ char ext_csd[512];
+ struct mmc_cmd cmd;
+ struct mmc_data data;
+
+ /* Get the Card Status Register */
+ cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
+ cmd.resp_type = MMC_RSP_R1;
+ cmd.cmdarg = 0;
+ cmd.flags = 0;
+
+ data.dest = ext_csd;
+ data.blocks = 1;
+ data.blocksize = 512;
+ data.flags = MMC_DATA_READ;
+
+ if (!mmc->send_cmd(mmc, &cmd, &data))
+ print_ext_csd(ext_csd);
+ }
}
return 0;
}
-U_BOOT_CMD(mmcinfo, 2, 0, do_mmcinfo,
- "mmcinfo <dev num>-- display MMC info\n",
+U_BOOT_CMD(mmcinfo, 3, 0, do_mmcinfo,
+ "mmcinfo <dev num> [csd | ext_csd] -- display MMC info\n",
""
);
@@ -182,7 +337,7 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
if (!mmc)
return 1;
- printf("\nMMC read: dev # %d, block # %d, count %d ... ",
+ printf("\nMMC read: dev # %d, block # %d, count %d\n",
dev, blk, cnt);
mmc_init(mmc);
@@ -207,7 +362,7 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
if (!mmc)
return 1;
- printf("\nMMC write: dev # %d, block # %d, count %d ... ",
+ printf("\nMMC write: dev # %d, block # %d, count %d\n",
dev, blk, cnt);
mmc_init(mmc);
@@ -217,6 +372,86 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
printf("%d blocks written: %s\n",
n, (n == cnt) ? "OK" : "ERROR");
return (n == cnt) ? 0 : 1;
+ } else if (strcmp(argv[1], READ_WRITE_TEST) == 0) {
+ int dev = simple_strtoul(argv[2], NULL, 10);
+ int blk = simple_strtoul(argv[3], NULL, 10);
+ u32 cnt = simple_strtoul(argv[4], NULL, 10);
+ u32 loops = simple_strtoul(argv[5], NULL, 10);
+ u32 n;
+ struct mmc *mmc = find_mmc_device(dev);
+ char *s = getenv("loadaddr");
+ /* Assume we can use up to 16MB at "loadaddr" */
+ static const u32 max_transfer_size = 8 * 1024 * 1024;
+ u32 transfer_size = cnt * mmc->block_dev.blksz;;
+ u8 *read_buf = (u8 *) simple_strtoul(s, NULL, 16);
+ u8 *ver_buf = (u8 *) read_buf + max_transfer_size;
+
+ if (!mmc)
+ return 1;
+
+ if (!mmc->block_dev.blksz)
+ mmc_init(mmc);
+
+ if (!loops) {
+ printf("mmc " READ_WRITE_TEST " failed! "
+ "loops(%u)\n", loops);
+ return 1;
+ }
+ if (!transfer_size ||
+ (transfer_size > max_transfer_size)) {
+ printf("mmc " READ_WRITE_TEST " failed! "
+ "(0 < transfer_size(%u) < %u)\n",
+ transfer_size, max_transfer_size);
+ return 1;
+ }
+
+ if (s == NULL)
+ return 1;
+
+ printf("\nMMC " READ_WRITE_TEST ": dev(%u), "
+ "block(%u), cnt(%u) loops(%u)\n",
+ dev, blk, cnt, loops);
+
+ while (loops--) {
+ memset(read_buf, 0, transfer_size);
+ memset(ver_buf, 0, transfer_size);
+ n = mmc->block_dev.block_read(dev, blk, cnt,
+ read_buf);
+ if (n != cnt) {
+ printf("mmc " READ_WRITE_TEST
+ " failed! loops(%u) n(%u)\n",
+ loops, n);
+ return 1;
+ }
+ /* Flush cache after read */
+ flush_cache((ulong)read_buf, transfer_size);
+ n = mmc->block_dev.block_write(dev, blk, cnt,
+ read_buf);
+ if (n != cnt) {
+ printf("mmc " READ_WRITE_TEST
+ " failed! loops(%u) n(%u)\n",
+ loops, n);
+ return 1;
+ }
+ n = mmc->block_dev.block_read(dev, blk, cnt,
+ ver_buf);
+ if (n != cnt) {
+ printf("mmc " READ_WRITE_TEST
+ " failed! loops(%u) n(%u)\n",
+ loops, n);
+ return 1;
+ }
+ /* Flush cache after read */
+ flush_cache((ulong)read_buf, transfer_size);
+ if (memcmp(read_buf, ver_buf, transfer_size)) {
+ printf("mmc " READ_WRITE_TEST
+ " failed! loops(%u)\n",
+ loops);
+ return 1;
+ }
+ }
+ printf("mmc " READ_WRITE_TEST " ok!\n");
+ return 0;
} else {
printf("Usage:\n%s\n", cmdtp->usage);
rc = 1;
@@ -231,6 +466,8 @@ U_BOOT_CMD(
"MMC sub system",
"read <device num> addr blk# cnt\n"
"mmc write <device num> addr blk# cnt\n"
+ "mmc " READ_WRITE_TEST " <device num> blk# cnt loops - reads, "
+ "writes,reads and verifies a chunk of data\n"
"mmc rescan <device num>\n"
"mmc list - lists available devices");
#endif
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 364dd8005..31af3ad87 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -53,6 +53,21 @@ static int mmc_set_blocklen(struct mmc *mmc, uint len)
return mmc_send_cmd(mmc, &cmd, NULL);
}
+static int mmc_set_block_count(struct mmc *mmc, uint blkcnt)
+{
+ struct mmc_cmd cmd;
+
+ cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
+ cmd.resp_type = MMC_RSP_R1;
+ if (mmc->card_caps & MMC_MODE_REL_WR)
+ cmd.cmdarg = 0x80000000 | blkcnt;
+ else
+ cmd.cmdarg = blkcnt;
+ cmd.flags = 0;
+
+ return mmc_send_cmd(mmc, &cmd, NULL);
+}
+
struct mmc *find_mmc_device(int dev_num)
{
struct mmc *m;
@@ -79,6 +94,7 @@ mmc_bwrite_multi(struct mmc *mmc, ulong start, ulong blkcnt, const void *src)
ulong blkwritecnt;
ulong blkleftcnt = blkcnt;
void *src_p = (void *) src;
+ uint max_block_cnt = 0xffff;
/*
* Each mmc host controller has a size limit in it's register, used
@@ -88,9 +104,14 @@ mmc_bwrite_multi(struct mmc *mmc, ulong start, ulong blkcnt, const void *src)
* instead.
*/
+ if ((mmc->card_caps & MMC_MODE_REL_WR) &&
+ !(mmc->wr_rel_param & EXT_CSD_WR_REL_PARAM_EN_REL_WR))
+ max_block_cnt = mmc->rel_wr_sec_c;
+
while (blkleftcnt > 0) {
- if (blkleftcnt > 0xffff)
- blkwritecnt = 0xffff;
+
+ if (blkleftcnt > max_block_cnt)
+ blkwritecnt = max_block_cnt;
else
blkwritecnt = blkleftcnt;
@@ -99,6 +120,15 @@ mmc_bwrite_multi(struct mmc *mmc, ulong start, ulong blkcnt, const void *src)
else
cmd.cmdarg = start * mmc->write_bl_len;
+ if (mmc->card_caps & MMC_MODE_REL_WR) {
+ err = mmc_set_block_count(mmc, blkwritecnt);
+ if (err) {
+ printf("MMC set block count failed, err=%d\n",
+ err);
+ return 0;
+ }
+ }
+
cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
cmd.resp_type = MMC_RSP_R1;
cmd.flags = 0;
@@ -114,15 +144,19 @@ mmc_bwrite_multi(struct mmc *mmc, ulong start, ulong blkcnt, const void *src)
return 0;
}
- cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
- cmd.cmdarg = 0;
- cmd.resp_type = MMC_RSP_R1b;
- cmd.flags = 0;
+ if (!(mmc->card_caps & MMC_MODE_REL_WR) ||
+ (max_block_cnt != mmc->rel_wr_sec_c)) {
+ cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err) {
- printf("MMC write - stop cmd failed, err=%d\n", err);
- return 0;
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err) {
+ printf("MMC write - stop cmd failed, err=%d\n",
+ err);
+ return 0;
+ }
}
blkleftcnt -= blkwritecnt;
@@ -174,12 +208,6 @@ mmc_bwrite(int dev_num, unsigned long start, lbaint_t blkcnt, const void *src)
return 0;
}
- err = mmc_set_blocklen(mmc, mmc->write_bl_len);
- if (err) {
- printf("MMC set write bl len failed, err=%d\n", err);
- return 0;
- }
-
if (blkcnt > 1)
return mmc_bwrite_multi(mmc, start, blkcnt, src);
else if (blkcnt == 1)
@@ -291,12 +319,6 @@ mmc_bread(int dev_num, unsigned long start, lbaint_t blkcnt, void *dst)
return 0;
}
- err = mmc_set_blocklen(mmc, mmc->read_bl_len);
- if (err) {
- printf("MMC set read bl len failed, err=%d\n", err);
- return 0;
- }
-
if (blkcnt > 1)
return mmc_bread_multi(mmc, start, blkcnt, dst);
else if (blkcnt == 1)
@@ -455,9 +477,10 @@ static int mmc_change_freq(struct mmc *mmc)
* Instead of probing according to the bus testing procedure,
* the buswitdh that is supported from the MMC device is hardcoded
* to both 8 and/or 4 bit. It is up to the host driver to set
- * other limitations.
+ * other limitations. This also applies to DDR mode.
*/
- mmc->card_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
+ mmc->card_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_DDR |
+ MMC_MODE_REL_WR;
/* Only version 4 supports high-speed */
if (mmc->version < MMC_VERSION_4)
@@ -477,16 +500,18 @@ static int mmc_change_freq(struct mmc *mmc)
* read.
*/
- cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf;
+ mmc->wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
+ mmc->rel_wr_sec_c = ext_csd[EXT_CSD_REL_WR_SEC_C];
- err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
+ if (mmc->rel_wr_sec_c == 1)
+ mmc->card_caps &= ~MMC_MODE_REL_WR;
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
if (err)
return err;
/* Now check to see that it worked */
err = mmc_send_ext_csd(mmc, ext_csd);
-
if (err)
return err;
@@ -498,11 +523,16 @@ static int mmc_change_freq(struct mmc *mmc)
* High Speed mode is set, two types: SDR 52MHz or SDR 26MHz
* DDR mode is not supported yet.
*/
+ cardtype = ext_csd[EXT_CSD_CARD_TYPE];
if (cardtype & MMC_HS_52MHZ)
mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
else
mmc->card_caps |= MMC_MODE_HS;
+ if (mmc->wr_rel_param & EXT_CSD_WR_REL_PARAM_HS_CTRL_REL)
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_WR_REL_SET, 1);
+
return 0;
}
@@ -556,19 +586,15 @@ static int sd_change_freq(struct mmc *mmc)
timeout = 3;
-retry_scr:
- data.dest = (char *)&scr;
- data.blocksize = 8;
- data.blocks = 1;
- data.flags = MMC_DATA_READ;
-
- err = mmc_send_cmd(mmc, &cmd, &data);
- if (err) {
- if (timeout--)
- goto retry_scr;
-
+ do {
+ data.dest = (char *)&scr;
+ data.blocksize = 8;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ err = mmc_send_cmd(mmc, &cmd, &data);
+ } while (err && timeout--);
+ if (!timeout)
return err;
- }
mmc->scr[0] = __be32_to_cpu(scr[0]);
mmc->scr[1] = __be32_to_cpu(scr[1]);
@@ -781,13 +807,11 @@ static int mmc_startup(struct mmc *mmc)
* Check for SD!
*/
if (mmc->high_capacity) {
- csize = (mmc->csd[1] & 0x3f) << 16
- | (mmc->csd[2] & 0xffff0000) >> 16;
+ csize = CSD_HC_SIZE(mmc->csd);
cmult = 8;
} else {
- csize = (mmc->csd[1] & 0x3ff) << 2
- | (mmc->csd[2] & 0xc0000000) >> 30;
- cmult = (mmc->csd[2] & 0x00038000) >> 15;
+ csize = CSD_C_SIZE(mmc->csd);
+ cmult = CSD_C_SIZE_MULT(mmc->csd);
}
/* This is only correct for MMC cards up to 2GB. SD? */
@@ -855,7 +879,41 @@ static int mmc_startup(struct mmc *mmc)
else
mmc_set_clock(mmc, 25000000);
} else {
- if (mmc->card_caps & MMC_MODE_8BIT) {
+ if ((mmc->card_caps & MMC_MODE_DDR_8BIT) == MMC_MODE_DDR_8BIT) {
+ /* Set the card to use 8 bit*/
+ printf("EXT_CSD_BUS_WIDTH_DDR_8\n");
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_DDR_8);
+ if (err)
+ return err;
+
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_CLASS,
+ 0xAA);
+ if (err)
+ return err;
+ printf("EXT_CSD_BUS_WIDTH_DDR_8\n");
+ mmc->ddr_en = 1;
+ mmc_set_bus_width(mmc, 8);
+ } else if ((mmc->card_caps & MMC_MODE_DDR_4BIT) ==
+ MMC_MODE_DDR_4BIT) {
+ /* Set the card to use 4 bit*/
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_DDR_4);
+ if (err)
+ return err;
+
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_CLASS,
+ 0xAA);
+ if (err)
+ return err;
+ printf("EXT_CSD_BUS_WIDTH_DDR_4\n");
+ mmc->ddr_en = 1;
+ mmc_set_bus_width(mmc, 4);
+ } else if (mmc->card_caps & MMC_MODE_8BIT) {
/* Set the card to use 8 bit*/
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
@@ -884,6 +942,15 @@ static int mmc_startup(struct mmc *mmc)
mmc_set_clock(mmc, 26000000);
} else
mmc_set_clock(mmc, 20000000);
+
+ if (mmc->card_caps & MMC_MODE_REL_WR) {
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_WR_REL_SET,
+ 0x1F);
+
+ if (err)
+ return err;
+ }
}
/* fill in device description */
@@ -987,6 +1054,13 @@ int mmc_init(struct mmc *mmc)
}
err = mmc_startup(mmc);
+
+ if (!err) {
+ err = mmc_set_blocklen(mmc, 512);
+ if (err)
+ printf("MMC set write bl len failed, err=%d\n", err);
+ }
+
debug("mmc_startup returns %d\n", err);
return err;
}
@@ -1001,6 +1075,12 @@ static int __def_mmc_init(bd_t *bis)
}
int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
+/*
+ * It seems attribute 'weak' does not work as intended. With gcc 4.4.1 and
+ * optimization O2 it always links in the weak function. Declare board_mmc_init
+ * as external.
+ */
+extern int board_mmc_init(bd_t *bis);
void print_mmc_devices(char separator)
{
diff --git a/include/mmc.h b/include/mmc.h
index bcc291568..57338fe11 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -42,8 +42,13 @@
#define MMC_MODE_HS 0x001
#define MMC_MODE_HS_52MHz 0x010
+#define MMC_MODE_1BIT 0x000
#define MMC_MODE_4BIT 0x100
#define MMC_MODE_8BIT 0x200
+#define MMC_MODE_DDR 0x400
+#define MMC_MODE_DDR_4BIT (MMC_MODE_4BIT | MMC_MODE_DDR)
+#define MMC_MODE_DDR_8BIT (MMC_MODE_8BIT | MMC_MODE_DDR)
+#define MMC_MODE_REL_WR 0x800
#define SD_DATA_4BIT 0x00040000
@@ -72,6 +77,7 @@
#define MMC_CMD_SET_BLOCKLEN 16
#define MMC_CMD_READ_SINGLE_BLOCK 17
#define MMC_CMD_READ_MULTIPLE_BLOCK 18
+#define MMC_CMD_SET_BLOCK_COUNT 23
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
#define MMC_CMD_APP_CMD 55
@@ -128,11 +134,20 @@
* EXT_CSD fields
*/
-#define EXT_CSD_BUS_WIDTH 183 /* R/W */
-#define EXT_CSD_HS_TIMING 185 /* R/W */
-#define EXT_CSD_CARD_TYPE 196 /* RO */
-#define EXT_CSD_REV 192 /* RO */
-#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
+#define EXT_CSD_WR_REL_PARAM 166 /* R */
+#define EXT_CSD_WR_REL_SET 167 /* R/W */
+#define EXT_CSD_BUS_WIDTH 183 /* R/W */
+#define EXT_CSD_HS_TIMING 185 /* R/W */
+#define EXT_CSD_POWER_CLASS 187 /* R/W */
+#define EXT_CSD_REV 192 /* RO */
+#define EXT_CSD_CARD_TYPE 196 /* RO */
+#define EXT_CSD_OUT_OF_INT_TIME 198 /* RO */
+#define EXT_CSD_MIN_PERF_R_8_52 209 /* RO */
+#define EXT_CSD_MIN_PERF_W_8_52 210 /* RO */
+#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
+#define EXT_CSD_REL_WR_SEC_C 222 /* RO */
+#define EXT_CSD_PWR_CL_DDR_52_195 238 /* RO */
+#define EXT_CSD_PWR_CL_DDR_52_360 239 /* RO */
/*
* EXT_CSD field definitions
@@ -148,6 +163,11 @@
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
+#define EXT_CSD_BUS_WIDTH_DDR_4 5 /* Card is in 4 bit ddr mode */
+#define EXT_CSD_BUS_WIDTH_DDR_8 6 /* Card is in 8 bit ddr mode */
+
+#define EXT_CSD_WR_REL_PARAM_HS_CTRL_REL (1 << 0)
+#define EXT_CSD_WR_REL_PARAM_EN_REL_WR (1 << 2)
#define R1_ILLEGAL_COMMAND (1 << 22)
#define R1_APP_CMD (1 << 5)
@@ -179,45 +199,49 @@ struct mmc_cid {
char pnm[7];
};
-struct mmc_csd
-{
- u8 csd_structure:2,
- spec_vers:4,
- rsvd1:2;
- u8 taac;
- u8 nsac;
- u8 tran_speed;
- u16 ccc:12,
- read_bl_len:4;
- u64 read_bl_partial:1,
- write_blk_misalign:1,
- read_blk_misalign:1,
- dsr_imp:1,
- rsvd2:2,
- c_size:12,
- vdd_r_curr_min:3,
- vdd_r_curr_max:3,
- vdd_w_curr_min:3,
- vdd_w_curr_max:3,
- c_size_mult:3,
- sector_size:5,
- erase_grp_size:5,
- wp_grp_size:5,
- wp_grp_enable:1,
- default_ecc:2,
- r2w_factor:3,
- write_bl_len:4,
- write_bl_partial:1,
- rsvd3:5;
- u8 file_format_grp:1,
- copy:1,
- perm_write_protect:1,
- tmp_write_protect:1,
- file_format:2,
- ecc:2;
- u8 crc:7;
- u8 one:1;
-};
+/*
+ * CSD_EXTRACT can be used as is for all CSD members except for c_size
+ * because c_size spans over a 32-bit boundary and must be expressed by a
+ * combination.
+ */
+#define CSD_EXTRACT(csd, bit, width) ((csd[3-(bit/32)] & \
+ (((1 << width) - 1) << (bit - (bit/32) * 32))) >> (bit - (bit/32) * 32))
+
+#define CSD_STRUCTURE(csd) CSD_EXTRACT(csd, 126, 2)
+#define CSD_SPEC_VERS(csd) CSD_EXTRACT(csd, 122, 4)
+#define CSD_TAAC(csd) CSD_EXTRACT(csd, 112, 8)
+#define CSD_NSAC(csd) CSD_EXTRACT(csd, 104, 8)
+#define CSD_TRAN_SPEED(csd) CSD_EXTRACT(csd, 96, 8)
+#define CSD_CCC(csd) CSD_EXTRACT(csd, 4, 12)
+#define CSD_READ_BL_LEN(csd) CSD_EXTRACT(csd, 80, 1)
+#define CSD_READ_BL_PARTIAL(csd) CSD_EXTRACT(csd, 79, 1)
+#define CSD_WRITE_BLK_MISALIGN(csd) CSD_EXTRACT(csd, 78, 1)
+#define CSD_READ_BLK_MISALIGN(csd) CSD_EXTRACT(csd, 77, 1)
+#define CSD_DSR_IMP(csd) CSD_EXTRACT(csd, 76, 1)
+#define CSD_C_SIZE(csd) (CSD_EXTRACT(csd, 64, 10) << 2 | \
+ CSD_EXTRACT(csd, 62, 2))
+#define CSD_HC_SIZE(csd) (CSD_EXTRACT(csd, 64, 8) << 16 | \
+ CSD_EXTRACT(csd, 48, 16))
+#define CSD_VDD_R_CURR_MIN(csd) CSD_EXTRACT(csd, 59, 3)
+#define CSD_VDD_R_CURR_MAX(csd) CSD_EXTRACT(csd, 56, 3)
+#define CSD_VDD_W_CURR_MIN(csd) CSD_EXTRACT(csd, 53, 3)
+#define CSD_VDD_W_CURR_MAX(csd) CSD_EXTRACT(csd, 50, 3)
+#define CSD_C_SIZE_MULT(csd) CSD_EXTRACT(csd, 47, 3)
+#define CSD_ERASE_GRP_SIZE(csd) CSD_EXTRACT(csd, 42, 5)
+#define CSD_ERASE_GRP_MULT(csd) CSD_EXTRACT(csd, 37, 5)
+#define CSD_WP_GRP_SIZE(csd) CSD_EXTRACT(csd, 32, 5)
+#define CSD_WP_GRP_ENABLE(csd) CSD_EXTRACT(csd, 31, 1)
+#define CSD_DEFAULT_ECC(csd) CSD_EXTRACT(csd, 29, 2)
+#define CSD_R2W_FACTOR(csd) CSD_EXTRACT(csd, 26, 3)
+#define CSD_WRITE_BL_LEN(csd) CSD_EXTRACT(csd, 22, 4)
+#define CSD_WRITE_BL_PARTIAL(csd) CSD_EXTRACT(csd, 21, 1)
+#define CSD_FILE_FORMAT_GRP(csd) CSD_EXTRACT(csd, 15, 1)
+#define CSD_COPY(csd) CSD_EXTRACT(csd, 14, 1)
+#define CSD_PERM_WRITE_PROTECT(csd) CSD_EXTRACT(csd, 13, 1)
+#define CSD_TMP_WRITE_PROTECT(csd) CSD_EXTRACT(csd, 12, 1)
+#define CSD_ECC(csd) CSD_EXTRACT(csd, 8, 2)
+#define CSD_CRC(csd) CSD_EXTRACT(csd, 1, 2)
+#define CSD_ONE(csd) CSD_EXTRACT(csd, 0, 1)
struct mmc_cmd {
ushort cmdidx;
@@ -258,6 +282,10 @@ struct mmc {
uint tran_speed;
uint read_bl_len;
uint write_bl_len;
+ uint data_timeout;
+ uint wr_rel_param;
+ uint rel_wr_sec_c;
+ u8 ddr_en;
u64 capacity;
block_dev_desc_t block_dev;
int (*send_cmd)(struct mmc *mmc,