
This patch improve the performance by spliting flag examination code in ftsdc010_send_cmd() into 3 functions. This patch also reordered the function which made better capability to some high performance cards against to the next version of ftsdc010 hardware.
Signed-off-by: Macpaul Lin macpaul@andestech.com --- drivers/mmc/ftsdc010_esdhc.c | 172 +++++++++++++++++++++++------------------- 1 files changed, 93 insertions(+), 79 deletions(-)
diff --git a/drivers/mmc/ftsdc010_esdhc.c b/drivers/mmc/ftsdc010_esdhc.c index e38dd87..538800b 100644 --- a/drivers/mmc/ftsdc010_esdhc.c +++ b/drivers/mmc/ftsdc010_esdhc.c @@ -90,8 +90,13 @@ static void ftsdc010_pio_read(struct mmc_host *host, char *buf, unsigned int siz
while (size) { status = readl(&host->reg->status); + debug("%s: size: %08x\n", __func__, size);
if (status & FTSDC010_STATUS_FIFO_ORUN) { + + debug("%s: FIFO OVERRUN: sta: %08x\n", + __func__, status); + fifo = host->fifo_len > size ? size : host->fifo_len;
@@ -146,7 +151,7 @@ static void ftsdc010_pio_write(struct mmc_host *host, const char *buf, while (size) { status = readl(&host->reg->status);
- if (status & FTSDC010_STATUS_FIFO_ORUN) { + if (status & FTSDC010_STATUS_FIFO_URUN) { fifo = host->fifo_len > size ? size : host->fifo_len;
@@ -158,7 +163,6 @@ static void ftsdc010_pio_write(struct mmc_host *host, const char *buf, writel(*ptr, &host->reg->dwr); ptr++; } - } else { udelay(1); if (++retry >= FTSDC010_PIO_RETRY) { @@ -169,56 +173,19 @@ static void ftsdc010_pio_write(struct mmc_host *host, const char *buf, } }
-static int ftsdc010_pio_check_status(struct mmc *mmc, struct mmc_cmd *cmd, +static int ftsdc010_check_rsp(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct mmc_host *host = mmc->priv; - unsigned int sta, clear; - unsigned int i; - - /* check response and hardware status */ - clear = 0; - - /* chech CMD_SEND */ - for (i = 0; i < FTSDC010_CMD_RETRY; i++) { - sta = readl(&host->reg->status); - /* Command Complete */ - if (sta & FTSDC010_STATUS_CMD_SEND) { - if (!data) - clear |= FTSDC010_CLR_CMD_SEND; - break; - } - } - - if (i > FTSDC010_CMD_RETRY) { - printf("%s: send command timeout\n", __func__); - return TIMEOUT; - }
- /* debug: print status register and command index*/ - debug("sta: %08x cmd %d\n", sta, cmd->cmdidx); - - /* handle data FIFO */ - if ((sta & FTSDC010_STATUS_FIFO_ORUN) || - (sta & FTSDC010_STATUS_FIFO_URUN)) { - - /* Wrong DATA FIFO Flag */ - if (data == NULL) - printf("%s, data fifo wrong: sta: %08x cmd %d\n", - __func__, sta, cmd->cmdidx); - - if (sta & FTSDC010_STATUS_FIFO_ORUN) - clear |= FTSDC010_STATUS_FIFO_ORUN; - if (sta & FTSDC010_STATUS_FIFO_URUN) - clear |= FTSDC010_STATUS_FIFO_URUN; - } + sta = readl(&host->reg->status); + debug("%s: sta: %08x cmd %d\n", __func__, sta, cmd->cmdidx);
/* check RSP TIMEOUT or FAIL */ if (sta & FTSDC010_STATUS_RSP_TIMEOUT) { /* RSP TIMEOUT */ - debug("%s: RSP timeout: sta: %08x cmd %d\n", - __func__, sta, cmd->cmdidx); + debug("%s: RSP timeout: sta: %08x\n", __func__, sta);
clear |= FTSDC010_CLR_RSP_TIMEOUT; writel(clear, &host->reg->clr); @@ -226,47 +193,62 @@ static int ftsdc010_pio_check_status(struct mmc *mmc, struct mmc_cmd *cmd, return TIMEOUT; } else if (sta & FTSDC010_STATUS_RSP_CRC_FAIL) { /* clear response fail bit */ - debug("%s: RSP CRC FAIL: sta: %08x cmd %d\n", - __func__, sta, cmd->cmdidx); + debug("%s: RSP CRC FAIL: sta: %08x\n", __func__, sta);
clear |= FTSDC010_CLR_RSP_CRC_FAIL; writel(clear, &host->reg->clr);
- return 0; + return COMM_ERR; } else if (sta & FTSDC010_STATUS_RSP_CRC_OK) {
/* clear response CRC OK bit */ clear |= FTSDC010_CLR_RSP_CRC_OK; }
+ writel(clear, &host->reg->clr); + return 0; +} + +static int ftsdc010_check_data(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct mmc_host *host = mmc->priv; + unsigned int sta, clear; + + sta = readl(&host->reg->status); + debug("%s: sta: %08x cmd %d\n", __func__, sta, cmd->cmdidx); + /* check DATA TIMEOUT or FAIL */ if (data) { + if (sta & FTSDC010_STATUS_DATA_END) { + /* Transfer Complete */ + clear |= FTSDC010_STATUS_DATA_END; + } + + if (sta & FTSDC010_STATUS_DATA_CRC_OK) { + /* Data CRC_OK */ + clear |= FTSDC010_STATUS_DATA_CRC_OK; + } + if (sta & FTSDC010_STATUS_DATA_TIMEOUT) { /* DATA TIMEOUT */ - debug("%s: DATA TIMEOUT: sta: %08x\n", - __func__, sta); + debug("%s: DATA TIMEOUT: sta: %08x\n", __func__, sta);
clear |= FTSDC010_STATUS_DATA_TIMEOUT; writel(sta, &host->reg->clr); + return TIMEOUT; } else if (sta & FTSDC010_STATUS_DATA_CRC_FAIL) { /* Error Interrupt */ - debug("%s: DATA CRC FAIL: sta: %08x\n", - __func__, sta); + debug("%s: DATA CRC FAIL: sta: %08x\n", __func__, sta);
clear |= FTSDC010_STATUS_DATA_CRC_FAIL; writel(clear, &host->reg->clr);
- return 0; - } else if (sta & FTSDC010_STATUS_DATA_END) { - /* Transfer Complete */ - clear |= FTSDC010_STATUS_DATA_END; + return COMM_ERR; } + writel(clear, &host->reg->clr); } - - /* transaction is success and clear status register */ - writel(clear, &host->reg->clr); - return 0; }
@@ -281,6 +263,9 @@ static int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, unsigned int ccon; unsigned int mask, tmpmask; unsigned int ret; + unsigned int sta, clear, i; + + ret = 0;
if (data) mask = FTSDC010_INT_MASK_RSP_TIMEOUT; @@ -290,13 +275,8 @@ static int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, mask = FTSDC010_INT_MASK_CMD_SEND;
/* write argu reg */ - debug("%s: cmd->arg: %08x\n", __func__, cmd->cmdarg); writel(cmd->cmdarg, &host->reg->argu);
- /* setup cmd reg */ - debug("cmd: %d\n", cmd->cmdidx); - debug("resp: %08x\n", cmd->resp_type); - /* setup commnad */ ccon = FTSDC010_CMD_IDX(cmd->cmdidx);
@@ -342,6 +322,45 @@ static int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, writel(ccon, &host->reg->cmd); udelay(4*FTSDC010_DELAY_UNIT);
+ /* check CMD_SEND */ + /* + * Note: + * Do not clear FTSDC010_CLR_CMD_SEND flag here, + * (write FTSDC010_CLR_CMD_SEND bit to clear register) + * it will made the driver becomes very slow. + * If the operation hasn't been finished, hardware will + * clear this bit automatically. + */ + for (i = 0; i < FTSDC010_CMD_RETRY; i++) { + sta = readl(&host->reg->status); + /* Command Complete */ + if (sta & FTSDC010_STATUS_CMD_SEND) { + if (!data) + clear |= FTSDC010_CLR_CMD_SEND; + break; + } + } + + if (i > FTSDC010_CMD_RETRY) { + printf("%s: send command timeout\n", __func__); + return TIMEOUT; + } + + /* check rsp status */ + ret = ftsdc010_check_rsp(mmc, cmd, data); + if (ret) + return ret; + + /* read response if we have RSP_OK */ + if (ccon & FTSDC010_CMD_LONG_RSP) { + cmd->response[0] = readl(&host->reg->rsp3); + cmd->response[1] = readl(&host->reg->rsp2); + cmd->response[2] = readl(&host->reg->rsp1); + cmd->response[3] = readl(&host->reg->rsp0); + } else { + cmd->response[0] = readl(&host->reg->rsp0); + } + /* read/write data */ if (data && (data->flags & MMC_DATA_READ)) { ftsdc010_pio_read(host, data->dest, @@ -351,19 +370,11 @@ static int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, data->blocksize * data->blocks); }
- /* pio check response status */ - ret = ftsdc010_pio_check_status(mmc, cmd, data); - if (!ret) { - /* if it is long response */ - if (ccon & FTSDC010_CMD_LONG_RSP) { - cmd->response[0] = readl(&host->reg->rsp3); - cmd->response[1] = readl(&host->reg->rsp2); - cmd->response[2] = readl(&host->reg->rsp1); - cmd->response[3] = readl(&host->reg->rsp0); - - } else { - cmd->response[0] = readl(&host->reg->rsp0); - } + /* check data status */ + if (data) { + ret = ftsdc010_check_data(mmc, cmd, data); + if (ret) + return ret; }
udelay(FTSDC010_DELAY_UNIT); @@ -431,8 +442,6 @@ static int ftsdc010_setup_data(struct mmc *mmc, struct mmc_data *data) /* always reset fifo since last transfer may fail */ dcon |= FTSDC010_DCR_FIFO_RST;
- /* handle sdio */ - dcon = data->blocksize | data->blocks << 15; if (data->blocks > 1) dcon |= FTSDC010_SDIO_CTRL1_SDIO_BLK_MODE; #endif @@ -518,7 +527,7 @@ static void ftsdc010_set_clk(struct mmc *mmc) break; }
- debug("%s: computed real_rete: %x, clk_div: %x\n", + debug("%s: computed real_rate: %x, clk_div: %x\n", __func__, real_rate, clk_div);
if (clk_div > 127) @@ -579,6 +588,7 @@ static void ftsdc010_set_ios(struct mmc *mmc) static void ftsdc010_reset(struct mmc_host *host) { unsigned int timeout; + unsigned int sta;
/* Do SDC_RST: Software reset for all register */ writel(FTSDC010_CMD_SDC_RST, &host->reg->cmd); @@ -598,6 +608,10 @@ static void ftsdc010_reset(struct mmc_host *host) timeout--; udelay(10*FTSDC010_DELAY_UNIT); } + + sta = readl(&host->reg->status); + if (sta & FTSDC010_STATUS_CARD_CHANGE) + writel(FTSDC010_CLR_CARD_CHANGE, &host->reg->clr); }
static int ftsdc010_core_init(struct mmc *mmc)