From patchwork Tue Nov 29 03:30:17 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Macpaul Lin X-Patchwork-Id: 128211 X-Patchwork-Delegate: afleming@freescale.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id DD2BB1007D2 for ; Tue, 29 Nov 2011 14:59:38 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 151B7281D1; Tue, 29 Nov 2011 04:59:35 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 8RALOng5nRdU; Tue, 29 Nov 2011 04:59:34 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 9345B281F8; Tue, 29 Nov 2011 04:59:32 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 57812281F8 for ; Tue, 29 Nov 2011 04:59:29 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id jCdoUWSpUrEy for ; Tue, 29 Nov 2011 04:59:27 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from atces.andestech.com (59-124-160-118.HINET-IP.hinet.net [59.124.160.118]) by theia.denx.de (Postfix) with ESMTP id 0D08A281D1 for ; Tue, 29 Nov 2011 04:59:24 +0100 (CET) Received: from ATCPCS06.andestech.com (atcpcs06.andestech.com [10.0.1.236]) by atces.andestech.com (Postfix) with ESMTP id 279D22E20012; Tue, 29 Nov 2011 11:30:31 +0800 (CST) Received: from app01.andestech.com ([10.0.4.31]) by ATCPCS06.andestech.com with Microsoft SMTPSVC(6.0.3790.4675); Tue, 29 Nov 2011 11:30:30 +0800 From: Macpaul Lin To: afleming@gmail.com, afleming@freescale.com, u-boot@lists.denx.de, macpaul@gmail.com Date: Tue, 29 Nov 2011 11:30:17 +0800 Message-Id: <1322537417-4439-1-git-send-email-macpaul@andestech.com> X-Mailer: git-send-email 1.7.3.5 In-Reply-To: References: X-OriginalArrivalTime: 29 Nov 2011 03:30:30.0803 (UTC) FILETIME=[3EE55630:01CCAE47] Cc: Macpaul Lin Subject: [U-Boot] [PATCH v4] ftsdc010: improve performance and capability X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de 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 --- Changes for v2: - Fix the problem if we read status register too fast in FTSDC010_CMD_RETRY loop. If we read status register here too fast, the hardware will report RSP_TIMEOUT incorrectly. Changes for v3: - Remove host high speed capability due to hardware limitation. - Remove unused variables. Changes for v4: - Add missing change log of replacing FTSDC010_STATUS_FIFO_ORUN by FTSDC010_STATUS_FIFO_URUN in ftsdc010_pio_write() - Replace comment of processing flag of DATA_TIMEOUT by simple format. - Fix incorrect register updating on processing DATA_TIMEOUT flag. drivers/mmc/ftsdc010_esdhc.c | 188 +++++++++++++++++++++++------------------- 1 files changed, 103 insertions(+), 85 deletions(-) diff --git a/drivers/mmc/ftsdc010_esdhc.c b/drivers/mmc/ftsdc010_esdhc.c index e38dd87..33cb5d6 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) { + + /* Transfer Complete */ + if (sta & FTSDC010_STATUS_DATA_END) + clear |= FTSDC010_STATUS_DATA_END; + + /* Data CRC_OK */ + if (sta & FTSDC010_STATUS_DATA_CRC_OK) + clear |= FTSDC010_STATUS_DATA_CRC_OK; + + /* DATA TIMEOUT or DATA CRC FAIL */ 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); + writel(clear, &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); + /* DATA CRC FAIL */ + 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, i; + + ret = 0; if (data) mask = FTSDC010_INT_MASK_RSP_TIMEOUT; @@ -290,13 +275,9 @@ 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); + debug("%s: argu: %08x\n", __func__, host->reg->argu); 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); @@ -340,7 +321,51 @@ static int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, /* write cmd reg */ debug("%s: ccon: %08x\n", __func__, ccon); writel(ccon, &host->reg->cmd); - udelay(4*FTSDC010_DELAY_UNIT); + + /* check CMD_SEND */ + for (i = 0; i < FTSDC010_CMD_RETRY; i++) { + /* + * If we read status register too fast + * will lead hardware error and the RSP_TIMEOUT + * flag will be raised incorrectly. + */ + udelay(16*FTSDC010_DELAY_UNIT); + sta = readl(&host->reg->status); + + /* Command Complete */ + /* + * Note: + * Do not clear FTSDC010_CLR_CMD_SEND flag. + * (by writing FTSDC010_CLR_CMD_SEND bit to clear register) + * It will make the driver becomes very slow. + * If the operation hasn't been finished, hardware will + * clear this bit automatically. + * In origin, the driver will clear this flag if there is + * no data need to be read. + */ + if (sta & FTSDC010_STATUS_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)) { @@ -351,19 +376,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 +448,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 @@ -497,7 +512,7 @@ static void ftsdc010_set_clk(struct mmc *mmc) { struct mmc_host *host = mmc->priv; unsigned char clk_div; - unsigned char real_rate; + unsigned int real_rate; unsigned int clock; debug("%s: mmc_set_clock: %x\n", __func__, mmc->clock); @@ -518,7 +533,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 +594,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 +614,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) @@ -650,8 +670,6 @@ int ftsdc010_mmc_init(int dev_index) mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; - mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; - mmc->f_min = CONFIG_SYS_CLK_FREQ / 2 / (2*128); mmc->f_max = CONFIG_SYS_CLK_FREQ / 2 / 2;