From patchwork Mon Jan 10 09:19:41 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhangfei Gao X-Patchwork-Id: 468021 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 p0A9JiHG019648 for ; Mon, 10 Jan 2011 09:19:45 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752270Ab1AJJTn (ORCPT ); Mon, 10 Jan 2011 04:19:43 -0500 Received: from mail-fx0-f46.google.com ([209.85.161.46]:45577 "EHLO mail-fx0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752234Ab1AJJTn (ORCPT ); Mon, 10 Jan 2011 04:19:43 -0500 Received: by fxm20 with SMTP id 20so18625836fxm.19 for ; Mon, 10 Jan 2011 01:19:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:received:in-reply-to :references:date:message-id:subject:from:to:cc:content-type; bh=bNgGAb5aX/GsWP39rACxHmXNNwHRoeMPfE7URXRyAoM=; b=s5QfngpkZOQwzUGndZpMmP3QojgsKymkgPXzARxEXY0pH7Jur9KyiMgvdPzYoWohjh QBUWrumYbArJJqX6gPrT2Di+qnyZGgGd85/efCnCGfHkHwUfVrSTXS4vraj2jMfRNdNH by/z2PaR56YR5dGfRSM6ABw0Lf0wY4jyvhaJU= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; b=XroxAmxl57afUVzxWWYGBpsit7GSFJ6We3GMM1TdznFONk74CB3f9feRErBkHgroBX q6ePN7r+o43H0f8E2Ya577B1ZBA3JAn1Wu/+ejSUE+tpNGtG5xPlGpZVlIKfOpnc6jiJ kiAAWkvuqKG9Awaw0F5nJ4ZoS0jrqkG8nmzis= MIME-Version: 1.0 Received: by 10.223.83.14 with SMTP id d14mr6305063fal.22.1294651181960; Mon, 10 Jan 2011 01:19:41 -0800 (PST) Received: by 10.223.122.210 with HTTP; Mon, 10 Jan 2011 01:19:41 -0800 (PST) In-Reply-To: References: Date: Mon, 10 Jan 2011 04:19:41 -0500 Message-ID: Subject: Re: [patch]: sdhci support emmc ddr50 mode [v4] From: zhangfei gao To: linux-mmc@vger.kernel.org Cc: Chris Ball 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]); Mon, 10 Jan 2011 09:19:45 +0000 (UTC) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index a8e89f3..1f1f185 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -623,6 +623,24 @@ static inline void mmc_set_ios(struct mmc_host *host) host->ops->set_ios(host, ios); } +int mmc_set_uhs(struct mmc_host *host, unsigned int uhs_mode) +{ + int ret = -EINVAL; + + if (host->ops->set_uhs) { + ret = host->ops->set_uhs(host, uhs_mode); + + /* + * According to sdhc standard spec v3.0 + * 1.8v regulator should be stable withing 5ms + */ + if (!ret) + mmc_delay(5); + } + + return ret; +} + /* * Control chip select pin on a host. */ diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 026c975..4b1ea7e 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -41,6 +41,7 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width); void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, unsigned int ddr); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); +int mmc_set_uhs(struct mmc_host *host, unsigned int uhs_mode); void mmc_set_timing(struct mmc_host *host, unsigned int timing); static inline void mmc_delay(unsigned int ms) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 86cac0d..657c5ef 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -529,6 +529,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ddr = MMC_1_2V_DDR_MODE; } + if (ddr) { + if (mmc_set_uhs(host, ddr)) { + printk(KERN_WARNING "%s: DDR mode not accepted by host " + "and reuse MMC_SDR_MODE.\n", + mmc_hostname(host)); + ddr = MMC_SDR_MODE; + } + } + /* * Activate wide bus and DDR (if supported). */ diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d5febe5..906f85b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -986,6 +986,22 @@ static void sdhci_finish_command(struct sdhci_host *host) host->cmd = NULL; } +static void sdhci_set_ddr(struct sdhci_host *host, unsigned int ddr) +{ + u16 con; + + if (ddr == MMC_SDR_MODE) + return; + + con = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if (con & SDHCI_CTRL2_1_8V) { + con &= ~SDHCI_CTRL2_UHS_MASK; + if (ddr & MMC_1_8V_DDR_MODE) + con |= SDHCI_CTRL2_DDR50; + sdhci_writew(host, con, SDHCI_HOST_CONTROL2); + } +} + static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { int div; @@ -1180,6 +1196,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } sdhci_set_clock(host, ios->clock); + sdhci_set_ddr(host, ios->ddr); if (ios->power_mode == MMC_POWER_OFF) sdhci_set_power(host, -1); @@ -1237,6 +1254,35 @@ out: spin_unlock_irqrestore(&host->lock, flags); } +static int sdhci_set_uhs(struct mmc_host *mmc, unsigned int uhs_mode) +{ + struct sdhci_host *host; + unsigned long flags; + int ret = 0; + + host = mmc_priv(mmc); + + spin_lock_irqsave(&host->lock, flags); + if (host->mmc->caps & MMC_CAP_1_8V_DDR) { + u16 con; + + con = sdhci_readw(host, SDHCI_HOST_CONTROL2); + con |= SDHCI_CTRL2_1_8V; + sdhci_writew(host, con, SDHCI_HOST_CONTROL2); + } else + goto err; + + spin_unlock_irqrestore(&host->lock, flags); + + if (host->ops->set_uhs) + ret = host->ops->set_uhs(host, uhs_mode); + + return ret; +err: + spin_unlock_irqrestore(&host->lock, flags); + return -EINVAL; +} + static int sdhci_get_ro(struct mmc_host *mmc) { struct sdhci_host *host; @@ -1287,6 +1333,7 @@ out: static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, .set_ios = sdhci_set_ios, + .set_uhs = sdhci_set_uhs, .get_ro = sdhci_get_ro, .enable_sdio_irq = sdhci_enable_sdio_irq, }; @@ -1744,7 +1791,7 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host); int sdhci_add_host(struct sdhci_host *host) { struct mmc_host *mmc; - unsigned int caps, ocr_avail; + unsigned int caps, caps_h = 0, ocr_avail; int ret; WARN_ON(host == NULL); @@ -1767,8 +1814,17 @@ int sdhci_add_host(struct sdhci_host *host) host->version); } - caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps : - sdhci_readl(host, SDHCI_CAPABILITIES); + if (host->quirks & SDHCI_QUIRK_MISSING_CAPS) + caps = host->caps; + else { + caps = sdhci_readl(host, SDHCI_CAPABILITIES); + caps_h = sdhci_readl(host, SDHCI_CAPABILITIES_1); + } + + if (caps & SDHCI_CAN_VDD_180) { + if (caps_h & SDHCI_CAN_SDR50) + mmc->caps |= (MMC_CAP_1_8V_DDR); + } if (host->quirks & SDHCI_QUIRK_FORCE_DMA) host->flags |= SDHCI_USE_SDMA; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 6e0969e..d4ef100 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -145,7 +145,14 @@ #define SDHCI_ACMD12_ERR 0x3C -/* 3E-3F reserved */ +#define SDHCI_HOST_CONTROL2 0x3E +#define SDHCI_CTRL2_UHS_MASK 0x0007 +#define SDHCI_CTRL2_SDR12 0x0000 +#define SDHCI_CTRL2_SDR25 0x0001 +#define SDHCI_CTRL2_SDR50 0x0002 +#define SDHCI_CTRL2_SDR104 0x0003 +#define SDHCI_CTRL2_DDR50 0x0004 +#define SDHCI_CTRL2_1_8V 0x0008 #define SDHCI_CAPABILITIES 0x40 #define SDHCI_TIMEOUT_CLK_MASK 0x0000003F @@ -167,6 +174,9 @@ #define SDHCI_CAN_64BIT 0x10000000 #define SDHCI_CAPABILITIES_1 0x44 +#define SDHCI_CAN_SDR50 0x00000001 +#define SDHCI_CAN_SDR104 0x00000002 +#define SDHCI_CAN_DDR50 0x00000004 #define SDHCI_MAX_CURRENT 0x48 @@ -222,6 +232,8 @@ struct sdhci_ops { void (*platform_send_init_74_clocks)(struct sdhci_host *host, u8 power_mode); unsigned int (*get_ro)(struct sdhci_host *host); + int (*set_uhs)(struct sdhci_host *host, + unsigned int uhs_mode); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index bcb793e..07131b0 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -117,6 +117,8 @@ struct mmc_host_ops { /* optional callback for HC quirks */ void (*init_card)(struct mmc_host *host, struct mmc_card *card); + + int (*set_uhs)(struct mmc_host *host, unsigned int uhs_mode); }; struct mmc_card;