From patchwork Tue Feb 15 09:35:09 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arindam Nath X-Patchwork-Id: 558451 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 p1F9cNXr027723 for ; Tue, 15 Feb 2011 09:38:26 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754593Ab1BOJi0 (ORCPT ); Tue, 15 Feb 2011 04:38:26 -0500 Received: from mail-iw0-f174.google.com ([209.85.214.174]:40842 "EHLO mail-iw0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754297Ab1BOJiZ (ORCPT ); Tue, 15 Feb 2011 04:38:25 -0500 Received: by mail-iw0-f174.google.com with SMTP id 9so5732646iwn.19 for ; Tue, 15 Feb 2011 01:38:25 -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=FdksQJgRxoO1s5OYJk4sPRi2ZdDsvYDWKhqCkB2awZc=; b=MdFRXNItF/f4iJyVd7DGqNqRxl+4bB/dccDABD8cLfgB+dA3sjGcH5RKvlx4efkhQR 9Zgv39u5/cLlUZEfU38xFv7icvbb5aj763LuIeTKrJUecQEloV0ssK/Y66Vyq9y8Nw0G mQxpIVFIlmzBkA3m0juVMTbcYR1p2u2o+poUU= 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=B2iEh0hP1wXvsvyPq3pIIR6KLfp3TuJrpVuquJy40yEEjEsexeT1j/BYUT+XwkpmOS SIMwEQ3NtTd5u5GZzdWVr6rnCnUsCyVg1KM5VigTldNtGV4/PiyHZz5bZjAp0gF8n+id vbHZwfXCHiPo1LKzQIHqt0MYJkNkuv+7WUhCg= Received: by 10.42.169.74 with SMTP id a10mr6458363icz.222.1297762704881; Tue, 15 Feb 2011 01:38:24 -0800 (PST) Received: from localhost ([122.167.0.108]) by mx.google.com with ESMTPS id g4sm1335015ick.23.2011.02.15.01.38.18 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 15 Feb 2011 01:38:24 -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 11/12] mmc: sdhci: add support for programmable clock mode Date: Tue, 15 Feb 2011 15:05:09 +0530 Message-Id: <1297762510-2696-12-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:26 +0000 (UTC) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 722f668..9f8fb84 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1036,7 +1036,7 @@ static void sdhci_finish_command(struct sdhci_host *host) static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { - int div; + int div = 0; /* Initialized for compiler warning */ u16 clk; unsigned long timeout; @@ -1055,14 +1055,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. */ @@ -1070,8 +1101,8 @@ 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_HI_MASK) >> SDHCI_DIV_MASK_LEN) @@ -2255,17 +2286,37 @@ int sdhci_add_host(struct sdhci_host *host) host->timeout_clk *= 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; /* diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 4746879..37a8c32 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -102,6 +102,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 @@ -190,6 +191,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 26b6278..1e7a654 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 */