* [PATCH v4 00/15] add support for host controller v3.00
@ 2011-05-05 6:48 Arindam Nath
2011-05-05 6:48 ` [PATCH v4 01/15] mmc: sd: add support for signal voltage switch procedure Arindam Nath
` (16 more replies)
0 siblings, 17 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:48 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd, Arindam Nath
V4
----
[01/15]: Move the definition of mmc_set_signal_voltage() from
sd_ops.c to core.c so that SDIO and MMC can also make
use of it.
[01/15]: Move mmc_set_signal_voltage(..., MMC_SIGNAL_VOLTAGE_330)
before mmc_send_app_op_cond().
[01/15]: Set 3.3V signaling voltage if ios->signal_voltage is set
to MMC_SIGNAL_VOLTAGE_330.
[01/15]: Provided #defines for SD OCR register bits S18R (bit 24),
XPC (bit 28), and CCS (bit 30).
[08/15]: Enable _only_ Buffer Read Ready interrupt and disable all
other interrupts at the begining of sdhci_execute_tuning(),
and do the reverse before exiting the function.
[08/15]: Set the block size to 64 before tuning.
[08/15]: Make sure Multi Block Select and DMA Enable in the Transfer
Mode register are set to 0 before tuning.
[09/15]: Enable preset value registers _only_ after a successfull
UHS-I initialization.
[12/15]: sdhci pxa add platform specific code for UHS signaling
[13/15]: mmc eMMC signal voltage does not use CMD11
[14/15]: sdhci add hooks for UHS setting by platform specific code
[15/15]: mmc add support for eMMC Dual Data Rate
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/15] mmc: sd: add support for signal voltage switch procedure
[PATCH 02/15] mmc: sd: query function modes for uhs cards
[PATCH 03/15] mmc: sd: add support for driver type selection
[PATCH 04/15] mmc: sdhci: reset sdclk before setting high speed enable
[PATCH 05/15] mmc: sd: add support for uhs bus speed mode selection
[PATCH 06/15] mmc: sd: set current limit for uhs cards
[PATCH 07/15] mmc: sd: report correct speed and capacity of uhs cards
[PATCH 08/15] mmc: sd: add support for tuning during uhs initialization
[PATCH 09/15] mmc: sdhci: enable preset value after uhs initialization
[PATCH 10/15] mmc: sdhci: add support for programmable clock mode
[PATCH 11/15] mmc: sdhci: add support for retuning mode 1
Philip Rakity (4):
[PATCH 12/15] sdhci pxa add platform specific code for UHS signaling
[PATCH 13/15] mmc eMMC signal voltage does not use CMD11
[PATCH 14/15] sdhci add hooks for UHS setting by platform specific code
[PATCH 15/15] mmc add support for eMMC Dual Data Rate
drivers/mmc/core/bus.c | 11 +-
drivers/mmc/core/core.c | 57 +++-
drivers/mmc/core/core.h | 5 +-
drivers/mmc/core/mmc.c | 35 ++-
drivers/mmc/core/sd.c | 405 ++++++++++++++++++++++--
drivers/mmc/core/sd.h | 3 +-
drivers/mmc/core/sdio.c | 3 +-
drivers/mmc/host/sdhci-pxa.c | 36 +++
drivers/mmc/host/sdhci.c | 707 +++++++++++++++++++++++++++++++++++++++---
drivers/mmc/host/sdhci.h | 47 +++-
include/linux/mmc/card.h | 43 +++
include/linux/mmc/host.h | 37 +++
include/linux/mmc/mmc.h | 1 +
include/linux/mmc/sd.h | 7 +
include/linux/mmc/sdhci.h | 11 +
15 files changed, 1307 insertions(+), 101 deletions(-)
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v4 01/15] mmc: sd: add support for signal voltage switch procedure
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
@ 2011-05-05 6:48 ` Arindam Nath
2011-05-06 10:36 ` zhangfei gao
2011-05-11 3:34 ` Chris Ball
2011-05-05 6:48 ` [PATCH v4 02/15] mmc: sd: query function modes for uhs cards Arindam Nath
` (15 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:48 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.com>
---
drivers/mmc/core/core.c | 34 ++++++++
drivers/mmc/core/core.h | 1 +
drivers/mmc/core/sd.c | 36 ++++++++-
drivers/mmc/host/sdhci.c | 193 ++++++++++++++++++++++++++++++++++++++++++---
drivers/mmc/host/sdhci.h | 18 ++++-
include/linux/mmc/host.h | 15 ++++
include/linux/mmc/sd.h | 7 ++
7 files changed, 287 insertions(+), 17 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 1dbc185..ea5c28d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -942,6 +942,40 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
return ocr;
}
+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;
+}
+
/*
* Select timing parameters for host.
*/
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index ca1fdde..7745dea 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -41,6 +41,7 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
unsigned int ddr);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
+int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
static inline void mmc_delay(unsigned int ms)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 6dac89f..b0cd285 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
@@ -420,12 +421,38 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
*/
err = mmc_send_if_cond(host, ocr);
if (!err)
- ocr |= 1 << 30;
+ ocr |= SD_OCR_CCS;
- 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 |= SD_OCR_S18R;
+
+ /* 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 |= SD_OCR_XPC;
+
+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 &= ~SD_OCR_S18R;
+ goto try_again;
+ }
+ }
+
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
@@ -773,6 +800,11 @@ int mmc_attach_sd(struct mmc_host *host)
BUG_ON(!host);
WARN_ON(!host->claimed);
+ /* Make sure we are at 3.3V signalling voltage */
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
+ if (err)
+ return err;
+
err = mmc_send_app_op_cond(host, 0, &ocr);
if (err)
return err;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index e5cfe70..52faa50 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",
@@ -1323,11 +1325,115 @@ 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 (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,
};
/*****************************************************************************\
@@ -1808,7 +1914,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);
@@ -1831,12 +1939,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;
@@ -1847,7 +1958,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) &&
@@ -1897,10 +2009,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;
@@ -1916,7 +2028,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);
@@ -1928,7 +2040,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;
/*
@@ -1955,21 +2067,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)
@@ -2029,7 +2196,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 c6e25a7..5cba2fe 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..c648878 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 */
@@ -32,6 +33,12 @@
#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
#define SD_APP_SEND_SCR 51 /* adtc R1 */
+/* OCR bit definitions */
+#define SD_OCR_S18R (1 << 24) /* 1.8V switching request */
+#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */
+#define SD_OCR_XPC (1 << 28) /* SDXC power control */
+#define SD_OCR_CCS (1 << 30) /* Card Capacity Status */
+
/*
* SD_SWITCH argument format:
*
--
1.7.1
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v4 02/15] mmc: sd: query function modes for uhs cards
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
2011-05-05 6:48 ` [PATCH v4 01/15] mmc: sd: add support for signal voltage switch procedure Arindam Nath
@ 2011-05-05 6:48 ` Arindam Nath
2011-05-06 10:37 ` zhangfei gao
2011-05-11 3:34 ` Chris Ball
2011-05-05 6:48 ` [PATCH v4 03/15] mmc: sd: add support for driver type selection Arindam Nath
` (14 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:48 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.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 b0cd285..d4410c9 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] 54+ messages in thread
* [PATCH v4 03/15] mmc: sd: add support for driver type selection
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
2011-05-05 6:48 ` [PATCH v4 01/15] mmc: sd: add support for signal voltage switch procedure Arindam Nath
2011-05-05 6:48 ` [PATCH v4 02/15] mmc: sd: query function modes for uhs cards Arindam Nath
@ 2011-05-05 6:48 ` Arindam Nath
2011-05-06 10:37 ` zhangfei gao
2011-05-11 3:34 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 04/15] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
` (13 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:48 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.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 ea5c28d..78a9b51 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -986,6 +986,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 7745dea..93f3397 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -43,6 +43,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
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 d4410c9..a6fa52f 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 |= SD_OCR_XPC;
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 &= ~SD_OCR_S18R;
@@ -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 & SD_ROCR_S18A) {
+ 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 52faa50..9f38317 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));
@@ -220,18 +220,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
@@ -803,14 +803,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)) {
@@ -1218,7 +1218,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)
@@ -1231,10 +1231,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)
@@ -1243,7 +1243,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,
@@ -2087,6 +2106,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 5cba2fe..04c41a4 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] 54+ messages in thread
* [PATCH v4 04/15] mmc: sdhci: reset sdclk before setting high speed enable
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (2 preceding siblings ...)
2011-05-05 6:48 ` [PATCH v4 03/15] mmc: sd: add support for driver type selection Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-06 10:38 ` zhangfei gao
2011-05-11 3:36 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 05/15] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
` (12 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.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 9f38317..309240c 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1243,13 +1243,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.
@@ -1261,8 +1260,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] 54+ messages in thread
* [PATCH v4 05/15] mmc: sd: add support for uhs bus speed mode selection
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (3 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 04/15] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-06 10:41 ` zhangfei gao
2011-05-11 3:36 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 06/15] mmc: sd: set current limit for uhs cards Arindam Nath
` (11 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.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 a6fa52f..0491978 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -457,6 +457,66 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
return 0;
}
+static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
+{
+ unsigned int bus_speed = 0, timing = 0;
+ int err;
+
+ /*
+ * If the host doesn't support any of the UHS-I modes, fallback on
+ * default speed.
+ */
+ if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
+ return 0;
+
+ if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
+ bus_speed = UHS_SDR104_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR104;
+ card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
+ } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
+ bus_speed = UHS_DDR50_BUS_SPEED;
+ timing = MMC_TIMING_UHS_DDR50;
+ card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+ } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+ MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
+ SD_MODE_UHS_SDR50)) {
+ bus_speed = UHS_SDR50_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR50;
+ card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
+ } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
+ bus_speed = UHS_SDR25_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR25;
+ card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+ } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
+ MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
+ SD_MODE_UHS_SDR12)) {
+ bus_speed = UHS_SDR12_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR12;
+ card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
+ }
+
+ card->sd_bus_speed = bus_speed;
+ err = mmc_sd_switch(card, 1, 0, bus_speed, status);
+ if (err)
+ return err;
+
+ if ((status[16] & 0xF) != bus_speed)
+ printk(KERN_WARNING "%s: Problem setting bus speed mode!\n",
+ mmc_hostname(card->host));
+ else {
+ mmc_set_timing(card->host, timing);
+ mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
+ }
+
+ return 0;
+}
+
/*
* UHS-I specific initialization procedure
*/
@@ -490,6 +550,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
/* Set the driver strength for the card */
err = sd_select_driver_type(card, status);
+ if (err)
+ goto out;
+
+ /* Set bus speed mode of the card */
+ err = sd_set_bus_speed_mode(card, status);
out:
kfree(status);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 309240c..de7e6e9 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1244,7 +1244,16 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
ctrl &= ~SDHCI_CTRL_HISPD;
if (host->version >= SDHCI_SPEC_300) {
- u16 ctrl_2;
+ u16 clk, ctrl_2;
+ unsigned int clock;
+
+ /* In case of UHS-I modes, set High Speed Enable */
+ if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
+ (ios->timing == MMC_TIMING_UHS_SDR104) ||
+ (ios->timing == MMC_TIMING_UHS_DDR50) ||
+ (ios->timing == MMC_TIMING_UHS_SDR25) ||
+ (ios->timing == MMC_TIMING_UHS_SDR12))
+ ctrl |= SDHCI_CTRL_HISPD;
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
@@ -1267,8 +1276,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* need to reset SD Clock Enable before changing High
* Speed Enable to avoid generating clock gliches.
*/
- u16 clk;
- unsigned int clock;
/* Reset SD Clock Enable */
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
@@ -1282,6 +1289,33 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
host->clock = 0;
sdhci_set_clock(host, clock);
}
+
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+ /* Select Bus Speed Mode for host */
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+ if (ios->timing == MMC_TIMING_UHS_SDR12)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+ else if (ios->timing == MMC_TIMING_UHS_SDR25)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+ else if (ios->timing == MMC_TIMING_UHS_SDR50)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+ else if (ios->timing == MMC_TIMING_UHS_SDR104)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+ else if (ios->timing == MMC_TIMING_UHS_DDR50)
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+
+ /* Reset SD Clock Enable */
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+
+ /* Re-enable SD Clock */
+ clock = host->clock;
+ host->clock = 0;
+ sdhci_set_clock(host, clock);
} else
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 04c41a4..5b12793 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -149,6 +149,12 @@
#define SDHCI_ACMD12_ERR 0x3C
#define SDHCI_HOST_CONTROL2 0x3E
+#define SDHCI_CTRL_UHS_MASK 0x0007
+#define SDHCI_CTRL_UHS_SDR12 0x0000
+#define SDHCI_CTRL_UHS_SDR25 0x0001
+#define SDHCI_CTRL_UHS_SDR50 0x0002
+#define SDHCI_CTRL_UHS_SDR104 0x0003
+#define SDHCI_CTRL_UHS_DDR50 0x0004
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 5393272..4ef6ded 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -81,7 +81,24 @@ struct sd_ssr {
struct sd_switch_caps {
unsigned int hs_max_dtr;
+ unsigned int uhs_max_dtr;
+#define UHS_SDR104_MAX_DTR 208000000
+#define UHS_SDR50_MAX_DTR 100000000
+#define UHS_DDR50_MAX_DTR 50000000
+#define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR
+#define UHS_SDR12_MAX_DTR 25000000
unsigned int sd3_bus_mode;
+#define UHS_SDR12_BUS_SPEED 0
+#define UHS_SDR25_BUS_SPEED 1
+#define UHS_SDR50_BUS_SPEED 2
+#define UHS_SDR104_BUS_SPEED 3
+#define UHS_DDR50_BUS_SPEED 4
+
+#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
+#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
+#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
+#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
+#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
unsigned int sd3_drv_type;
#define SD_DRIVER_TYPE_B 0x01
#define SD_DRIVER_TYPE_A 0x02
@@ -166,6 +183,8 @@ struct mmc_card {
const char **info; /* info strings */
struct sdio_func_tuple *tuples; /* unknown common tuples */
+ unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
+
struct dentry *debugfs_root;
};
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 949e4d5..6237599 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -50,6 +50,11 @@ struct mmc_ios {
#define MMC_TIMING_LEGACY 0
#define MMC_TIMING_MMC_HS 1
#define MMC_TIMING_SD_HS 2
+#define MMC_TIMING_UHS_SDR12 MMC_TIMING_LEGACY
+#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
+#define MMC_TIMING_UHS_SDR50 3
+#define MMC_TIMING_UHS_SDR104 4
+#define MMC_TIMING_UHS_DDR50 5
unsigned char ddr; /* dual data rate used */
--
1.7.1
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v4 06/15] mmc: sd: set current limit for uhs cards
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (4 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 05/15] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-06 10:38 ` zhangfei gao
2011-05-11 3:37 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 07/15] mmc: sd: report correct speed and capacity of " Arindam Nath
` (10 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.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 0491978..9270d82 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 de7e6e9..9176911 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2217,6 +2217,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] 54+ messages in thread
* [PATCH v4 07/15] mmc: sd: report correct speed and capacity of uhs cards
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (5 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 06/15] mmc: sd: set current limit for uhs cards Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-06 10:39 ` zhangfei gao
2011-05-11 3:37 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 08/15] mmc: sd: add support for tuning during uhs initialization Arindam Nath
` (9 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.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 9270d82..ee32dd8 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] 54+ messages in thread
* [PATCH v4 08/15] mmc: sd: add support for tuning during uhs initialization
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (6 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 07/15] mmc: sd: report correct speed and capacity of " Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-06 10:39 ` zhangfei gao
2011-05-11 3:38 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 09/15] mmc: sdhci: enable preset value after " Arindam Nath
` (8 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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 program the block size to 64 in the
Block Size register. We make sure that DMA Enable and Multi Block
Select in the Transfer Mode register are 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 and re-enable other interrupts.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
Acked-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.com>
---
drivers/mmc/core/sd.c | 6 ++
drivers/mmc/host/sdhci.c | 173 ++++++++++++++++++++++++++++++++++++++++++++-
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, 187 insertions(+), 1 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ee32dd8..84cb8c3 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 9176911..85775cf 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 *);
@@ -968,7 +970,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);
@@ -1502,12 +1506,162 @@ 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);
+
+ 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.
+ *
+ * Note: The spec clearly says that when tuning sequence
+ * is being performed, the controller does not generate
+ * interrupts other than Buffer Read Ready interrupt. But
+ * to make sure we don't hit a controller bug, we _only_
+ * enable Buffer Read Ready interrupt here.
+ */
+ ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+ sdhci_clear_set_irqs(host, ier, 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 {
+ 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;
+
+ /*
+ * In response to CMD19, the card sends 64 bytes of tuning
+ * block to the Host Controller. So we set the block size
+ * to 64 here.
+ */
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
+
+ /*
+ * The tuning block is sent by the card to the host controller.
+ * So we set the TRNS_READ bit in the Transfer Mode register.
+ * This also takes care of setting DMA Enable and Multi Block
+ * Select in the same register to 0.
+ */
+ sdhci_writew(host, SDHCI_TRNS_READ, 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 ": Timeout waiting for "
+ "Buffer Read Ready interrupt during tuning "
+ "procedure, 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_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,
};
/*****************************************************************************\
@@ -1725,6 +1879,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
@@ -2161,6 +2325,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;
@@ -2314,6 +2482,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 5b12793..b32fc32 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 92e1c9a..b74c853 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -111,6 +111,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 */
@@ -147,6 +148,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] 54+ messages in thread
* [PATCH v4 09/15] mmc: sdhci: enable preset value after uhs initialization
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (7 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 08/15] mmc: sd: add support for tuning during uhs initialization Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-06 10:40 ` zhangfei gao
2011-05-11 3:38 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 10/15] mmc: sdhci: add support for programmable clock mode Arindam Nath
` (7 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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.
Since Preset Value Enable makes sense only for UHS-I cards, we enable this
feature after successfull UHS-I initialization. We also reset Preset Value
Enable next time before initialization.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.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 84cb8c3..851c8fc 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -931,6 +931,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
/* Card is an ultra-high-speed card */
mmc_sd_card_set_uhs(card);
+
+ /*
+ * Since initialization is now complete, enable preset
+ * value registers for UHS-I cards.
+ */
+ if (host->ops->enable_preset_value)
+ host->ops->enable_preset_value(host, 1);
} else {
/*
* Attempt to change to high-speed (if supported)
@@ -1101,6 +1108,10 @@ int mmc_attach_sd(struct mmc_host *host)
if (err)
return err;
+ /* Disable preset value enable if already set since last time */
+ if (host->ops->enable_preset_value)
+ host->ops->enable_preset_value(host, 0);
+
err = mmc_send_app_op_cond(host, 0, &ocr);
if (err)
return err;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 85775cf..cb767a0 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1655,6 +1655,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,
@@ -1662,6 +1693,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] 54+ messages in thread
* [PATCH v4 10/15] mmc: sdhci: add support for programmable clock mode
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (8 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 09/15] mmc: sdhci: enable preset value after " Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-06 10:40 ` zhangfei gao
2011-05-11 3:39 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 11/15] mmc: sdhci: add support for retuning mode 1 Arindam Nath
` (6 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.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 cb767a0..8ed2e1b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1013,8 +1013,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)
@@ -1032,14 +1032,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. */
@@ -1047,10 +1078,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;
@@ -2314,17 +2345,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 b32fc32..0208164 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 b74c853..a88a799 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -117,6 +117,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] 54+ messages in thread
* [PATCH v4 11/15] mmc: sdhci: add support for retuning mode 1
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (9 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 10/15] mmc: sdhci: add support for programmable clock mode Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-06 10:40 ` zhangfei gao
2011-05-11 3:39 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling Arindam Nath
` (5 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, 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>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.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 8ed2e1b..3c04547 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)
{
@@ -1206,8 +1208,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);
@@ -1679,6 +1701,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_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
spin_unlock(&host->lock);
enable_irq(host->irq);
@@ -1781,6 +1834,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;
/*
@@ -1854,6 +1911,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 *
@@ -2128,6 +2199,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;
@@ -2169,6 +2249,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;
}
@@ -2420,6 +2506,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
@@ -2565,9 +2666,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)
@@ -2661,6 +2768,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 0208164..fb74dc6 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 a88a799..e902618 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -112,6 +112,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 */
@@ -152,6 +153,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] 54+ messages in thread
* [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (10 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 11/15] mmc: sdhci: add support for retuning mode 1 Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-11 1:54 ` Chris Ball
2011-05-11 8:48 ` zhangfei gao
2011-05-05 6:49 ` [PATCH v4 13/15] mmc eMMC signal voltage does not use CMD11 Arindam Nath
` (4 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd, Arindam Nath
Marvell controller requires 1.8V bit in UHS control register 2
be set when doing UHS. eMMC does not require 1.8V for DDR.
add platform code to handle this.
Signed-off-by: Philip Rakity <prakity@marvell.com>
Reviewed-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/host/sdhci-pxa.c | 36 ++++++++++++++++++++++++++++++++++++
1 files changed, 36 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
index 5a61208..b52c3e6 100644
--- a/drivers/mmc/host/sdhci-pxa.c
+++ b/drivers/mmc/host/sdhci-pxa.c
@@ -69,7 +69,40 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
}
}
+static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
+{
+ u16 ctrl_2;
+
+ /*
+ * Set V18_EN -- UHS modes do not work without this.
+ * does not change signaling voltage
+ */
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+ /* Select Bus Speed Mode for host */
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+ if (uhs == MMC_TIMING_UHS_SDR12)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+ else if (uhs == MMC_TIMING_UHS_SDR25)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+ else if (uhs == MMC_TIMING_UHS_SDR50) {
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+ ctrl_2 |= SDHCI_CTRL_VDD_180;
+ } else if (uhs == MMC_TIMING_UHS_SDR104) {
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+ ctrl_2 |= SDHCI_CTRL_VDD_180;
+ } else if (uhs == MMC_TIMING_UHS_DDR50) {
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+ ctrl_2 |= SDHCI_CTRL_VDD_180;
+ }
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+ pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
+ __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
+ return 0;
+}
+
static struct sdhci_ops sdhci_pxa_ops = {
+ .set_uhs_signaling = set_uhs_signaling,
.set_clock = set_clock,
};
@@ -141,6 +174,9 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
if (pdata->quirks)
host->quirks |= pdata->quirks;
+ /* enable 1/8V DDR capable */
+ host->mmc->caps |= MMC_CAP_1_8V_DDR;
+
/* If slot design supports 8 bit data, indicate this to MMC. */
if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
host->mmc->caps |= MMC_CAP_8_BIT_DATA;
--
1.7.1
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v4 13/15] mmc eMMC signal voltage does not use CMD11
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (11 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-11 1:51 ` Chris Ball
2011-05-11 9:19 ` zhangfei gao
2011-05-05 6:49 ` [PATCH v4 14/15] sdhci add hooks for UHS setting by platform specific code Arindam Nath
` (3 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd, Arindam Nath
eMMC chips do not use CMD11 when changing voltage. Add extra
argument to call to indicate if CMD11 needs to be sent.
Signed-off-by: Philip Rakity <prakity@marvell.com>
Reviewed-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/core.c | 4 ++--
drivers/mmc/core/core.h | 3 ++-
drivers/mmc/core/sd.c | 4 ++--
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 78a9b51..74b4409 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -942,7 +942,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
return ocr;
}
-int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
+int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, int cmd11)
{
struct mmc_command cmd;
int err = 0;
@@ -953,7 +953,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
* Send CMD11 only if the request is to switch the card to
* 1.8V signalling.
*/
- if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+ if ((signal_voltage != MMC_SIGNAL_VOLTAGE_330) && cmd11) {
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_SWITCH_VOLTAGE;
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 93f3397..3c11e17 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -41,7 +41,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
unsigned int ddr);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
-int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
+int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
+ int cmd11);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 851c8fc..d2a05ab 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -731,7 +731,7 @@ try_again:
*/
if (!mmc_host_is_spi(host) && rocr &&
((*rocr & 0x41000000) == 0x41000000)) {
- err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, 1);
if (err) {
ocr &= ~SD_OCR_S18R;
goto try_again;
@@ -1104,7 +1104,7 @@ int mmc_attach_sd(struct mmc_host *host)
WARN_ON(!host->claimed);
/* Make sure we are at 3.3V signalling voltage */
- err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);
if (err)
return err;
--
1.7.1
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v4 14/15] sdhci add hooks for UHS setting by platform specific code
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (12 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 13/15] mmc eMMC signal voltage does not use CMD11 Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-05 6:49 ` [PATCH v4 15/15] mmc add support for eMMC Dual Data Rate Arindam Nath
` (2 subsequent siblings)
16 siblings, 0 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd, Arindam Nath
Allow platform specific code to set UHS registers if
implementation requires speciial platform specific handling
Signed-off-by: Philip Rakity <prakity@marvell.com>
Reviewed-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/host/sdhci.c | 33 ++++++++++++++++++---------------
drivers/mmc/host/sdhci.h | 2 ++
2 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 3c04547..c46561c 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1347,27 +1347,30 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
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);
+ if (host->ops->set_uhs_signaling)
+ host->ops->set_uhs_signaling(host, ios->timing);
+ else {
+ 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;
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+ }
/* Re-enable SD Clock */
clock = host->clock;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index fb74dc6..55e6f9a 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -270,6 +270,8 @@ struct sdhci_ops {
unsigned int (*get_ro)(struct sdhci_host *host);
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
+ int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
+
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
--
1.7.1
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v4 15/15] mmc add support for eMMC Dual Data Rate
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (13 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 14/15] sdhci add hooks for UHS setting by platform specific code Arindam Nath
@ 2011-05-05 6:49 ` Arindam Nath
2011-05-05 8:18 ` [PATCH v4 00/15] add support for host controller v3.00 Nath, Arindam
2011-05-11 3:43 ` Chris Ball
16 siblings, 0 replies; 54+ messages in thread
From: Arindam Nath @ 2011-05-05 6:49 UTC (permalink / raw)
To: cjb
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd, Arindam Nath
eMMC voltage change not required for 1.8V. 3.3V and 1.8V vcc
are capable of doing DDR. vccq of 1.8v is not required.
Signed-off-by: Philip Rakity <prakity@marvell.com>
Reviewed-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/core.c | 14 ++------------
drivers/mmc/core/core.h | 2 --
drivers/mmc/core/mmc.c | 35 ++++++++++++++++++++++++++++++-----
include/linux/mmc/host.h | 1 +
4 files changed, 33 insertions(+), 19 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 74b4409..f4c5ac2 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -718,22 +718,12 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
}
/*
- * Change data bus width and DDR mode of a host.
- */
-void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
- unsigned int ddr)
-{
- host->ios.bus_width = width;
- host->ios.ddr = ddr;
- mmc_set_ios(host);
-}
-
-/*
* Change data bus width of a host.
*/
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
- mmc_set_bus_width_ddr(host, width, MMC_SDR_MODE);
+ host->ios.bus_width = width;
+ mmc_set_ios(host);
}
/**
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 3c11e17..51b2f5b 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -38,8 +38,6 @@ void mmc_ungate_clock(struct mmc_host *host);
void mmc_set_ungated(struct mmc_host *host);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
-void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
- unsigned int ddr);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
int cmd11);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index a2c795e..0433fe6 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -20,6 +20,7 @@
#include "core.h"
#include "bus.h"
#include "mmc_ops.h"
+#include "sd_ops.h"
static const unsigned int tran_exp[] = {
10000, 100000, 1000000, 10000000,
@@ -633,10 +634,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/
if (mmc_card_highspeed(card)) {
if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
- && (host->caps & (MMC_CAP_1_8V_DDR)))
+ && ((host->caps & (MMC_CAP_1_8V_DDR |
+ MMC_CAP_UHS_DDR50))
+ == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))
ddr = MMC_1_8V_DDR_MODE;
else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
- && (host->caps & (MMC_CAP_1_2V_DDR)))
+ && ((host->caps & (MMC_CAP_1_2V_DDR |
+ MMC_CAP_UHS_DDR50))
+ == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))
ddr = MMC_1_2V_DDR_MODE;
}
@@ -670,8 +675,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
ext_csd_bits[idx][0],
0);
if (!err) {
- mmc_set_bus_width_ddr(card->host,
- bus_width, MMC_SDR_MODE);
+ mmc_set_bus_width(card->host, bus_width);
/*
* If controller can't handle bus width test,
* use the highest bus width to maintain
@@ -697,8 +701,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
1 << bus_width, ddr);
goto free_card;
} else if (ddr) {
+ /*
+ * eMMC cards can support 3.3V to 1.2V i/o (vccq)
+ * signaling.
+ *
+ * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq.
+ *
+ * 1.8V vccq at 3.3V core voltage (vcc) is not required
+ * in the JEDEC spec for DDR.
+ *
+ * Do not force change in vccq since we are obviously
+ * working and no change to vccq is needed.
+ *
+ * WARNING: eMMC rules are NOT the same as SD DDR
+ */
+ if (ddr == EXT_CSD_CARD_TYPE_DDR_1_2V) {
+ err = mmc_set_signal_voltage(host,
+ MMC_SIGNAL_VOLTAGE_120, 0);
+ if (err)
+ goto err;
+ }
mmc_card_set_ddr_mode(card);
- mmc_set_bus_width_ddr(card->host, bus_width, ddr);
+ mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
+ mmc_set_bus_width(card->host, bus_width);
}
}
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 2209e01..dcae3b4 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -66,6 +66,7 @@ struct mmc_ios {
#define MMC_SIGNAL_VOLTAGE_330 0
#define MMC_SIGNAL_VOLTAGE_180 1
+#define MMC_SIGNAL_VOLTAGE_120 2
unsigned char drv_type; /* driver type (A, B, C, D) */
--
1.7.1
^ permalink raw reply related [flat|nested] 54+ messages in thread
* RE: [PATCH v4 00/15] add support for host controller v3.00
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (14 preceding siblings ...)
2011-05-05 6:49 ` [PATCH v4 15/15] mmc add support for eMMC Dual Data Rate Arindam Nath
@ 2011-05-05 8:18 ` Nath, Arindam
2011-05-11 3:43 ` Chris Ball
16 siblings, 0 replies; 54+ messages in thread
From: Nath, Arindam @ 2011-05-05 8:18 UTC (permalink / raw)
To: cjb@laptop.org
Cc: prakity@marvell.com, zhangfei.gao@gmail.com,
subhashj@codeaurora.org, linux-mmc@vger.kernel.org, Su, Henry,
Lu, Aaron, anath.amd@gmail.com
Hi All,
The patches already have one Tested-by and Reviewed-by from Philip. Would someone else also like to give their approval?
Thanks,
Arindam
> -----Original Message-----
> From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> Nath
> Sent: Thursday, May 05, 2011 12:19 PM
> To: cjb@laptop.org
> Cc: prakity@marvell.com; zhangfei.gao@gmail.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; Su, Henry; Lu,
> Aaron; anath.amd@gmail.com; Nath, Arindam
> Subject: [PATCH v4 00/15] add support for host controller v3.00
>
> V4
> ----
> [01/15]: Move the definition of mmc_set_signal_voltage() from
> sd_ops.c to core.c so that SDIO and MMC can also make
> use of it.
> [01/15]: Move mmc_set_signal_voltage(..., MMC_SIGNAL_VOLTAGE_330)
> before mmc_send_app_op_cond().
> [01/15]: Set 3.3V signaling voltage if ios->signal_voltage is set
> to MMC_SIGNAL_VOLTAGE_330.
> [01/15]: Provided #defines for SD OCR register bits S18R (bit 24),
> XPC (bit 28), and CCS (bit 30).
> [08/15]: Enable _only_ Buffer Read Ready interrupt and disable all
> other interrupts at the begining of sdhci_execute_tuning(),
> and do the reverse before exiting the function.
> [08/15]: Set the block size to 64 before tuning.
> [08/15]: Make sure Multi Block Select and DMA Enable in the Transfer
> Mode register are set to 0 before tuning.
> [09/15]: Enable preset value registers _only_ after a successfull
> UHS-I initialization.
>
>
> [12/15]: sdhci pxa add platform specific code for UHS signaling
> [13/15]: mmc eMMC signal voltage does not use CMD11
> [14/15]: sdhci add hooks for UHS setting by platform specific code
> [15/15]: mmc add support for eMMC Dual Data Rate
>
> 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/15] mmc: sd: add support for signal voltage switch
> procedure
> [PATCH 02/15] mmc: sd: query function modes for uhs cards
> [PATCH 03/15] mmc: sd: add support for driver type selection
> [PATCH 04/15] mmc: sdhci: reset sdclk before setting high speed
> enable
> [PATCH 05/15] mmc: sd: add support for uhs bus speed mode selection
> [PATCH 06/15] mmc: sd: set current limit for uhs cards
> [PATCH 07/15] mmc: sd: report correct speed and capacity of uhs cards
> [PATCH 08/15] mmc: sd: add support for tuning during uhs
> initialization
> [PATCH 09/15] mmc: sdhci: enable preset value after uhs
> initialization
> [PATCH 10/15] mmc: sdhci: add support for programmable clock mode
> [PATCH 11/15] mmc: sdhci: add support for retuning mode 1
>
> Philip Rakity (4):
> [PATCH 12/15] sdhci pxa add platform specific code for UHS signaling
> [PATCH 13/15] mmc eMMC signal voltage does not use CMD11
> [PATCH 14/15] sdhci add hooks for UHS setting by platform specific
> code
> [PATCH 15/15] mmc add support for eMMC Dual Data Rate
>
> drivers/mmc/core/bus.c | 11 +-
> drivers/mmc/core/core.c | 57 +++-
> drivers/mmc/core/core.h | 5 +-
> drivers/mmc/core/mmc.c | 35 ++-
> drivers/mmc/core/sd.c | 405 ++++++++++++++++++++++--
> drivers/mmc/core/sd.h | 3 +-
> drivers/mmc/core/sdio.c | 3 +-
> drivers/mmc/host/sdhci-pxa.c | 36 +++
> drivers/mmc/host/sdhci.c | 707
> +++++++++++++++++++++++++++++++++++++++---
> drivers/mmc/host/sdhci.h | 47 +++-
> include/linux/mmc/card.h | 43 +++
> include/linux/mmc/host.h | 37 +++
> include/linux/mmc/mmc.h | 1 +
> include/linux/mmc/sd.h | 7 +
> include/linux/mmc/sdhci.h | 11 +
> 15 files changed, 1307 insertions(+), 101 deletions(-)
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 01/15] mmc: sd: add support for signal voltage switch procedure
2011-05-05 6:48 ` [PATCH v4 01/15] mmc: sd: add support for signal voltage switch procedure Arindam Nath
@ 2011-05-06 10:36 ` zhangfei gao
2011-05-11 3:34 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:36 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:48 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> drivers/mmc/core/core.c | 34 ++++++++
> drivers/mmc/core/core.h | 1 +
> drivers/mmc/core/sd.c | 36 ++++++++-
> drivers/mmc/host/sdhci.c | 193 ++++++++++++++++++++++++++++++++++++++++++---
> drivers/mmc/host/sdhci.h | 18 ++++-
> include/linux/mmc/host.h | 15 ++++
> include/linux/mmc/sd.h | 7 ++
> 7 files changed, 287 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 1dbc185..ea5c28d 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -942,6 +942,40 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
> return ocr;
> }
>
> +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;
> +}
> +
> /*
> * Select timing parameters for host.
> */
> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> index ca1fdde..7745dea 100644
> --- a/drivers/mmc/core/core.h
> +++ b/drivers/mmc/core/core.h
> @@ -41,6 +41,7 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
> void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
> unsigned int ddr);
> u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
> void mmc_set_timing(struct mmc_host *host, unsigned int timing);
>
> static inline void mmc_delay(unsigned int ms)
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 6dac89f..b0cd285 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
> @@ -420,12 +421,38 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> */
> err = mmc_send_if_cond(host, ocr);
> if (!err)
> - ocr |= 1 << 30;
> + ocr |= SD_OCR_CCS;
>
> - 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 |= SD_OCR_S18R;
> +
> + /* 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 |= SD_OCR_XPC;
> +
> +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 &= ~SD_OCR_S18R;
> + goto try_again;
> + }
> + }
> +
> if (mmc_host_is_spi(host))
> err = mmc_send_cid(host, cid);
> else
> @@ -773,6 +800,11 @@ int mmc_attach_sd(struct mmc_host *host)
> BUG_ON(!host);
> WARN_ON(!host->claimed);
>
> + /* Make sure we are at 3.3V signalling voltage */
> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
> + if (err)
> + return err;
> +
> err = mmc_send_app_op_cond(host, 0, &ocr);
> if (err)
> return err;
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index e5cfe70..52faa50 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",
> @@ -1323,11 +1325,115 @@ 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 (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,
> };
>
> /*****************************************************************************\
> @@ -1808,7 +1914,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);
> @@ -1831,12 +1939,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;
> @@ -1847,7 +1958,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) &&
> @@ -1897,10 +2009,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;
> @@ -1916,7 +2028,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);
> @@ -1928,7 +2040,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;
>
> /*
> @@ -1955,21 +2067,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)
> @@ -2029,7 +2196,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 c6e25a7..5cba2fe 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..c648878 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 */
> @@ -32,6 +33,12 @@
> #define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
> #define SD_APP_SEND_SCR 51 /* adtc R1 */
>
> +/* OCR bit definitions */
> +#define SD_OCR_S18R (1 << 24) /* 1.8V switching request */
> +#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */
> +#define SD_OCR_XPC (1 << 28) /* SDXC power control */
> +#define SD_OCR_CCS (1 << 30) /* Card Capacity Status */
> +
> /*
> * SD_SWITCH argument format:
> *
> --
> 1.7.1
>
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 02/15] mmc: sd: query function modes for uhs cards
2011-05-05 6:48 ` [PATCH v4 02/15] mmc: sd: query function modes for uhs cards Arindam Nath
@ 2011-05-06 10:37 ` zhangfei gao
2011-05-11 3:34 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:37 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:48 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> 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 b0cd285..d4410c9 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 [flat|nested] 54+ messages in thread
* Re: [PATCH v4 03/15] mmc: sd: add support for driver type selection
2011-05-05 6:48 ` [PATCH v4 03/15] mmc: sd: add support for driver type selection Arindam Nath
@ 2011-05-06 10:37 ` zhangfei gao
2011-05-11 3:34 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:37 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:48 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> 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 ea5c28d..78a9b51 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -986,6 +986,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 7745dea..93f3397 100644
> --- a/drivers/mmc/core/core.h
> +++ b/drivers/mmc/core/core.h
> @@ -43,6 +43,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
> u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
> 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 d4410c9..a6fa52f 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 |= SD_OCR_XPC;
>
> 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 &= ~SD_OCR_S18R;
> @@ -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 & SD_ROCR_S18A) {
> + 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 52faa50..9f38317 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));
> @@ -220,18 +220,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
> @@ -803,14 +803,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)) {
> @@ -1218,7 +1218,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)
> @@ -1231,10 +1231,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)
> @@ -1243,7 +1243,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,
> @@ -2087,6 +2106,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 5cba2fe..04c41a4 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 [flat|nested] 54+ messages in thread
* Re: [PATCH v4 04/15] mmc: sdhci: reset sdclk before setting high speed enable
2011-05-05 6:49 ` [PATCH v4 04/15] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
@ 2011-05-06 10:38 ` zhangfei gao
2011-05-11 3:36 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:38 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> 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 9f38317..309240c 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1243,13 +1243,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.
> @@ -1261,8 +1260,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 [flat|nested] 54+ messages in thread
* Re: [PATCH v4 06/15] mmc: sd: set current limit for uhs cards
2011-05-05 6:49 ` [PATCH v4 06/15] mmc: sd: set current limit for uhs cards Arindam Nath
@ 2011-05-06 10:38 ` zhangfei gao
2011-05-11 3:37 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:38 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> 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 0491978..9270d82 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 de7e6e9..9176911 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -2217,6 +2217,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 [flat|nested] 54+ messages in thread
* Re: [PATCH v4 07/15] mmc: sd: report correct speed and capacity of uhs cards
2011-05-05 6:49 ` [PATCH v4 07/15] mmc: sd: report correct speed and capacity of " Arindam Nath
@ 2011-05-06 10:39 ` zhangfei gao
2011-05-11 3:37 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:39 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> 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 9270d82..ee32dd8 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 [flat|nested] 54+ messages in thread
* Re: [PATCH v4 08/15] mmc: sd: add support for tuning during uhs initialization
2011-05-05 6:49 ` [PATCH v4 08/15] mmc: sd: add support for tuning during uhs initialization Arindam Nath
@ 2011-05-06 10:39 ` zhangfei gao
2011-05-11 3:38 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:39 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> 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 program the block size to 64 in the
> Block Size register. We make sure that DMA Enable and Multi Block
> Select in the Transfer Mode register are 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 and re-enable other interrupts.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> Acked-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> drivers/mmc/core/sd.c | 6 ++
> drivers/mmc/host/sdhci.c | 173 ++++++++++++++++++++++++++++++++++++++++++++-
> 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, 187 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index ee32dd8..84cb8c3 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 9176911..85775cf 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 *);
> @@ -968,7 +970,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);
> @@ -1502,12 +1506,162 @@ 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);
> +
> + 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.
> + *
> + * Note: The spec clearly says that when tuning sequence
> + * is being performed, the controller does not generate
> + * interrupts other than Buffer Read Ready interrupt. But
> + * to make sure we don't hit a controller bug, we _only_
> + * enable Buffer Read Ready interrupt here.
> + */
> + ier = sdhci_readl(host, SDHCI_INT_ENABLE);
> + sdhci_clear_set_irqs(host, ier, 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 {
> + 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;
> +
> + /*
> + * In response to CMD19, the card sends 64 bytes of tuning
> + * block to the Host Controller. So we set the block size
> + * to 64 here.
> + */
> + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
> +
> + /*
> + * The tuning block is sent by the card to the host controller.
> + * So we set the TRNS_READ bit in the Transfer Mode register.
> + * This also takes care of setting DMA Enable and Multi Block
> + * Select in the same register to 0.
> + */
> + sdhci_writew(host, SDHCI_TRNS_READ, 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 ": Timeout waiting for "
> + "Buffer Read Ready interrupt during tuning "
> + "procedure, 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_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,
> };
>
> /*****************************************************************************\
> @@ -1725,6 +1879,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
> @@ -2161,6 +2325,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;
> @@ -2314,6 +2482,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 5b12793..b32fc32 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 92e1c9a..b74c853 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -111,6 +111,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 */
>
> @@ -147,6 +148,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] 54+ messages in thread
* Re: [PATCH v4 09/15] mmc: sdhci: enable preset value after uhs initialization
2011-05-05 6:49 ` [PATCH v4 09/15] mmc: sdhci: enable preset value after " Arindam Nath
@ 2011-05-06 10:40 ` zhangfei gao
2011-05-11 3:38 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:40 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:49 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.
> Since Preset Value Enable makes sense only for UHS-I cards, we enable this
> feature after successfull UHS-I initialization. We also reset Preset Value
> Enable next time before initialization.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> 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 84cb8c3..851c8fc 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -931,6 +931,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
>
> /* Card is an ultra-high-speed card */
> mmc_sd_card_set_uhs(card);
> +
> + /*
> + * Since initialization is now complete, enable preset
> + * value registers for UHS-I cards.
> + */
> + if (host->ops->enable_preset_value)
> + host->ops->enable_preset_value(host, 1);
> } else {
> /*
> * Attempt to change to high-speed (if supported)
> @@ -1101,6 +1108,10 @@ int mmc_attach_sd(struct mmc_host *host)
> if (err)
> return err;
>
> + /* Disable preset value enable if already set since last time */
> + if (host->ops->enable_preset_value)
> + host->ops->enable_preset_value(host, 0);
> +
> err = mmc_send_app_op_cond(host, 0, &ocr);
> if (err)
> return err;
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 85775cf..cb767a0 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1655,6 +1655,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,
> @@ -1662,6 +1693,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] 54+ messages in thread
* Re: [PATCH v4 10/15] mmc: sdhci: add support for programmable clock mode
2011-05-05 6:49 ` [PATCH v4 10/15] mmc: sdhci: add support for programmable clock mode Arindam Nath
@ 2011-05-06 10:40 ` zhangfei gao
2011-05-11 3:39 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:40 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> 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 cb767a0..8ed2e1b 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1013,8 +1013,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)
> @@ -1032,14 +1032,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. */
> @@ -1047,10 +1078,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;
> @@ -2314,17 +2345,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 b32fc32..0208164 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 b74c853..a88a799 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -117,6 +117,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 [flat|nested] 54+ messages in thread
* Re: [PATCH v4 11/15] mmc: sdhci: add support for retuning mode 1
2011-05-05 6:49 ` [PATCH v4 11/15] mmc: sdhci: add support for retuning mode 1 Arindam Nath
@ 2011-05-06 10:40 ` zhangfei gao
2011-05-11 3:39 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:40 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> 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 8ed2e1b..3c04547 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)
> {
> @@ -1206,8 +1208,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);
> @@ -1679,6 +1701,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_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
> spin_unlock(&host->lock);
> enable_irq(host->irq);
> @@ -1781,6 +1834,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;
>
> /*
> @@ -1854,6 +1911,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 *
> @@ -2128,6 +2199,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;
> @@ -2169,6 +2249,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;
> }
>
> @@ -2420,6 +2506,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
> @@ -2565,9 +2666,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)
> @@ -2661,6 +2768,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 0208164..fb74dc6 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 a88a799..e902618 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -112,6 +112,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 */
>
> @@ -152,6 +153,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 [flat|nested] 54+ messages in thread
* Re: [PATCH v4 05/15] mmc: sd: add support for uhs bus speed mode selection
2011-05-05 6:49 ` [PATCH v4 05/15] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
@ 2011-05-06 10:41 ` zhangfei gao
2011-05-11 3:36 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-06 10:41 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao<zhangfei.gao@marvell.com>
Verified with Toshiba uhs card and general hs card, on mmp2 in SDMA mode.
> ---
> drivers/mmc/core/sd.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 40 ++++++++++++++++++++++++++--
> drivers/mmc/host/sdhci.h | 6 ++++
> include/linux/mmc/card.h | 19 +++++++++++++
> include/linux/mmc/host.h | 5 +++
> 5 files changed, 132 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index a6fa52f..0491978 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -457,6 +457,66 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
> return 0;
> }
>
> +static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
> +{
> + unsigned int bus_speed = 0, timing = 0;
> + int err;
> +
> + /*
> + * If the host doesn't support any of the UHS-I modes, fallback on
> + * default speed.
> + */
> + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
> + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
> + return 0;
> +
> + if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
> + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
> + bus_speed = UHS_SDR104_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR104;
> + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> + } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
> + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
> + bus_speed = UHS_DDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_DDR50;
> + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> + } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
> + MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
> + SD_MODE_UHS_SDR50)) {
> + bus_speed = UHS_SDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR50;
> + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> + } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
> + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
> + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
> + bus_speed = UHS_SDR25_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR25;
> + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> + } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
> + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
> + MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
> + SD_MODE_UHS_SDR12)) {
> + bus_speed = UHS_SDR12_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR12;
> + card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
> + }
> +
> + card->sd_bus_speed = bus_speed;
> + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> + if (err)
> + return err;
> +
> + if ((status[16] & 0xF) != bus_speed)
> + printk(KERN_WARNING "%s: Problem setting bus speed mode!\n",
> + mmc_hostname(card->host));
> + else {
> + mmc_set_timing(card->host, timing);
> + mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
> + }
> +
> + return 0;
> +}
> +
> /*
> * UHS-I specific initialization procedure
> */
> @@ -490,6 +550,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
>
> /* Set the driver strength for the card */
> err = sd_select_driver_type(card, status);
> + if (err)
> + goto out;
> +
> + /* Set bus speed mode of the card */
> + err = sd_set_bus_speed_mode(card, status);
>
> out:
> kfree(status);
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 309240c..de7e6e9 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1244,7 +1244,16 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> ctrl &= ~SDHCI_CTRL_HISPD;
>
> if (host->version >= SDHCI_SPEC_300) {
> - u16 ctrl_2;
> + u16 clk, ctrl_2;
> + unsigned int clock;
> +
> + /* In case of UHS-I modes, set High Speed Enable */
> + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> + (ios->timing == MMC_TIMING_UHS_DDR50) ||
> + (ios->timing == MMC_TIMING_UHS_SDR25) ||
> + (ios->timing == MMC_TIMING_UHS_SDR12))
> + ctrl |= SDHCI_CTRL_HISPD;
>
> ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> @@ -1267,8 +1276,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> * need to reset SD Clock Enable before changing High
> * Speed Enable to avoid generating clock gliches.
> */
> - u16 clk;
> - unsigned int clock;
>
> /* Reset SD Clock Enable */
> clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> @@ -1282,6 +1289,33 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> host->clock = 0;
> sdhci_set_clock(host, clock);
> }
> +
> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +
> + /* Select Bus Speed Mode for host */
> + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
> + if (ios->timing == MMC_TIMING_UHS_SDR12)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
> + else if (ios->timing == MMC_TIMING_UHS_SDR25)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> + else if (ios->timing == MMC_TIMING_UHS_SDR104)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> +
> + /* Reset SD Clock Enable */
> + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> + clk &= ~SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> +
> + /* Re-enable SD Clock */
> + clock = host->clock;
> + host->clock = 0;
> + sdhci_set_clock(host, clock);
> } else
> sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
>
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 04c41a4..5b12793 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -149,6 +149,12 @@
> #define SDHCI_ACMD12_ERR 0x3C
>
> #define SDHCI_HOST_CONTROL2 0x3E
> +#define SDHCI_CTRL_UHS_MASK 0x0007
> +#define SDHCI_CTRL_UHS_SDR12 0x0000
> +#define SDHCI_CTRL_UHS_SDR25 0x0001
> +#define SDHCI_CTRL_UHS_SDR50 0x0002
> +#define SDHCI_CTRL_UHS_SDR104 0x0003
> +#define SDHCI_CTRL_UHS_DDR50 0x0004
> #define SDHCI_CTRL_VDD_180 0x0008
> #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
> #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 5393272..4ef6ded 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -81,7 +81,24 @@ struct sd_ssr {
>
> struct sd_switch_caps {
> unsigned int hs_max_dtr;
> + unsigned int uhs_max_dtr;
> +#define UHS_SDR104_MAX_DTR 208000000
> +#define UHS_SDR50_MAX_DTR 100000000
> +#define UHS_DDR50_MAX_DTR 50000000
> +#define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR
> +#define UHS_SDR12_MAX_DTR 25000000
> unsigned int sd3_bus_mode;
> +#define UHS_SDR12_BUS_SPEED 0
> +#define UHS_SDR25_BUS_SPEED 1
> +#define UHS_SDR50_BUS_SPEED 2
> +#define UHS_SDR104_BUS_SPEED 3
> +#define UHS_DDR50_BUS_SPEED 4
> +
> +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> unsigned int sd3_drv_type;
> #define SD_DRIVER_TYPE_B 0x01
> #define SD_DRIVER_TYPE_A 0x02
> @@ -166,6 +183,8 @@ struct mmc_card {
> const char **info; /* info strings */
> struct sdio_func_tuple *tuples; /* unknown common tuples */
>
> + unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
> +
> struct dentry *debugfs_root;
> };
>
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 949e4d5..6237599 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -50,6 +50,11 @@ struct mmc_ios {
> #define MMC_TIMING_LEGACY 0
> #define MMC_TIMING_MMC_HS 1
> #define MMC_TIMING_SD_HS 2
> +#define MMC_TIMING_UHS_SDR12 MMC_TIMING_LEGACY
> +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> +#define MMC_TIMING_UHS_SDR50 3
> +#define MMC_TIMING_UHS_SDR104 4
> +#define MMC_TIMING_UHS_DDR50 5
>
> unsigned char ddr; /* dual data rate used */
>
> --
> 1.7.1
>
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 13/15] mmc eMMC signal voltage does not use CMD11
2011-05-05 6:49 ` [PATCH v4 13/15] mmc eMMC signal voltage does not use CMD11 Arindam Nath
@ 2011-05-11 1:51 ` Chris Ball
2011-05-11 9:19 ` zhangfei gao
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 1:51 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi Arindam,
If this patch was written by Philip, it should begin with:
From: Philip Rakity <prakity@marvell.com>
(And if it was written by you, it should have your S-o-b line.)
On Thu, May 05 2011, Arindam Nath wrote:
> eMMC chips do not use CMD11 when changing voltage. Add extra
> argument to call to indicate if CMD11 needs to be sent.
>
> Signed-off-by: Philip Rakity <prakity@marvell.com>
> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/core.c | 4 ++--
> drivers/mmc/core/core.h | 3 ++-
> drivers/mmc/core/sd.c | 4 ++--
> 3 files changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 78a9b51..74b4409 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -942,7 +942,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
> return ocr;
> }
>
> -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, int cmd11)
It's fine to use the bool type for booleans.
> {
> struct mmc_command cmd;
> int err = 0;
> @@ -953,7 +953,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
> * Send CMD11 only if the request is to switch the card to
> * 1.8V signalling.
> */
> - if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
> + if ((signal_voltage != MMC_SIGNAL_VOLTAGE_330) && cmd11) {
> memset(&cmd, 0, sizeof(struct mmc_command));
Please replace "struct mmc_command cmd; ... memset(&cmd, 0,
sizeof(struct mmc_command));" with "struct mmc_command cmd = {0};".
>
> cmd.opcode = SD_SWITCH_VOLTAGE;
> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> index 93f3397..3c11e17 100644
> --- a/drivers/mmc/core/core.h
> +++ b/drivers/mmc/core/core.h
> @@ -41,7 +41,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
> void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
> unsigned int ddr);
> u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
> + int cmd11);
> void mmc_set_timing(struct mmc_host *host, unsigned int timing);
> void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 851c8fc..d2a05ab 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -731,7 +731,7 @@ try_again:
> */
> if (!mmc_host_is_spi(host) && rocr &&
> ((*rocr & 0x41000000) == 0x41000000)) {
> - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, 1);
Use TRUE instead of 1.
> if (err) {
> ocr &= ~SD_OCR_S18R;
> goto try_again;
> @@ -1104,7 +1104,7 @@ int mmc_attach_sd(struct mmc_host *host)
> WARN_ON(!host->claimed);
>
> /* Make sure we are at 3.3V signalling voltage */
> - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);
Use FALSE instead of 0.
> if (err)
> return err;
Thanks,
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling
2011-05-05 6:49 ` [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling Arindam Nath
@ 2011-05-11 1:54 ` Chris Ball
2011-05-11 8:48 ` zhangfei gao
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 1:54 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath wrote:
> Marvell controller requires 1.8V bit in UHS control register 2
> be set when doing UHS. eMMC does not require 1.8V for DDR.
> add platform code to handle this.
>
> Signed-off-by: Philip Rakity <prakity@marvell.com>
> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/host/sdhci-pxa.c | 36 ++++++++++++++++++++++++++++++++++++
> 1 files changed, 36 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
> index 5a61208..b52c3e6 100644
> --- a/drivers/mmc/host/sdhci-pxa.c
> +++ b/drivers/mmc/host/sdhci-pxa.c
> @@ -69,7 +69,40 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
> }
> }
>
> +static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
> +{
> + u16 ctrl_2;
> +
> + /*
> + * Set V18_EN -- UHS modes do not work without this.
> + * does not change signaling voltage
> + */
> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +
> + /* Select Bus Speed Mode for host */
> + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
There's something wrong with the tabbing for the comment above.
> + if (uhs == MMC_TIMING_UHS_SDR12)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
> + else if (uhs == MMC_TIMING_UHS_SDR25)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> + else if (uhs == MMC_TIMING_UHS_SDR50) {
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> + ctrl_2 |= SDHCI_CTRL_VDD_180;
> + } else if (uhs == MMC_TIMING_UHS_SDR104) {
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> + ctrl_2 |= SDHCI_CTRL_VDD_180;
> + } else if (uhs == MMC_TIMING_UHS_DDR50) {
> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> + ctrl_2 |= SDHCI_CTRL_VDD_180;
> + }
Maybe a switch-case here would be easier on the eyes?
> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> + pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
> + __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
> + return 0;
> +}
> +
> static struct sdhci_ops sdhci_pxa_ops = {
> + .set_uhs_signaling = set_uhs_signaling,
> .set_clock = set_clock,
> };
>
> @@ -141,6 +174,9 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
> if (pdata->quirks)
> host->quirks |= pdata->quirks;
>
> + /* enable 1/8V DDR capable */
> + host->mmc->caps |= MMC_CAP_1_8V_DDR;
> +
> /* If slot design supports 8 bit data, indicate this to MMC. */
> if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
> host->mmc->caps |= MMC_CAP_8_BIT_DATA;
Thanks,
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 01/15] mmc: sd: add support for signal voltage switch procedure
2011-05-05 6:48 ` [PATCH v4 01/15] mmc: sd: add support for signal voltage switch procedure Arindam Nath
2011-05-06 10:36 ` zhangfei gao
@ 2011-05-11 3:34 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:34 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40 with the trivial changes below:
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index ea5c28d..5005a63 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -944,7 +944,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
{
- struct mmc_command cmd;
+ struct mmc_command cmd = {0};
int err = 0;
BUG_ON(!host);
@@ -954,8 +954,6 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
* 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;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 52faa50..7fffc5e 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1400,9 +1400,8 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
present_state = sdhci_readl(host,
SDHCI_PRESENT_STATE);
if ((present_state & SDHCI_DATA_LVL_MASK) ==
- SDHCI_DATA_LVL_MASK) {
+ SDHCI_DATA_LVL_MASK)
return 0;
- }
}
}
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v4 02/15] mmc: sd: query function modes for uhs cards
2011-05-05 6:48 ` [PATCH v4 02/15] mmc: sd: query function modes for uhs cards Arindam Nath
2011-05-06 10:37 ` zhangfei gao
@ 2011-05-11 3:34 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:34 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40 with the trivial changes below:
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index d4410c9..027d7cb 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -190,7 +190,7 @@ 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*/
+ /* Check if Physical Layer Spec v3.0 is supported */
scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1);
if (UNSTUFF_BITS(resp, 55, 1))
@@ -288,13 +288,11 @@ static int mmc_read_switch(struct mmc_card *card)
* If the host or the card can't do the switch,
* fail more gracefully.
*/
- if ((err != -EINVAL)
- && (err != -ENOSYS)
- && (err != -EFAULT))
+ if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
goto out;
printk(KERN_WARNING "%s: problem reading Bus Speed modes.\n",
@@ -310,14 +308,11 @@ static int mmc_read_switch(struct mmc_card *card)
* If the host or the card can't do the switch,
* fail more gracefully.
*/
- if ((err != -EINVAL)
- && (err != -ENOSYS)
- && (err != -EFAULT))
+ if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
goto out;
printk(KERN_WARNING "%s: problem reading "
@@ -332,14 +327,11 @@ static int mmc_read_switch(struct mmc_card *card)
* If the host or the card can't do the switch,
* fail more gracefully.
*/
- if ((err != -EINVAL)
- && (err != -ENOSYS)
- && (err != -EFAULT))
+ if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
goto out;
printk(KERN_WARNING "%s: problem reading "
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v4 03/15] mmc: sd: add support for driver type selection
2011-05-05 6:48 ` [PATCH v4 03/15] mmc: sd: add support for driver type selection Arindam Nath
2011-05-06 10:37 ` zhangfei gao
@ 2011-05-11 3:34 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:34 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40 with the trivial changes below:
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 96a3af5..5b7c998 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -540,8 +540,7 @@ struct device_type sd_type = {
/*
* Fetch CID from card.
*/
-int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid,
- u32 *rocr)
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
{
int err;
diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
index 5106b44..4b34b24 100644
--- a/drivers/mmc/core/sd.h
+++ b/drivers/mmc/core/sd.h
@@ -5,8 +5,7 @@
extern struct device_type sd_type;
-int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid,
- u32 *rocr);
+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 c4a6614..4d0c15b 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -369,9 +369,8 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto err;
}
- if (ocr & R4_MEMORY_PRESENT
- && mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid,
- NULL) == 0) {
+ if ((ocr & R4_MEMORY_PRESENT) &&
+ 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 ||
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v4 04/15] mmc: sdhci: reset sdclk before setting high speed enable
2011-05-05 6:49 ` [PATCH v4 04/15] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
2011-05-06 10:38 ` zhangfei gao
@ 2011-05-11 3:36 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:36 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40.
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 05/15] mmc: sd: add support for uhs bus speed mode selection
2011-05-05 6:49 ` [PATCH v4 05/15] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
2011-05-06 10:41 ` zhangfei gao
@ 2011-05-11 3:36 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:36 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40.
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 06/15] mmc: sd: set current limit for uhs cards
2011-05-05 6:49 ` [PATCH v4 06/15] mmc: sd: set current limit for uhs cards Arindam Nath
2011-05-06 10:38 ` zhangfei gao
@ 2011-05-11 3:37 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:37 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40.
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 07/15] mmc: sd: report correct speed and capacity of uhs cards
2011-05-05 6:49 ` [PATCH v4 07/15] mmc: sd: report correct speed and capacity of " Arindam Nath
2011-05-06 10:39 ` zhangfei gao
@ 2011-05-11 3:37 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:37 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40.
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 08/15] mmc: sd: add support for tuning during uhs initialization
2011-05-05 6:49 ` [PATCH v4 08/15] mmc: sd: add support for tuning during uhs initialization Arindam Nath
2011-05-06 10:39 ` zhangfei gao
@ 2011-05-11 3:38 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:38 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, 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 program the block size to 64 in the
> Block Size register. We make sure that DMA Enable and Multi Block
> Select in the Transfer Mode register are 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 and re-enable other interrupts.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> Acked-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40 with the trivial changes below:
(There's no need to memset cmd.resp separately, right?)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 91173cd..39cceee 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1557,24 +1557,19 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
*/
timeout = 150;
do {
- struct mmc_command cmd;
- struct mmc_request mrq;
+ struct mmc_command cmd = {0};
+ struct mmc_request mrq = {0};
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;
@@ -1880,8 +1875,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
/* 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) {
+ if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
+ MMC_SEND_TUNING_BLOCK) {
host->tuning_done = 1;
wake_up(&host->buf_ready_int);
return;
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v4 09/15] mmc: sdhci: enable preset value after uhs initialization
2011-05-05 6:49 ` [PATCH v4 09/15] mmc: sdhci: enable preset value after " Arindam Nath
2011-05-06 10:40 ` zhangfei gao
@ 2011-05-11 3:38 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:38 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath 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.
> Since Preset Value Enable makes sense only for UHS-I cards, we enable this
> feature after successfull UHS-I initialization. We also reset Preset Value
> Enable next time before initialization.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40 with the changes below:
(Note that a function prototype is changed.)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 0577a9c..b461b29 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -931,7 +931,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
* value registers for UHS-I cards.
*/
if (host->ops->enable_preset_value)
- host->ops->enable_preset_value(host, 1);
+ host->ops->enable_preset_value(host, true);
} else {
/*
* Attempt to change to high-speed (if supported)
@@ -1104,7 +1104,7 @@ int mmc_attach_sd(struct mmc_host *host)
/* Disable preset value enable if already set since last time */
if (host->ops->enable_preset_value)
- host->ops->enable_preset_value(host, 0);
+ host->ops->enable_preset_value(host, false);
err = mmc_send_app_op_cond(host, 0, &ocr);
if (err)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index ae10558..407eb99 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1649,7 +1649,7 @@ out:
return err;
}
-static void sdhci_enable_preset_value(struct mmc_host *mmc, int enable)
+static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
{
struct sdhci_host *host;
u16 ctrl;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 2209e01..6716bd1 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -137,7 +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);
+ void (*enable_preset_value)(struct mmc_host *host, bool enable);
};
struct mmc_card;
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v4 10/15] mmc: sdhci: add support for programmable clock mode
2011-05-05 6:49 ` [PATCH v4 10/15] mmc: sdhci: add support for programmable clock mode Arindam Nath
2011-05-06 10:40 ` zhangfei gao
@ 2011-05-11 3:39 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:39 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40.
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 11/15] mmc: sdhci: add support for retuning mode 1
2011-05-05 6:49 ` [PATCH v4 11/15] mmc: sdhci: add support for retuning mode 1 Arindam Nath
2011-05-06 10:40 ` zhangfei gao
@ 2011-05-11 3:39 ` Chris Ball
1 sibling, 0 replies; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:39 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi,
On Thu, May 05 2011, Arindam Nath wrote:
> 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>
> Reviewed-by: Philip Rakity <prakity@marvell.com>
> Tested-by: Philip Rakity <prakity@marvell.com>
Thanks, pushed to mmc-next for .40 with the trivial changes below:
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index d90c300..e00c759 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1218,8 +1218,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
* tuning procedure before sending command.
*/
if ((host->flags & SDHCI_NEEDS_RETUNING) &&
- !(present_state & (SDHCI_DOING_WRITE |
- SDHCI_DOING_READ))) {
+ !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
spin_unlock_irqrestore(&host->lock, flags);
sdhci_execute_tuning(mmc);
spin_lock_irqsave(&host->lock, flags);
@@ -1703,8 +1702,8 @@ out:
*/
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);
+ 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 {
@@ -1831,7 +1830,6 @@ static void sdhci_tasklet_finish(unsigned long param)
if (host->version >= SDHCI_SPEC_300)
del_timer(&host->tuning_timer);
-
mrq = host->mrq;
/*
@@ -2194,8 +2192,7 @@ 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 &&
+ 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 +
@@ -2244,8 +2241,7 @@ int sdhci_resume_host(struct sdhci_host *host)
sdhci_enable_card_detection(host);
/* Set the re-tuning expiration flag */
- if ((host->version >= SDHCI_SPEC_300) &&
- host->tuning_count &&
+ if (host->version >= SDHCI_SPEC_300 && host->tuning_count &&
host->tuning_mode == SDHCI_TUNING_MODE_1)
host->flags |= SDHCI_NEEDS_RETUNING;
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v4 00/15] add support for host controller v3.00
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
` (15 preceding siblings ...)
2011-05-05 8:18 ` [PATCH v4 00/15] add support for host controller v3.00 Nath, Arindam
@ 2011-05-11 3:43 ` Chris Ball
2011-05-11 6:13 ` Nath, Arindam
16 siblings, 1 reply; 54+ messages in thread
From: Chris Ball @ 2011-05-11 3:43 UTC (permalink / raw)
To: Arindam Nath
Cc: prakity, zhangfei.gao, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi Arindam,
On Thu, May 05 2011, Arindam Nath wrote:
> 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.
I've pushed 01-11 to mmc-next now, and will push 12-15 once they're
resubmitted with authorship status resolved.
Thanks very much for all of your hard work on this patchset!
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 54+ messages in thread
* RE: [PATCH v4 00/15] add support for host controller v3.00
2011-05-11 3:43 ` Chris Ball
@ 2011-05-11 6:13 ` Nath, Arindam
0 siblings, 0 replies; 54+ messages in thread
From: Nath, Arindam @ 2011-05-11 6:13 UTC (permalink / raw)
To: Chris Ball
Cc: prakity@marvell.com, zhangfei.gao@gmail.com,
subhashj@codeaurora.org, linux-mmc@vger.kernel.org, Su, Henry,
Lu, Aaron, anath.amd@gmail.com
Hi Chris,
> -----Original Message-----
> From: Chris Ball [mailto:cjb@laptop.org]
> Sent: Wednesday, May 11, 2011 9:13 AM
> To: Nath, Arindam
> Cc: prakity@marvell.com; zhangfei.gao@gmail.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; Su, Henry; Lu,
> Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v4 00/15] add support for host controller v3.00
>
> Hi Arindam,
>
> On Thu, May 05 2011, Arindam Nath wrote:
> > 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.
>
> I've pushed 01-11 to mmc-next now, and will push 12-15 once they're
> resubmitted with authorship status resolved.
Thanks to you for taking time out to make the necessary changes, and pushing it to mmc-next. I will contact Philip to resolve the authorship issues with the rest of the patches, and then re-submit them soon.
Thanks,
Arindam
>
> Thanks very much for all of your hard work on this patchset!
>
> - Chris.
> --
> Chris Ball <cjb@laptop.org> <http://printf.net/>
> One Laptop Per Child
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling
2011-05-05 6:49 ` [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling Arindam Nath
2011-05-11 1:54 ` Chris Ball
@ 2011-05-11 8:48 ` zhangfei gao
2011-05-11 8:52 ` Philip Rakity
1 sibling, 1 reply; 54+ messages in thread
From: zhangfei gao @ 2011-05-11 8:48 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> Marvell controller requires 1.8V bit in UHS control register 2
> be set when doing UHS. eMMC does not require 1.8V for DDR.
> add platform code to handle this.
>
> Signed-off-by: Philip Rakity <prakity@marvell.com>
> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/host/sdhci-pxa.c | 36 ++++++++++++++++++++++++++++++++++++
> 1 files changed, 36 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
> index 5a61208..b52c3e6 100644
> --- a/drivers/mmc/host/sdhci-pxa.c
> +++ b/drivers/mmc/host/sdhci-pxa.c
> @@ -69,7 +69,40 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
> }
> }
>
> +static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
> +{
> + u16 ctrl_2;
> +
> + /*
> + * Set V18_EN -- UHS modes do not work without this.
> + * does not change signaling voltage
> + */
> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +
> + /* Select Bus Speed Mode for host */
> + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
> + if (uhs == MMC_TIMING_UHS_SDR12)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
> + else if (uhs == MMC_TIMING_UHS_SDR25)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> + else if (uhs == MMC_TIMING_UHS_SDR50) {
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> + ctrl_2 |= SDHCI_CTRL_VDD_180;
> + } else if (uhs == MMC_TIMING_UHS_SDR104) {
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> + ctrl_2 |= SDHCI_CTRL_VDD_180;
> + } else if (uhs == MMC_TIMING_UHS_DDR50) {
> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> + ctrl_2 |= SDHCI_CTRL_VDD_180;
> + }
> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> + pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
> + __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
> + return 0;
> +}
Why move common register accessing from sdhci.c to specific driver?
> +
> static struct sdhci_ops sdhci_pxa_ops = {
> + .set_uhs_signaling = set_uhs_signaling,
> .set_clock = set_clock,
> };
>
> @@ -141,6 +174,9 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
> if (pdata->quirks)
> host->quirks |= pdata->quirks;
>
> + /* enable 1/8V DDR capable */
> + host->mmc->caps |= MMC_CAP_1_8V_DDR;
> +
> /* If slot design supports 8 bit data, indicate this to MMC. */
> if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
> host->mmc->caps |= MMC_CAP_8_BIT_DATA;
> --
> 1.7.1
>
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling
2011-05-11 8:48 ` zhangfei gao
@ 2011-05-11 8:52 ` Philip Rakity
2011-05-11 9:28 ` zhangfei gao
0 siblings, 1 reply; 54+ messages in thread
From: Philip Rakity @ 2011-05-11 8:52 UTC (permalink / raw)
To: zhangfei gao
Cc: Arindam Nath, cjb@laptop.org, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
\
On May 11, 2011, at 1:48 AM, zhangfei gao wrote:
> On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
>> Marvell controller requires 1.8V bit in UHS control register 2
>> be set when doing UHS. eMMC does not require 1.8V for DDR.
>> add platform code to handle this.
>>
>> Signed-off-by: Philip Rakity <prakity@marvell.com>
>> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
>> ---
>> drivers/mmc/host/sdhci-pxa.c | 36 ++++++++++++++++++++++++++++++++++++
>> 1 files changed, 36 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
>> index 5a61208..b52c3e6 100644
>> --- a/drivers/mmc/host/sdhci-pxa.c
>> +++ b/drivers/mmc/host/sdhci-pxa.c
>> @@ -69,7 +69,40 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
>> }
>> }
>>
>> +static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
>> +{
>> + u16 ctrl_2;
>> +
>> + /*
>> + * Set V18_EN -- UHS modes do not work without this.
>> + * does not change signaling voltage
>> + */
>> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>> +
>> + /* Select Bus Speed Mode for host */
>> + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
>> + if (uhs == MMC_TIMING_UHS_SDR12)
>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
>> + else if (uhs == MMC_TIMING_UHS_SDR25)
>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
>> + else if (uhs == MMC_TIMING_UHS_SDR50) {
>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>> + } else if (uhs == MMC_TIMING_UHS_SDR104) {
>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>> + } else if (uhs == MMC_TIMING_UHS_DDR50) {
>> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>> + }
>> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
>> + pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
>> + __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
>> + return 0;
>> +}
>
> Why move common register accessing from sdhci.c to specific driver?
In general case you do not know what the specific host controller work arounds are required.
on mmp2 this code works (no need for V18 for low speed UHS)
but other controllers it could be different
>
>> +
>> static struct sdhci_ops sdhci_pxa_ops = {
>> + .set_uhs_signaling = set_uhs_signaling,
>> .set_clock = set_clock,
>> };
>>
>> @@ -141,6 +174,9 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
>> if (pdata->quirks)
>> host->quirks |= pdata->quirks;
>>
>> + /* enable 1/8V DDR capable */
>> + host->mmc->caps |= MMC_CAP_1_8V_DDR;
>> +
>> /* If slot design supports 8 bit data, indicate this to MMC. */
>> if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
>> host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>> --
>> 1.7.1
>>
>>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 13/15] mmc eMMC signal voltage does not use CMD11
2011-05-05 6:49 ` [PATCH v4 13/15] mmc eMMC signal voltage does not use CMD11 Arindam Nath
2011-05-11 1:51 ` Chris Ball
@ 2011-05-11 9:19 ` zhangfei gao
2011-05-11 15:01 ` Philip Rakity
1 sibling, 1 reply; 54+ messages in thread
From: zhangfei gao @ 2011-05-11 9:19 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
> eMMC chips do not use CMD11 when changing voltage. Add extra
> argument to call to indicate if CMD11 needs to be sent.
mmc_set_signal_voltage or start_signal_voltage_switch may not support
emmc, not only cmd11, but also start_signal_voltage_switch is wrote
according to sd phys spec.
JESD84-A441 A8.1 describes how to support dual voltage of emmc,
however it only support to 1.8v.
Still not find emmc card which could support 1.2v.
>
> Signed-off-by: Philip Rakity <prakity@marvell.com>
> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/core.c | 4 ++--
> drivers/mmc/core/core.h | 3 ++-
> drivers/mmc/core/sd.c | 4 ++--
> 3 files changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 78a9b51..74b4409 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -942,7 +942,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
> return ocr;
> }
>
> -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, int cmd11)
> {
> struct mmc_command cmd;
> int err = 0;
> @@ -953,7 +953,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
> * Send CMD11 only if the request is to switch the card to
> * 1.8V signalling.
> */
> - if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
> + if ((signal_voltage != MMC_SIGNAL_VOLTAGE_330) && cmd11) {
> memset(&cmd, 0, sizeof(struct mmc_command));
>
> cmd.opcode = SD_SWITCH_VOLTAGE;
> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> index 93f3397..3c11e17 100644
> --- a/drivers/mmc/core/core.h
> +++ b/drivers/mmc/core/core.h
> @@ -41,7 +41,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
> void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
> unsigned int ddr);
> u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
> + int cmd11);
> void mmc_set_timing(struct mmc_host *host, unsigned int timing);
> void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 851c8fc..d2a05ab 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -731,7 +731,7 @@ try_again:
> */
> if (!mmc_host_is_spi(host) && rocr &&
> ((*rocr & 0x41000000) == 0x41000000)) {
> - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, 1);
> if (err) {
> ocr &= ~SD_OCR_S18R;
> goto try_again;
> @@ -1104,7 +1104,7 @@ int mmc_attach_sd(struct mmc_host *host)
> WARN_ON(!host->claimed);
>
> /* Make sure we are at 3.3V signalling voltage */
> - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);
> if (err)
> return err;
>
> --
> 1.7.1
>
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling
2011-05-11 8:52 ` Philip Rakity
@ 2011-05-11 9:28 ` zhangfei gao
2011-05-11 9:47 ` Philip Rakity
0 siblings, 1 reply; 54+ messages in thread
From: zhangfei gao @ 2011-05-11 9:28 UTC (permalink / raw)
To: Philip Rakity
Cc: Arindam Nath, cjb@laptop.org, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On Wed, May 11, 2011 at 4:52 AM, Philip Rakity <prakity@marvell.com> wrote:
>
> \
> On May 11, 2011, at 1:48 AM, zhangfei gao wrote:
>
>> On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
>>> Marvell controller requires 1.8V bit in UHS control register 2
>>> be set when doing UHS. eMMC does not require 1.8V for DDR.
>>> add platform code to handle this.
>>>
>>> Signed-off-by: Philip Rakity <prakity@marvell.com>
>>> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
>>> ---
>>> drivers/mmc/host/sdhci-pxa.c | 36 ++++++++++++++++++++++++++++++++++++
>>> 1 files changed, 36 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
>>> index 5a61208..b52c3e6 100644
>>> --- a/drivers/mmc/host/sdhci-pxa.c
>>> +++ b/drivers/mmc/host/sdhci-pxa.c
>>> @@ -69,7 +69,40 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
>>> }
>>> }
>>>
>>> +static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
>>> +{
>>> + u16 ctrl_2;
>>> +
>>> + /*
>>> + * Set V18_EN -- UHS modes do not work without this.
>>> + * does not change signaling voltage
>>> + */
>>> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>>> +
>>> + /* Select Bus Speed Mode for host */
>>> + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
>>> + if (uhs == MMC_TIMING_UHS_SDR12)
>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
>>> + else if (uhs == MMC_TIMING_UHS_SDR25)
>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
>>> + else if (uhs == MMC_TIMING_UHS_SDR50) {
>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>> + } else if (uhs == MMC_TIMING_UHS_SDR104) {
>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>> + } else if (uhs == MMC_TIMING_UHS_DDR50) {
>>> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>> + }
>>> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
>>> + pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
>>> + __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
>>> + return 0;
>>> +}
>>
>> Why move common register accessing from sdhci.c to specific driver?
>
> In general case you do not know what the specific host controller work arounds are required.
> on mmp2 this code works (no need for V18 for low speed UHS)
> but other controllers it could be different
Sorry, not understand, the patch is for sd uhs card or for emmc?
For sd uhs card, no workaround is needed on mmp2, but require external
pmic to provide 1.8v io voltage.
>
>>
>>> +
>>> static struct sdhci_ops sdhci_pxa_ops = {
>>> + .set_uhs_signaling = set_uhs_signaling,
>>> .set_clock = set_clock,
>>> };
>>>
>>> @@ -141,6 +174,9 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
>>> if (pdata->quirks)
>>> host->quirks |= pdata->quirks;
>>>
>>> + /* enable 1/8V DDR capable */
>>> + host->mmc->caps |= MMC_CAP_1_8V_DDR;
>>> +
>>> /* If slot design supports 8 bit data, indicate this to MMC. */
>>> if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
>>> host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>>> --
>>> 1.7.1
>>>
>>>
>
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling
2011-05-11 9:28 ` zhangfei gao
@ 2011-05-11 9:47 ` Philip Rakity
2011-05-12 1:53 ` Philip Rakity
2011-05-13 8:03 ` zhangfei gao
0 siblings, 2 replies; 54+ messages in thread
From: Philip Rakity @ 2011-05-11 9:47 UTC (permalink / raw)
To: zhangfei gao
Cc: Arindam Nath, cjb@laptop.org, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On May 11, 2011, at 2:28 AM, zhangfei gao wrote:
> On Wed, May 11, 2011 at 4:52 AM, Philip Rakity <prakity@marvell.com> wrote:
>>
>> \
>> On May 11, 2011, at 1:48 AM, zhangfei gao wrote:
>>
>>> On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
>>>> Marvell controller requires 1.8V bit in UHS control register 2
>>>> be set when doing UHS. eMMC does not require 1.8V for DDR.
>>>> add platform code to handle this.
>>>>
>>>> Signed-off-by: Philip Rakity <prakity@marvell.com>
>>>> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
>>>> ---
>>>> drivers/mmc/host/sdhci-pxa.c | 36 ++++++++++++++++++++++++++++++++++++
>>>> 1 files changed, 36 insertions(+), 0 deletions(-)
>>>>
>>>> diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
>>>> index 5a61208..b52c3e6 100644
>>>> --- a/drivers/mmc/host/sdhci-pxa.c
>>>> +++ b/drivers/mmc/host/sdhci-pxa.c
>>>> @@ -69,7 +69,40 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
>>>> }
>>>> }
>>>>
>>>> +static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
>>>> +{
>>>> + u16 ctrl_2;
>>>> +
>>>> + /*
>>>> + * Set V18_EN -- UHS modes do not work without this.
>>>> + * does not change signaling voltage
>>>> + */
>>>> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>>>> +
>>>> + /* Select Bus Speed Mode for host */
>>>> + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
>>>> + if (uhs == MMC_TIMING_UHS_SDR12)
>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
>>>> + else if (uhs == MMC_TIMING_UHS_SDR25)
>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
>>>> + else if (uhs == MMC_TIMING_UHS_SDR50) {
>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>> + } else if (uhs == MMC_TIMING_UHS_SDR104) {
>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>> + } else if (uhs == MMC_TIMING_UHS_DDR50) {
>>>> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>> + }
>>>> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
>>>> + pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
>>>> + __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
>>>> + return 0;
>>>> +}
>>>
>>> Why move common register accessing from sdhci.c to specific driver?
>>
>> In general case you do not know what the specific host controller work arounds are required.
>> on mmp2 this code works (no need for V18 for low speed UHS)
>> but other controllers it could be different
>
> Sorry, not understand, the patch is for sd uhs card or for emmc?
> For sd uhs card, no workaround is needed on mmp2, but require external
> pmic to provide 1.8v io voltage.
require 1.8V when setting UHS modes. See latest MMP2 Documentation.
>
>>
>>>
>>>> +
>>>> static struct sdhci_ops sdhci_pxa_ops = {
>>>> + .set_uhs_signaling = set_uhs_signaling,
>>>> .set_clock = set_clock,
>>>> };
>>>>
>>>> @@ -141,6 +174,9 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
>>>> if (pdata->quirks)
>>>> host->quirks |= pdata->quirks;
>>>>
>>>> + /* enable 1/8V DDR capable */
>>>> + host->mmc->caps |= MMC_CAP_1_8V_DDR;
>>>> +
>>>> /* If slot design supports 8 bit data, indicate this to MMC. */
>>>> if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
>>>> host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>>>> --
>>>> 1.7.1
>>>>
>>>>
>>
>>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 13/15] mmc eMMC signal voltage does not use CMD11
2011-05-11 9:19 ` zhangfei gao
@ 2011-05-11 15:01 ` Philip Rakity
2011-05-12 1:53 ` Philip Rakity
0 siblings, 1 reply; 54+ messages in thread
From: Philip Rakity @ 2011-05-11 15:01 UTC (permalink / raw)
To: zhangfei gao
Cc: Arindam Nath, cjb@laptop.org, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On May 11, 2011, at 2:19 AM, zhangfei gao wrote:
> On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
>> eMMC chips do not use CMD11 when changing voltage. Add extra
>> argument to call to indicate if CMD11 needs to be sent.
>
> mmc_set_signal_voltage or start_signal_voltage_switch may not support
> emmc, not only cmd11, but also start_signal_voltage_switch is wrote
> according to sd phys spec.
the callback may go to a different driver then sdhci.c
> JESD84-A441 A8.1 describes how to support dual voltage of emmc,
> however it only support to 1.8v.
> Still not find emmc card which could support 1.2v.
eMMC
vcc -- 3.3v then vccq can be 3.3 or 1.8 or 1.2v
vcc -- 1.8v then vccq can be 1.8v or 1.2v
1.2 v vccq support is indicated in the CARD TYPE [196]
a standard SD host controller does not support 1.2v vccq but folks could extend the standard.
eMMC is not expected to be voltage switched. The expectation is the board designer will
fix the voltages and no changes are needed.
vccq is independent of vcc. see section 12.5.3.
>
>>
>> Signed-off-by: Philip Rakity <prakity@marvell.com>
>> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
>> ---
>> drivers/mmc/core/core.c | 4 ++--
>> drivers/mmc/core/core.h | 3 ++-
>> drivers/mmc/core/sd.c | 4 ++--
>> 3 files changed, 6 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 78a9b51..74b4409 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -942,7 +942,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
>> return ocr;
>> }
>>
>> -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
>> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, int cmd11)
>> {
>> struct mmc_command cmd;
>> int err = 0;
>> @@ -953,7 +953,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
>> * Send CMD11 only if the request is to switch the card to
>> * 1.8V signalling.
>> */
>> - if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
>> + if ((signal_voltage != MMC_SIGNAL_VOLTAGE_330) && cmd11) {
>> memset(&cmd, 0, sizeof(struct mmc_command));
>>
>> cmd.opcode = SD_SWITCH_VOLTAGE;
>> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
>> index 93f3397..3c11e17 100644
>> --- a/drivers/mmc/core/core.h
>> +++ b/drivers/mmc/core/core.h
>> @@ -41,7 +41,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
>> void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
>> unsigned int ddr);
>> u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
>> -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
>> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
>> + int cmd11);
>> void mmc_set_timing(struct mmc_host *host, unsigned int timing);
>> void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
>>
>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>> index 851c8fc..d2a05ab 100644
>> --- a/drivers/mmc/core/sd.c
>> +++ b/drivers/mmc/core/sd.c
>> @@ -731,7 +731,7 @@ try_again:
>> */
>> if (!mmc_host_is_spi(host) && rocr &&
>> ((*rocr & 0x41000000) == 0x41000000)) {
>> - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
>> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, 1);
>> if (err) {
>> ocr &= ~SD_OCR_S18R;
>> goto try_again;
>> @@ -1104,7 +1104,7 @@ int mmc_attach_sd(struct mmc_host *host)
>> WARN_ON(!host->claimed);
>>
>> /* Make sure we are at 3.3V signalling voltage */
>> - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
>> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);
>> if (err)
>> return err;
>>
>> --
>> 1.7.1
>>
>>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling
2011-05-11 9:47 ` Philip Rakity
@ 2011-05-12 1:53 ` Philip Rakity
2011-05-13 8:03 ` zhangfei gao
1 sibling, 0 replies; 54+ messages in thread
From: Philip Rakity @ 2011-05-12 1:53 UTC (permalink / raw)
To: Philip Rakity
Cc: zhangfei gao, Arindam Nath, cjb@laptop.org,
subhashj@codeaurora.org, linux-mmc@vger.kernel.org,
henry.su@amd.com, aaron.lu@amd.com, anath.amd@gmail.com
resend
On May 11, 2011, at 2:47 AM, Philip Rakity wrote:
>
> On May 11, 2011, at 2:28 AM, zhangfei gao wrote:
>
>> On Wed, May 11, 2011 at 4:52 AM, Philip Rakity <prakity@marvell.com> wrote:
>>>
>>> \
>>> On May 11, 2011, at 1:48 AM, zhangfei gao wrote:
>>>
>>>> On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
>>>>> Marvell controller requires 1.8V bit in UHS control register 2
>>>>> be set when doing UHS. eMMC does not require 1.8V for DDR.
>>>>> add platform code to handle this.
>>>>>
>>>>> Signed-off-by: Philip Rakity <prakity@marvell.com>
>>>>> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
>>>>> ---
>>>>> drivers/mmc/host/sdhci-pxa.c | 36 ++++++++++++++++++++++++++++++++++++
>>>>> 1 files changed, 36 insertions(+), 0 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
>>>>> index 5a61208..b52c3e6 100644
>>>>> --- a/drivers/mmc/host/sdhci-pxa.c
>>>>> +++ b/drivers/mmc/host/sdhci-pxa.c
>>>>> @@ -69,7 +69,40 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
>>>>> }
>>>>> }
>>>>>
>>>>> +static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
>>>>> +{
>>>>> + u16 ctrl_2;
>>>>> +
>>>>> + /*
>>>>> + * Set V18_EN -- UHS modes do not work without this.
>>>>> + * does not change signaling voltage
>>>>> + */
>>>>> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>>>>> +
>>>>> + /* Select Bus Speed Mode for host */
>>>>> + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
>>>>> + if (uhs == MMC_TIMING_UHS_SDR12)
>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
>>>>> + else if (uhs == MMC_TIMING_UHS_SDR25)
>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
>>>>> + else if (uhs == MMC_TIMING_UHS_SDR50) {
>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>> + } else if (uhs == MMC_TIMING_UHS_SDR104) {
>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>> + } else if (uhs == MMC_TIMING_UHS_DDR50) {
>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>> + }
>>>>> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
>>>>> + pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
>>>>> + __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
>>>>> + return 0;
>>>>> +}
>>>>
>>>> Why move common register accessing from sdhci.c to specific driver?
>>>
>>> In general case you do not know what the specific host controller work arounds are required.
>>> on mmp2 this code works (no need for V18 for low speed UHS)
>>> but other controllers it could be different
>>
>> Sorry, not understand, the patch is for sd uhs card or for emmc?
>> For sd uhs card, no workaround is needed on mmp2, but require external
>> pmic to provide 1.8v io voltage.
>
>
> require 1.8V when setting UHS modes. See latest MMP2 Documentation.
>>
>>>
>>>>
>>>>> +
>>>>> static struct sdhci_ops sdhci_pxa_ops = {
>>>>> + .set_uhs_signaling = set_uhs_signaling,
>>>>> .set_clock = set_clock,
>>>>> };
>>>>>
>>>>> @@ -141,6 +174,9 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
>>>>> if (pdata->quirks)
>>>>> host->quirks |= pdata->quirks;
>>>>>
>>>>> + /* enable 1/8V DDR capable */
>>>>> + host->mmc->caps |= MMC_CAP_1_8V_DDR;
>>>>> +
>>>>> /* If slot design supports 8 bit data, indicate this to MMC. */
>>>>> if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
>>>>> host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>>>>> --
>>>>> 1.7.1
>>>>>
>>>>>
>>>
>>>
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 13/15] mmc eMMC signal voltage does not use CMD11
2011-05-11 15:01 ` Philip Rakity
@ 2011-05-12 1:53 ` Philip Rakity
0 siblings, 0 replies; 54+ messages in thread
From: Philip Rakity @ 2011-05-12 1:53 UTC (permalink / raw)
To: Philip Rakity
Cc: zhangfei gao, Arindam Nath, cjb@laptop.org,
subhashj@codeaurora.org, linux-mmc@vger.kernel.org,
henry.su@amd.com, aaron.lu@amd.com, anath.amd@gmail.com
resend
On May 11, 2011, at 8:01 AM, Philip Rakity wrote:
>
> On May 11, 2011, at 2:19 AM, zhangfei gao wrote:
>
>> On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
>>> eMMC chips do not use CMD11 when changing voltage. Add extra
>>> argument to call to indicate if CMD11 needs to be sent.
>>
>> mmc_set_signal_voltage or start_signal_voltage_switch may not support
>> emmc, not only cmd11, but also start_signal_voltage_switch is wrote
>> according to sd phys spec.
>
> the callback may go to a different driver then sdhci.c
>
>> JESD84-A441 A8.1 describes how to support dual voltage of emmc,
>> however it only support to 1.8v.
>> Still not find emmc card which could support 1.2v.
>
> eMMC
> vcc -- 3.3v then vccq can be 3.3 or 1.8 or 1.2v
> vcc -- 1.8v then vccq can be 1.8v or 1.2v
>
> 1.2 v vccq support is indicated in the CARD TYPE [196]
>
> a standard SD host controller does not support 1.2v vccq but folks could extend the standard.
>
> eMMC is not expected to be voltage switched. The expectation is the board designer will
> fix the voltages and no changes are needed.
>
> vccq is independent of vcc. see section 12.5.3.
>
>
>>
>>>
>>> Signed-off-by: Philip Rakity <prakity@marvell.com>
>>> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
>>> ---
>>> drivers/mmc/core/core.c | 4 ++--
>>> drivers/mmc/core/core.h | 3 ++-
>>> drivers/mmc/core/sd.c | 4 ++--
>>> 3 files changed, 6 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>>> index 78a9b51..74b4409 100644
>>> --- a/drivers/mmc/core/core.c
>>> +++ b/drivers/mmc/core/core.c
>>> @@ -942,7 +942,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
>>> return ocr;
>>> }
>>>
>>> -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
>>> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, int cmd11)
>>> {
>>> struct mmc_command cmd;
>>> int err = 0;
>>> @@ -953,7 +953,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
>>> * Send CMD11 only if the request is to switch the card to
>>> * 1.8V signalling.
>>> */
>>> - if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
>>> + if ((signal_voltage != MMC_SIGNAL_VOLTAGE_330) && cmd11) {
>>> memset(&cmd, 0, sizeof(struct mmc_command));
>>>
>>> cmd.opcode = SD_SWITCH_VOLTAGE;
>>> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
>>> index 93f3397..3c11e17 100644
>>> --- a/drivers/mmc/core/core.h
>>> +++ b/drivers/mmc/core/core.h
>>> @@ -41,7 +41,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
>>> void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
>>> unsigned int ddr);
>>> u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
>>> -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
>>> +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
>>> + int cmd11);
>>> void mmc_set_timing(struct mmc_host *host, unsigned int timing);
>>> void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
>>>
>>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>>> index 851c8fc..d2a05ab 100644
>>> --- a/drivers/mmc/core/sd.c
>>> +++ b/drivers/mmc/core/sd.c
>>> @@ -731,7 +731,7 @@ try_again:
>>> */
>>> if (!mmc_host_is_spi(host) && rocr &&
>>> ((*rocr & 0x41000000) == 0x41000000)) {
>>> - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
>>> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, 1);
>>> if (err) {
>>> ocr &= ~SD_OCR_S18R;
>>> goto try_again;
>>> @@ -1104,7 +1104,7 @@ int mmc_attach_sd(struct mmc_host *host)
>>> WARN_ON(!host->claimed);
>>>
>>> /* Make sure we are at 3.3V signalling voltage */
>>> - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
>>> + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);
>>> if (err)
>>> return err;
>>>
>>> --
>>> 1.7.1
>>>
>>>
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling
2011-05-11 9:47 ` Philip Rakity
2011-05-12 1:53 ` Philip Rakity
@ 2011-05-13 8:03 ` zhangfei gao
2011-05-15 21:42 ` Philip Rakity
1 sibling, 1 reply; 54+ messages in thread
From: zhangfei gao @ 2011-05-13 8:03 UTC (permalink / raw)
To: Philip Rakity
Cc: Arindam Nath, cjb@laptop.org, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On Wed, May 11, 2011 at 5:47 AM, Philip Rakity <prakity@marvell.com> wrote:
>
> On May 11, 2011, at 2:28 AM, zhangfei gao wrote:
>
>> On Wed, May 11, 2011 at 4:52 AM, Philip Rakity <prakity@marvell.com> wrote:
>>>
>>> \
>>> On May 11, 2011, at 1:48 AM, zhangfei gao wrote:
>>>
>>>> On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
>>>>> Marvell controller requires 1.8V bit in UHS control register 2
>>>>> be set when doing UHS. eMMC does not require 1.8V for DDR.
>>>>> add platform code to handle this.
>>>>>
>>>>> Signed-off-by: Philip Rakity <prakity@marvell.com>
>>>>> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
>>>>> ---
>>>>> drivers/mmc/host/sdhci-pxa.c | 36 ++++++++++++++++++++++++++++++++++++
>>>>> 1 files changed, 36 insertions(+), 0 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
>>>>> index 5a61208..b52c3e6 100644
>>>>> --- a/drivers/mmc/host/sdhci-pxa.c
>>>>> +++ b/drivers/mmc/host/sdhci-pxa.c
>>>>> @@ -69,7 +69,40 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
>>>>> }
>>>>> }
>>>>>
>>>>> +static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
>>>>> +{
>>>>> + u16 ctrl_2;
>>>>> +
>>>>> + /*
>>>>> + * Set V18_EN -- UHS modes do not work without this.
>>>>> + * does not change signaling voltage
>>>>> + */
>>>>> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>>>>> +
>>>>> + /* Select Bus Speed Mode for host */
>>>>> + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
>>>>> + if (uhs == MMC_TIMING_UHS_SDR12)
>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
>>>>> + else if (uhs == MMC_TIMING_UHS_SDR25)
>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
>>>>> + else if (uhs == MMC_TIMING_UHS_SDR50) {
>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>> + } else if (uhs == MMC_TIMING_UHS_SDR104) {
>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>> + } else if (uhs == MMC_TIMING_UHS_DDR50) {
>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>> + }
>>>>> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
>>>>> + pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
>>>>> + __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
>>>>> + return 0;
>>>>> +}
>>>>
>>>> Why move common register accessing from sdhci.c to specific driver?
>>>
>>> In general case you do not know what the specific host controller work arounds are required.
>>> on mmp2 this code works (no need for V18 for low speed UHS)
>>> but other controllers it could be different
>>
>> Sorry, not understand, the patch is for sd uhs card or for emmc?
>> For sd uhs card, no workaround is needed on mmp2, but require external
>> pmic to provide 1.8v io voltage.
>
>
> require 1.8V when setting UHS modes. See latest MMP2 Documentation.
Do you mean set SDHCI_CTRL_VDD_180?
It is already set in sdhci_start_signal_voltage_switch, so does not
required in specific driver.
>>
>>>
>>>>
>>>>> +
>>>>> static struct sdhci_ops sdhci_pxa_ops = {
>>>>> + .set_uhs_signaling = set_uhs_signaling,
>>>>> .set_clock = set_clock,
>>>>> };
>>>>>
>>>>> @@ -141,6 +174,9 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
>>>>> if (pdata->quirks)
>>>>> host->quirks |= pdata->quirks;
>>>>>
>>>>> + /* enable 1/8V DDR capable */
>>>>> + host->mmc->caps |= MMC_CAP_1_8V_DDR;
>>>>> +
>>>>> /* If slot design supports 8 bit data, indicate this to MMC. */
>>>>> if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
>>>>> host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>>>>> --
>>>>> 1.7.1
>>>>>
>>>>>
>>>
>>>
>
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling
2011-05-13 8:03 ` zhangfei gao
@ 2011-05-15 21:42 ` Philip Rakity
2011-05-16 5:57 ` zhangfei gao
0 siblings, 1 reply; 54+ messages in thread
From: Philip Rakity @ 2011-05-15 21:42 UTC (permalink / raw)
To: zhangfei gao
Cc: Arindam Nath, cjb@laptop.org, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On May 13, 2011, at 1:03 AM, zhangfei gao wrote:
> On Wed, May 11, 2011 at 5:47 AM, Philip Rakity <prakity@marvell.com> wrote:
>>
>> On May 11, 2011, at 2:28 AM, zhangfei gao wrote:
>>
>>> On Wed, May 11, 2011 at 4:52 AM, Philip Rakity <prakity@marvell.com> wrote:
>>>>
>>>> \
>>>> On May 11, 2011, at 1:48 AM, zhangfei gao wrote:
>>>>
>>>>> On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
>>>>>> Marvell controller requires 1.8V bit in UHS control register 2
>>>>>> be set when doing UHS. eMMC does not require 1.8V for DDR.
>>>>>> add platform code to handle this.
>>>>>>
>>>>>> Signed-off-by: Philip Rakity <prakity@marvell.com>
>>>>>> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
>>>>>> ---
>>>>>> drivers/mmc/host/sdhci-pxa.c | 36 ++++++++++++++++++++++++++++++++++++
>>>>>> 1 files changed, 36 insertions(+), 0 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
>>>>>> index 5a61208..b52c3e6 100644
>>>>>> --- a/drivers/mmc/host/sdhci-pxa.c
>>>>>> +++ b/drivers/mmc/host/sdhci-pxa.c
>>>>>> @@ -69,7 +69,40 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
>>>>>> }
>>>>>> }
>>>>>>
>>>>>> +static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
>>>>>> +{
>>>>>> + u16 ctrl_2;
>>>>>> +
>>>>>> + /*
>>>>>> + * Set V18_EN -- UHS modes do not work without this.
>>>>>> + * does not change signaling voltage
>>>>>> + */
>>>>>> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>>>>>> +
>>>>>> + /* Select Bus Speed Mode for host */
>>>>>> + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
>>>>>> + if (uhs == MMC_TIMING_UHS_SDR12)
>>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
>>>>>> + else if (uhs == MMC_TIMING_UHS_SDR25)
>>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
>>>>>> + else if (uhs == MMC_TIMING_UHS_SDR50) {
>>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
>>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>>> + } else if (uhs == MMC_TIMING_UHS_SDR104) {
>>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
>>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>>> + } else if (uhs == MMC_TIMING_UHS_DDR50) {
>>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
>>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>>> + }
>>>>>> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
>>>>>> + pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
>>>>>> + __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
>>>>>> + return 0;
>>>>>> +}
>>>>>
>>>>> Why move common register accessing from sdhci.c to specific driver?
>>>>
>>>> In general case you do not know what the specific host controller work arounds are required.
>>>> on mmp2 this code works (no need for V18 for low speed UHS)
>>>> but other controllers it could be different
>>>
>>> Sorry, not understand, the patch is for sd uhs card or for emmc?
>>> For sd uhs card, no workaround is needed on mmp2, but require external
>>> pmic to provide 1.8v io voltage.
>>
>>
>> require 1.8V when setting UHS modes. See latest MMP2 Documentation.
>
> Do you mean set SDHCI_CTRL_VDD_180?
> It is already set in sdhci_start_signal_voltage_switch, so does not
> required in specific driver.
eMMC voltages are fixed. DDR support is available at 3.3v vcc and 3.3v vccq.
There is no voltage switch necessary. The card type value indicates that 3.3 vccq
or 1.8v vccq is available. Since we are communicating with the card DDR must
be available without a 1.8v vccq voltage switch.
mmp2 has the requirement that to do DDR at any vccq we must set the VDD_180 bit.
This bit set + DDR mode bit enables DDR. DDR mode bit on its own is not sufficient.
>
>>>
>>>>
>>>>>
>>>>>> +
>>>>>> static struct sdhci_ops sdhci_pxa_ops = {
>>>>>> + .set_uhs_signaling = set_uhs_signaling,
>>>>>> .set_clock = set_clock,
>>>>>> };
>>>>>>
>>>>>> @@ -141,6 +174,9 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
>>>>>> if (pdata->quirks)
>>>>>> host->quirks |= pdata->quirks;
>>>>>>
>>>>>> + /* enable 1/8V DDR capable */
>>>>>> + host->mmc->caps |= MMC_CAP_1_8V_DDR;
>>>>>> +
>>>>>> /* If slot design supports 8 bit data, indicate this to MMC. */
>>>>>> if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
>>>>>> host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>>>>>> --
>>>>>> 1.7.1
>>>>>>
>>>>>>
>>>>
>>>>
>>
>>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling
2011-05-15 21:42 ` Philip Rakity
@ 2011-05-16 5:57 ` zhangfei gao
0 siblings, 0 replies; 54+ messages in thread
From: zhangfei gao @ 2011-05-16 5:57 UTC (permalink / raw)
To: Philip Rakity
Cc: Arindam Nath, cjb@laptop.org, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On Sun, May 15, 2011 at 5:42 PM, Philip Rakity <prakity@marvell.com> wrote:
>
> On May 13, 2011, at 1:03 AM, zhangfei gao wrote:
>
>> On Wed, May 11, 2011 at 5:47 AM, Philip Rakity <prakity@marvell.com> wrote:
>>>
>>> On May 11, 2011, at 2:28 AM, zhangfei gao wrote:
>>>
>>>> On Wed, May 11, 2011 at 4:52 AM, Philip Rakity <prakity@marvell.com> wrote:
>>>>>
>>>>> \
>>>>> On May 11, 2011, at 1:48 AM, zhangfei gao wrote:
>>>>>
>>>>>> On Thu, May 5, 2011 at 2:49 AM, Arindam Nath <arindam.nath@amd.com> wrote:
>>>>>>> Marvell controller requires 1.8V bit in UHS control register 2
>>>>>>> be set when doing UHS. eMMC does not require 1.8V for DDR.
>>>>>>> add platform code to handle this.
>>>>>>>
>>>>>>> Signed-off-by: Philip Rakity <prakity@marvell.com>
>>>>>>> Reviewed-by: Arindam Nath <arindam.nath@amd.com>
>>>>>>> ---
>>>>>>> drivers/mmc/host/sdhci-pxa.c | 36 ++++++++++++++++++++++++++++++++++++
>>>>>>> 1 files changed, 36 insertions(+), 0 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
>>>>>>> index 5a61208..b52c3e6 100644
>>>>>>> --- a/drivers/mmc/host/sdhci-pxa.c
>>>>>>> +++ b/drivers/mmc/host/sdhci-pxa.c
>>>>>>> @@ -69,7 +69,40 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
>>>>>>> }
>>>>>>> }
>>>>>>>
>>>>>>> +static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
>>>>>>> +{
>>>>>>> + u16 ctrl_2;
>>>>>>> +
>>>>>>> + /*
>>>>>>> + * Set V18_EN -- UHS modes do not work without this.
>>>>>>> + * does not change signaling voltage
>>>>>>> + */
>>>>>>> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>>>>>>> +
>>>>>>> + /* Select Bus Speed Mode for host */
>>>>>>> + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
>>>>>>> + if (uhs == MMC_TIMING_UHS_SDR12)
>>>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
>>>>>>> + else if (uhs == MMC_TIMING_UHS_SDR25)
>>>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
>>>>>>> + else if (uhs == MMC_TIMING_UHS_SDR50) {
>>>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
>>>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>>>> + } else if (uhs == MMC_TIMING_UHS_SDR104) {
>>>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
>>>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>>>> + } else if (uhs == MMC_TIMING_UHS_DDR50) {
>>>>>>> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
>>>>>>> + ctrl_2 |= SDHCI_CTRL_VDD_180;
>>>>>>> + }
>>>>>>> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
>>>>>>> + pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
>>>>>>> + __func__, mmc_hostname(host->mmc), uhs, ctrl_2);
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>
>>>>>> Why move common register accessing from sdhci.c to specific driver?
>>>>>
>>>>> In general case you do not know what the specific host controller work arounds are required.
>>>>> on mmp2 this code works (no need for V18 for low speed UHS)
>>>>> but other controllers it could be different
>>>>
>>>> Sorry, not understand, the patch is for sd uhs card or for emmc?
>>>> For sd uhs card, no workaround is needed on mmp2, but require external
>>>> pmic to provide 1.8v io voltage.
>>>
>>>
>>> require 1.8V when setting UHS modes. See latest MMP2 Documentation.
>>
>> Do you mean set SDHCI_CTRL_VDD_180?
>> It is already set in sdhci_start_signal_voltage_switch, so does not
>> required in specific driver.
>
>
> eMMC voltages are fixed. DDR support is available at 3.3v vcc and 3.3v vccq.
> There is no voltage switch necessary. The card type value indicates that 3.3 vccq
> or 1.8v vccq is available. Since we are communicating with the card DDR must
> be available without a 1.8v vccq voltage switch.
>
> mmp2 has the requirement that to do DDR at any vccq we must set the VDD_180 bit.
> This bit set + DDR mode bit enables DDR. DDR mode bit on its own is not sufficient.
It's fine to mmp2 for emmc ddr50 mode.
Just concern other controller also need to set SDHCI_CTRL_VDD_180 via
the call back , since "UHS Mode Select" description is "UHS-I mode is
effective when SDHCI_CTRL_VDD_180 is set to 1", regardless of DDR50
mode work at 3.3v or 1.8v.
>
>
>
>
>
>>
>>>>
>>>>>
>>>>>>
>>>>>>> +
>>>>>>> static struct sdhci_ops sdhci_pxa_ops = {
>>>>>>> + .set_uhs_signaling = set_uhs_signaling,
>>>>>>> .set_clock = set_clock,
>>>>>>> };
>>>>>>>
>>>>>>> @@ -141,6 +174,9 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
>>>>>>> if (pdata->quirks)
>>>>>>> host->quirks |= pdata->quirks;
>>>>>>>
>>>>>>> + /* enable 1/8V DDR capable */
>>>>>>> + host->mmc->caps |= MMC_CAP_1_8V_DDR;
>>>>>>> +
>>>>>>> /* If slot design supports 8 bit data, indicate this to MMC. */
>>>>>>> if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
>>>>>>> host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>>>>>>> --
>>>>>>> 1.7.1
>>>>>>>
>>>>>>>
>>>>>
>>>>>
>>>
>>>
>
>
^ permalink raw reply [flat|nested] 54+ messages in thread
end of thread, other threads:[~2011-05-16 5:57 UTC | newest]
Thread overview: 54+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-05-05 6:48 [PATCH v4 00/15] add support for host controller v3.00 Arindam Nath
2011-05-05 6:48 ` [PATCH v4 01/15] mmc: sd: add support for signal voltage switch procedure Arindam Nath
2011-05-06 10:36 ` zhangfei gao
2011-05-11 3:34 ` Chris Ball
2011-05-05 6:48 ` [PATCH v4 02/15] mmc: sd: query function modes for uhs cards Arindam Nath
2011-05-06 10:37 ` zhangfei gao
2011-05-11 3:34 ` Chris Ball
2011-05-05 6:48 ` [PATCH v4 03/15] mmc: sd: add support for driver type selection Arindam Nath
2011-05-06 10:37 ` zhangfei gao
2011-05-11 3:34 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 04/15] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
2011-05-06 10:38 ` zhangfei gao
2011-05-11 3:36 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 05/15] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
2011-05-06 10:41 ` zhangfei gao
2011-05-11 3:36 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 06/15] mmc: sd: set current limit for uhs cards Arindam Nath
2011-05-06 10:38 ` zhangfei gao
2011-05-11 3:37 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 07/15] mmc: sd: report correct speed and capacity of " Arindam Nath
2011-05-06 10:39 ` zhangfei gao
2011-05-11 3:37 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 08/15] mmc: sd: add support for tuning during uhs initialization Arindam Nath
2011-05-06 10:39 ` zhangfei gao
2011-05-11 3:38 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 09/15] mmc: sdhci: enable preset value after " Arindam Nath
2011-05-06 10:40 ` zhangfei gao
2011-05-11 3:38 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 10/15] mmc: sdhci: add support for programmable clock mode Arindam Nath
2011-05-06 10:40 ` zhangfei gao
2011-05-11 3:39 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 11/15] mmc: sdhci: add support for retuning mode 1 Arindam Nath
2011-05-06 10:40 ` zhangfei gao
2011-05-11 3:39 ` Chris Ball
2011-05-05 6:49 ` [PATCH v4 12/15] sdhci pxa add platform specific code for UHS signaling Arindam Nath
2011-05-11 1:54 ` Chris Ball
2011-05-11 8:48 ` zhangfei gao
2011-05-11 8:52 ` Philip Rakity
2011-05-11 9:28 ` zhangfei gao
2011-05-11 9:47 ` Philip Rakity
2011-05-12 1:53 ` Philip Rakity
2011-05-13 8:03 ` zhangfei gao
2011-05-15 21:42 ` Philip Rakity
2011-05-16 5:57 ` zhangfei gao
2011-05-05 6:49 ` [PATCH v4 13/15] mmc eMMC signal voltage does not use CMD11 Arindam Nath
2011-05-11 1:51 ` Chris Ball
2011-05-11 9:19 ` zhangfei gao
2011-05-11 15:01 ` Philip Rakity
2011-05-12 1:53 ` Philip Rakity
2011-05-05 6:49 ` [PATCH v4 14/15] sdhci add hooks for UHS setting by platform specific code Arindam Nath
2011-05-05 6:49 ` [PATCH v4 15/15] mmc add support for eMMC Dual Data Rate Arindam Nath
2011-05-05 8:18 ` [PATCH v4 00/15] add support for host controller v3.00 Nath, Arindam
2011-05-11 3:43 ` Chris Ball
2011-05-11 6:13 ` Nath, Arindam
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).