From patchwork Fri Mar 4 11:32:43 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arindam Nath X-Patchwork-Id: 608771 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 p24BXOPv029838 for ; Fri, 4 Mar 2011 11:34:31 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759409Ab1CDLeX (ORCPT ); Fri, 4 Mar 2011 06:34:23 -0500 Received: from mail-iw0-f174.google.com ([209.85.214.174]:35883 "EHLO mail-iw0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752011Ab1CDLeX (ORCPT ); Fri, 4 Mar 2011 06:34:23 -0500 Received: by mail-iw0-f174.google.com with SMTP id 34so1816372iwn.19 for ; Fri, 04 Mar 2011 03:34:23 -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=/VZcPQLTkVhDnDL237KmUHbcWO/IPkotdS5G4duFMBY=; b=xXDxnJDoNfGFCAI1RKtUeZYVbME0DzY1Sco9FfBNblaIR9oNNqRZL4HNK34PbbG8Ov oAEHhI8Un+gvZHx2MNpZaK3tnr7xg873OWM/M5ww3eVU2OeUVmlYEY/MLkSQ8yX4AZZD gHOx+glSKm/TO60ZY/JJDYwuzg9RzbOocicQY= 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=tXkcU8LKipD3ecObHmjQaXvH+UODMgb289MvmKejxhkiFGwY+zn43kNpYZf4n49Zo8 0YSrK4pnKNQwvJfrcm2hHCBLw5tN90NXgKeGy8tZopIg0McAFbkSeuLh48GOQvol3YPf hr13yodqVVwOLo1AuO7LVvNbH1IY32Wu6qrIM= Received: by 10.42.75.74 with SMTP id z10mr354478icj.393.1299238462976; Fri, 04 Mar 2011 03:34:22 -0800 (PST) Received: from localhost ([122.167.0.108]) by mx.google.com with ESMTPS id d10sm1880187ibb.6.2011.03.04.03.34.16 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 04 Mar 2011 03:34:22 -0800 (PST) From: Arindam Nath To: cjb@laptop.org Cc: zhangfei.gao@gmail.com, prakity@marvell.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 v2 06/12] mmc: sd: add support for uhs bus speed mode selection Date: Fri, 4 Mar 2011 17:02:43 +0530 Message-Id: <1299238369-1768-7-git-send-email-arindam.nath@amd.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1299238369-1768-1-git-send-email-arindam.nath@amd.com> References: <1299238369-1768-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, 04 Mar 2011 11:34:31 +0000 (UTC) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index f6a4fab..ec0d8e6 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -464,6 +464,92 @@ 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, timing; + 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) { + if (card->sw_caps.uhs_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->sw_caps.uhs_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->sw_caps.uhs_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->sw_caps.uhs_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_SDR50) { + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) || + (card->sw_caps.uhs_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->sw_caps.uhs_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->sw_caps.uhs_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_DDR50) { + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) || + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) || + (card->sw_caps.uhs_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->sw_caps.uhs_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_SDR25) { + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) || + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) || + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) || + (card->sw_caps.uhs_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; + } + } + + 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 { + if (bus_speed) { + mmc_set_timing(card->host, timing); + mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr); + } + } + + return 0; +} + /* * UHS-I specific initialization procedure */ @@ -499,6 +585,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 5d3bb11..f127fa2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1278,7 +1278,13 @@ 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; + + /* 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)) + ctrl |= SDHCI_CTRL_HISPD; ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) { @@ -1300,7 +1306,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; /* Reset SD Clock Enable */ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } + + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + /* Select Bus Speed Mode for host */ + 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 */ + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index a407b5b..5bf244d 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -150,6 +150,11 @@ #define SDHCI_ACMD12_ERR 0x3C #define SDHCI_HOST_CONTROL2 0x3E +#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_B 0x0000 #define SDHCI_CTRL_DRV_TYPE_A 0x0010 diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 2d7f7a3..0b24c41 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -75,7 +75,23 @@ 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 50000000 unsigned int uhs_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 uhs_drv_type; #define SD_DRIVER_TYPE_B 0x01 #define SD_DRIVER_TYPE_A 0x02 diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index bc2121e..4dfff6d 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -50,6 +50,10 @@ struct mmc_ios { #define MMC_TIMING_LEGACY 0 #define MMC_TIMING_MMC_HS 1 #define MMC_TIMING_SD_HS 2 +#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 */