* [PATCH v3 00/11] add support for host controller v3.00
@ 2011-04-20 9:30 Arindam Nath
2011-04-20 9:30 ` [PATCH v3 01/11] mmc: sd: add support for signal voltage switch procedure Arindam Nath
` (11 more replies)
0 siblings, 12 replies; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:30 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
V3
----
[01/11]: Set bit 24 and bit 28 of OCR within mmc_sd_get_cid(),
and only retry sending ACMD41 with bit 24 reset in case
signal voltage switch procedure fails.
[01/11]: Change (*rocr & 0x41000000) to ((*rocr & 0x41000000) ==
0x41000000) to check for both CCS and S18A to be set in
the response of ACMD41.
[01/11]: Change the condition if (err == -EAGAIN) to if (err), in
order to retry sending ACMD41 because of any error during
signal voltage switch procedure.
[01/11]: Add a new variable signal_voltage to struct mmc_ios,
which holds whether the request is to change to 1.8V or
3.3V signalling voltage.
[02/11]: Remove redundant code to find bus speed modes for SD2.0
and SD3.0 cards separately.
[02/11]: Change the variable names from uhs_* to sd3_* to make
them appropriate to the context of their usage.
[03/11]: Change variable names from *_set_drv_type to *_drv_type.
[03/11]: Set driver type even when the default driver type B is
used.
[03/11]: Clear bits 05-04 of Host Control 2 register before
setting the new driver strength.
[04/11]: Use sdhci_set_clock() to make sure the clock is stable
before re-enabling SD clock.
[05/11]: Initialize bus_speed and timing to 0 at the beginning of
sd_set_bus_speed_mode() to avoid compiler warning.
[06/11]: Initialize current_limit to 0 to avoid compiler warning.
[06/11]: Remove usage of get_max_current_180() and replace this
with MMC_CAP_MAX_CURRENT_*.
[06/11]: Set the current limit even for the default current limit
of 200mA.
[06/11]: Set the current limit only for SDR50, SDR104, and DDR50
UHS-I modes, otherwise set the default current limit.
[07/11]: Change mmc_*_ultrahighspeed() to mmc_sd_*_uhs().
[08/11]: Re-read Host Control 2 register before clearing
*_TUNED_CLK and *_EXEC_TUNING.
[08/11]: Make sdhci_execute_tuning() return 'int' rather than
'void' so that we can check for error conditions during
tuning failure.
[08/11]: Make sure to return 0 for controllers which provide
support for retuning even if tuning fails. For other
controllers, return error code.
[09/11]: Disable using Preset Value when a new card is inserted,
and enable its use only after a successfull UHS-I
initializaton.
[11/11]: Remove sdhci_start_retuning_timer() completely, and start
the re-tuning timer from within sdhci_execute_tuning()
the very first time it is executed.
V2
----
[01/12]: Make saved_abort_cmd part of struct sdhci_host rather
than global variable.
[01/12]: Clear SDHCI_USE_SDMA _iff_ SDHCI_USE_ADMA is set.
[01/12]: Set either Auto CMD23 or Auto CMD12, but not both, in
the Transfer Mode register.
[02/12]: Check host controller version before reading
SDHCI_CAPABILITIES_1.
[02/12]: Remove spinlock from sdhci_start_signal_voltage_switch
and use usleep_range() rather than mdelay().
[02/12]: Set S18R in OCR to 1 for all UHS-I modes.
[02/12]: NULL pointer check for start_signal_voltage_switch().
[02/12]: Set MMC_CAP_UHS_SDR50 if MMC_CAP_UHS_SDR104 is set.
[06/12]: Add checking for SDR25 in sd_set_bus_speed_mode().
[09/12]: Remove checking for MMC_SEND_TUNING_BLOCK within
sdhci_set_transfer_mode(), since cmd.data is set to
NULL inside sdhci_execute_tuning().
[11/12]: Correctly set clk to SDHCI_PROG_CLOCK_MODE when host
controller supports Programmable Clock Mode.
V1
----
The patches below add support for Host Controller v3.00 as per the
spec v3.00. It also adds support for UHS-I cards as per Physical
Layer Specification v3.01.
Thanks for review.
Regards,
Arindam
Arindam Nath (11):
[PATCH 01/11] mmc: sd: add support for signal voltage switch procedure
[PATCH 02/11] mmc: sd: query function modes for uhs cards
[PATCH 03/11] mmc: sd: add support for driver type selection
[PATCH 04/11] mmc: sdhci: reset sdclk before setting high speed enable
[PATCH 05/11] mmc: sd: add support for uhs bus speed mode selection
[PATCH 06/11] mmc: sd: set current limit for uhs cards
[PATCH 07/11] mmc: sd: report correct speed and capacity of uhs cards
[PATCH 08/11] mmc: sd: add support for tuning during uhs initialization
[PATCH 09/11] mmc: sdhci: enable preset value after uhs initialization
[PATCH 10/11] mmc: sdhci: add support for programmable clock mode
[PATCH 11/11] mmc: sdhci: add support for retuning mode 1
drivers/mmc/core/bus.c | 11 +-
drivers/mmc/core/core.c | 9 +
drivers/mmc/core/core.h | 1 +
drivers/mmc/core/sd.c | 405 ++++++++++++++++++++++++---
drivers/mmc/core/sd.h | 3 +-
drivers/mmc/core/sd_ops.c | 34 +++
drivers/mmc/core/sd_ops.h | 1 +
drivers/mmc/core/sdio.c | 3 +-
drivers/mmc/host/sdhci.c | 689 ++++++++++++++++++++++++++++++++++++++++++---
drivers/mmc/host/sdhci.h | 45 +++-
include/linux/mmc/card.h | 43 +++
include/linux/mmc/host.h | 36 +++
include/linux/mmc/mmc.h | 1 +
include/linux/mmc/sd.h | 1 +
include/linux/mmc/sdhci.h | 11 +
15 files changed, 1211 insertions(+), 82 deletions(-)
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 01/11] mmc: sd: add support for signal voltage switch procedure
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
@ 2011-04-20 9:30 ` Arindam Nath
2011-04-27 9:48 ` zhangfei gao
2011-04-20 9:30 ` [PATCH v3 02/11] mmc: sd: query function modes for uhs cards Arindam Nath
` (10 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:30 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
Host Controller v3.00 adds another Capabilities register. Apart
from other things, this new register indicates whether the Host
Controller supports SDR50, SDR104, and DDR50 UHS-I modes. The spec
doesn't mention about explicit support for SDR12 and SDR25 UHS-I
modes, so the Host Controller v3.00 should support them by default.
Also if the controller supports SDR104 mode, it will also support
SDR50 mode as well. So depending on the host support, we set the
corresponding MMC_CAP_* flags. One more new register. Host Control2
is added in v3.00, which is used during Signal Voltage Switch
procedure described below.
Since as per v3.00 spec, UHS-I supported hosts should set S18R
to 1, we set S18R (bit 24) of OCR before sending ACMD41. We also
need to set XPC (bit 28) of OCR in case the host can supply >150mA.
This support is indicated by the Maximum Current Capabilities
register of the Host Controller.
If the response of ACMD41 has both CCS and S18A set, we start the
signal voltage switch procedure, which if successfull, will switch
the card from 3.3V signalling to 1.8V signalling. Signal voltage
switch procedure adds support for a new command CMD11 in the
Physical Layer Spec v3.01. As part of this procedure, we need to
set 1.8V Signalling Enable (bit 3) of Host Control2 register, which
if remains set after 5ms, means the switch to 1.8V signalling is
successfull. Otherwise, we clear bit 24 of OCR and retry the
initialization sequence. When we remove the card, and insert the
same or another card, we need to make sure that we start with 3.3V
signalling voltage. So we call mmc_set_signal_voltage() with
MMC_SIGNAL_VOLTAGE_330 set so that we are back to 3.3V signalling
voltage before we actually start initializing the card.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 34 ++++++++-
drivers/mmc/core/sd_ops.c | 34 ++++++++
drivers/mmc/core/sd_ops.h | 1 +
drivers/mmc/host/sdhci.c | 194 ++++++++++++++++++++++++++++++++++++++++++---
drivers/mmc/host/sdhci.h | 18 ++++-
include/linux/mmc/host.h | 15 ++++
include/linux/mmc/sd.h | 1 +
7 files changed, 281 insertions(+), 16 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 6dac89f..98aed60 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -403,6 +403,7 @@ struct device_type sd_type = {
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
{
int err;
+ u32 rocr;
/*
* Since we're changing the OCR value, we seem to
@@ -422,10 +423,36 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
if (!err)
ocr |= 1 << 30;
- err = mmc_send_app_op_cond(host, ocr, NULL);
+ /*
+ * If the host supports one of UHS-I modes, request the card
+ * to switch to 1.8V signaling level.
+ */
+ if (host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))
+ ocr |= 1 << 24;
+
+ /* If the host can supply more than 150mA, XPC should be set to 1. */
+ if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
+ MMC_CAP_SET_XPC_180))
+ ocr |= 1 << 28;
+
+try_again:
+ err = mmc_send_app_op_cond(host, ocr, &rocr);
if (err)
return err;
+ /*
+ * In case CCS and S18A in the response is set, start Signal Voltage
+ * Switch procedure. SPI mode doesn't support CMD11.
+ */
+ if (!mmc_host_is_spi(host) && ((rocr & 0x41000000) == 0x41000000)) {
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
+ if (err) {
+ ocr &= ~(1 << 24);
+ goto try_again;
+ }
+ }
+
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
@@ -781,6 +808,11 @@ int mmc_attach_sd(struct mmc_host *host)
if (host->ocr_avail_sd)
host->ocr_avail = host->ocr_avail_sd;
+ /* Make sure we are at 3.3V signalling voltage */
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
+ if (err)
+ return err;
+
/*
* We need to get OCR a different way for SPI.
*/
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index a206aea..9be0d4c 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -145,6 +145,40 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
return 0;
}
+int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
+{
+ struct mmc_command cmd;
+ int err = 0;
+
+ BUG_ON(!host);
+
+ /*
+ * Send CMD11 only if the request is to switch the card to
+ * 1.8V signalling.
+ */
+ if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = SD_SWITCH_VOLTAGE;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err)
+ return err;
+
+ if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
+ return -EIO;
+ }
+
+ host->ios.signal_voltage = signal_voltage;
+
+ if (host->ops->start_signal_voltage_switch)
+ err = host->ops->start_signal_voltage_switch(host, &host->ios);
+
+ return err;
+}
+
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd = {0};
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index ffc2305..d5bdda5 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp);
int mmc_app_sd_status(struct mmc_card *card, void *ssr);
+int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
#endif
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index f31077d..56efd94 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -83,6 +83,8 @@ static void sdhci_dumpregs(struct sdhci_host *host)
printk(KERN_DEBUG DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
sdhci_readw(host, SDHCI_COMMAND),
sdhci_readl(host, SDHCI_MAX_CURRENT));
+ printk(KERN_DEBUG DRIVER_NAME ": Host ctl2: 0x%08x\n",
+ sdhci_readw(host, SDHCI_HOST_CONTROL2));
if (host->flags & SDHCI_USE_ADMA)
printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
@@ -1297,11 +1299,116 @@ out:
spin_unlock_irqrestore(&host->lock, flags);
}
+static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ struct sdhci_host *host;
+ u8 pwr;
+ u16 clk, ctrl;
+ u32 present_state;
+
+ host = mmc_priv(mmc);
+
+ /*
+ * Signal Voltage Switching is only applicable for Host Controllers
+ * v3.00 and above.
+ */
+ if (host->version < SDHCI_SPEC_300)
+ return 0;
+
+ /*
+ * We first check whether the request is to set signalling voltage
+ * to 3.3V. If so, we change the voltage to 3.3V and return quickly.
+ */
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if ((ctrl & SDHCI_CTRL_VDD_180) &&
+ (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)) {
+ /* Set 1.8V Signal Enable in the Host Control2 register to 0 */
+ ctrl &= ~SDHCI_CTRL_VDD_180;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ /* Wait for 5ms */
+ usleep_range(5000, 5500);
+
+ /* 3.3V regulator output should be stable within 5 ms */
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if (!(ctrl & SDHCI_CTRL_VDD_180))
+ return 0;
+ else {
+ printk(KERN_INFO DRIVER_NAME ": Switching to 3.3V "
+ "signalling voltage failed\n");
+ return -EIO;
+ }
+ } else if (!(ctrl & SDHCI_CTRL_VDD_180) &&
+ (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)) {
+ /* Stop SDCLK */
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ /* Check whether DAT[3:0] is 0000 */
+ present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ if (!((present_state & SDHCI_DATA_LVL_MASK) >>
+ SDHCI_DATA_LVL_SHIFT)) {
+ /*
+ * Enable 1.8V Signal Enable in the Host Control2
+ * register
+ */
+ ctrl |= SDHCI_CTRL_VDD_180;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ /* Wait for 5ms */
+ usleep_range(5000, 5500);
+
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if (ctrl & SDHCI_CTRL_VDD_180) {
+ /* Provide SDCLK again and wait for 1ms*/
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+ usleep_range(1000, 1500);
+
+ /*
+ * If DAT[3:0] level is 1111b, then the card
+ * was successfully switched to 1.8V signaling.
+ */
+ present_state = sdhci_readl(host,
+ SDHCI_PRESENT_STATE);
+ if ((present_state & SDHCI_DATA_LVL_MASK) ==
+ SDHCI_DATA_LVL_MASK) {
+ return 0;
+ }
+ }
+ }
+
+ /*
+ * If we are here, that means the switch to 1.8V signaling
+ * failed. We power cycle the card, and retry initialization
+ * sequence by setting S18R to 0.
+ */
+ pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
+ pwr &= ~SDHCI_POWER_ON;
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+
+ /* Wait for 1ms as per the spec */
+ usleep_range(1000, 1500);
+ pwr |= SDHCI_POWER_ON;
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+
+ printk(KERN_INFO DRIVER_NAME ": Switching to 1.8V signalling "
+ "voltage failed, retrying with S18R set to 0\n");
+ return -EAGAIN;
+ } else
+ /* No signal voltage switch required */
+ return 0;
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
.get_ro = sdhci_get_ro,
.enable_sdio_irq = sdhci_enable_sdio_irq,
+ .start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
};
/*****************************************************************************\
@@ -1775,7 +1882,9 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host);
int sdhci_add_host(struct sdhci_host *host)
{
struct mmc_host *mmc;
- unsigned int caps, ocr_avail;
+ u32 caps[2];
+ u32 max_current_caps;
+ unsigned int ocr_avail;
int ret;
WARN_ON(host == NULL);
@@ -1798,12 +1907,15 @@ int sdhci_add_host(struct sdhci_host *host)
host->version);
}
- caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
+ caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
sdhci_readl(host, SDHCI_CAPABILITIES);
+ caps[1] = (host->version >= SDHCI_SPEC_300) ?
+ sdhci_readl(host, SDHCI_CAPABILITIES_1) : 0;
+
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_SDMA;
- else if (!(caps & SDHCI_CAN_DO_SDMA))
+ else if (!(caps[0] & SDHCI_CAN_DO_SDMA))
DBG("Controller doesn't have SDMA capability\n");
else
host->flags |= SDHCI_USE_SDMA;
@@ -1814,7 +1926,8 @@ int sdhci_add_host(struct sdhci_host *host)
host->flags &= ~SDHCI_USE_SDMA;
}
- if ((host->version >= SDHCI_SPEC_200) && (caps & SDHCI_CAN_DO_ADMA2))
+ if ((host->version >= SDHCI_SPEC_200) &&
+ (caps[0] & SDHCI_CAN_DO_ADMA2))
host->flags |= SDHCI_USE_ADMA;
if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
@@ -1864,10 +1977,10 @@ int sdhci_add_host(struct sdhci_host *host)
}
if (host->version >= SDHCI_SPEC_300)
- host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK)
+ host->max_clk = (caps[0] & SDHCI_CLOCK_V3_BASE_MASK)
>> SDHCI_CLOCK_BASE_SHIFT;
else
- host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK)
+ host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK)
>> SDHCI_CLOCK_BASE_SHIFT;
host->max_clk *= 1000000;
@@ -1883,7 +1996,7 @@ int sdhci_add_host(struct sdhci_host *host)
}
host->timeout_clk =
- (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
+ (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
if (host->timeout_clk == 0) {
if (host->ops->get_timeout_clock) {
host->timeout_clk = host->ops->get_timeout_clock(host);
@@ -1895,7 +2008,7 @@ int sdhci_add_host(struct sdhci_host *host)
return -ENODEV;
}
}
- if (caps & SDHCI_TIMEOUT_CLK_UNIT)
+ if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
host->timeout_clk *= 1000;
/*
@@ -1922,21 +2035,76 @@ int sdhci_add_host(struct sdhci_host *host)
if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
mmc->caps |= MMC_CAP_4_BIT_DATA;
- if (caps & SDHCI_CAN_DO_HISPD)
+ if (caps[0] & SDHCI_CAN_DO_HISPD)
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
mmc_card_is_removable(mmc))
mmc->caps |= MMC_CAP_NEEDS_POLL;
+ /* UHS-I mode(s) supported by the host controller. */
+ if (host->version >= SDHCI_SPEC_300)
+ mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
+
+ /* SDR104 supports also implies SDR50 support */
+ if (caps[1] & SDHCI_SUPPORT_SDR104)
+ mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
+ else if (caps[1] & SDHCI_SUPPORT_SDR50)
+ mmc->caps |= MMC_CAP_UHS_SDR50;
+
+ if (caps[1] & SDHCI_SUPPORT_DDR50)
+ mmc->caps |= MMC_CAP_UHS_DDR50;
+
ocr_avail = 0;
- if (caps & SDHCI_CAN_VDD_330)
+ /*
+ * According to SD Host Controller spec v3.00, if the Host System
+ * can afford more than 150mA, Host Driver should set XPC to 1. Also
+ * the value is meaningful only if Voltage Support in the Capabilities
+ * register is set. The actual current value is 4 times the register
+ * value.
+ */
+ max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
+
+ if (caps[0] & SDHCI_CAN_VDD_330) {
+ int max_current_330;
+
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
- if (caps & SDHCI_CAN_VDD_300)
+
+ max_current_330 = ((max_current_caps &
+ SDHCI_MAX_CURRENT_330_MASK) >>
+ SDHCI_MAX_CURRENT_330_SHIFT) *
+ SDHCI_MAX_CURRENT_MULTIPLIER;
+
+ if (max_current_330 > 150)
+ mmc->caps |= MMC_CAP_SET_XPC_330;
+ }
+ if (caps[0] & SDHCI_CAN_VDD_300) {
+ int max_current_300;
+
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
- if (caps & SDHCI_CAN_VDD_180)
+
+ max_current_300 = ((max_current_caps &
+ SDHCI_MAX_CURRENT_300_MASK) >>
+ SDHCI_MAX_CURRENT_300_SHIFT) *
+ SDHCI_MAX_CURRENT_MULTIPLIER;
+
+ if (max_current_300 > 150)
+ mmc->caps |= MMC_CAP_SET_XPC_300;
+ }
+ if (caps[0] & SDHCI_CAN_VDD_180) {
+ int max_current_180;
+
ocr_avail |= MMC_VDD_165_195;
+ max_current_180 = ((max_current_caps &
+ SDHCI_MAX_CURRENT_180_MASK) >>
+ SDHCI_MAX_CURRENT_180_SHIFT) *
+ SDHCI_MAX_CURRENT_MULTIPLIER;
+
+ if (max_current_180 > 150)
+ mmc->caps |= MMC_CAP_SET_XPC_180;
+ }
+
mmc->ocr_avail = ocr_avail;
mmc->ocr_avail_sdio = ocr_avail;
if (host->ocr_avail_sdio)
@@ -1996,7 +2164,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
mmc->max_blk_size = 2;
} else {
- mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
+ mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
SDHCI_MAX_BLOCK_SHIFT;
if (mmc->max_blk_size >= 3) {
printk(KERN_WARNING "%s: Invalid maximum block size, "
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 85750a9..4f00b31 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -68,6 +68,8 @@
#define SDHCI_DATA_AVAILABLE 0x00000800
#define SDHCI_CARD_PRESENT 0x00010000
#define SDHCI_WRITE_PROTECT 0x00080000
+#define SDHCI_DATA_LVL_MASK 0x00F00000
+#define SDHCI_DATA_LVL_SHIFT 20
#define SDHCI_HOST_CONTROL 0x28
#define SDHCI_CTRL_LED 0x01
@@ -146,7 +148,8 @@
#define SDHCI_ACMD12_ERR 0x3C
-/* 3E-3F reserved */
+#define SDHCI_HOST_CONTROL2 0x3E
+#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CAPABILITIES 0x40
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
@@ -167,9 +170,20 @@
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000
+#define SDHCI_SUPPORT_SDR50 0x00000001
+#define SDHCI_SUPPORT_SDR104 0x00000002
+#define SDHCI_SUPPORT_DDR50 0x00000004
+
#define SDHCI_CAPABILITIES_1 0x44
-#define SDHCI_MAX_CURRENT 0x48
+#define SDHCI_MAX_CURRENT 0x48
+#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
+#define SDHCI_MAX_CURRENT_330_SHIFT 0
+#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
+#define SDHCI_MAX_CURRENT_300_SHIFT 8
+#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
+#define SDHCI_MAX_CURRENT_180_SHIFT 16
+#define SDHCI_MAX_CURRENT_MULTIPLIER 4
/* 4C-4F reserved for more max current */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 0fffa5c..bde5a0b 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -56,6 +56,11 @@ struct mmc_ios {
#define MMC_SDR_MODE 0
#define MMC_1_2V_DDR_MODE 1
#define MMC_1_8V_DDR_MODE 2
+
+ unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
+
+#define MMC_SIGNAL_VOLTAGE_330 0
+#define MMC_SIGNAL_VOLTAGE_180 1
};
struct mmc_host_ops {
@@ -117,6 +122,8 @@ struct mmc_host_ops {
/* optional callback for HC quirks */
void (*init_card)(struct mmc_host *host, struct mmc_card *card);
+
+ int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
};
struct mmc_card;
@@ -173,6 +180,14 @@ struct mmc_host {
/* DDR mode at 1.2V */
#define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */
#define MMC_CAP_BUS_WIDTH_TEST (1 << 14) /* CMD14/CMD19 bus width ok */
+#define MMC_CAP_UHS_SDR12 (1 << 15) /* Host supports UHS SDR12 mode */
+#define MMC_CAP_UHS_SDR25 (1 << 16) /* Host supports UHS SDR25 mode */
+#define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */
+#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */
+#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */
+#define MMC_CAP_SET_XPC_330 (1 << 20) /* Host supports >150mA current at 3.3V */
+#define MMC_CAP_SET_XPC_300 (1 << 21) /* Host supports >150mA current at 3.0V */
+#define MMC_CAP_SET_XPC_180 (1 << 22) /* Host supports >150mA current at 1.8V */
mmc_pm_flag_t pm_caps; /* supported pm features */
diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
index 3fd85e0..835b715 100644
--- a/include/linux/mmc/sd.h
+++ b/include/linux/mmc/sd.h
@@ -17,6 +17,7 @@
/* This is basically the same command as for MMC with some quirks. */
#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
+#define SD_SWITCH_VOLTAGE 11 /* ac R1 */
/* class 10 */
#define SD_SWITCH 6 /* adtc [31:0] See below R1 */
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 02/11] mmc: sd: query function modes for uhs cards
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
2011-04-20 9:30 ` [PATCH v3 01/11] mmc: sd: add support for signal voltage switch procedure Arindam Nath
@ 2011-04-20 9:30 ` Arindam Nath
2011-04-20 9:30 ` [PATCH v3 03/11] mmc: sd: add support for driver type selection Arindam Nath
` (9 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:30 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
SD cards which conform to Physical Layer Spec v3.01 can support
additional Bus Speed Modes, Driver Strength, and Current Limit
other than the default values. We use CMD6 mode 0 to read these
additional card functions. The values read here will be used
during UHS-I initialization steps.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 69 ++++++++++++++++++++++++++++++++++++++++-----
include/linux/mmc/card.h | 4 ++
2 files changed, 65 insertions(+), 8 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 98aed60..8b25047 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -189,6 +189,9 @@ static int mmc_decode_scr(struct mmc_card *card)
scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);
+ if (scr->sda_vsn == SCR_SPEC_VER_2)
+ /* Check if Physical Layer Spec v3.0 is supported*/
+ scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1);
if (UNSTUFF_BITS(resp, 55, 1))
card->erased_byte = 0xFF;
@@ -278,25 +281,75 @@ static int mmc_read_switch(struct mmc_card *card)
return -ENOMEM;
}
+ /* Find out the supported Bus Speed Modes. */
err = mmc_sd_switch(card, 0, 0, 1, status);
if (err) {
- /* If the host or the card can't do the switch,
- * fail more gracefully. */
+ /*
+ * If the host or the card can't do the switch,
+ * fail more gracefully.
+ */
if ((err != -EINVAL)
- && (err != -ENOSYS)
- && (err != -EFAULT))
+ && (err != -ENOSYS)
+ && (err != -EFAULT))
goto out;
- printk(KERN_WARNING "%s: problem reading switch "
- "capabilities, performance might suffer.\n",
+ printk(KERN_WARNING "%s: problem reading Bus Speed modes.\n",
mmc_hostname(card->host));
err = 0;
goto out;
}
- if (status[13] & 0x02)
- card->sw_caps.hs_max_dtr = 50000000;
+ if (card->scr.sda_spec3) {
+ card->sw_caps.sd3_bus_mode = status[13];
+
+ /* Find out Driver Strengths supported by the card */
+ err = mmc_sd_switch(card, 0, 2, 1, status);
+ if (err) {
+ /*
+ * If the host or the card can't do the switch,
+ * fail more gracefully.
+ */
+ if ((err != -EINVAL)
+ && (err != -ENOSYS)
+ && (err != -EFAULT))
+ goto out;
+
+ printk(KERN_WARNING "%s: problem reading "
+ "Driver Strength.\n",
+ mmc_hostname(card->host));
+ err = 0;
+
+ goto out;
+ }
+
+ card->sw_caps.sd3_drv_type = status[9];
+
+ /* Find out Current Limits supported by the card */
+ err = mmc_sd_switch(card, 0, 3, 1, status);
+ if (err) {
+ /*
+ * If the host or the card can't do the switch,
+ * fail more gracefully.
+ */
+ if ((err != -EINVAL)
+ && (err != -ENOSYS)
+ && (err != -EFAULT))
+ goto out;
+
+ printk(KERN_WARNING "%s: problem reading "
+ "Current Limit.\n",
+ mmc_hostname(card->host));
+ err = 0;
+
+ goto out;
+ }
+
+ card->sw_caps.sd3_curr_limit = status[7];
+ } else {
+ if (status[13] & 0x02)
+ card->sw_caps.hs_max_dtr = 50000000;
+ }
out:
kfree(status);
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 72a9868..56f4d92 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -67,6 +67,7 @@ struct mmc_ext_csd {
struct sd_scr {
unsigned char sda_vsn;
+ unsigned char sda_spec3;
unsigned char bus_widths;
#define SD_SCR_BUS_WIDTH_1 (1<<0)
#define SD_SCR_BUS_WIDTH_4 (1<<2)
@@ -80,6 +81,9 @@ struct sd_ssr {
struct sd_switch_caps {
unsigned int hs_max_dtr;
+ unsigned int sd3_bus_mode;
+ unsigned int sd3_drv_type;
+ unsigned int sd3_curr_limit;
};
struct sdio_cccr {
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 03/11] mmc: sd: add support for driver type selection
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
2011-04-20 9:30 ` [PATCH v3 01/11] mmc: sd: add support for signal voltage switch procedure Arindam Nath
2011-04-20 9:30 ` [PATCH v3 02/11] mmc: sd: query function modes for uhs cards Arindam Nath
@ 2011-04-20 9:30 ` Arindam Nath
2011-04-20 9:30 ` [PATCH v3 04/11] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
` (8 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:30 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
This patch adds support for setting driver strength during UHS-I
initialization prcedure. Since UHS-I cards set S18A (bit 24) in
response to ACMD41, we use this as a base for UHS-I initialization.
We modify the parameter list of mmc_sd_get_cid() so that we can
save the ROCR from ACMD41 to check whether bit 24 is set.
We decide whether the Host Controller supports A, C, or D driver
type depending on the Capabilities register. Driver type B is
suported by default. We then set the appropriate driver type for
the card using CMD6 mode 1. As per Host Controller spec v3.00, we
set driver type for the host only if Preset Value Enable in the
Host Control2 register is not set. SDHCI_HOST_CONTROL has been
renamed to SDHCI_HOST_CONTROL1 to conform to the spec.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/core.c | 9 +++
drivers/mmc/core/core.h | 1 +
drivers/mmc/core/sd.c | 153 ++++++++++++++++++++++++++++++++++++++--------
drivers/mmc/core/sd.h | 3 +-
drivers/mmc/core/sdio.c | 3 +-
drivers/mmc/host/sdhci.c | 49 +++++++++++---
drivers/mmc/host/sdhci.h | 11 +++-
include/linux/mmc/card.h | 4 +
include/linux/mmc/host.h | 10 +++
9 files changed, 203 insertions(+), 40 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 1dbc185..4766b0c 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -952,6 +952,15 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing)
}
/*
+ * Select appropriate driver type for host.
+ */
+void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
+{
+ host->ios.drv_type = drv_type;
+ mmc_set_ios(host);
+}
+
+/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
* We then wait a bit for the power to stabilise. Finally,
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index ca1fdde..6114ca5 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -42,6 +42,7 @@ 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);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
+void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
static inline void mmc_delay(unsigned int ms)
{
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 8b25047..08f5011 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -405,6 +405,98 @@ out:
return err;
}
+static int sd_select_driver_type(struct mmc_card *card, u8 *status)
+{
+ int host_drv_type = 0, card_drv_type = 0;
+ int err;
+
+ /*
+ * If the host doesn't support any of the Driver Types A,C or D,
+ * default Driver Type B is used.
+ */
+ if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C
+ | MMC_CAP_DRIVER_TYPE_D)))
+ return 0;
+
+ if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) {
+ host_drv_type = MMC_SET_DRIVER_TYPE_A;
+ if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
+ card_drv_type = MMC_SET_DRIVER_TYPE_A;
+ else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_B)
+ card_drv_type = MMC_SET_DRIVER_TYPE_B;
+ else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
+ card_drv_type = MMC_SET_DRIVER_TYPE_C;
+ } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
+ host_drv_type = MMC_SET_DRIVER_TYPE_C;
+ if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
+ card_drv_type = MMC_SET_DRIVER_TYPE_C;
+ } else if (!(card->host->caps & MMC_CAP_DRIVER_TYPE_D)) {
+ /*
+ * If we are here, that means only the default driver type
+ * B is supported by the host.
+ */
+ host_drv_type = MMC_SET_DRIVER_TYPE_B;
+ if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_B)
+ card_drv_type = MMC_SET_DRIVER_TYPE_B;
+ else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
+ card_drv_type = MMC_SET_DRIVER_TYPE_C;
+ }
+
+ err = mmc_sd_switch(card, 1, 2, card_drv_type, status);
+ if (err)
+ return err;
+
+ if ((status[15] & 0xF) != card_drv_type) {
+ printk(KERN_WARNING "%s: Problem setting driver strength!\n",
+ mmc_hostname(card->host));
+ return 0;
+ }
+
+ mmc_set_driver_type(card->host, host_drv_type);
+
+ return 0;
+}
+
+/*
+ * UHS-I specific initialization procedure
+ */
+static int mmc_sd_init_uhs_card(struct mmc_card *card)
+{
+ int err;
+ u8 *status;
+
+ if (!card->scr.sda_spec3)
+ return 0;
+
+ if (!(card->csd.cmdclass & CCC_SWITCH))
+ return 0;
+
+ status = kmalloc(64, GFP_KERNEL);
+ if (!status) {
+ printk(KERN_ERR "%s: could not allocate a buffer for "
+ "switch capabilities.\n", mmc_hostname(card->host));
+ return -ENOMEM;
+ }
+
+ /* Set 4-bit bus width */
+ if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
+ (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+ err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+ if (err)
+ goto out;
+
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+ }
+
+ /* Set the driver strength for the card */
+ err = sd_select_driver_type(card, status);
+
+out:
+ kfree(status);
+
+ return err;
+}
+
MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
card->raw_cid[2], card->raw_cid[3]);
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
@@ -453,10 +545,10 @@ struct device_type sd_type = {
/*
* Fetch CID from card.
*/
-int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid,
+ u32 *rocr)
{
int err;
- u32 rocr;
/*
* Since we're changing the OCR value, we seem to
@@ -490,7 +582,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
ocr |= 1 << 28;
try_again:
- err = mmc_send_app_op_cond(host, ocr, &rocr);
+ err = mmc_send_app_op_cond(host, ocr, rocr);
if (err)
return err;
@@ -498,7 +590,8 @@ try_again:
* In case CCS and S18A in the response is set, start Signal Voltage
* Switch procedure. SPI mode doesn't support CMD11.
*/
- if (!mmc_host_is_spi(host) && ((rocr & 0x41000000) == 0x41000000)) {
+ if (!mmc_host_is_spi(host) && rocr &&
+ ((*rocr & 0x41000000) == 0x41000000)) {
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
if (err) {
ocr &= ~(1 << 24);
@@ -633,11 +726,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *card;
int err;
u32 cid[4];
+ u32 rocr = 0;
BUG_ON(!host);
WARN_ON(!host->claimed);
- err = mmc_sd_get_cid(host, ocr, cid);
+ err = mmc_sd_get_cid(host, ocr, cid, &rocr);
if (err)
return err;
@@ -690,30 +784,37 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if (err)
goto free_card;
- /*
- * Attempt to change to high-speed (if supported)
- */
- err = mmc_sd_switch_hs(card);
- if (err > 0)
- mmc_sd_go_highspeed(card);
- else if (err)
- goto free_card;
-
- /*
- * Set bus speed.
- */
- mmc_set_clock(host, mmc_sd_get_max_clock(card));
-
- /*
- * Switch to wider bus (if supported).
- */
- if ((host->caps & MMC_CAP_4_BIT_DATA) &&
- (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
- err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+ /* Initialization sequence for UHS-I cards */
+ if (rocr & (1 << 24)) {
+ err = mmc_sd_init_uhs_card(card);
if (err)
goto free_card;
+ } else {
+ /*
+ * Attempt to change to high-speed (if supported)
+ */
+ err = mmc_sd_switch_hs(card);
+ if (err > 0)
+ mmc_sd_go_highspeed(card);
+ else if (err)
+ goto free_card;
+
+ /*
+ * Set bus speed.
+ */
+ mmc_set_clock(host, mmc_sd_get_max_clock(card));
- mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+ /*
+ * Switch to wider bus (if supported).
+ */
+ if ((host->caps & MMC_CAP_4_BIT_DATA) &&
+ (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+ err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+ if (err)
+ goto free_card;
+
+ mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+ }
}
host->card = card;
diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
index 3d8800f..5106b44 100644
--- a/drivers/mmc/core/sd.h
+++ b/drivers/mmc/core/sd.h
@@ -5,7 +5,8 @@
extern struct device_type sd_type;
-int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid);
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid,
+ u32 *rocr);
int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
void mmc_decode_cid(struct mmc_card *card);
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 1e60959..c4a6614 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -370,7 +370,8 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
}
if (ocr & R4_MEMORY_PRESENT
- && mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid) == 0) {
+ && mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid,
+ NULL) == 0) {
card->type = MMC_TYPE_SD_COMBO;
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 56efd94..faf332f 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -61,7 +61,7 @@ static void sdhci_dumpregs(struct sdhci_host *host)
sdhci_readw(host, SDHCI_TRANSFER_MODE));
printk(KERN_DEBUG DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
sdhci_readl(host, SDHCI_PRESENT_STATE),
- sdhci_readb(host, SDHCI_HOST_CONTROL));
+ sdhci_readb(host, SDHCI_HOST_CONTROL1));
printk(KERN_DEBUG DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
sdhci_readb(host, SDHCI_POWER_CONTROL),
sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
@@ -214,18 +214,18 @@ static void sdhci_activate_led(struct sdhci_host *host)
{
u8 ctrl;
- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL1);
ctrl |= SDHCI_CTRL_LED;
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
}
static void sdhci_deactivate_led(struct sdhci_host *host)
{
u8 ctrl;
- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL1);
ctrl &= ~SDHCI_CTRL_LED;
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
}
#ifdef SDHCI_USE_LEDS_CLASS
@@ -797,14 +797,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
* is ADMA.
*/
if (host->version >= SDHCI_SPEC_200) {
- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL1);
ctrl &= ~SDHCI_CTRL_DMA_MASK;
if ((host->flags & SDHCI_REQ_USE_DMA) &&
(host->flags & SDHCI_USE_ADMA))
ctrl |= SDHCI_CTRL_ADMA32;
else
ctrl |= SDHCI_CTRL_SDMA;
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
}
if (!(host->flags & SDHCI_REQ_USE_DMA)) {
@@ -1212,7 +1212,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->ops->platform_8bit_width)
host->ops->platform_8bit_width(host, ios->bus_width);
else {
- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL1);
if (ios->bus_width == MMC_BUS_WIDTH_8) {
ctrl &= ~SDHCI_CTRL_4BITBUS;
if (host->version >= SDHCI_SPEC_300)
@@ -1225,10 +1225,10 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
ctrl &= ~SDHCI_CTRL_4BITBUS;
}
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
}
- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL1);
if ((ios->timing == MMC_TIMING_SD_HS ||
ios->timing == MMC_TIMING_MMC_HS)
@@ -1237,7 +1237,26 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
ctrl &= ~SDHCI_CTRL_HISPD;
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
+
+ if (host->version >= SDHCI_SPEC_300) {
+ u16 ctrl_2;
+
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
+ /*
+ * We only need to set Driver Strength if the
+ * preset value enable is not set.
+ */
+ ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;
+ if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
+ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
+ else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C)
+ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;
+
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+ }
+ }
/*
* Some (ENE) controllers go apeshit on some ios operation,
@@ -2055,6 +2074,14 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_SUPPORT_DDR50)
mmc->caps |= MMC_CAP_UHS_DDR50;
+ /* Driver Type(s) (A, C, D) supported by the host */
+ if (caps[1] & SDHCI_DRIVER_TYPE_A)
+ mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
+ if (caps[1] & SDHCI_DRIVER_TYPE_C)
+ mmc->caps |= MMC_CAP_DRIVER_TYPE_C;
+ if (caps[1] & SDHCI_DRIVER_TYPE_D)
+ mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
+
ocr_avail = 0;
/*
* According to SD Host Controller spec v3.00, if the Host System
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 4f00b31..03c1df9 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -71,7 +71,7 @@
#define SDHCI_DATA_LVL_MASK 0x00F00000
#define SDHCI_DATA_LVL_SHIFT 20
-#define SDHCI_HOST_CONTROL 0x28
+#define SDHCI_HOST_CONTROL1 0x28
#define SDHCI_CTRL_LED 0x01
#define SDHCI_CTRL_4BITBUS 0x02
#define SDHCI_CTRL_HISPD 0x04
@@ -150,6 +150,12 @@
#define SDHCI_HOST_CONTROL2 0x3E
#define SDHCI_CTRL_VDD_180 0x0008
+#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
+#define SDHCI_CTRL_DRV_TYPE_B 0x0000
+#define SDHCI_CTRL_DRV_TYPE_A 0x0010
+#define SDHCI_CTRL_DRV_TYPE_C 0x0020
+#define SDHCI_CTRL_DRV_TYPE_D 0x0030
+#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
@@ -173,6 +179,9 @@
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
#define SDHCI_SUPPORT_DDR50 0x00000004
+#define SDHCI_DRIVER_TYPE_A 0x00000010
+#define SDHCI_DRIVER_TYPE_C 0x00000020
+#define SDHCI_DRIVER_TYPE_D 0x00000040
#define SDHCI_CAPABILITIES_1 0x44
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 56f4d92..5393272 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -83,6 +83,10 @@ struct sd_switch_caps {
unsigned int hs_max_dtr;
unsigned int sd3_bus_mode;
unsigned int sd3_drv_type;
+#define SD_DRIVER_TYPE_B 0x01
+#define SD_DRIVER_TYPE_A 0x02
+#define SD_DRIVER_TYPE_C 0x04
+#define SD_DRIVER_TYPE_D 0x08
unsigned int sd3_curr_limit;
};
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index bde5a0b..949e4d5 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -61,6 +61,13 @@ struct mmc_ios {
#define MMC_SIGNAL_VOLTAGE_330 0
#define MMC_SIGNAL_VOLTAGE_180 1
+
+ unsigned char drv_type; /* driver type (A, B, C, D) */
+
+#define MMC_SET_DRIVER_TYPE_B 0
+#define MMC_SET_DRIVER_TYPE_A 1
+#define MMC_SET_DRIVER_TYPE_C 2
+#define MMC_SET_DRIVER_TYPE_D 3
};
struct mmc_host_ops {
@@ -188,6 +195,9 @@ struct mmc_host {
#define MMC_CAP_SET_XPC_330 (1 << 20) /* Host supports >150mA current at 3.3V */
#define MMC_CAP_SET_XPC_300 (1 << 21) /* Host supports >150mA current at 3.0V */
#define MMC_CAP_SET_XPC_180 (1 << 22) /* Host supports >150mA current at 1.8V */
+#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
+#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
+#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */
mmc_pm_flag_t pm_caps; /* supported pm features */
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 04/11] mmc: sdhci: reset sdclk before setting high speed enable
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
` (2 preceding siblings ...)
2011-04-20 9:30 ` [PATCH v3 03/11] mmc: sd: add support for driver type selection Arindam Nath
@ 2011-04-20 9:30 ` Arindam Nath
2011-04-20 9:30 ` [PATCH v3 05/11] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
` (7 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:30 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
As per Host Controller spec v3.00, we reset SDCLK before setting
High Speed Enable, and then set it back to avoid generating clock
gliches. Before enabling SDCLK again, we make sure the clock is
stable, so we use sdhci_set_clock().
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/host/sdhci.c | 27 ++++++++++++++++++++++++---
1 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index faf332f..175f858 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1237,13 +1237,12 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
ctrl &= ~SDHCI_CTRL_HISPD;
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
-
if (host->version >= SDHCI_SPEC_300) {
u16 ctrl_2;
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
/*
* We only need to set Driver Strength if the
* preset value enable is not set.
@@ -1255,8 +1254,30 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+ } else {
+ /*
+ * According to SDHC Spec v3.00, if the Preset Value
+ * Enable in the Host Control 2 register is set, we
+ * 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);
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
+
+ /* Re-enable SD Clock */
+ clock = host->clock;
+ host->clock = 0;
+ sdhci_set_clock(host, clock);
}
- }
+ } else
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
/*
* Some (ENE) controllers go apeshit on some ios operation,
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 05/11] mmc: sd: add support for uhs bus speed mode selection
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
` (3 preceding siblings ...)
2011-04-20 9:30 ` [PATCH v3 04/11] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
@ 2011-04-20 9:30 ` Arindam Nath
2011-04-20 9:30 ` [PATCH v3 06/11] mmc: sd: set current limit for uhs cards Arindam Nath
` (6 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:30 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
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 <arindam.nath@amd.com>
---
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 08f5011..ba71654 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 175f858..758c9f1 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1238,7 +1238,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)) {
@@ -1261,8 +1270,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);
@@ -1276,6 +1283,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 03c1df9..aed1203 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 */
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 06/11] mmc: sd: set current limit for uhs cards
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
` (4 preceding siblings ...)
2011-04-20 9:30 ` [PATCH v3 05/11] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
@ 2011-04-20 9:30 ` Arindam Nath
2011-04-20 9:30 ` [PATCH v3 07/11] mmc: sd: report correct speed and capacity of " Arindam Nath
` (5 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:30 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
We decide on the current limit to be set for the card based on the
Capability of Host Controller to provide current at 1.8V signalling,
and the maximum current limit of the card as indicated by CMD6
mode 0. We then set the current limit for the card using CMD6 mode 1.
As per the Physical Layer Spec v3.01, the current limit switch is
only applicable for SDR50, SDR104, and DDR50 bus speed modes. For
other UHS-I modes, we set the default current limit of 200mA.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci.c | 10 +++++++
include/linux/mmc/card.h | 9 ++++++
include/linux/mmc/host.h | 4 +++
4 files changed, 86 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ba71654..9af0c94 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -517,6 +517,64 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
return 0;
}
+static int sd_set_current_limit(struct mmc_card *card, u8 *status)
+{
+ int current_limit = 0;
+ int err;
+
+ /*
+ * Current limit switch is only defined for SDR50, SDR104, and DDR50
+ * bus speed modes. For other bus speed modes, we set the default
+ * current limit of 200mA.
+ */
+ if ((card->sd_bus_speed == UHS_SDR50_BUS_SPEED) ||
+ (card->sd_bus_speed == UHS_SDR104_BUS_SPEED) ||
+ (card->sd_bus_speed == UHS_DDR50_BUS_SPEED)) {
+ if (card->host->caps & MMC_CAP_MAX_CURRENT_800) {
+ if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_800)
+ current_limit = SD_SET_CURRENT_LIMIT_800;
+ else if (card->sw_caps.sd3_curr_limit &
+ SD_MAX_CURRENT_600)
+ current_limit = SD_SET_CURRENT_LIMIT_600;
+ else if (card->sw_caps.sd3_curr_limit &
+ SD_MAX_CURRENT_400)
+ current_limit = SD_SET_CURRENT_LIMIT_400;
+ else if (card->sw_caps.sd3_curr_limit &
+ SD_MAX_CURRENT_200)
+ current_limit = SD_SET_CURRENT_LIMIT_200;
+ } else if (card->host->caps & MMC_CAP_MAX_CURRENT_600) {
+ if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_600)
+ current_limit = SD_SET_CURRENT_LIMIT_600;
+ else if (card->sw_caps.sd3_curr_limit &
+ SD_MAX_CURRENT_400)
+ current_limit = SD_SET_CURRENT_LIMIT_400;
+ else if (card->sw_caps.sd3_curr_limit &
+ SD_MAX_CURRENT_200)
+ current_limit = SD_SET_CURRENT_LIMIT_200;
+ } else if (card->host->caps & MMC_CAP_MAX_CURRENT_400) {
+ if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400)
+ current_limit = SD_SET_CURRENT_LIMIT_400;
+ else if (card->sw_caps.sd3_curr_limit &
+ SD_MAX_CURRENT_200)
+ current_limit = SD_SET_CURRENT_LIMIT_200;
+ } else if (card->host->caps & MMC_CAP_MAX_CURRENT_200) {
+ if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200)
+ current_limit = SD_SET_CURRENT_LIMIT_200;
+ }
+ } else
+ current_limit = SD_SET_CURRENT_LIMIT_200;
+
+ err = mmc_sd_switch(card, 1, 3, current_limit, status);
+ if (err)
+ return err;
+
+ if (((status[15] >> 4) & 0x0F) != current_limit)
+ printk(KERN_WARNING "%s: Problem setting current limit!\n",
+ mmc_hostname(card->host));
+
+ return 0;
+}
+
/*
* UHS-I specific initialization procedure
*/
@@ -555,6 +613,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
/* Set bus speed mode of the card */
err = sd_set_bus_speed_mode(card, status);
+ if (err)
+ goto out;
+
+ /* Set current limit for the card */
+ err = sd_set_current_limit(card, status);
out:
kfree(status);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 758c9f1..9141c5b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2185,6 +2185,16 @@ int sdhci_add_host(struct sdhci_host *host)
if (max_current_180 > 150)
mmc->caps |= MMC_CAP_SET_XPC_180;
+
+ /* Maximum current capabilities of the host at 1.8V */
+ if (max_current_180 >= 800)
+ mmc->caps |= MMC_CAP_MAX_CURRENT_800;
+ else if (max_current_180 >= 600)
+ mmc->caps |= MMC_CAP_MAX_CURRENT_600;
+ else if (max_current_180 >= 400)
+ mmc->caps |= MMC_CAP_MAX_CURRENT_400;
+ else
+ mmc->caps |= MMC_CAP_MAX_CURRENT_200;
}
mmc->ocr_avail = ocr_avail;
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 4ef6ded..47b5ad3 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -105,6 +105,15 @@ struct sd_switch_caps {
#define SD_DRIVER_TYPE_C 0x04
#define SD_DRIVER_TYPE_D 0x08
unsigned int sd3_curr_limit;
+#define SD_SET_CURRENT_LIMIT_200 0
+#define SD_SET_CURRENT_LIMIT_400 1
+#define SD_SET_CURRENT_LIMIT_600 2
+#define SD_SET_CURRENT_LIMIT_800 3
+
+#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200)
+#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400)
+#define SD_MAX_CURRENT_600 (1 << SD_SET_CURRENT_LIMIT_600)
+#define SD_MAX_CURRENT_800 (1 << SD_SET_CURRENT_LIMIT_800)
};
struct sdio_cccr {
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 6237599..52b5dc9 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -203,6 +203,10 @@ struct mmc_host {
#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */
+#define MMC_CAP_MAX_CURRENT_200 (1 << 26) /* Host max current limit is 200mA */
+#define MMC_CAP_MAX_CURRENT_400 (1 << 27) /* Host max current limit is 400mA */
+#define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */
+#define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */
mmc_pm_flag_t pm_caps; /* supported pm features */
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 07/11] mmc: sd: report correct speed and capacity of uhs cards
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
` (5 preceding siblings ...)
2011-04-20 9:30 ` [PATCH v3 06/11] mmc: sd: set current limit for uhs cards Arindam Nath
@ 2011-04-20 9:30 ` Arindam Nath
2011-04-20 9:30 ` [PATCH v3 08/11] mmc: sd: add support for tuning during uhs initialization Arindam Nath
` (4 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:30 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
Since only UHS-I cards respond with S18A set in response to ACMD41,
we set the card as ultra-high-speed after successfull initialization.
We need to decide whether a card is SDXC based on the C_SIZE field
of CSDv2.0 register. According to Physical Layer spec v3.01, the
minimum value of C_SIZE for SDXC card is 00FFFFh.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/bus.c | 11 ++++++++---
drivers/mmc/core/sd.c | 10 +++++++++-
include/linux/mmc/card.h | 7 +++++++
3 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index d6d62fd..393d817 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -274,8 +274,12 @@ int mmc_add_card(struct mmc_card *card)
break;
case MMC_TYPE_SD:
type = "SD";
- if (mmc_card_blockaddr(card))
- type = "SDHC";
+ if (mmc_card_blockaddr(card)) {
+ if (mmc_card_ext_capacity(card))
+ type = "SDXC";
+ else
+ type = "SDHC";
+ }
break;
case MMC_TYPE_SDIO:
type = "SDIO";
@@ -299,7 +303,8 @@ int mmc_add_card(struct mmc_card *card)
} else {
printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
mmc_hostname(card->host),
- mmc_card_highspeed(card) ? "high speed " : "",
+ mmc_sd_card_uhs(card) ? "ultra high speed " :
+ (mmc_card_highspeed(card) ? "high speed " : ""),
mmc_card_ddr_mode(card) ? "DDR " : "",
type, card->rca);
}
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 9af0c94..1594e51 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -130,7 +130,7 @@ static int mmc_decode_csd(struct mmc_card *card)
break;
case 1:
/*
- * This is a block-addressed SDHC card. Most
+ * This is a block-addressed SDHC or SDXC card. Most
* interesting fields are unused and have fixed
* values. To avoid getting tripped by buggy cards,
* we assume those fixed values ourselves.
@@ -144,6 +144,11 @@ static int mmc_decode_csd(struct mmc_card *card)
e = UNSTUFF_BITS(resp, 96, 3);
csd->max_dtr = tran_exp[e] * tran_mant[m];
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
+ csd->c_size = UNSTUFF_BITS(resp, 48, 22);
+
+ /* SDXC cards have a minimum C_SIZE of 0x00FFFF */
+ if (csd->c_size >= 0xFFFF)
+ mmc_card_set_ext_capacity(card);
m = UNSTUFF_BITS(resp, 48, 22);
csd->capacity = (1 + m) << 10;
@@ -917,6 +922,9 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
err = mmc_sd_init_uhs_card(card);
if (err)
goto free_card;
+
+ /* Card is an ultra-high-speed card */
+ mmc_sd_card_set_uhs(card);
} else {
/*
* Attempt to change to high-speed (if supported)
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 47b5ad3..d8dffc9 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -30,6 +30,7 @@ struct mmc_csd {
unsigned short cmdclass;
unsigned short tacc_clks;
unsigned int tacc_ns;
+ unsigned int c_size;
unsigned int r2w_factor;
unsigned int max_dtr;
unsigned int erase_size; /* In sectors */
@@ -158,6 +159,8 @@ struct mmc_card {
#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */
#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */
#define MMC_STATE_HIGHSPEED_DDR (1<<4) /* card is in high speed mode */
+#define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */
+#define MMC_CARD_SDXC (1<<6) /* card is SDXC */
unsigned int quirks; /* card quirks */
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
@@ -293,12 +296,16 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
+#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
+#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
#define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
+#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
+#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
static inline int mmc_card_lenient_fn0(const struct mmc_card *c)
{
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 08/11] mmc: sd: add support for tuning during uhs initialization
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
` (6 preceding siblings ...)
2011-04-20 9:30 ` [PATCH v3 07/11] mmc: sd: report correct speed and capacity of " Arindam Nath
@ 2011-04-20 9:30 ` Arindam Nath
2011-04-27 18:44 ` Philip Rakity
2011-04-20 9:30 ` [PATCH v3 09/11] mmc: sdhci: enable preset value after uhs initialization Arindam Nath
` (3 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:30 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
Host Controller needs tuning during initialization to operate SDR50
and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is
indicated by bit 45 of the Host Controller Capabilities register.
A new command CMD19 has been defined in the Physical Layer spec
v3.01 to request the card to send tuning pattern.
We enable Buffer Read Ready interrupt at the very begining of tuning
procedure, because that is the only interrupt generated by the Host
Controller during tuning. We make sure that DMA Enable is set to 0
before actually sending CMD19. The tuning block is sent by the card
to the Host Controller using DAT lines, so we set Data Present
Select (bit 5) in the Command register. The Host Controller is
responsible for doing the verfication of tuning block sent by the
card at the hardware level. After sending CMD19, we wait for Buffer
Read Ready interrupt. In case we don't receive an interrupt after
the specified timeout value, we fall back on fixed sampling clock by
setting Execute Tuning (bit 6) and Sampling Clock Select (bit 7) of
Host Control2 register to 0. Before exiting the tuning procedure, we
disable Buffer Read Ready interrupt.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 6 ++
drivers/mmc/host/sdhci.c | 157 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/mmc/host/sdhci.h | 3 +
include/linux/mmc/host.h | 1 +
include/linux/mmc/mmc.h | 1 +
include/linux/mmc/sdhci.h | 4 +
6 files changed, 171 insertions(+), 1 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 1594e51..f3ecdef 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -623,6 +623,12 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
/* Set current limit for the card */
err = sd_set_current_limit(card, status);
+ if (err)
+ goto out;
+
+ /* SPI mode doesn't define CMD19 */
+ if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
+ err = card->host->ops->execute_tuning(card->host);
out:
kfree(status);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9141c5b..9065157 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -38,6 +38,8 @@
#define SDHCI_USE_LEDS_CLASS
#endif
+#define MAX_TUNING_LOOP 40
+
static unsigned int debug_quirks = 0;
static void sdhci_finish_data(struct sdhci_host *);
@@ -962,7 +964,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
flags |= SDHCI_CMD_CRC;
if (cmd->flags & MMC_RSP_OPCODE)
flags |= SDHCI_CMD_INDEX;
- if (cmd->data)
+
+ /* CMD19 is special in that the Data Present Select should be set */
+ if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
flags |= SDHCI_CMD_DATA;
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
@@ -1477,12 +1481,146 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return 0;
}
+static int sdhci_execute_tuning(struct mmc_host *mmc)
+{
+ struct sdhci_host *host;
+ u16 ctrl;
+ int tuning_loop_counter = MAX_TUNING_LOOP;
+ unsigned long timeout;
+ int err = 0;
+
+ host = mmc_priv(mmc);
+
+ disable_irq(host->irq);
+ spin_lock(&host->lock);
+
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+ /*
+ * Host Controller needs tuning only in case of SDR104 mode
+ * and for SDR50 mode when Use Tuning for SDR50 is set in
+ * Capabilities register.
+ */
+ if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
+ (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
+ (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
+ ctrl |= SDHCI_CTRL_EXEC_TUNING;
+ else {
+ spin_unlock(&host->lock);
+ enable_irq(host->irq);
+ return 0;
+ }
+
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ /*
+ * As per the Host Controller spec v3.00, tuning command
+ * generates Buffer Read Ready interrupt, so enable that.
+ */
+ sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
+
+ /*
+ * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
+ * of loops reaches 40 times or a timeout of 150ms occurs.
+ */
+ timeout = 150;
+ do {
+ u16 mode;
+ struct mmc_command cmd;
+ struct mmc_request mrq;
+
+ if (!tuning_loop_counter && !timeout)
+ break;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_SEND_TUNING_BLOCK;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ memset(&cmd.resp, 0, sizeof(cmd.resp));
+ cmd.retries = 0;
+
+ cmd.data = NULL;
+ cmd.error = 0;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ mrq.cmd = &cmd;
+ host->mrq = &mrq;
+
+ /* Make sure DMA Enable is set to 0 before tuning */
+ mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
+ if (mode & SDHCI_TRNS_DMA) {
+ mode &= ~SDHCI_TRNS_DMA;
+ sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
+ }
+
+ sdhci_send_command(host, &cmd);
+
+ host->cmd = NULL;
+ host->mrq = NULL;
+
+ spin_unlock(&host->lock);
+ enable_irq(host->irq);
+
+ /* Wait for Buffer Read Ready interrupt */
+ wait_event_interruptible_timeout(host->buf_ready_int,
+ (host->tuning_done == 1),
+ msecs_to_jiffies(50));
+ disable_irq(host->irq);
+ spin_lock(&host->lock);
+
+ if (!host->tuning_done) {
+ printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
+ " failed, falling back to fixed sampling"
+ " clock\n");
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+ ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ err = -EIO;
+ goto out;
+ }
+
+ host->tuning_done = 0;
+
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ tuning_loop_counter--;
+ timeout--;
+ mdelay(1);
+ } while (ctrl & SDHCI_CTRL_EXEC_TUNING);
+
+ /*
+ * The Host Driver has exhausted the maximum number of loops allowed,
+ * so use fixed sampling frequency.
+ */
+ if (!tuning_loop_counter || !timeout) {
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ } else {
+ if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
+ printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
+ " failed, falling back to fixed sampling"
+ " clock\n");
+ err = -EIO;
+ }
+ }
+
+out:
+ sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
+ spin_unlock(&host->lock);
+ enable_irq(host->irq);
+
+ return err;
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
.get_ro = sdhci_get_ro,
.enable_sdio_irq = sdhci_enable_sdio_irq,
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
+ .execute_tuning = sdhci_execute_tuning,
};
/*****************************************************************************\
@@ -1693,6 +1831,16 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
{
BUG_ON(intmask == 0);
+ /* CMD19 generates _only_ Buffer Read Ready interrupt */
+ if (intmask & SDHCI_INT_DATA_AVAIL) {
+ if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
+ == MMC_SEND_TUNING_BLOCK) {
+ host->tuning_done = 1;
+ wake_up(&host->buf_ready_int);
+ return;
+ }
+ }
+
if (!host->data) {
/*
* The "data complete" interrupt is also used to
@@ -2129,6 +2277,10 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_SUPPORT_DDR50)
mmc->caps |= MMC_CAP_UHS_DDR50;
+ /* Does the host needs tuning for SDR50? */
+ if (caps[1] & SDHCI_USE_SDR50_TUNING)
+ host->flags |= SDHCI_SDR50_NEEDS_TUNING;
+
/* Driver Type(s) (A, C, D) supported by the host */
if (caps[1] & SDHCI_DRIVER_TYPE_A)
mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
@@ -2282,6 +2434,9 @@ int sdhci_add_host(struct sdhci_host *host)
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
+ if (host->version >= SDHCI_SPEC_300)
+ init_waitqueue_head(&host->buf_ready_int);
+
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
mmc_hostname(mmc), host);
if (ret)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index aed1203..0b782db 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -161,6 +161,8 @@
#define SDHCI_CTRL_DRV_TYPE_A 0x0010
#define SDHCI_CTRL_DRV_TYPE_C 0x0020
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
+#define SDHCI_CTRL_EXEC_TUNING 0x0040
+#define SDHCI_CTRL_TUNED_CLK 0x0080
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
@@ -188,6 +190,7 @@
#define SDHCI_DRIVER_TYPE_A 0x00000010
#define SDHCI_DRIVER_TYPE_C 0x00000020
#define SDHCI_DRIVER_TYPE_D 0x00000040
+#define SDHCI_USE_SDR50_TUNING 0x00002000
#define SDHCI_CAPABILITIES_1 0x44
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 52b5dc9..ca7007f 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -136,6 +136,7 @@ struct mmc_host_ops {
void (*init_card)(struct mmc_host *host, struct mmc_card *card);
int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
+ int (*execute_tuning)(struct mmc_host *host);
};
struct mmc_card;
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 373b2bf..9fa5a73 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -50,6 +50,7 @@
#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */
#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */
#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
+#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
/* class 3 */
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 83bd9f7..26b6278 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -109,6 +109,7 @@ struct sdhci_host {
#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
+#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
unsigned int version; /* SDHCI spec. version */
@@ -145,6 +146,9 @@ struct sdhci_host {
unsigned int ocr_avail_sd;
unsigned int ocr_avail_mmc;
+ wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
+ unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
+
unsigned long private[0] ____cacheline_aligned;
};
#endif /* __SDHCI_H */
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 09/11] mmc: sdhci: enable preset value after uhs initialization
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
` (7 preceding siblings ...)
2011-04-20 9:30 ` [PATCH v3 08/11] mmc: sd: add support for tuning during uhs initialization Arindam Nath
@ 2011-04-20 9:30 ` Arindam Nath
2011-04-28 5:16 ` zhangfei gao
2011-04-20 9:31 ` [PATCH v3 10/11] mmc: sdhci: add support for programmable clock mode Arindam Nath
` (2 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:30 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
According to the Host Controller spec v3.00, setting Preset Value Enable
in the Host Control2 register lets SDCLK Frequency Select, Clock Generator
Select and Driver Strength Select to be set automatically by the Host
Controller based on the UHS-I mode set. This patch enables this feature.
We also reset Preset Value Enable next time before initialization.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 11 +++++++++++
drivers/mmc/host/sdhci.c | 32 ++++++++++++++++++++++++++++++++
include/linux/mmc/host.h | 1 +
3 files changed, 44 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index f3ecdef..08305ab 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -959,6 +959,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
}
}
+ /*
+ * Since initialization is now complete, enable preset
+ * value registers.
+ */
+ if (host->ops->enable_preset_value)
+ host->ops->enable_preset_value(host, 1);
+
host->card = card;
return 0;
@@ -1109,6 +1116,10 @@ int mmc_attach_sd(struct mmc_host *host)
if (err)
return err;
+ /* Disable preset value enable if already set from last time */
+ if (host->ops->enable_preset_value)
+ host->ops->enable_preset_value(host, 0);
+
/*
* We need to get OCR a different way for SPI.
*/
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9065157..c25ab83 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1614,6 +1614,37 @@ out:
return err;
}
+static void sdhci_enable_preset_value(struct mmc_host *mmc, int enable)
+{
+ struct sdhci_host *host;
+ u16 ctrl;
+ unsigned long flags;
+
+ host = mmc_priv(mmc);
+
+ /* Host Controller v3.00 defines preset value registers */
+ if (host->version < SDHCI_SPEC_300)
+ return;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+ /*
+ * We only enable or disable Preset Value if they are not already
+ * enabled or disabled respectively. Otherwise, we bail out.
+ */
+ if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
+ ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ } else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
+ ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
@@ -1621,6 +1652,7 @@ static const struct mmc_host_ops sdhci_ops = {
.enable_sdio_irq = sdhci_enable_sdio_irq,
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.execute_tuning = sdhci_execute_tuning,
+ .enable_preset_value = sdhci_enable_preset_value,
};
/*****************************************************************************\
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index ca7007f..2209e01 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -137,6 +137,7 @@ struct mmc_host_ops {
int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
int (*execute_tuning)(struct mmc_host *host);
+ void (*enable_preset_value)(struct mmc_host *host, int enable);
};
struct mmc_card;
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 10/11] mmc: sdhci: add support for programmable clock mode
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
` (8 preceding siblings ...)
2011-04-20 9:30 ` [PATCH v3 09/11] mmc: sdhci: enable preset value after uhs initialization Arindam Nath
@ 2011-04-20 9:31 ` Arindam Nath
2011-04-20 9:31 ` [PATCH v3 11/11] mmc: sdhci: add support for retuning mode 1 Arindam Nath
2011-04-29 18:40 ` [PATCH v3 00/11] add support for host controller v3.00 Subhash Jadavani
11 siblings, 0 replies; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:31 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
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 <arindam.nath@amd.com>
---
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 c25ab83..ca5914b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1007,8 +1007,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)
@@ -1026,14 +1026,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. */
@@ -1041,10 +1072,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;
@@ -2266,17 +2297,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 | MMC_CAP_ERASE;
/*
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 0b782db..84b8c2c 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -101,6 +101,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
@@ -191,6 +192,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 */
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 11/11] mmc: sdhci: add support for retuning mode 1
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
` (9 preceding siblings ...)
2011-04-20 9:31 ` [PATCH v3 10/11] mmc: sdhci: add support for programmable clock mode Arindam Nath
@ 2011-04-20 9:31 ` Arindam Nath
2011-04-29 18:40 ` [PATCH v3 00/11] add support for host controller v3.00 Subhash Jadavani
11 siblings, 0 replies; 22+ messages in thread
From: Arindam Nath @ 2011-04-20 9:31 UTC (permalink / raw)
To: cjb
Cc: linux-mmc, prakity, subhashj, zhangfei.gao, henry.su, aaron.lu,
anath.amd, Arindam Nath
Host Controller v3.00 can support retuning modes 1,2 or 3 depending on
the bits 46-47 of the Capabilities register. Also, the timer count for
retuning is indicated by bits 40-43 of the same register. We initialize
timer_list for retuning the first time we execute tuning procedure. This
condition is indicated by SDHCI_NEEDS_RETUNING not being set. Since
retuning mode 1 sets a limit of 4MB on the maximum data length, we set
max_blk_count appropriately. Once the tuning timer expires, we set
SDHCI_NEEDS_RETUNING flag, and if the flag is set, we execute tuning
procedure before sending the next command. We need to restore mmc_request
structure after executing retuning procedure since host->mrq is used
inside the procedure to send CMD19. We also disable and re-enable this
flag during suspend and resume respectively, as per the spec v3.00.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/host/sdhci.c | 113 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/mmc/host/sdhci.h | 6 ++-
include/linux/mmc/sdhci.h | 6 ++
3 files changed, 122 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index ca5914b..d81f66f 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -46,6 +46,8 @@ static void sdhci_finish_data(struct sdhci_host *);
static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
static void sdhci_finish_command(struct sdhci_host *);
+static int sdhci_execute_tuning(struct mmc_host *mmc);
+static void sdhci_tuning_timer(unsigned long data);
static void sdhci_dumpregs(struct sdhci_host *host)
{
@@ -1200,8 +1202,28 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
- } else
+ } else {
+ u32 present_state;
+
+ present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ /*
+ * Check if the re-tuning timer has already expired and there
+ * is no on-going data transfer. If so, we need to execute
+ * tuning procedure before sending command.
+ */
+ if ((host->flags & SDHCI_NEEDS_RETUNING) &&
+ !(present_state & (SDHCI_DOING_WRITE |
+ SDHCI_DOING_READ))) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ sdhci_execute_tuning(mmc);
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Restore original mmc_request structure */
+ host->mrq = mrq;
+ }
+
sdhci_send_command(host, mrq->cmd);
+ }
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
@@ -1638,6 +1660,37 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
}
out:
+ /*
+ * If this is the very first time we are here, we start the retuning
+ * timer. Since only during the first time, SDHCI_NEEDS_RETUNING
+ * flag won't be set, we check this condition before actually starting
+ * the timer.
+ */
+ if (!(host->flags & SDHCI_NEEDS_RETUNING) && host->tuning_count &&
+ (host->tuning_mode == SDHCI_TUNING_MODE_1)) {
+ mod_timer(&host->tuning_timer, jiffies + host->tuning_count *
+ HZ);
+ /* Tuning mode 1 limits the maximum data length to 4MB */
+ mmc->max_blk_count = (4 * 1024 * 1024) / mmc->max_blk_size;
+ } else {
+ host->flags &= ~SDHCI_NEEDS_RETUNING;
+ /* Reload the new initial value for timer */
+ if (host->tuning_mode == SDHCI_TUNING_MODE_1)
+ mod_timer(&host->tuning_timer, jiffies +
+ host->tuning_count * HZ);
+ }
+
+ /*
+ * In case tuning fails, host controllers which support re-tuning can
+ * try tuning again at a later time, when the re-tuning timer expires.
+ * So for these controllers, we return 0. Since there might be other
+ * controllers who do not have this capability, we return error for
+ * them.
+ */
+ if (err && host->tuning_count &&
+ (host->tuning_mode == SDHCI_TUNING_MODE_1))
+ err = 0;
+
sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
spin_unlock(&host->lock);
enable_irq(host->irq);
@@ -1733,6 +1786,10 @@ static void sdhci_tasklet_finish(unsigned long param)
del_timer(&host->timer);
+ if (host->version >= SDHCI_SPEC_300)
+ del_timer(&host->tuning_timer);
+
+
mrq = host->mrq;
/*
@@ -1806,6 +1863,20 @@ static void sdhci_timeout_timer(unsigned long data)
spin_unlock_irqrestore(&host->lock, flags);
}
+static void sdhci_tuning_timer(unsigned long data)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = (struct sdhci_host *)data;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ host->flags |= SDHCI_NEEDS_RETUNING;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
/*****************************************************************************\
* *
* Interrupt handling *
@@ -2080,6 +2151,15 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
sdhci_disable_card_detection(host);
+ /* Disable tuning since we are suspending */
+ if ((host->version >= SDHCI_SPEC_300) &&
+ host->tuning_count &&
+ (host->tuning_mode == SDHCI_TUNING_MODE_1)) {
+ host->flags &= ~SDHCI_NEEDS_RETUNING;
+ mod_timer(&host->tuning_timer, jiffies +
+ host->tuning_count * HZ);
+ }
+
ret = mmc_suspend_host(host->mmc);
if (ret)
return ret;
@@ -2121,6 +2201,12 @@ int sdhci_resume_host(struct sdhci_host *host)
ret = mmc_resume_host(host->mmc);
sdhci_enable_card_detection(host);
+ /* Set the re-tuning expiration flag */
+ if ((host->version >= SDHCI_SPEC_300) &&
+ host->tuning_count &&
+ (host->tuning_mode == SDHCI_TUNING_MODE_1))
+ host->flags |= SDHCI_NEEDS_RETUNING;
+
return ret;
}
@@ -2372,6 +2458,21 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_DRIVER_TYPE_D)
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
+ /* Initial value for re-tuning timer count */
+ host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
+ SDHCI_RETUNING_TIMER_COUNT_SHIFT;
+
+ /*
+ * In case Re-tuning Timer is not disabled, the actual value of
+ * re-tuning timer will be 2 ^ (n - 1).
+ */
+ if (host->tuning_count)
+ host->tuning_count = 1 << (host->tuning_count - 1);
+
+ /* Re-tuning mode supported by the Host Controller */
+ host->tuning_mode = (caps[1] & SDHCI_RETUNING_MODE_MASK) >>
+ SDHCI_RETUNING_MODE_SHIFT;
+
ocr_avail = 0;
/*
* According to SD Host Controller spec v3.00, if the Host System
@@ -2517,9 +2618,15 @@ int sdhci_add_host(struct sdhci_host *host)
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
- if (host->version >= SDHCI_SPEC_300)
+ if (host->version >= SDHCI_SPEC_300) {
init_waitqueue_head(&host->buf_ready_int);
+ /* Initialize re-tuning timer */
+ init_timer(&host->tuning_timer);
+ host->tuning_timer.data = (unsigned long)host;
+ host->tuning_timer.function = sdhci_tuning_timer;
+ }
+
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
mmc_hostname(mmc), host);
if (ret)
@@ -2613,6 +2720,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
free_irq(host->irq, host);
del_timer_sync(&host->timer);
+ if (host->version >= SDHCI_SPEC_300)
+ del_timer_sync(&host->tuning_timer);
tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 84b8c2c..8a386ee 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -191,7 +191,11 @@
#define SDHCI_DRIVER_TYPE_A 0x00000010
#define SDHCI_DRIVER_TYPE_C 0x00000020
#define SDHCI_DRIVER_TYPE_D 0x00000040
-#define SDHCI_USE_SDR50_TUNING 0x00002000
+#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00
+#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8
+#define SDHCI_USE_SDR50_TUNING 0x00002000
+#define SDHCI_RETUNING_MODE_MASK 0x0000C000
+#define SDHCI_RETUNING_MODE_SHIFT 14
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
#define SDHCI_CLOCK_MUL_SHIFT 16
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 1e7a654..8bd9244 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -110,6 +110,7 @@ struct sdhci_host {
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
+#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
unsigned int version; /* SDHCI spec. version */
@@ -150,6 +151,11 @@ struct sdhci_host {
wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
+ unsigned int tuning_count; /* Timer count for re-tuning */
+ unsigned int tuning_mode; /* Re-tuning mode supported by host */
+#define SDHCI_TUNING_MODE_1 0
+ struct timer_list tuning_timer; /* Timer for tuning */
+
unsigned long private[0] ____cacheline_aligned;
};
#endif /* __SDHCI_H */
--
1.7.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH v3 01/11] mmc: sd: add support for signal voltage switch procedure
2011-04-20 9:30 ` [PATCH v3 01/11] mmc: sd: add support for signal voltage switch procedure Arindam Nath
@ 2011-04-27 9:48 ` zhangfei gao
2011-04-27 10:17 ` Nath, Arindam
0 siblings, 1 reply; 22+ messages in thread
From: zhangfei gao @ 2011-04-27 9:48 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, linux-mmc, prakity, subhashj, henry.su, aaron.lu, anath.amd
On Wed, Apr 20, 2011 at 5:30 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> Host Controller v3.00 adds another Capabilities register. Apart
> from other things, this new register indicates whether the Host
> Controller supports SDR50, SDR104, and DDR50 UHS-I modes. The spec
> doesn't mention about explicit support for SDR12 and SDR25 UHS-I
> modes, so the Host Controller v3.00 should support them by default.
> Also if the controller supports SDR104 mode, it will also support
> SDR50 mode as well. So depending on the host support, we set the
> corresponding MMC_CAP_* flags. One more new register. Host Control2
> is added in v3.00, which is used during Signal Voltage Switch
> procedure described below.
>
> Since as per v3.00 spec, UHS-I supported hosts should set S18R
> to 1, we set S18R (bit 24) of OCR before sending ACMD41. We also
> need to set XPC (bit 28) of OCR in case the host can supply >150mA.
> This support is indicated by the Maximum Current Capabilities
> register of the Host Controller.
>
> If the response of ACMD41 has both CCS and S18A set, we start the
> signal voltage switch procedure, which if successfull, will switch
> the card from 3.3V signalling to 1.8V signalling. Signal voltage
> switch procedure adds support for a new command CMD11 in the
> Physical Layer Spec v3.01. As part of this procedure, we need to
> set 1.8V Signalling Enable (bit 3) of Host Control2 register, which
> if remains set after 5ms, means the switch to 1.8V signalling is
> successfull. Otherwise, we clear bit 24 of OCR and retry the
> initialization sequence. When we remove the card, and insert the
> same or another card, we need to make sure that we start with 3.3V
> signalling voltage. So we call mmc_set_signal_voltage() with
> MMC_SIGNAL_VOLTAGE_330 set so that we are back to 3.3V signalling
> voltage before we actually start initializing the card.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
Hi, Arindam
The uhs card works OK,
However after use uhs card, the io voltage keeps 1.8v, and
mmc_send_app_op_cond will return error, so no chance to go back to
3.3v.
The result is hs card can not be detected after using uhs card.
Also some hs card have timeout issue here when reading partition
table, still not know it is local reason.
> ---
> drivers/mmc/core/sd.c | 34 ++++++++-
> drivers/mmc/core/sd_ops.c | 34 ++++++++
> drivers/mmc/core/sd_ops.h | 1 +
> drivers/mmc/host/sdhci.c | 194 ++++++++++++++++++++++++++++++++++++++++++---
> drivers/mmc/host/sdhci.h | 18 ++++-
> include/linux/mmc/host.h | 15 ++++
> include/linux/mmc/sd.h | 1 +
> 7 files changed, 281 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 6dac89f..98aed60 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -403,6 +403,7 @@ struct device_type sd_type = {
> int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> {
> int err;
> + u32 rocr;
>
> /*
> * Since we're changing the OCR value, we seem to
> @@ -422,10 +423,36 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> if (!err)
> ocr |= 1 << 30;
>
> - err = mmc_send_app_op_cond(host, ocr, NULL);
> + /*
> + * If the host supports one of UHS-I modes, request the card
> + * to switch to 1.8V signaling level.
> + */
> + if (host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
> + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))
> + ocr |= 1 << 24;
> +
> + /* If the host can supply more than 150mA, XPC should be set to 1. */
> + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> + MMC_CAP_SET_XPC_180))
> + ocr |= 1 << 28;
> +
> +try_again:
> + err = mmc_send_app_op_cond(host, ocr, &rocr);
> if (err)
> return err;
>
> + /*
> + * In case CCS and S18A in the response is set, start Signal Voltage
> + * Switch procedure. SPI mode doesn't support CMD11.
> + */
> + if (!mmc_host_is_spi(host) && ((rocr & 0x41000000) == 0x41000000)) {
> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
> + if (err) {
> + ocr &= ~(1 << 24);
> + goto try_again;
> + }
> + }
> +
> if (mmc_host_is_spi(host))
> err = mmc_send_cid(host, cid);
> else
> @@ -781,6 +808,11 @@ int mmc_attach_sd(struct mmc_host *host)
> if (host->ocr_avail_sd)
> host->ocr_avail = host->ocr_avail_sd;
>
> + /* Make sure we are at 3.3V signalling voltage */
> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
> + if (err)
> + return err;
The code here may have no chance to execute, since
mmc_send_app_op_cond already failed if v_io is 1.8v for hs card.
Could you move the voltage setting before mmc_send_app_op_cond.
> +
> /*
> * We need to get OCR a different way for SPI.
> */
> diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> index a206aea..9be0d4c 100644
> --- a/drivers/mmc/core/sd_ops.c
> +++ b/drivers/mmc/core/sd_ops.c
> @@ -145,6 +145,40 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
> return 0;
> }
>
> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
> +{
> + struct mmc_command cmd;
> + int err = 0;
> +
> + BUG_ON(!host);
> +
> + /*
> + * Send CMD11 only if the request is to switch the card to
> + * 1.8V signalling.
> + */
> + if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
> + memset(&cmd, 0, sizeof(struct mmc_command));
> +
> + cmd.opcode = SD_SWITCH_VOLTAGE;
> + cmd.arg = 0;
> + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
> +
> + err = mmc_wait_for_cmd(host, &cmd, 0);
> + if (err)
> + return err;
> +
> + if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
> + return -EIO;
> + }
> +
> + host->ios.signal_voltage = signal_voltage;
> +
> + if (host->ops->start_signal_voltage_switch)
> + err = host->ops->start_signal_voltage_switch(host, &host->ios);
> +
> + return err;
> +}
> +
> int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> {
> struct mmc_command cmd = {0};
> diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> index ffc2305..d5bdda5 100644
> --- a/drivers/mmc/core/sd_ops.h
> +++ b/drivers/mmc/core/sd_ops.h
> @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
> int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> u8 value, u8 *resp);
> int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
>
> #endif
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index f31077d..56efd94 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -83,6 +83,8 @@ static void sdhci_dumpregs(struct sdhci_host *host)
> printk(KERN_DEBUG DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
> sdhci_readw(host, SDHCI_COMMAND),
> sdhci_readl(host, SDHCI_MAX_CURRENT));
> + printk(KERN_DEBUG DRIVER_NAME ": Host ctl2: 0x%08x\n",
> + sdhci_readw(host, SDHCI_HOST_CONTROL2));
>
> if (host->flags & SDHCI_USE_ADMA)
> printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
> @@ -1297,11 +1299,116 @@ out:
> spin_unlock_irqrestore(&host->lock, flags);
> }
>
> +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> + struct mmc_ios *ios)
> +{
> + struct sdhci_host *host;
> + u8 pwr;
> + u16 clk, ctrl;
> + u32 present_state;
> +
> + host = mmc_priv(mmc);
> +
> + /*
> + * Signal Voltage Switching is only applicable for Host Controllers
> + * v3.00 and above.
> + */
> + if (host->version < SDHCI_SPEC_300)
> + return 0;
> +
> + /*
> + * We first check whether the request is to set signalling voltage
> + * to 3.3V. If so, we change the voltage to 3.3V and return quickly.
> + */
> + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> + if ((ctrl & SDHCI_CTRL_VDD_180) &&
The check here may fail, could you remove this check, for example
ctrl=0x10 and no chance to 3.3v.
> + (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)) {
> + /* Set 1.8V Signal Enable in the Host Control2 register to 0 */
> + ctrl &= ~SDHCI_CTRL_VDD_180;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + /* Wait for 5ms */
> + usleep_range(5000, 5500);
> +
> + /* 3.3V regulator output should be stable within 5 ms */
> + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> + if (!(ctrl & SDHCI_CTRL_VDD_180))
> + return 0;
> + else {
> + printk(KERN_INFO DRIVER_NAME ": Switching to 3.3V "
> + "signalling voltage failed\n");
> + return -EIO;
> + }
> + } else if (!(ctrl & SDHCI_CTRL_VDD_180) &&
> + (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)) {
> + /* Stop SDCLK */
> + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> + clk &= ~SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> + /* Check whether DAT[3:0] is 0000 */
> + present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
> + if (!((present_state & SDHCI_DATA_LVL_MASK) >>
> + SDHCI_DATA_LVL_SHIFT)) {
> + /*
> + * Enable 1.8V Signal Enable in the Host Control2
> + * register
> + */
> + ctrl |= SDHCI_CTRL_VDD_180;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + /* Wait for 5ms */
> + usleep_range(5000, 5500);
> +
> + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> + if (ctrl & SDHCI_CTRL_VDD_180) {
> + /* Provide SDCLK again and wait for 1ms*/
> + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> + clk |= SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> + usleep_range(1000, 1500);
> +
> + /*
> + * If DAT[3:0] level is 1111b, then the card
> + * was successfully switched to 1.8V signaling.
> + */
> + present_state = sdhci_readl(host,
> + SDHCI_PRESENT_STATE);
> + if ((present_state & SDHCI_DATA_LVL_MASK) ==
> + SDHCI_DATA_LVL_MASK) {
> + return 0;
> + }
> + }
> + }
> +
> + /*
> + * If we are here, that means the switch to 1.8V signaling
> + * failed. We power cycle the card, and retry initialization
> + * sequence by setting S18R to 0.
> + */
> + pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
> + pwr &= ~SDHCI_POWER_ON;
> + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
> +
> + /* Wait for 1ms as per the spec */
> + usleep_range(1000, 1500);
> + pwr |= SDHCI_POWER_ON;
> + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
> +
> + printk(KERN_INFO DRIVER_NAME ": Switching to 1.8V signalling "
> + "voltage failed, retrying with S18R set to 0\n");
> + return -EAGAIN;
> + } else
> + /* No signal voltage switch required */
> + return 0;
> +}
> +
> static const struct mmc_host_ops sdhci_ops = {
> .request = sdhci_request,
> .set_ios = sdhci_set_ios,
> .get_ro = sdhci_get_ro,
> .enable_sdio_irq = sdhci_enable_sdio_irq,
> + .start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
> };
>
> /*****************************************************************************\
> @@ -1775,7 +1882,9 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host);
> int sdhci_add_host(struct sdhci_host *host)
> {
> struct mmc_host *mmc;
> - unsigned int caps, ocr_avail;
> + u32 caps[2];
> + u32 max_current_caps;
> + unsigned int ocr_avail;
> int ret;
>
> WARN_ON(host == NULL);
> @@ -1798,12 +1907,15 @@ int sdhci_add_host(struct sdhci_host *host)
> host->version);
> }
>
> - caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
> + caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
> sdhci_readl(host, SDHCI_CAPABILITIES);
>
> + caps[1] = (host->version >= SDHCI_SPEC_300) ?
> + sdhci_readl(host, SDHCI_CAPABILITIES_1) : 0;
> +
> if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
> host->flags |= SDHCI_USE_SDMA;
> - else if (!(caps & SDHCI_CAN_DO_SDMA))
> + else if (!(caps[0] & SDHCI_CAN_DO_SDMA))
> DBG("Controller doesn't have SDMA capability\n");
> else
> host->flags |= SDHCI_USE_SDMA;
> @@ -1814,7 +1926,8 @@ int sdhci_add_host(struct sdhci_host *host)
> host->flags &= ~SDHCI_USE_SDMA;
> }
>
> - if ((host->version >= SDHCI_SPEC_200) && (caps & SDHCI_CAN_DO_ADMA2))
> + if ((host->version >= SDHCI_SPEC_200) &&
> + (caps[0] & SDHCI_CAN_DO_ADMA2))
> host->flags |= SDHCI_USE_ADMA;
>
> if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
> @@ -1864,10 +1977,10 @@ int sdhci_add_host(struct sdhci_host *host)
> }
>
> if (host->version >= SDHCI_SPEC_300)
> - host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK)
> + host->max_clk = (caps[0] & SDHCI_CLOCK_V3_BASE_MASK)
> >> SDHCI_CLOCK_BASE_SHIFT;
> else
> - host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK)
> + host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK)
> >> SDHCI_CLOCK_BASE_SHIFT;
>
> host->max_clk *= 1000000;
> @@ -1883,7 +1996,7 @@ int sdhci_add_host(struct sdhci_host *host)
> }
>
> host->timeout_clk =
> - (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
> + (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
> if (host->timeout_clk == 0) {
> if (host->ops->get_timeout_clock) {
> host->timeout_clk = host->ops->get_timeout_clock(host);
> @@ -1895,7 +2008,7 @@ int sdhci_add_host(struct sdhci_host *host)
> return -ENODEV;
> }
> }
> - if (caps & SDHCI_TIMEOUT_CLK_UNIT)
> + if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
> host->timeout_clk *= 1000;
>
> /*
> @@ -1922,21 +2035,76 @@ int sdhci_add_host(struct sdhci_host *host)
> if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
> mmc->caps |= MMC_CAP_4_BIT_DATA;
>
> - if (caps & SDHCI_CAN_DO_HISPD)
> + if (caps[0] & SDHCI_CAN_DO_HISPD)
> mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
>
> if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
> mmc_card_is_removable(mmc))
> mmc->caps |= MMC_CAP_NEEDS_POLL;
>
> + /* UHS-I mode(s) supported by the host controller. */
> + if (host->version >= SDHCI_SPEC_300)
> + mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
> +
> + /* SDR104 supports also implies SDR50 support */
> + if (caps[1] & SDHCI_SUPPORT_SDR104)
> + mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
> + else if (caps[1] & SDHCI_SUPPORT_SDR50)
> + mmc->caps |= MMC_CAP_UHS_SDR50;
> +
> + if (caps[1] & SDHCI_SUPPORT_DDR50)
> + mmc->caps |= MMC_CAP_UHS_DDR50;
> +
> ocr_avail = 0;
> - if (caps & SDHCI_CAN_VDD_330)
> + /*
> + * According to SD Host Controller spec v3.00, if the Host System
> + * can afford more than 150mA, Host Driver should set XPC to 1. Also
> + * the value is meaningful only if Voltage Support in the Capabilities
> + * register is set. The actual current value is 4 times the register
> + * value.
> + */
> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> +
> + if (caps[0] & SDHCI_CAN_VDD_330) {
> + int max_current_330;
> +
> ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
> - if (caps & SDHCI_CAN_VDD_300)
> +
> + max_current_330 = ((max_current_caps &
> + SDHCI_MAX_CURRENT_330_MASK) >>
> + SDHCI_MAX_CURRENT_330_SHIFT) *
> + SDHCI_MAX_CURRENT_MULTIPLIER;
> +
> + if (max_current_330 > 150)
> + mmc->caps |= MMC_CAP_SET_XPC_330;
> + }
> + if (caps[0] & SDHCI_CAN_VDD_300) {
> + int max_current_300;
> +
> ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
> - if (caps & SDHCI_CAN_VDD_180)
> +
> + max_current_300 = ((max_current_caps &
> + SDHCI_MAX_CURRENT_300_MASK) >>
> + SDHCI_MAX_CURRENT_300_SHIFT) *
> + SDHCI_MAX_CURRENT_MULTIPLIER;
> +
> + if (max_current_300 > 150)
> + mmc->caps |= MMC_CAP_SET_XPC_300;
> + }
> + if (caps[0] & SDHCI_CAN_VDD_180) {
> + int max_current_180;
> +
> ocr_avail |= MMC_VDD_165_195;
>
> + max_current_180 = ((max_current_caps &
> + SDHCI_MAX_CURRENT_180_MASK) >>
> + SDHCI_MAX_CURRENT_180_SHIFT) *
> + SDHCI_MAX_CURRENT_MULTIPLIER;
> +
> + if (max_current_180 > 150)
> + mmc->caps |= MMC_CAP_SET_XPC_180;
> + }
> +
> mmc->ocr_avail = ocr_avail;
> mmc->ocr_avail_sdio = ocr_avail;
> if (host->ocr_avail_sdio)
> @@ -1996,7 +2164,7 @@ int sdhci_add_host(struct sdhci_host *host)
> if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
> mmc->max_blk_size = 2;
> } else {
> - mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
> + mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
> SDHCI_MAX_BLOCK_SHIFT;
> if (mmc->max_blk_size >= 3) {
> printk(KERN_WARNING "%s: Invalid maximum block size, "
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 85750a9..4f00b31 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -68,6 +68,8 @@
> #define SDHCI_DATA_AVAILABLE 0x00000800
> #define SDHCI_CARD_PRESENT 0x00010000
> #define SDHCI_WRITE_PROTECT 0x00080000
> +#define SDHCI_DATA_LVL_MASK 0x00F00000
> +#define SDHCI_DATA_LVL_SHIFT 20
>
> #define SDHCI_HOST_CONTROL 0x28
> #define SDHCI_CTRL_LED 0x01
> @@ -146,7 +148,8 @@
>
> #define SDHCI_ACMD12_ERR 0x3C
>
> -/* 3E-3F reserved */
> +#define SDHCI_HOST_CONTROL2 0x3E
> +#define SDHCI_CTRL_VDD_180 0x0008
>
> #define SDHCI_CAPABILITIES 0x40
> #define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
> @@ -167,9 +170,20 @@
> #define SDHCI_CAN_VDD_180 0x04000000
> #define SDHCI_CAN_64BIT 0x10000000
>
> +#define SDHCI_SUPPORT_SDR50 0x00000001
> +#define SDHCI_SUPPORT_SDR104 0x00000002
> +#define SDHCI_SUPPORT_DDR50 0x00000004
> +
> #define SDHCI_CAPABILITIES_1 0x44
>
> -#define SDHCI_MAX_CURRENT 0x48
> +#define SDHCI_MAX_CURRENT 0x48
> +#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
> +#define SDHCI_MAX_CURRENT_330_SHIFT 0
> +#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
> +#define SDHCI_MAX_CURRENT_300_SHIFT 8
> +#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
> +#define SDHCI_MAX_CURRENT_180_SHIFT 16
> +#define SDHCI_MAX_CURRENT_MULTIPLIER 4
>
> /* 4C-4F reserved for more max current */
>
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 0fffa5c..bde5a0b 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -56,6 +56,11 @@ struct mmc_ios {
> #define MMC_SDR_MODE 0
> #define MMC_1_2V_DDR_MODE 1
> #define MMC_1_8V_DDR_MODE 2
> +
> + unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
> +
> +#define MMC_SIGNAL_VOLTAGE_330 0
> +#define MMC_SIGNAL_VOLTAGE_180 1
> };
>
> struct mmc_host_ops {
> @@ -117,6 +122,8 @@ struct mmc_host_ops {
>
> /* optional callback for HC quirks */
> void (*init_card)(struct mmc_host *host, struct mmc_card *card);
> +
> + int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
> };
>
> struct mmc_card;
> @@ -173,6 +180,14 @@ struct mmc_host {
> /* DDR mode at 1.2V */
> #define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */
> #define MMC_CAP_BUS_WIDTH_TEST (1 << 14) /* CMD14/CMD19 bus width ok */
> +#define MMC_CAP_UHS_SDR12 (1 << 15) /* Host supports UHS SDR12 mode */
> +#define MMC_CAP_UHS_SDR25 (1 << 16) /* Host supports UHS SDR25 mode */
> +#define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */
> +#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */
> +#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */
> +#define MMC_CAP_SET_XPC_330 (1 << 20) /* Host supports >150mA current at 3.3V */
> +#define MMC_CAP_SET_XPC_300 (1 << 21) /* Host supports >150mA current at 3.0V */
> +#define MMC_CAP_SET_XPC_180 (1 << 22) /* Host supports >150mA current at 1.8V */
>
> mmc_pm_flag_t pm_caps; /* supported pm features */
>
> diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
> index 3fd85e0..835b715 100644
> --- a/include/linux/mmc/sd.h
> +++ b/include/linux/mmc/sd.h
> @@ -17,6 +17,7 @@
> /* This is basically the same command as for MMC with some quirks. */
> #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
> #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
> +#define SD_SWITCH_VOLTAGE 11 /* ac R1 */
>
> /* class 10 */
> #define SD_SWITCH 6 /* adtc [31:0] See below R1 */
> --
> 1.7.1
>
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* RE: [PATCH v3 01/11] mmc: sd: add support for signal voltage switch procedure
2011-04-27 9:48 ` zhangfei gao
@ 2011-04-27 10:17 ` Nath, Arindam
2011-04-28 3:17 ` zhangfei gao
0 siblings, 1 reply; 22+ messages in thread
From: Nath, Arindam @ 2011-04-27 10:17 UTC (permalink / raw)
To: zhangfei gao
Cc: cjb@laptop.org, linux-mmc@vger.kernel.org, prakity@marvell.com,
subhashj@codeaurora.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Zhangfei,
[...]
> Hi, Arindam
>
> The uhs card works OK,
> However after use uhs card, the io voltage keeps 1.8v, and
> mmc_send_app_op_cond will return error, so no chance to go back to
> 3.3v.
> The result is hs card can not be detected after using uhs card.
> Also some hs card have timeout issue here when reading partition
> table, still not know it is local reason.
[...]
> > @@ -781,6 +808,11 @@ int mmc_attach_sd(struct mmc_host *host)
> > if (host->ocr_avail_sd)
> > host->ocr_avail = host->ocr_avail_sd;
> >
> > + /* Make sure we are at 3.3V signalling voltage */
> > + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
> > + if (err)
> > + return err;
>
> The code here may have no chance to execute, since
> mmc_send_app_op_cond already failed if v_io is 1.8v for hs card.
> Could you move the voltage setting before mmc_send_app_op_cond.
Thanks once again for your sharp review. Yes, it makes sense to move mmc_set_signal_voltage() before mmc_send_app_op_cond(). Will do it.
[...]
> > @@ -1297,11 +1299,116 @@ out:
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> >
> > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> > + struct mmc_ios *ios)
> > +{
> > + struct sdhci_host *host;
> > + u8 pwr;
> > + u16 clk, ctrl;
> > + u32 present_state;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + /*
> > + * Signal Voltage Switching is only applicable for Host
> Controllers
> > + * v3.00 and above.
> > + */
> > + if (host->version < SDHCI_SPEC_300)
> > + return 0;
> > +
> > + /*
> > + * We first check whether the request is to set signalling
> voltage
> > + * to 3.3V. If so, we change the voltage to 3.3V and return
> quickly.
> > + */
> > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > + if ((ctrl & SDHCI_CTRL_VDD_180) &&
>
> The check here may fail, could you remove this check, for example
> ctrl=0x10 and no chance to 3.3v.
If ctrl=0x10, it would mean the signaling voltage is already at 3.3V, so why do we need to set it to 3.3V again?
Thanks,
Arindam
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v3 08/11] mmc: sd: add support for tuning during uhs initialization
2011-04-20 9:30 ` [PATCH v3 08/11] mmc: sd: add support for tuning during uhs initialization Arindam Nath
@ 2011-04-27 18:44 ` Philip Rakity
2011-04-28 6:02 ` Nath, Arindam
0 siblings, 1 reply; 22+ messages in thread
From: Philip Rakity @ 2011-04-27 18:44 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb@laptop.org, linux-mmc@vger.kernel.org,
subhashj@codeaurora.org, zhangfei.gao@gmail.com, henry.su@amd.com,
aaron.lu@amd.com, anath.amd@gmail.com
Hi Arindam,
On Apr 20, 2011, at 2:30 AM, Arindam Nath wrote:
> Host Controller needs tuning during initialization to operate SDR50
> and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is
> indicated by bit 45 of the Host Controller Capabilities register.
> A new command CMD19 has been defined in the Physical Layer spec
> v3.01 to request the card to send tuning pattern.
>
> We enable Buffer Read Ready interrupt at the very begining of tuning
> procedure, because that is the only interrupt generated by the Host
> Controller during tuning. We make sure that DMA Enable is set to 0
> before actually sending CMD19. The tuning block is sent by the card
> to the Host Controller using DAT lines, so we set Data Present
> Select (bit 5) in the Command register. The Host Controller is
> responsible for doing the verfication of tuning block sent by the
> card at the hardware level. After sending CMD19, we wait for Buffer
> Read Ready interrupt. In case we don't receive an interrupt after
> the specified timeout value, we fall back on fixed sampling clock by
> setting Execute Tuning (bit 6) and Sampling Clock Select (bit 7) of
> Host Control2 register to 0. Before exiting the tuning procedure, we
> disable Buffer Read Ready interrupt.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 6 ++
> drivers/mmc/host/sdhci.c | 157 ++++++++++++++++++++++++++++++++++++++++++++-
> drivers/mmc/host/sdhci.h | 3 +
> include/linux/mmc/host.h | 1 +
> include/linux/mmc/mmc.h | 1 +
> include/linux/mmc/sdhci.h | 4 +
> 6 files changed, 171 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 1594e51..f3ecdef 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -623,6 +623,12 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
>
> /* Set current limit for the card */
> err = sd_set_current_limit(card, status);
> + if (err)
> + goto out;
> +
> + /* SPI mode doesn't define CMD19 */
> + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
> + err = card->host->ops->execute_tuning(card->host);
>
> out:
> kfree(status);
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 9141c5b..9065157 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -38,6 +38,8 @@
> #define SDHCI_USE_LEDS_CLASS
> #endif
>
> +#define MAX_TUNING_LOOP 40
> +
> static unsigned int debug_quirks = 0;
>
> static void sdhci_finish_data(struct sdhci_host *);
> @@ -962,7 +964,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> flags |= SDHCI_CMD_CRC;
> if (cmd->flags & MMC_RSP_OPCODE)
> flags |= SDHCI_CMD_INDEX;
> - if (cmd->data)
> +
> + /* CMD19 is special in that the Data Present Select should be set */
> + if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
> flags |= SDHCI_CMD_DATA;
>
> sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
> @@ -1477,12 +1481,146 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> return 0;
> }
>
> +static int sdhci_execute_tuning(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u16 ctrl;
u32 ier;
> + int tuning_loop_counter = MAX_TUNING_LOOP;
> + unsigned long timeout;
> + int err = 0;
> +
> + host = mmc_priv(mmc);
> +
> + disable_irq(host->irq);
> + spin_lock(&host->lock);
is this needed ? I believe we should be locked from the core/ layer.
> +
> + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +
> + /*
> + * Host Controller needs tuning only in case of SDR104 mode
> + * and for SDR50 mode when Use Tuning for SDR50 is set in
> + * Capabilities register.
> + */
> + if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
> + (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
> + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> + else {
> + spin_unlock(&host->lock);
> + enable_irq(host->irq);
> + return 0;
> + }
based on exchanges with SD Host Chair we need to set block size and transfer mode.
Directly wriiting TRANSFER_MODE clears DMA bit. No DMA is needed for SDHCI_INT_DATA_AVAIL
to be signaled.
Suggest
> + if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
> + (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
> + (host->flags & SDHCI_SDR50_NEEDS_TUNING))) {
ctrl |= SDHCI_CTRL_EXEC_TUNING;
sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
} else {
> + spin_unlock(&host->lock);
> + enable_irq(host->irq);
> + return 0;
> + }
> +
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + /*
> + * As per the Host Controller spec v3.00, tuning command
> + * generates Buffer Read Ready interrupt, so enable that.
> + */
> + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
delete line above -- it keeps the other interrupts enabled but we should only
receive the DATA AVAIL interrupt. We should protect against a bad controller
change to
>> ier = sdhci_readl(host, SDHCI_INT_ENABLE);
>> sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL);
This will ONLY enable SDHCI_INT_DATA_AVAIL.
>
>
> +
> + /*
> + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
> + * of loops reaches 40 times or a timeout of 150ms occurs.
> + */
> + timeout = 150;
> + do {
> + u16 mode;
> + struct mmc_command cmd;
> + struct mmc_request mrq;
> +
> + if (!tuning_loop_counter && !timeout)
> + break;
> +
> + memset(&cmd, 0, sizeof(struct mmc_command));
> + cmd.opcode = MMC_SEND_TUNING_BLOCK;
> + cmd.arg = 0;
> + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> +
> + memset(&cmd.resp, 0, sizeof(cmd.resp));
> + cmd.retries = 0;
> +
> + cmd.data = NULL;
> + cmd.error = 0;
> +
> + memset(&mrq, 0, sizeof(struct mmc_request));
> + mrq.cmd = &cmd;
> + host->mrq = &mrq;
> +
=====
> + /* Make sure DMA Enable is set to 0 before tuning */
> + mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
> + if (mode & SDHCI_TRNS_DMA) {
> + mode &= ~SDHCI_TRNS_DMA;
> + sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
> + }
this code should be deleted and moved up.
=====
> +
> + sdhci_send_command(host, &cmd);
> +
> + host->cmd = NULL;
> + host->mrq = NULL;
> +
> + spin_unlock(&host->lock);
> + enable_irq(host->irq);
> +
> + /* Wait for Buffer Read Ready interrupt */
> + wait_event_interruptible_timeout(host->buf_ready_int,
> + (host->tuning_done == 1),
> + msecs_to_jiffies(50));
> + disable_irq(host->irq);
> + spin_lock(&host->lock);
> +
> + if (!host->tuning_done) {
> + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> + " failed, falling back to fixed sampling"
> + " clock\n");
> + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + err = -EIO;
> + goto out;
> + }
> +
> + host->tuning_done = 0;
> +
> + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> + tuning_loop_counter--;
> + timeout--;
> + mdelay(1);
> + } while (ctrl & SDHCI_CTRL_EXEC_TUNING);
> +
> + /*
> + * The Host Driver has exhausted the maximum number of loops allowed,
> + * so use fixed sampling frequency.
> + */
> + if (!tuning_loop_counter || !timeout) {
> + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> + } else {
> + if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
> + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> + " failed, falling back to fixed sampling"
> + " clock\n");
> + err = -EIO;
> + }
> + }
> +
> +out:
> + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
delete the above line
change to
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
> + spin_unlock(&host->lock);
> + enable_irq(host->irq);
> +
> + return err;
> +}
> +
> static const struct mmc_host_ops sdhci_ops = {
> .request = sdhci_request,
> .set_ios = sdhci_set_ios,
> .get_ro = sdhci_get_ro,
> .enable_sdio_irq = sdhci_enable_sdio_irq,
> .start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
> + .execute_tuning = sdhci_execute_tuning,
> };
>
> /*****************************************************************************\
> @@ -1693,6 +1831,16 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
> {
> BUG_ON(intmask == 0);
>
> + /* CMD19 generates _only_ Buffer Read Ready interrupt */
> + if (intmask & SDHCI_INT_DATA_AVAIL) {
> + if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
> + == MMC_SEND_TUNING_BLOCK) {
> + host->tuning_done = 1;
> + wake_up(&host->buf_ready_int);
> + return;
> + }
> + }
> +
> if (!host->data) {
> /*
> * The "data complete" interrupt is also used to
> @@ -2129,6 +2277,10 @@ int sdhci_add_host(struct sdhci_host *host)
> if (caps[1] & SDHCI_SUPPORT_DDR50)
> mmc->caps |= MMC_CAP_UHS_DDR50;
>
> + /* Does the host needs tuning for SDR50? */
> + if (caps[1] & SDHCI_USE_SDR50_TUNING)
> + host->flags |= SDHCI_SDR50_NEEDS_TUNING;
> +
> /* Driver Type(s) (A, C, D) supported by the host */
> if (caps[1] & SDHCI_DRIVER_TYPE_A)
> mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
> @@ -2282,6 +2434,9 @@ int sdhci_add_host(struct sdhci_host *host)
>
> setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
>
> + if (host->version >= SDHCI_SPEC_300)
> + init_waitqueue_head(&host->buf_ready_int);
> +
> ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
> mmc_hostname(mmc), host);
> if (ret)
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index aed1203..0b782db 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -161,6 +161,8 @@
> #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> #define SDHCI_CTRL_DRV_TYPE_C 0x0020
> #define SDHCI_CTRL_DRV_TYPE_D 0x0030
> +#define SDHCI_CTRL_EXEC_TUNING 0x0040
> +#define SDHCI_CTRL_TUNED_CLK 0x0080
> #define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
>
> #define SDHCI_CAPABILITIES 0x40
> @@ -188,6 +190,7 @@
> #define SDHCI_DRIVER_TYPE_A 0x00000010
> #define SDHCI_DRIVER_TYPE_C 0x00000020
> #define SDHCI_DRIVER_TYPE_D 0x00000040
> +#define SDHCI_USE_SDR50_TUNING 0x00002000
>
> #define SDHCI_CAPABILITIES_1 0x44
>
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 52b5dc9..ca7007f 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -136,6 +136,7 @@ struct mmc_host_ops {
> void (*init_card)(struct mmc_host *host, struct mmc_card *card);
>
> int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
> + int (*execute_tuning)(struct mmc_host *host);
> };
>
> struct mmc_card;
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 373b2bf..9fa5a73 100644
> --- a/include/linux/mmc/mmc.h
> +++ b/include/linux/mmc/mmc.h
> @@ -50,6 +50,7 @@
> #define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */
> #define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */
> #define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
> +#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
>
> /* class 3 */
> #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> index 83bd9f7..26b6278 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -109,6 +109,7 @@ struct sdhci_host {
> #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
> #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
>
> unsigned int version; /* SDHCI spec. version */
>
> @@ -145,6 +146,9 @@ struct sdhci_host {
> unsigned int ocr_avail_sd;
> unsigned int ocr_avail_mmc;
>
> + wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
> + unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
> +
> unsigned long private[0] ____cacheline_aligned;
> };
> #endif /* __SDHCI_H */
> --
> 1.7.1
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v3 01/11] mmc: sd: add support for signal voltage switch procedure
2011-04-27 10:17 ` Nath, Arindam
@ 2011-04-28 3:17 ` zhangfei gao
2011-04-28 5:48 ` Nath, Arindam
0 siblings, 1 reply; 22+ messages in thread
From: zhangfei gao @ 2011-04-28 3:17 UTC (permalink / raw)
To: Nath, Arindam
Cc: cjb@laptop.org, linux-mmc@vger.kernel.org, prakity@marvell.com,
subhashj@codeaurora.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
On Wed, Apr 27, 2011 at 6:17 AM, Nath, Arindam <Arindam.Nath@amd.com> wrote:
> Hi Zhangfei,
>
> [...]
>
>> Hi, Arindam
>>
>> The uhs card works OK,
>> However after use uhs card, the io voltage keeps 1.8v, and
>> mmc_send_app_op_cond will return error, so no chance to go back to
>> 3.3v.
>> The result is hs card can not be detected after using uhs card.
>> Also some hs card have timeout issue here when reading partition
>> table, still not know it is local reason.
>
> [...]
>
>> > @@ -781,6 +808,11 @@ int mmc_attach_sd(struct mmc_host *host)
>> > if (host->ocr_avail_sd)
>> > host->ocr_avail = host->ocr_avail_sd;
>> >
>> > + /* Make sure we are at 3.3V signalling voltage */
>> > + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
>> > + if (err)
>> > + return err;
>>
>> The code here may have no chance to execute, since
>> mmc_send_app_op_cond already failed if v_io is 1.8v for hs card.
>> Could you move the voltage setting before mmc_send_app_op_cond.
>
> Thanks once again for your sharp review. Yes, it makes sense to move mmc_set_signal_voltage() before mmc_send_app_op_cond(). Will do it.
>
> [...]
>
>> > @@ -1297,11 +1299,116 @@ out:
>> > spin_unlock_irqrestore(&host->lock, flags);
>> > }
>> >
>> > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
>> > + struct mmc_ios *ios)
>> > +{
>> > + struct sdhci_host *host;
>> > + u8 pwr;
>> > + u16 clk, ctrl;
>> > + u32 present_state;
>> > +
>> > + host = mmc_priv(mmc);
>> > +
>> > + /*
>> > + * Signal Voltage Switching is only applicable for Host
>> Controllers
>> > + * v3.00 and above.
>> > + */
>> > + if (host->version < SDHCI_SPEC_300)
>> > + return 0;
>> > +
>> > + /*
>> > + * We first check whether the request is to set signalling
>> voltage
>> > + * to 3.3V. If so, we change the voltage to 3.3V and return
>> quickly.
>> > + */
>> > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>> > + if ((ctrl & SDHCI_CTRL_VDD_180) &&
>>
>> The check here may fail, could you remove this check, for example
>> ctrl=0x10 and no chance to 3.3v.
>
> If ctrl=0x10, it would mean the signaling voltage is already at 3.3V, so why do we need to set it to 3.3V again?
The test here shows ctrl return back to 0x10 once the card is removed
even the io voltage is 1.8v.
And the first time SDHCI_CTRL_VDD_180 is not set, so the default
voltage is used if using external regulator.
>
> Thanks,
> Arindam
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v3 09/11] mmc: sdhci: enable preset value after uhs initialization
2011-04-20 9:30 ` [PATCH v3 09/11] mmc: sdhci: enable preset value after uhs initialization Arindam Nath
@ 2011-04-28 5:16 ` zhangfei gao
2011-04-28 5:50 ` Nath, Arindam
0 siblings, 1 reply; 22+ messages in thread
From: zhangfei gao @ 2011-04-28 5:16 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, linux-mmc, prakity, subhashj, henry.su, aaron.lu, anath.amd
On Wed, Apr 20, 2011 at 5:30 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> According to the Host Controller spec v3.00, setting Preset Value Enable
> in the Host Control2 register lets SDCLK Frequency Select, Clock Generator
> Select and Driver Strength Select to be set automatically by the Host
> Controller based on the UHS-I mode set. This patch enables this feature.
> We also reset Preset Value Enable next time before initialization.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
Hi, Arindam
This patch cause abnormal behavior of general hs card, since
enable_preset_value(host, 1) is set to both uhs card and general card
here.
Your patch serious basically works with the fix, thanks a lot.
Additional call back to enable external regulator output voltage is
needed on our platform for testing.
Thanks
> ---
> drivers/mmc/core/sd.c | 11 +++++++++++
> drivers/mmc/host/sdhci.c | 32 ++++++++++++++++++++++++++++++++
> include/linux/mmc/host.h | 1 +
> 3 files changed, 44 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index f3ecdef..08305ab 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -959,6 +959,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
> }
> }
>
> + /*
> + * Since initialization is now complete, enable preset
> + * value registers.
> + */
> + if (host->ops->enable_preset_value)
> + host->ops->enable_preset_value(host, 1);
> +
Could you move this after mmc_sd_card_set_uhs, and only for uhs card.
General hs card may not properly if enable this feature.
> host->card = card;
> return 0;
>
> @@ -1109,6 +1116,10 @@ int mmc_attach_sd(struct mmc_host *host)
> if (err)
> return err;
>
> + /* Disable preset value enable if already set from last time */
> + if (host->ops->enable_preset_value)
> + host->ops->enable_preset_value(host, 0);
> +
> /*
> * We need to get OCR a different way for SPI.
> */
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 9065157..c25ab83 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1614,6 +1614,37 @@ out:
> return err;
> }
>
> +static void sdhci_enable_preset_value(struct mmc_host *mmc, int enable)
> +{
> + struct sdhci_host *host;
> + u16 ctrl;
> + unsigned long flags;
> +
> + host = mmc_priv(mmc);
> +
> + /* Host Controller v3.00 defines preset value registers */
> + if (host->version < SDHCI_SPEC_300)
> + return;
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +
> + /*
> + * We only enable or disable Preset Value if they are not already
> + * enabled or disabled respectively. Otherwise, we bail out.
> + */
> + if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> + } else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> + }
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> static const struct mmc_host_ops sdhci_ops = {
> .request = sdhci_request,
> .set_ios = sdhci_set_ios,
> @@ -1621,6 +1652,7 @@ static const struct mmc_host_ops sdhci_ops = {
> .enable_sdio_irq = sdhci_enable_sdio_irq,
> .start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
> .execute_tuning = sdhci_execute_tuning,
> + .enable_preset_value = sdhci_enable_preset_value,
> };
>
> /*****************************************************************************\
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index ca7007f..2209e01 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -137,6 +137,7 @@ struct mmc_host_ops {
>
> int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
> int (*execute_tuning)(struct mmc_host *host);
> + void (*enable_preset_value)(struct mmc_host *host, int enable);
> };
>
> struct mmc_card;
> --
> 1.7.1
>
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* RE: [PATCH v3 01/11] mmc: sd: add support for signal voltage switch procedure
2011-04-28 3:17 ` zhangfei gao
@ 2011-04-28 5:48 ` Nath, Arindam
0 siblings, 0 replies; 22+ messages in thread
From: Nath, Arindam @ 2011-04-28 5:48 UTC (permalink / raw)
To: zhangfei gao
Cc: cjb@laptop.org, linux-mmc@vger.kernel.org, prakity@marvell.com,
subhashj@codeaurora.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Zhangfei,
> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Thursday, April 28, 2011 8:48 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; linux-mmc@vger.kernel.org; prakity@marvell.com;
> subhashj@codeaurora.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v3 01/11] mmc: sd: add support for signal voltage
> switch procedure
>
> On Wed, Apr 27, 2011 at 6:17 AM, Nath, Arindam <Arindam.Nath@amd.com>
> wrote:
> > Hi Zhangfei,
> >
> > [...]
> >
> >> Hi, Arindam
> >>
> >> The uhs card works OK,
> >> However after use uhs card, the io voltage keeps 1.8v, and
> >> mmc_send_app_op_cond will return error, so no chance to go back to
> >> 3.3v.
> >> The result is hs card can not be detected after using uhs card.
> >> Also some hs card have timeout issue here when reading partition
> >> table, still not know it is local reason.
> >
> > [...]
> >
> >> > @@ -781,6 +808,11 @@ int mmc_attach_sd(struct mmc_host *host)
> >> > if (host->ocr_avail_sd)
> >> > host->ocr_avail = host->ocr_avail_sd;
> >> >
> >> > + /* Make sure we are at 3.3V signalling voltage */
> >> > + err = mmc_set_signal_voltage(host,
> MMC_SIGNAL_VOLTAGE_330);
> >> > + if (err)
> >> > + return err;
> >>
> >> The code here may have no chance to execute, since
> >> mmc_send_app_op_cond already failed if v_io is 1.8v for hs card.
> >> Could you move the voltage setting before mmc_send_app_op_cond.
> >
> > Thanks once again for your sharp review. Yes, it makes sense to move
> mmc_set_signal_voltage() before mmc_send_app_op_cond(). Will do it.
> >
> > [...]
> >
> >> > @@ -1297,11 +1299,116 @@ out:
> >> > spin_unlock_irqrestore(&host->lock, flags);
> >> > }
> >> >
> >> > +static int sdhci_start_signal_voltage_switch(struct mmc_host
> *mmc,
> >> > + struct mmc_ios *ios)
> >> > +{
> >> > + struct sdhci_host *host;
> >> > + u8 pwr;
> >> > + u16 clk, ctrl;
> >> > + u32 present_state;
> >> > +
> >> > + host = mmc_priv(mmc);
> >> > +
> >> > + /*
> >> > + * Signal Voltage Switching is only applicable for Host
> >> Controllers
> >> > + * v3.00 and above.
> >> > + */
> >> > + if (host->version < SDHCI_SPEC_300)
> >> > + return 0;
> >> > +
> >> > + /*
> >> > + * We first check whether the request is to set signalling
> >> voltage
> >> > + * to 3.3V. If so, we change the voltage to 3.3V and
> return
> >> quickly.
> >> > + */
> >> > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> >> > + if ((ctrl & SDHCI_CTRL_VDD_180) &&
> >>
> >> The check here may fail, could you remove this check, for example
> >> ctrl=0x10 and no chance to 3.3v.
> >
> > If ctrl=0x10, it would mean the signaling voltage is already at 3.3V,
> so why do we need to set it to 3.3V again?
>
> The test here shows ctrl return back to 0x10 once the card is removed
> even the io voltage is 1.8v.
> And the first time SDHCI_CTRL_VDD_180 is not set, so the default
> voltage is used if using external regulator.
Thanks for explaining how it might impact other HCs. I will make the check only for 3.3V. That should work for you.
Thanks,
Arindam
^ permalink raw reply [flat|nested] 22+ messages in thread
* RE: [PATCH v3 09/11] mmc: sdhci: enable preset value after uhs initialization
2011-04-28 5:16 ` zhangfei gao
@ 2011-04-28 5:50 ` Nath, Arindam
0 siblings, 0 replies; 22+ messages in thread
From: Nath, Arindam @ 2011-04-28 5:50 UTC (permalink / raw)
To: zhangfei gao
Cc: cjb@laptop.org, linux-mmc@vger.kernel.org, prakity@marvell.com,
subhashj@codeaurora.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Zhangfei,
> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Thursday, April 28, 2011 10:47 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; linux-mmc@vger.kernel.org; prakity@marvell.com;
> subhashj@codeaurora.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v3 09/11] mmc: sdhci: enable preset value after uhs
> initialization
>
> On Wed, Apr 20, 2011 at 5:30 AM, Arindam Nath <arindam.nath@amd.com>
> wrote:
> > According to the Host Controller spec v3.00, setting Preset Value
> Enable
> > in the Host Control2 register lets SDCLK Frequency Select, Clock
> Generator
> > Select and Driver Strength Select to be set automatically by the Host
> > Controller based on the UHS-I mode set. This patch enables this
> feature.
> > We also reset Preset Value Enable next time before initialization.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>
> Hi, Arindam
>
> This patch cause abnormal behavior of general hs card, since
> enable_preset_value(host, 1) is set to both uhs card and general card
> here.
> Your patch serious basically works with the fix, thanks a lot.
> Additional call back to enable external regulator output voltage is
> needed on our platform for testing.
>
> Thanks
>
> > ---
> > drivers/mmc/core/sd.c | 11 +++++++++++
> > drivers/mmc/host/sdhci.c | 32 ++++++++++++++++++++++++++++++++
> > include/linux/mmc/host.h | 1 +
> > 3 files changed, 44 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index f3ecdef..08305ab 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -959,6 +959,13 @@ static int mmc_sd_init_card(struct mmc_host
> *host, u32 ocr,
> > }
> > }
> >
> > + /*
> > + * Since initialization is now complete, enable preset
> > + * value registers.
> > + */
> > + if (host->ops->enable_preset_value)
> > + host->ops->enable_preset_value(host, 1);
> > +
>
> Could you move this after mmc_sd_card_set_uhs, and only for uhs card.
> General hs card may not properly if enable this feature.
Yes, that would make sense. Will fix it.
Thanks,
Arindam
^ permalink raw reply [flat|nested] 22+ messages in thread
* RE: [PATCH v3 08/11] mmc: sd: add support for tuning during uhs initialization
2011-04-27 18:44 ` Philip Rakity
@ 2011-04-28 6:02 ` Nath, Arindam
2011-04-28 8:29 ` Question about timer in struct sdhci_host Ryan Liu
0 siblings, 1 reply; 22+ messages in thread
From: Nath, Arindam @ 2011-04-28 6:02 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, linux-mmc@vger.kernel.org,
subhashj@codeaurora.org, zhangfei.gao@gmail.com, Su, Henry,
Lu, Aaron, anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Thursday, April 28, 2011 12:15 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; linux-mmc@vger.kernel.org; subhashj@codeaurora.org;
> zhangfei.gao@gmail.com; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v3 08/11] mmc: sd: add support for tuning during
> uhs initialization
>
>
[...]
> > @@ -1477,12 +1481,146 @@ static int
> sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> > return 0;
> > }
> >
> > +static int sdhci_execute_tuning(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u16 ctrl;
> u32 ier;
> > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > + unsigned long timeout;
> > + int err = 0;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + disable_irq(host->irq);
> > + spin_lock(&host->lock);
>
> is this needed ? I believe we should be locked from the core/ layer.
Yes, this is needed. Please note that sdhci_execute_tuning() is not only invoked during initialization, but also when re-tuning timer expires.
> > +
> > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > +
> > + /*
> > + * Host Controller needs tuning only in case of SDR104 mode
> > + * and for SDR50 mode when Use Tuning for SDR50 is set in
> > + * Capabilities register.
> > + */
> > + if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
> > + (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
> > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > + else {
> > + spin_unlock(&host->lock);
> > + enable_irq(host->irq);
> > + return 0;
> > + }
>
>
> based on exchanges with SD Host Chair we need to set block size and
> transfer mode.
> Directly wriiting TRANSFER_MODE clears DMA bit. No DMA is needed for
> SDHCI_INT_DATA_AVAIL
> to be signaled.
>
> Suggest
>
> > + if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
> > + (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
> > + (host->flags & SDHCI_SDR50_NEEDS_TUNING))) {
> ctrl |= SDHCI_CTRL_EXEC_TUNING;
> sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
> SDHCI_BLOCK_SIZE);
> sdhci_writew(host, SDHCI_TRNS_READ,
> SDHCI_TRANSFER_MODE);
> } else {
> > + spin_unlock(&host->lock);
> > + enable_irq(host->irq);
> > + return 0;
> > + }
>
This suggestion looks okay to me.
>
> > +
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + /*
> > + * As per the Host Controller spec v3.00, tuning command
> > + * generates Buffer Read Ready interrupt, so enable that.
> > + */
> > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
>
> delete line above -- it keeps the other interrupts enabled but we
> should only
> receive the DATA AVAIL interrupt. We should protect against a bad
> controller
>
> change to
>
> >> ier = sdhci_readl(host, SDHCI_INT_ENABLE);
> >> sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL);
>
>
> This will ONLY enable SDHCI_INT_DATA_AVAIL.
Even though the spec clearly mentions that,
"While the tuning sequence is being performed, the Host Controller does not generate interrupts (including Command Complete) except Buffer Read Ready and CMD19 response errors are not indicated."
I think your suggestion would make sense of buggy controllers.
> >
> >
> > +
> > + /*
> > + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the
> number
> > + * of loops reaches 40 times or a timeout of 150ms occurs.
> > + */
> > + timeout = 150;
> > + do {
> > + u16 mode;
> > + struct mmc_command cmd;
> > + struct mmc_request mrq;
> > +
> > + if (!tuning_loop_counter && !timeout)
> > + break;
> > +
> > + memset(&cmd, 0, sizeof(struct mmc_command));
> > + cmd.opcode = MMC_SEND_TUNING_BLOCK;
> > + cmd.arg = 0;
> > + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> > +
> > + memset(&cmd.resp, 0, sizeof(cmd.resp));
> > + cmd.retries = 0;
> > +
> > + cmd.data = NULL;
> > + cmd.error = 0;
> > +
> > + memset(&mrq, 0, sizeof(struct mmc_request));
> > + mrq.cmd = &cmd;
> > + host->mrq = &mrq;
> > +
>
> =====
> > + /* Make sure DMA Enable is set to 0 before tuning */
> > + mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
> > + if (mode & SDHCI_TRNS_DMA) {
> > + mode &= ~SDHCI_TRNS_DMA;
> > + sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
> > + }
> this code should be deleted and moved up.
IMO, it should better stay here. Even though there can be no data transfer commands during tuning procedure, but there can still be commands using CMD lines only, so there is a possibility of interleaving. In that case, it's better to program TRANSFER_MODE register every time before sending CMD19. Please consider re-tuning mode 1.
Thanks,
Arindam
^ permalink raw reply [flat|nested] 22+ messages in thread
* Question about timer in struct sdhci_host
2011-04-28 6:02 ` Nath, Arindam
@ 2011-04-28 8:29 ` Ryan Liu
0 siblings, 0 replies; 22+ messages in thread
From: Ryan Liu @ 2011-04-28 8:29 UTC (permalink / raw)
To: linux-mmc
Hi list,
Maybe some simple questions, but I do not find good answers from google.
The timer is setup in sdhci_add_host, and modified to 10s later in
sdhci_send_command.
1. This timer seems to bu used to indicate that host controller does not
send IRQ to the cpu core(ex. ARM) after 10s since sdhci_send_command. Am
I right?
2. Why is 10s? Isn't it too long? User space applications may produce
large MMC requests to the MMC queue.
Also, cancle the requests will waste much time. The user may think
the device is dead.
Thanks.
Ryan
^ permalink raw reply [flat|nested] 22+ messages in thread
* RE: [PATCH v3 00/11] add support for host controller v3.00
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
` (10 preceding siblings ...)
2011-04-20 9:31 ` [PATCH v3 11/11] mmc: sdhci: add support for retuning mode 1 Arindam Nath
@ 2011-04-29 18:40 ` Subhash Jadavani
11 siblings, 0 replies; 22+ messages in thread
From: Subhash Jadavani @ 2011-04-29 18:40 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: linux-mmc, prakity, zhangfei.gao, henry.su, aaron.lu, anath.amd
Hi Arindam/Chris/All,
Sorry for possible dumb question but I am not much aware about SD licensing
terms. I think SDv3.00 spec (or any other older SD specs) is licensed under
some NDA agreement and then there is also a freely available simplified SD
spec as well. So is there no restriction on open source the drivers
supporting SD3.0 spec? How does this works out without violating the NDA
agreement? Your comments would be helpful to understand this.
Regards,
Subhash
> -----Original Message-----
> From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> Nath
> Sent: Wednesday, April 20, 2011 2:31 AM
> To: cjb@laptop.org
> Cc: linux-mmc@vger.kernel.org; prakity@marvell.com;
> subhashj@codeaurora.org; zhangfei.gao@gmail.com; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v3 00/11] add support for host controller v3.00
>
> V3
> ----
> [01/11]: Set bit 24 and bit 28 of OCR within mmc_sd_get_cid(),
> and only retry sending ACMD41 with bit 24 reset in case
> signal voltage switch procedure fails.
> [01/11]: Change (*rocr & 0x41000000) to ((*rocr & 0x41000000) ==
> 0x41000000) to check for both CCS and S18A to be set in
> the response of ACMD41.
> [01/11]: Change the condition if (err == -EAGAIN) to if (err), in
> order to retry sending ACMD41 because of any error during
> signal voltage switch procedure.
> [01/11]: Add a new variable signal_voltage to struct mmc_ios,
> which holds whether the request is to change to 1.8V or
> 3.3V signalling voltage.
> [02/11]: Remove redundant code to find bus speed modes for SD2.0
> and SD3.0 cards separately.
> [02/11]: Change the variable names from uhs_* to sd3_* to make
> them appropriate to the context of their usage.
> [03/11]: Change variable names from *_set_drv_type to *_drv_type.
> [03/11]: Set driver type even when the default driver type B is
> used.
> [03/11]: Clear bits 05-04 of Host Control 2 register before
> setting the new driver strength.
> [04/11]: Use sdhci_set_clock() to make sure the clock is stable
> before re-enabling SD clock.
> [05/11]: Initialize bus_speed and timing to 0 at the beginning of
> sd_set_bus_speed_mode() to avoid compiler warning.
> [06/11]: Initialize current_limit to 0 to avoid compiler warning.
> [06/11]: Remove usage of get_max_current_180() and replace this
> with MMC_CAP_MAX_CURRENT_*.
> [06/11]: Set the current limit even for the default current limit
> of 200mA.
> [06/11]: Set the current limit only for SDR50, SDR104, and DDR50
> UHS-I modes, otherwise set the default current limit.
> [07/11]: Change mmc_*_ultrahighspeed() to mmc_sd_*_uhs().
> [08/11]: Re-read Host Control 2 register before clearing
> *_TUNED_CLK and *_EXEC_TUNING.
> [08/11]: Make sdhci_execute_tuning() return 'int' rather than
> 'void' so that we can check for error conditions during
> tuning failure.
> [08/11]: Make sure to return 0 for controllers which provide
> support for retuning even if tuning fails. For other
> controllers, return error code.
> [09/11]: Disable using Preset Value when a new card is inserted,
> and enable its use only after a successfull UHS-I
> initializaton.
> [11/11]: Remove sdhci_start_retuning_timer() completely, and start
> the re-tuning timer from within sdhci_execute_tuning()
> the very first time it is executed.
>
> V2
> ----
> [01/12]: Make saved_abort_cmd part of struct sdhci_host rather
> than global variable.
> [01/12]: Clear SDHCI_USE_SDMA _iff_ SDHCI_USE_ADMA is set.
> [01/12]: Set either Auto CMD23 or Auto CMD12, but not both, in
> the Transfer Mode register.
> [02/12]: Check host controller version before reading
> SDHCI_CAPABILITIES_1.
> [02/12]: Remove spinlock from sdhci_start_signal_voltage_switch
> and use usleep_range() rather than mdelay().
> [02/12]: Set S18R in OCR to 1 for all UHS-I modes.
> [02/12]: NULL pointer check for start_signal_voltage_switch().
> [02/12]: Set MMC_CAP_UHS_SDR50 if MMC_CAP_UHS_SDR104 is set.
> [06/12]: Add checking for SDR25 in sd_set_bus_speed_mode().
> [09/12]: Remove checking for MMC_SEND_TUNING_BLOCK within
> sdhci_set_transfer_mode(), since cmd.data is set to
> NULL inside sdhci_execute_tuning().
> [11/12]: Correctly set clk to SDHCI_PROG_CLOCK_MODE when host
> controller supports Programmable Clock Mode.
>
> V1
> ----
> The patches below add support for Host Controller v3.00 as per the
> spec v3.00. It also adds support for UHS-I cards as per Physical
> Layer Specification v3.01.
>
> Thanks for review.
>
> Regards,
> Arindam
>
> Arindam Nath (11):
> [PATCH 01/11] mmc: sd: add support for signal voltage switch
> procedure
> [PATCH 02/11] mmc: sd: query function modes for uhs cards
> [PATCH 03/11] mmc: sd: add support for driver type selection
> [PATCH 04/11] mmc: sdhci: reset sdclk before setting high speed
> enable
> [PATCH 05/11] mmc: sd: add support for uhs bus speed mode selection
> [PATCH 06/11] mmc: sd: set current limit for uhs cards
> [PATCH 07/11] mmc: sd: report correct speed and capacity of uhs cards
> [PATCH 08/11] mmc: sd: add support for tuning during uhs
> initialization
> [PATCH 09/11] mmc: sdhci: enable preset value after uhs
> initialization
> [PATCH 10/11] mmc: sdhci: add support for programmable clock mode
> [PATCH 11/11] mmc: sdhci: add support for retuning mode 1
>
> drivers/mmc/core/bus.c | 11 +-
> drivers/mmc/core/core.c | 9 +
> drivers/mmc/core/core.h | 1 +
> drivers/mmc/core/sd.c | 405 ++++++++++++++++++++++++---
> drivers/mmc/core/sd.h | 3 +-
> drivers/mmc/core/sd_ops.c | 34 +++
> drivers/mmc/core/sd_ops.h | 1 +
> drivers/mmc/core/sdio.c | 3 +-
> drivers/mmc/host/sdhci.c | 689
> ++++++++++++++++++++++++++++++++++++++++++---
> drivers/mmc/host/sdhci.h | 45 +++-
> include/linux/mmc/card.h | 43 +++
> include/linux/mmc/host.h | 36 +++
> include/linux/mmc/mmc.h | 1 +
> include/linux/mmc/sd.h | 1 +
> include/linux/mmc/sdhci.h | 11 +
> 15 files changed, 1211 insertions(+), 82 deletions(-)
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2011-04-29 18:40 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-04-20 9:30 [PATCH v3 00/11] add support for host controller v3.00 Arindam Nath
2011-04-20 9:30 ` [PATCH v3 01/11] mmc: sd: add support for signal voltage switch procedure Arindam Nath
2011-04-27 9:48 ` zhangfei gao
2011-04-27 10:17 ` Nath, Arindam
2011-04-28 3:17 ` zhangfei gao
2011-04-28 5:48 ` Nath, Arindam
2011-04-20 9:30 ` [PATCH v3 02/11] mmc: sd: query function modes for uhs cards Arindam Nath
2011-04-20 9:30 ` [PATCH v3 03/11] mmc: sd: add support for driver type selection Arindam Nath
2011-04-20 9:30 ` [PATCH v3 04/11] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
2011-04-20 9:30 ` [PATCH v3 05/11] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
2011-04-20 9:30 ` [PATCH v3 06/11] mmc: sd: set current limit for uhs cards Arindam Nath
2011-04-20 9:30 ` [PATCH v3 07/11] mmc: sd: report correct speed and capacity of " Arindam Nath
2011-04-20 9:30 ` [PATCH v3 08/11] mmc: sd: add support for tuning during uhs initialization Arindam Nath
2011-04-27 18:44 ` Philip Rakity
2011-04-28 6:02 ` Nath, Arindam
2011-04-28 8:29 ` Question about timer in struct sdhci_host Ryan Liu
2011-04-20 9:30 ` [PATCH v3 09/11] mmc: sdhci: enable preset value after uhs initialization Arindam Nath
2011-04-28 5:16 ` zhangfei gao
2011-04-28 5:50 ` Nath, Arindam
2011-04-20 9:31 ` [PATCH v3 10/11] mmc: sdhci: add support for programmable clock mode Arindam Nath
2011-04-20 9:31 ` [PATCH v3 11/11] mmc: sdhci: add support for retuning mode 1 Arindam Nath
2011-04-29 18:40 ` [PATCH v3 00/11] add support for host controller v3.00 Subhash Jadavani
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).