From patchwork Tue Feb 15 09:35:07 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arindam Nath X-Patchwork-Id: 558421 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p1F9c1qq027319 for ; Tue, 15 Feb 2011 09:38:01 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754538Ab1BOJiA (ORCPT ); Tue, 15 Feb 2011 04:38:00 -0500 Received: from mail-iy0-f174.google.com ([209.85.210.174]:53726 "EHLO mail-iy0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754297Ab1BOJh7 (ORCPT ); Tue, 15 Feb 2011 04:37:59 -0500 Received: by iyj8 with SMTP id 8so5730570iyj.19 for ; Tue, 15 Feb 2011 01:37:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:sender:from:to:cc:subject:date:message-id :x-mailer:in-reply-to:references; bh=hSAZ4PjNiP/BO0w7zrKjpL/ysLJk8SrUDv5QS1YYlAs=; b=sX6pkGYhebltdNEAo194vbbNuisc57M41URayelAUu/SrQHYTbNAGDKF8Id7gqtuSu 2zpltUwz4LhHCqsfF+vKf41voWE4I+Nxlbxv2RQz/r2dXgxA3Qsuw8muXIKn/I99VmS0 QzXlyoNZlGzuWN08GYnZxLIzQI81AebKFYm6c= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=sender:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; b=QXs34vTzyEYExK5janntfZFbjEtSBf2/FcLOmFN2ocl1xubjwgjtP5jjU+1YdFjfKq /qLnachgzWQ6WNpJCOuRzLamoTcT4YsoXH1ThU9/tIpEpwH00Wtv1tTmDkcF0wiOMOBc 8JTuOIHArusD2BkHD+zOxOrAxPzzgTugt9kqs= Received: by 10.231.207.66 with SMTP id fx2mr3731833ibb.108.1297762679402; Tue, 15 Feb 2011 01:37:59 -0800 (PST) Received: from localhost ([122.167.0.108]) by mx.google.com with ESMTPS id u9sm3444681ibe.20.2011.02.15.01.37.52 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 15 Feb 2011 01:37:58 -0800 (PST) From: Arindam Nath To: cjb@laptop.org Cc: linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com, anath.amd@gmail.com, Arindam Nath Subject: [PATCH 09/12] mmc: sd: add support for tuning during uhs initialization Date: Tue, 15 Feb 2011 15:05:07 +0530 Message-Id: <1297762510-2696-10-git-send-email-arindam.nath@amd.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1297762510-2696-1-git-send-email-arindam.nath@amd.com> References: <1297762510-2696-1-git-send-email-arindam.nath@amd.com> Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Tue, 15 Feb 2011 09:38:01 +0000 (UTC) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index c52d427..3113cb6 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -624,6 +624,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) /* Set current limit for the card */ err = sd_set_current_limit(card, status); + /* SPI mode doesn't define CMD19 */ + if (!mmc_host_is_spi(card->host)) + card->host->ops->execute_tuning(card->host); + out: kfree(status); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index fc2cba6..6f3f0dc 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -39,6 +39,8 @@ #define SDHCI_USE_LEDS_CLASS #endif +#define MAX_TUNING_LOOP 40 + static unsigned int debug_quirks = 0; static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *); @@ -872,7 +874,10 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, if (data->flags & MMC_DATA_READ) mode |= SDHCI_TRNS_READ; - if (host->flags & SDHCI_REQ_USE_DMA) + + /* CMD19 requires DMA Enable to be set to 0 */ + if ((host->flags & SDHCI_REQ_USE_DMA) && + (host->cmd->opcode != MMC_SEND_TUNING_BLOCK)) mode |= SDHCI_TRNS_DMA; sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); @@ -988,7 +993,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) flags |= SDHCI_CMD_CRC; if (cmd->flags & MMC_RSP_OPCODE) flags |= SDHCI_CMD_INDEX; - if (cmd->data) + + /* CMD19 is special in that the Data Present Select should be set */ + if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK)) flags |= SDHCI_CMD_DATA; sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); @@ -1494,6 +1501,119 @@ static int sdhci_get_max_current_180(struct mmc_host *mmc) return max_current_180; } +static void sdhci_execute_tuning(struct mmc_host *mmc) +{ + struct sdhci_host *host; + u16 ctrl; + int tuning_loop_counter = MAX_TUNING_LOOP; + unsigned long flags; + unsigned long timeout; + + host = mmc_priv(mmc); + + spin_lock_irqsave(&host->lock, flags); + + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + /* + * Host Controller needs tuning only in case of SDR104 mode + * and for SDR50 mode when Use Tuning for SDR50 is set in + * Capabilities register. + */ + if ((ctrl & SDHCI_CTRL_UHS_SDR104) || + ((ctrl & SDHCI_CTRL_UHS_SDR50) && + (host->flags & SDHCI_SDR50_NEEDS_TUNING))) + ctrl |= SDHCI_CTRL_EXEC_TUNING; + else { + spin_unlock_irqrestore(&host->lock, flags); + return; + } + + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + + /* + * As per the Host Controller spec v3.00, tuning command + * generates Buffer Read Ready interrupt, so enable that. + */ + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL); + + /* + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number + * of loops reaches 40 times or a timeout of 150ms occurs. + */ + timeout = 150; + do { + struct mmc_command cmd; + struct mmc_request mrq; + + if (!tuning_loop_counter && !timeout) + break; + + memset(&cmd, 0, sizeof(struct mmc_command)); + cmd.opcode = MMC_SEND_TUNING_BLOCK; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&cmd.resp, 0, sizeof(cmd.resp)); + cmd.retries = 0; + + cmd.data = NULL; + cmd.error = 0; + + memset(&mrq, 0, sizeof(struct mmc_request)); + mrq.cmd = &cmd; + host->mrq = &mrq; + sdhci_send_command(host, &cmd); + + host->cmd = NULL; + host->mrq = NULL; + + spin_unlock_irqrestore(&host->lock, flags); + + /* Wait for Buffer Read Ready interrupt */ + wait_event_interruptible_timeout(host->buf_ready_int, + (host->tuning_done == 1), + msecs_to_jiffies(50)); + spin_lock_irqsave(&host->lock, flags); + + if (!host->tuning_done) { + printk(KERN_INFO DRIVER_NAME ": Tuning procedure" + " failed, falling back to fixed sampling" + " clock\n"); + ctrl &= ~SDHCI_CTRL_TUNED_CLK; + ctrl &= ~SDHCI_CTRL_EXEC_TUNING; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + + goto out; + } + + host->tuning_done = 0; + + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + tuning_loop_counter--; + timeout--; + mdelay(1); + } while (ctrl & SDHCI_CTRL_EXEC_TUNING); + + /* + * The Host Driver has exhausted the maximum number of loops allowed, + * so use fixed sampling frequency. + */ + if (!tuning_loop_counter || !timeout) { + ctrl &= ~SDHCI_CTRL_TUNED_CLK; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + } else { + if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) + printk(KERN_INFO DRIVER_NAME ": Tuning procedure" + " failed, falling back to fixed sampling" + " clock\n"); + } + +out: + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL); + spin_unlock_irqrestore(&host->lock, flags); +} + static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, .set_ios = sdhci_set_ios, @@ -1501,6 +1621,7 @@ static const struct mmc_host_ops sdhci_ops = { .enable_sdio_irq = sdhci_enable_sdio_irq, .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, .get_max_current_180 = sdhci_get_max_current_180, + .execute_tuning = sdhci_execute_tuning, }; /*****************************************************************************\ @@ -1712,6 +1833,16 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) { BUG_ON(intmask == 0); + /* CMD19 generates _only_ Buffer Read Ready interrupt */ + if (intmask & SDHCI_INT_DATA_AVAIL) { + if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) + == MMC_SEND_TUNING_BLOCK) { + host->tuning_done = 1; + wake_up(&host->buf_ready_int); + return; + } + } + if (!host->data) { /* * The "data complete" interrupt is also used to @@ -2126,6 +2257,10 @@ int sdhci_add_host(struct sdhci_host *host) if (caps[1] & SDHCI_SUPPORT_DDR50) mmc->caps |= MMC_CAP_UHS_DDR50; + /* Does the host needs tuning for SDR50? */ + if (caps[1] & SDHCI_USE_SDR50_TUNING) + host->flags |= SDHCI_SDR50_NEEDS_TUNING; + /* Driver Type(s) (A, C, D) supported by the host */ if (caps[1] & SDHCI_DRIVER_TYPE_A) mmc->caps |= MMC_CAP_DRIVER_TYPE_A; @@ -2269,6 +2404,9 @@ int sdhci_add_host(struct sdhci_host *host) setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); + if (host->version >= SDHCI_SPEC_300) + init_waitqueue_head(&host->buf_ready_int); + ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, mmc_hostname(mmc), host); if (ret) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 5bf244d..4746879 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -160,6 +160,8 @@ #define SDHCI_CTRL_DRV_TYPE_A 0x0010 #define SDHCI_CTRL_DRV_TYPE_C 0x0020 #define SDHCI_CTRL_DRV_TYPE_D 0x0030 +#define SDHCI_CTRL_EXEC_TUNING 0x0040 +#define SDHCI_CTRL_TUNED_CLK 0x0080 #define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000 #define SDHCI_CAPABILITIES 0x40 @@ -187,6 +189,7 @@ #define SDHCI_DRIVER_TYPE_A 0x00000010 #define SDHCI_DRIVER_TYPE_C 0x00000020 #define SDHCI_DRIVER_TYPE_D 0x00000040 +#define SDHCI_USE_SDR50_TUNING 0x00002000 #define SDHCI_CAPABILITIES_1 0x44 diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 65a003d..4abb964 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -129,6 +129,7 @@ struct mmc_host_ops { int (*start_signal_voltage_switch)(struct mmc_host *host); int (*get_max_current_180)(struct mmc_host *mmc); + void (*execute_tuning)(struct mmc_host *host); }; struct mmc_card; diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 612301f..9194f9e 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -50,6 +50,7 @@ #define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ #define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */ #define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ +#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */ /* class 3 */ #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 83bd9f7..26b6278 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -109,6 +109,7 @@ struct sdhci_host { #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */ #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */ unsigned int version; /* SDHCI spec. version */ @@ -145,6 +146,9 @@ struct sdhci_host { unsigned int ocr_avail_sd; unsigned int ocr_avail_mmc; + wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ + unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ + unsigned long private[0] ____cacheline_aligned; }; #endif /* __SDHCI_H */