From patchwork Thu May 5 06:49:01 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arindam Nath X-Patchwork-Id: 756012 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter2.kernel.org (8.14.4/8.14.3) with ESMTP id p456pEaK021009 for ; Thu, 5 May 2011 06:51:14 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752029Ab1EEGvM (ORCPT ); Thu, 5 May 2011 02:51:12 -0400 Received: from mail-px0-f173.google.com ([209.85.212.173]:45304 "EHLO mail-px0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751990Ab1EEGvL (ORCPT ); Thu, 5 May 2011 02:51:11 -0400 Received: by pxi16 with SMTP id 16so1438947pxi.4 for ; Wed, 04 May 2011 23:51:11 -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=MMULwvK3H7ws53aPVnmcOKAkhR879FlbZieWUqFTkSQ=; b=iwPIDhfMzj+S3mjHzx+i4ktXXjxYkgLlHm5dERc7jMq6cW9V6evt7yEkHQzkl8Ph2i AmetZr1L06LbIB85QSWEuCs7/0vQM5vZckNJPaZCtfPUhQGU9eDzqX0g6kqcg5Ap2Qs3 poZmWwmUZpH0YNS2mGWzlsDwqqhLnHYBK3HmU= 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=KB57yJEj+gkL5EVLlm5v7vKksg2nN5UfLqYZyvr4ul44rdILMgNbj9SqTb/l/27Y2c GsEjdK/wlvg6BySwHfD8xtBnqWZhDLLf6eWmUH/BW6Ux56oRzN3Mz8ak1I7eo0pl4gbY l1IGn3qBQ7YT/phSkpqAITgnE3/IgAbDICM/U= Received: by 10.142.248.38 with SMTP id v38mr1084112wfh.15.1304578271041; Wed, 04 May 2011 23:51:11 -0700 (PDT) Received: from localhost ([122.166.82.113]) by mx.google.com with ESMTPS id z10sm2425586wfj.12.2011.05.04.23.51.01 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 04 May 2011 23:51:10 -0700 (PDT) From: Arindam Nath To: cjb@laptop.org Cc: prakity@marvell.com, zhangfei.gao@gmail.com, subhashj@codeaurora.org, linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com, anath.amd@gmail.com, Arindam Nath Subject: [PATCH v4 05/15] mmc: sd: add support for uhs bus speed mode selection Date: Thu, 5 May 2011 12:19:01 +0530 Message-Id: <1304578151-1775-6-git-send-email-arindam.nath@amd.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1304578151-1775-1-git-send-email-arindam.nath@amd.com> References: <1304578151-1775-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 (demeter2.kernel.org [140.211.167.43]); Thu, 05 May 2011 06:51:14 +0000 (UTC) This patch adds support for setting UHS-I bus speed mode during UHS-I initialization procedure. Since both the host and card can support more than one bus speed, we select the highest speed based on both of their capabilities. First we set the bus speed mode for the card using CMD6 mode 1, and then we program the host controller to support the required speed mode. We also set High Speed Enable in case one of the UHS-I modes is selected. We take care to reset SD clock before setting UHS mode in the Host Control2 register, and then re-enable it as per the Host Controller spec v3.00. We then set the clock frequency for the UHS-I mode selected. Signed-off-by: Arindam Nath Reviewed-by: Philip Rakity Tested-by: Philip Rakity --- drivers/mmc/core/sd.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci.c | 40 ++++++++++++++++++++++++++-- drivers/mmc/host/sdhci.h | 6 ++++ include/linux/mmc/card.h | 19 +++++++++++++ include/linux/mmc/host.h | 5 +++ 5 files changed, 132 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index a6fa52f..0491978 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -457,6 +457,66 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) return 0; } +static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status) +{ + unsigned int bus_speed = 0, timing = 0; + int err; + + /* + * If the host doesn't support any of the UHS-I modes, fallback on + * default speed. + */ + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))) + return 0; + + if ((card->host->caps & MMC_CAP_UHS_SDR104) && + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) { + bus_speed = UHS_SDR104_BUS_SPEED; + timing = MMC_TIMING_UHS_SDR104; + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR; + } else if ((card->host->caps & MMC_CAP_UHS_DDR50) && + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) { + bus_speed = UHS_DDR50_BUS_SPEED; + timing = MMC_TIMING_UHS_DDR50; + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR; + } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | + MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode & + SD_MODE_UHS_SDR50)) { + bus_speed = UHS_SDR50_BUS_SPEED; + timing = MMC_TIMING_UHS_SDR50; + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR; + } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) && + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) { + bus_speed = UHS_SDR25_BUS_SPEED; + timing = MMC_TIMING_UHS_SDR25; + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR; + } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode & + SD_MODE_UHS_SDR12)) { + bus_speed = UHS_SDR12_BUS_SPEED; + timing = MMC_TIMING_UHS_SDR12; + card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR; + } + + card->sd_bus_speed = bus_speed; + err = mmc_sd_switch(card, 1, 0, bus_speed, status); + if (err) + return err; + + if ((status[16] & 0xF) != bus_speed) + printk(KERN_WARNING "%s: Problem setting bus speed mode!\n", + mmc_hostname(card->host)); + else { + mmc_set_timing(card->host, timing); + mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr); + } + + return 0; +} + /* * UHS-I specific initialization procedure */ @@ -490,6 +550,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) /* Set the driver strength for the card */ err = sd_select_driver_type(card, status); + if (err) + goto out; + + /* Set bus speed mode of the card */ + err = sd_set_bus_speed_mode(card, status); out: kfree(status); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 309240c..de7e6e9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1244,7 +1244,16 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ctrl &= ~SDHCI_CTRL_HISPD; if (host->version >= SDHCI_SPEC_300) { - u16 ctrl_2; + u16 clk, ctrl_2; + unsigned int clock; + + /* In case of UHS-I modes, set High Speed Enable */ + if ((ios->timing == MMC_TIMING_UHS_SDR50) || + (ios->timing == MMC_TIMING_UHS_SDR104) || + (ios->timing == MMC_TIMING_UHS_DDR50) || + (ios->timing == MMC_TIMING_UHS_SDR25) || + (ios->timing == MMC_TIMING_UHS_SDR12)) + ctrl |= SDHCI_CTRL_HISPD; ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) { @@ -1267,8 +1276,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * need to reset SD Clock Enable before changing High * Speed Enable to avoid generating clock gliches. */ - u16 clk; - unsigned int clock; /* Reset SD Clock Enable */ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); @@ -1282,6 +1289,33 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->clock = 0; sdhci_set_clock(host, clock); } + + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + /* Select Bus Speed Mode for host */ + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + if (ios->timing == MMC_TIMING_UHS_SDR12) + ctrl_2 |= SDHCI_CTRL_UHS_SDR12; + else if (ios->timing == MMC_TIMING_UHS_SDR25) + ctrl_2 |= SDHCI_CTRL_UHS_SDR25; + else if (ios->timing == MMC_TIMING_UHS_SDR50) + ctrl_2 |= SDHCI_CTRL_UHS_SDR50; + else if (ios->timing == MMC_TIMING_UHS_SDR104) + ctrl_2 |= SDHCI_CTRL_UHS_SDR104; + else if (ios->timing == MMC_TIMING_UHS_DDR50) + ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + + /* Reset SD Clock Enable */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); + + /* Re-enable SD Clock */ + clock = host->clock; + host->clock = 0; + sdhci_set_clock(host, clock); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 04c41a4..5b12793 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -149,6 +149,12 @@ #define SDHCI_ACMD12_ERR 0x3C #define SDHCI_HOST_CONTROL2 0x3E +#define SDHCI_CTRL_UHS_MASK 0x0007 +#define SDHCI_CTRL_UHS_SDR12 0x0000 +#define SDHCI_CTRL_UHS_SDR25 0x0001 +#define SDHCI_CTRL_UHS_SDR50 0x0002 +#define SDHCI_CTRL_UHS_SDR104 0x0003 +#define SDHCI_CTRL_UHS_DDR50 0x0004 #define SDHCI_CTRL_VDD_180 0x0008 #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 #define SDHCI_CTRL_DRV_TYPE_B 0x0000 diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 5393272..4ef6ded 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -81,7 +81,24 @@ struct sd_ssr { struct sd_switch_caps { unsigned int hs_max_dtr; + unsigned int uhs_max_dtr; +#define UHS_SDR104_MAX_DTR 208000000 +#define UHS_SDR50_MAX_DTR 100000000 +#define UHS_DDR50_MAX_DTR 50000000 +#define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR +#define UHS_SDR12_MAX_DTR 25000000 unsigned int sd3_bus_mode; +#define UHS_SDR12_BUS_SPEED 0 +#define UHS_SDR25_BUS_SPEED 1 +#define UHS_SDR50_BUS_SPEED 2 +#define UHS_SDR104_BUS_SPEED 3 +#define UHS_DDR50_BUS_SPEED 4 + +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED) +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED) +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED) +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED) +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED) unsigned int sd3_drv_type; #define SD_DRIVER_TYPE_B 0x01 #define SD_DRIVER_TYPE_A 0x02 @@ -166,6 +183,8 @@ struct mmc_card { const char **info; /* info strings */ struct sdio_func_tuple *tuples; /* unknown common tuples */ + unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ + struct dentry *debugfs_root; }; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 949e4d5..6237599 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -50,6 +50,11 @@ struct mmc_ios { #define MMC_TIMING_LEGACY 0 #define MMC_TIMING_MMC_HS 1 #define MMC_TIMING_SD_HS 2 +#define MMC_TIMING_UHS_SDR12 MMC_TIMING_LEGACY +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS +#define MMC_TIMING_UHS_SDR50 3 +#define MMC_TIMING_UHS_SDR104 4 +#define MMC_TIMING_UHS_DDR50 5 unsigned char ddr; /* dual data rate used */