From patchwork Fri Apr 15 10:42:59 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arindam Nath X-Patchwork-Id: 710601 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 p3FAiQcQ030134 for ; Fri, 15 Apr 2011 10:44:26 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755799Ab1DOKoS (ORCPT ); Fri, 15 Apr 2011 06:44:18 -0400 Received: from mail-pv0-f174.google.com ([74.125.83.174]:37925 "EHLO mail-pv0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755793Ab1DOKoQ (ORCPT ); Fri, 15 Apr 2011 06:44:16 -0400 Received: by mail-pv0-f174.google.com with SMTP id 12so1009144pvg.19 for ; Fri, 15 Apr 2011 03:44:15 -0700 (PDT) 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=sb07BGA1feYK9jL2AA/8wOXug1xsSGF29L495HwZm9U=; b=xezrMzsdLDzpCqNExcO8NzVXpoi65z+WUVQJ7z12KkEz9jzNWTTFlZQvHNDEZwbrPA Ltuuq3/m8kwg1BICPiIz+zECJvqX7gC+tTtTnzeClHCTPv4cNxYWdOzv9MNMMolLNFKx OQed8Mwzfr1bOS+z2+sjDeeLe5RPVRqogYIc4= 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=oy6NC/Fu4LYO2N4t5EEs3T/dBhR3sDbX6RakMrccBenOQvmenAbmJnetXmU7kwKGo2 MssnueH17j6L7Pu+t32n/agAG4MT/TSoGKGFMz/JkEeOaat242nR/k+GKPZeOaldDyrk z+2Y1SIb5LT3U9AhOujQXGNo01N7Tkc8JIKQ0= Received: by 10.142.196.14 with SMTP id t14mr793106wff.89.1302864255803; Fri, 15 Apr 2011 03:44:15 -0700 (PDT) Received: from localhost ([122.167.17.41]) by mx.google.com with ESMTPS id m10sm3548996wfl.11.2011.04.15.03.44.08 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 15 Apr 2011 03:44:15 -0700 (PDT) From: Arindam Nath To: cjb@laptop.org Cc: linux-mmc@vger.kernel.org, subhashj@codeaurora.org, prakity@marvell.com, zhangfei.gao@gmail.com, henry.su@amd.com, aaron.lu@amd.com, anath.amd@gmail.com, Arindam Nath Subject: [PATCH v3 11/12] mmc: sdhci: add support for programmable clock mode Date: Fri, 15 Apr 2011 16:12:59 +0530 Message-Id: <1302864180-1858-4-git-send-email-arindam.nath@amd.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1302864180-1858-1-git-send-email-arindam.nath@amd.com> References: <1302864180-1858-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]); Fri, 15 Apr 2011 10:44:27 +0000 (UTC) Host Controller v3.00 supports programmable clock mode as an optional feature. The support for this mode is indicated by non-zero value in bits 48-55 of the Capabilities register. If supported, the actual value of Clock Multiplier is one more than the value provided in the bit fields. We only set Clock Generator Select (bit 5) and SDCLK Frequency Select (bits 8-15) of the Clock Control register in case Preset Value Enable is not set, otherwise these fields are automatically set by the Host Controller based on the UHS mode selected. Also, since the maximum and minimum clock frequency in this mode can be (Base Clock * Clock Mul) and (Base Clock * Clock Mul)/1024 respectively, f_max and f_min have been recalculated to reflect this change. Signed-off-by: Arindam Nath --- drivers/mmc/host/sdhci.c | 81 ++++++++++++++++++++++++++++++++++++-------- drivers/mmc/host/sdhci.h | 3 ++ include/linux/mmc/sdhci.h | 1 + 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4d32464..d11e577 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1039,8 +1039,8 @@ static void sdhci_finish_command(struct sdhci_host *host) static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { - int div; - u16 clk; + int div = 0; /* Initialized for compiler warning */ + u16 clk = 0; unsigned long timeout; if (clock == host->clock) @@ -1058,14 +1058,45 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) goto out; if (host->version >= SDHCI_SPEC_300) { - /* Version 3.00 divisors must be a multiple of 2. */ - if (host->max_clk <= clock) - div = 1; - else { - for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) { - if ((host->max_clk / div) <= clock) - break; + /* + * Check if the Host Controller supports Programmable Clock + * Mode. + */ + if (host->clk_mul) { + u16 ctrl; + + /* + * We need to figure out whether the Host Driver needs + * to select Programmable Clock Mode, or the value can + * be set automatically by the Host Controller based on + * the Preset Value registers. + */ + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if (!(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { + for (div = 1; div <= 1024; div++) { + if (((host->max_clk * host->clk_mul) / + div) <= clock) + break; + } + /* + * Set Programmable Clock Mode in the Clock + * Control register. + */ + clk = SDHCI_PROG_CLOCK_MODE; + div--; + } + } else { + /* Version 3.00 divisors must be a multiple of 2. */ + if (host->max_clk <= clock) + div = 1; + else { + for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; + div += 2) { + if ((host->max_clk / div) <= clock) + break; + } } + div >>= 1; } } else { /* Version 2.00 divisors must be a power of 2. */ @@ -1073,10 +1104,10 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) if ((host->max_clk / div) <= clock) break; } + div >>= 1; } - div >>= 1; - clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; + clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; clk |= SDHCI_CLOCK_INT_EN; @@ -2321,17 +2352,37 @@ int sdhci_add_host(struct sdhci_host *host) host->timeout_clk = host->clock / 1000; /* + * In case of Host Controller v3.00, find out whether clock + * multiplier is supported. + */ + host->clk_mul = (caps[1] & SDHCI_CLOCK_MUL_MASK) >> + SDHCI_CLOCK_MUL_SHIFT; + + /* + * In case the value in Clock Multiplier is 0, then programmable + * clock mode is not supported, otherwise the actual clock + * multiplier is one more than the value of Clock Multiplier + * in the Capabilities Register. + */ + if (host->clk_mul) + host->clk_mul += 1; + + /* * Set host parameters. */ mmc->ops = &sdhci_ops; + mmc->f_max = host->max_clk; if (host->ops->get_min_clock) mmc->f_min = host->ops->get_min_clock(host); - else if (host->version >= SDHCI_SPEC_300) - mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; - else + else if (host->version >= SDHCI_SPEC_300) { + if (host->clk_mul) { + mmc->f_min = (host->max_clk * host->clk_mul) / 1024; + mmc->f_max = host->max_clk * host->clk_mul; + } else + mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; + } else mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; - mmc->f_max = host->max_clk; mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE; /* diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index e134c75..4709091 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -103,6 +103,7 @@ #define SDHCI_DIV_MASK 0xFF #define SDHCI_DIV_MASK_LEN 8 #define SDHCI_DIV_HI_MASK 0x300 +#define SDHCI_PROG_CLOCK_MODE 0x0020 #define SDHCI_CLOCK_CARD_EN 0x0004 #define SDHCI_CLOCK_INT_STABLE 0x0002 #define SDHCI_CLOCK_INT_EN 0x0001 @@ -193,6 +194,8 @@ #define SDHCI_DRIVER_TYPE_C 0x00000020 #define SDHCI_DRIVER_TYPE_D 0x00000040 #define SDHCI_USE_SDR50_TUNING 0x00002000 +#define SDHCI_CLOCK_MUL_MASK 0x00FF0000 +#define SDHCI_CLOCK_MUL_SHIFT 16 #define SDHCI_CAPABILITIES_1 0x44 diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 5203b97..4be4022 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -115,6 +115,7 @@ struct sdhci_host { unsigned int max_clk; /* Max possible freq (MHz) */ unsigned int timeout_clk; /* Timeout freq (KHz) */ + unsigned int clk_mul; /* Clock Muliplier value */ unsigned int clock; /* Current clock (MHz) */ u8 pwr; /* Current voltage */