* [PATCH v2 00/12] add support for host controller v3.00
@ 2011-03-04 11:32 Arindam Nath
2011-03-04 11:32 ` [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
` (13 more replies)
0 siblings, 14 replies; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd, Arindam Nath
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 (12):
[PATCH 01/12] mmc: sdhci: add support for auto CMD23
[PATCH 02/12] mmc: sd: add support for signal voltage switch procedure
[PATCH 03/12] mmc: sd: query function modes for uhs cards
[PATCH 04/12] mmc: sd: add support for driver type selection
[PATCH 05/12] mmc: sdhci: reset sdclk before setting high speed enable
[PATCH 06/12] mmc: sd: add support for uhs bus speed mode selection
[PATCH 07/12] mmc: sd: set current limit for uhs cards
[PATCH 08/12] mmc: sd: report correct speed and capacity of uhs cards
[PATCH 09/12] mmc: sd: add support for tuning during uhs initialization
[PATCH 10/12] mmc: sdhci: enable preset value after uhs initialization
[PATCH 11/12] mmc: sdhci: add support for programmable clock mode
[PATCH 12/12] mmc: sdhci: add support for retuning mode 1
drivers/mmc/core/bus.c | 11 +-
drivers/mmc/core/core.c | 9 +
drivers/mmc/core/core.h | 1 +
drivers/mmc/core/sd.c | 436 +++++++++++++++++++++++++---
drivers/mmc/core/sd.h | 3 +-
drivers/mmc/core/sd_ops.c | 32 ++
drivers/mmc/core/sd_ops.h | 1 +
drivers/mmc/core/sdio.c | 3 +-
drivers/mmc/host/sdhci.c | 697 ++++++++++++++++++++++++++++++++++++++++++---
drivers/mmc/host/sdhci.h | 45 +++-
include/linux/mmc/card.h | 43 +++
include/linux/mmc/host.h | 26 ++
include/linux/mmc/mmc.h | 1 +
include/linux/mmc/sd.h | 3 +-
include/linux/mmc/sdhci.h | 13 +
15 files changed, 1228 insertions(+), 96 deletions(-)
^ permalink raw reply [flat|nested] 125+ messages in thread
* [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-09 12:22 ` subhashj
2011-03-15 11:23 ` Subhash Jadavani
2011-03-04 11:32 ` [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
` (12 subsequent siblings)
13 siblings, 2 replies; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd, Arindam Nath
Host Controller v3.00 and later support Auto CMD23 in the Transfer
Mode register. Since Auto CMD23 can be used with or without DMA,
and if used with DMA, it should _only_ be ADMA, we check against
SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
is set.
A new definition for SDHCI_ARGUMENT2 register has been added in v3.00
spec, which is the same as SDHCI_DMA_ADDRESS. We program the block
count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to stop
multiple block transfers. But during error recovery procedure, we will
need to send Abort command, so we use a variable saved_abort_cmd to
save the stop command to be used later.
Two bits are added to SCR register as per the Physical Layer Spec v3.01,
which specify whether the card supports CMD20 and/or CMD23. We use this
as one of the conditions to decide whether to enable Auto CMD23 or not.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 6 ++++
drivers/mmc/host/sdhci.c | 69 +++++++++++++++++++++++++++++++++++++++++---
drivers/mmc/host/sdhci.h | 2 +
include/linux/mmc/card.h | 4 ++
include/linux/mmc/sd.h | 2 +-
include/linux/mmc/sdhci.h | 2 +
6 files changed, 79 insertions(+), 6 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index d18c32b..b3f8a3c 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -188,6 +188,12 @@ 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 (scr->sda_spec3)
+ scr->cmd_support = UNSTUFF_BITS(resp, 32, 2);
+ }
if (UNSTUFF_BITS(resp, 55, 1))
card->erased_byte = 0xFF;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9e15f41..8914a25 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -25,6 +25,7 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
#include "sdhci.h"
@@ -812,6 +813,30 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
}
+/*
+ * Does the Host Controller support Auto CMD23?
+ *
+ * There are four preconditions for Auto CMD23 to be supported:
+ * 1. Host Controller v3.00 or later
+ * 2. Card supports CMD23
+ * 3. If DMA is used, it should be ADMA
+ * 4. Only when CMD18 or CMD25 is issued
+ */
+static int sdhci_host_auto_cmd23_supported(struct sdhci_host *host,
+ struct mmc_request *mrq)
+{
+ struct mmc_card *card = host->mmc->card;
+
+ if ((host->version >= SDHCI_SPEC_300) &&
+ card && (card->scr.cmd_support & SD_SCR_CMD23_SUPPORT) &&
+ !(host->flags & SDHCI_USE_SDMA) &&
+ ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
+ (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)))
+ return 1;
+
+ return 0;
+}
+
static void sdhci_set_transfer_mode(struct sdhci_host *host,
struct mmc_data *data)
{
@@ -825,10 +850,21 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
mode = SDHCI_TRNS_BLK_CNT_EN;
if (data->blocks > 1) {
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
- mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
- else
- mode |= SDHCI_TRNS_MULTI;
+ mode |= SDHCI_TRNS_ACMD12;
+ else if (sdhci_host_auto_cmd23_supported(host, host->mrq)) {
+ /*
+ * Host Controller v3.00 can automatically send CMD23
+ * before CMD18 or CMD25. For that, we need to enable
+ * Auto CMD23 in the Transfer Mode register and
+ * program the block count in Argument 2 register.
+ */
+ mode |= SDHCI_TRNS_AUTO_CMD23;
+ sdhci_writel(host, data->blocks, SDHCI_ARGUMENT2);
+ }
+
+ mode |= SDHCI_TRNS_MULTI;
}
+
if (data->flags & MMC_DATA_READ)
mode |= SDHCI_TRNS_READ;
if (host->flags & SDHCI_REQ_USE_DMA)
@@ -1131,6 +1167,23 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
#ifndef SDHCI_USE_LEDS_CLASS
sdhci_activate_led(host);
#endif
+ /*
+ * Since the block count for CMD23 has already been specified in the
+ * Argument 2 register, we don't need CMD12 to stop multiple block
+ * transfers.
+ */
+ if (sdhci_host_auto_cmd23_supported(host, mrq)) {
+ if (mrq->stop) {
+ /*
+ * Save the stop command here to be used during
+ * error recovery
+ */
+ host->saved_abort_cmd = mrq->data->stop;
+ mrq->data->stop = NULL;
+ mrq->stop = NULL;
+ }
+ }
+
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
if (mrq->stop) {
mrq->data->stop = NULL;
@@ -1396,6 +1449,7 @@ static void sdhci_timeout_timer(unsigned long data)
if (host->data) {
host->data->error = -ETIMEDOUT;
+ host->data->stop = host->saved_abort_cmd;
sdhci_finish_data(host);
} else {
if (host->cmd)
@@ -1534,9 +1588,10 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
host->data->error = -EIO;
}
- if (host->data->error)
+ if (host->data->error) {
+ host->data->stop = host->saved_abort_cmd;
sdhci_finish_data(host);
- else {
+ } else {
if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
sdhci_transfer_pio(host);
@@ -1792,6 +1847,10 @@ int sdhci_add_host(struct sdhci_host *host)
host->flags &= ~SDHCI_USE_ADMA;
}
+ /* If the host can perform ADMA operation, we reset SDMA flag */
+ if (host->flags & SDHCI_USE_ADMA)
+ host->flags &= ~SDHCI_USE_SDMA;
+
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma) {
if (host->ops->enable_dma(host)) {
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 6e0969e..223762c 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -25,6 +25,7 @@
*/
#define SDHCI_DMA_ADDRESS 0x00
+#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
#define SDHCI_BLOCK_SIZE 0x04
#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
@@ -37,6 +38,7 @@
#define SDHCI_TRNS_DMA 0x01
#define SDHCI_TRNS_BLK_CNT_EN 0x02
#define SDHCI_TRNS_ACMD12 0x04
+#define SDHCI_TRNS_AUTO_CMD23 0x08
#define SDHCI_TRNS_READ 0x10
#define SDHCI_TRNS_MULTI 0x20
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 8ce0827..22b0335 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -58,9 +58,13 @@ 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)
+ unsigned char cmd_support;
+#define SD_SCR_CMD20_SUPPORT (1<<0)
+#define SD_SCR_CMD23_SUPPORT (1<<1)
};
struct sd_ssr {
diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
index 3fd85e0..178363b 100644
--- a/include/linux/mmc/sd.h
+++ b/include/linux/mmc/sd.h
@@ -59,7 +59,7 @@
#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */
#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */
-#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 */
+#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 - 3.0x */
/*
* SD bus widths
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 83bd9f7..282d158 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -145,6 +145,8 @@ struct sdhci_host {
unsigned int ocr_avail_sd;
unsigned int ocr_avail_mmc;
+ struct mmc_command *saved_abort_cmd; /* Abort command saved for data error recovery */
+
unsigned long private[0] ____cacheline_aligned;
};
#endif /* __SDHCI_H */
--
1.7.1
^ permalink raw reply related [flat|nested] 125+ messages in thread
* [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
2011-03-04 11:32 ` [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-04 11:47 ` Wolfram Sang
` (5 more replies)
2011-03-04 11:32 ` [PATCH v2 03/12] mmc: sd: query function modes for uhs cards Arindam Nath
` (11 subsequent siblings)
13 siblings, 6 replies; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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 support 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.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 37 ++++++++++-
drivers/mmc/core/sd_ops.c | 32 ++++++++++
drivers/mmc/core/sd_ops.h | 1 +
drivers/mmc/host/sdhci.c | 148 +++++++++++++++++++++++++++++++++++++++++----
drivers/mmc/host/sdhci.h | 18 +++++-
include/linux/mmc/host.h | 10 +++
include/linux/mmc/sd.h | 1 +
7 files changed, 229 insertions(+), 18 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index b3f8a3c..3e82599 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -408,6 +408,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
@@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
if (!err)
ocr |= 1 << 30;
- err = mmc_send_app_op_cond(host, ocr, NULL);
+ /* If the host can supply more than 150mA, XPC should be set to 1. */
+ if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
+ MMC_CAP_SET_XPC_180))
+ ocr |= 1 << 28;
+
+ 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)) {
+ err = mmc_start_voltage_switch(host);
+ if (err)
+ return err;
+ }
+
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
@@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
}
/*
+ * 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))
+ host->ocr |= (1 << 24);
+
+ /*
* Detect and init the card.
*/
err = mmc_sd_init_card(host, host->ocr, NULL);
- if (err)
- goto err;
+ if (err == -EAGAIN) {
+ /*
+ * Retry initialization with S18R set to 0.
+ */
+ host->ocr &= ~(1 << 24);
+ err = mmc_sd_init_card(host, host->ocr, NULL);
+ if (err)
+ goto err;
+ }
mmc_release_host(host);
err = mmc_add_card(host->card);
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 797cdb5..8a23e2e 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
return 0;
}
+int mmc_start_voltage_switch(struct mmc_host *host)
+{
+ struct mmc_command cmd;
+ int err;
+
+ BUG_ON(!host);
+
+ /*
+ * If the host does not provide signal voltage switch procedure, we
+ * set S18R to 0, and then retry initialization sequence.
+ */
+ if (!host->ops->start_signal_voltage_switch)
+ return -EAGAIN;
+
+ 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;
+
+ err = host->ops->start_signal_voltage_switch(host);
+
+ return err;
+}
+
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index ffc2305..3cfba59 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp);
int mmc_app_sd_status(struct mmc_card *card, void *ssr);
+int mmc_start_voltage_switch(struct mmc_host *host);
#endif
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 8914a25..5487a0b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -85,6 +85,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",
@@ -1337,11 +1339,70 @@ out:
spin_unlock_irqrestore(&host->lock, flags);
}
+static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
+{
+ struct sdhci_host *host;
+ u8 pwr;
+ u16 clk, ctrl;
+ u32 present_state;
+
+ host = mmc_priv(mmc);
+
+ /* Stop SDCLK */
+ host = mmc_priv(mmc);
+ 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_readw(host, SDHCI_HOST_CONTROL2);
+ 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. Stop
+ * power to 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);
+
+ return -EAGAIN;
+}
+
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,
};
/*****************************************************************************\
@@ -1799,7 +1860,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);
@@ -1822,12 +1885,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);
+ if (host->version >= SDHCI_SPEC_300)
+ caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
+
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;
@@ -1838,7 +1904,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) &&
@@ -1892,10 +1959,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;
@@ -1911,7 +1978,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);
@@ -1923,7 +1990,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;
/*
@@ -1950,21 +2017,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)
@@ -2024,7 +2146,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 223762c..95d70e6 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -69,6 +69,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
@@ -147,7 +149,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
@@ -168,9 +171,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 bcb793e..ad7daa3 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -117,6 +117,8 @@ struct mmc_host_ops {
/* optional callback for HC quirks */
void (*init_card)(struct mmc_host *host, struct mmc_card *card);
+
+ int (*start_signal_voltage_switch)(struct mmc_host *host);
};
struct mmc_card;
@@ -173,6 +175,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 178363b..3ba5aa6 100644
--- a/include/linux/mmc/sd.h
+++ b/include/linux/mmc/sd.h
@@ -17,6 +17,7 @@
/* This is basically the same command as for MMC with some quirks. */
#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
+#define SD_SWITCH_VOLTAGE 11 /* ac R1 */
/* class 10 */
#define SD_SWITCH 6 /* adtc [31:0] See below R1 */
--
1.7.1
^ permalink raw reply related [flat|nested] 125+ messages in thread
* [PATCH v2 03/12] mmc: sd: query function modes for uhs cards
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
2011-03-04 11:32 ` [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
2011-03-04 11:32 ` [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-09 14:08 ` subhashj
2011-03-04 11:32 ` [PATCH v2 04/12] mmc: sd: add support for driver type selection Arindam Nath
` (10 subsequent siblings)
13 siblings, 1 reply; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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>
---
drivers/mmc/core/sd.c | 99 +++++++++++++++++++++++++++++++++++++++-------
include/linux/mmc/card.h | 3 +
2 files changed, 87 insertions(+), 15 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 3e82599..a63956b 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -283,25 +283,94 @@ static int mmc_read_switch(struct mmc_card *card)
return -ENOMEM;
}
- 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 ((err != -EINVAL)
- && (err != -ENOSYS)
- && (err != -EFAULT))
+ if (card->scr.sda_spec3) {
+ /* First 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 ((err != -EINVAL)
+ && (err != -ENOSYS)
+ && (err != -EFAULT))
+ goto out;
+
+ printk(KERN_WARNING "%s: problem reading "
+ "Bus Speed modes.\n",
+ mmc_hostname(card->host));
+ err = 0;
+
goto out;
+ }
- printk(KERN_WARNING "%s: problem reading switch "
- "capabilities, performance might suffer.\n",
- mmc_hostname(card->host));
- err = 0;
+ card->sw_caps.uhs_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;
- }
+ goto out;
+ }
- if (status[13] & 0x02)
- card->sw_caps.hs_max_dtr = 50000000;
+ card->sw_caps.uhs_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.uhs_curr_limit = status[7];
+ } else {
+ 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 ((err != -EINVAL)
+ && (err != -ENOSYS)
+ && (err != -EFAULT))
+ goto out;
+
+ printk(KERN_WARNING "%s: problem reading switch "
+ "capabilities, performance might suffer.\n",
+ mmc_hostname(card->host));
+ err = 0;
+ goto out;
+ }
+
+ 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 22b0335..7080f22 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -75,6 +75,9 @@ struct sd_ssr {
struct sd_switch_caps {
unsigned int hs_max_dtr;
+ unsigned int uhs_bus_mode;
+ unsigned int uhs_drv_type;
+ unsigned int uhs_curr_limit;
};
struct sdio_cccr {
--
1.7.1
^ permalink raw reply related [flat|nested] 125+ messages in thread
* [PATCH v2 04/12] mmc: sd: add support for driver type selection
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (2 preceding siblings ...)
2011-03-04 11:32 ` [PATCH v2 03/12] mmc: sd: query function modes for uhs cards Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-09 5:33 ` Philip Rakity
2011-03-10 6:57 ` subhashj
2011-03-04 11:32 ` [PATCH v2 05/12] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
` (9 subsequent siblings)
13 siblings, 2 replies; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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. We then set the
appropriate driver type for the card using CMD6 mode 1. As per
Host Controller spec v3.00, we set driver type for the host only
if Preset Value Enable in the Host Control2 register is not set.
SDHCI_HOST_CONTROL has been renamed to SDHCI_HOST_CONTROL1 to
conform to the spec.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/core.c | 9 +++
drivers/mmc/core/core.h | 1 +
drivers/mmc/core/sd.c | 140 +++++++++++++++++++++++++++++++++++++---------
drivers/mmc/core/sd.h | 3 +-
drivers/mmc/core/sdio.c | 3 +-
drivers/mmc/host/sdhci.c | 48 ++++++++++++----
drivers/mmc/host/sdhci.h | 10 +++-
include/linux/mmc/card.h | 4 +
include/linux/mmc/host.h | 8 +++
9 files changed, 186 insertions(+), 40 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 6625c05..daa535a 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -947,6 +947,15 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing)
}
/*
+ * Select appropriate driver type for host.
+ */
+void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
+{
+ host->ios.drv_type = drv_type;
+ mmc_set_ios(host);
+}
+
+/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
* We then wait a bit for the power to stabilise. Finally,
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index ca1fdde..6114ca5 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -42,6 +42,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
unsigned int ddr);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
+void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
static inline void mmc_delay(unsigned int ms)
{
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index a63956b..f6a4fab 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -426,6 +426,86 @@ out:
return err;
}
+static int sd_select_driver_type(struct mmc_card *card, u8 *status)
+{
+ int host_set_drv_type, card_set_drv_type;
+ 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_set_drv_type = MMC_SET_DRIVER_TYPE_A;
+ if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
+ card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
+ else if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
+ card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
+ } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
+ host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
+ if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
+ card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
+ }
+
+ err = mmc_sd_switch(card, 1, 2, card_set_drv_type, status);
+ if (err)
+ return err;
+
+ if ((status[15] & 0xF) != card_set_drv_type)
+ printk(KERN_WARNING "%s: Problem setting driver strength!\n",
+ mmc_hostname(card->host));
+ else
+ mmc_set_driver_type(card->host, host_set_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;
+
+ err = -EIO;
+
+ 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],
@@ -474,10 +554,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
@@ -502,7 +582,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
MMC_CAP_SET_XPC_180))
ocr |= 1 << 28;
- err = mmc_send_app_op_cond(host, ocr, &rocr);
+ err = mmc_send_app_op_cond(host, ocr, rocr);
if (err)
return err;
@@ -510,7 +590,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
* 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)) {
+ if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x41000000)) {
err = mmc_start_voltage_switch(host);
if (err)
return err;
@@ -643,11 +723,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *card;
int err;
u32 cid[4];
+ u32 rocr;
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;
@@ -700,30 +781,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 & 0x01000000) {
+ 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 5c4a54d..6d16684 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -364,7 +364,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 5487a0b..1645687 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -63,7 +63,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));
@@ -216,18 +216,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
@@ -786,14 +786,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
* 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)) {
@@ -1252,7 +1252,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)
@@ -1265,10 +1265,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)
@@ -1277,7 +1277,25 @@ 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.
+ */
+ 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,
@@ -2037,6 +2055,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 95d70e6..a407b5b 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -72,7 +72,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
@@ -151,6 +151,11 @@
#define SDHCI_HOST_CONTROL2 0x3E
#define SDHCI_CTRL_VDD_180 0x0008
+#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
@@ -174,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 7080f22..2d7f7a3 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -77,6 +77,10 @@ struct sd_switch_caps {
unsigned int hs_max_dtr;
unsigned int uhs_bus_mode;
unsigned int uhs_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 uhs_curr_limit;
};
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index ad7daa3..bc2121e 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 drv_type; /* driver type (A, C, D) */
+
+#define MMC_SET_DRIVER_TYPE_A 1
+#define MMC_SET_DRIVER_TYPE_C 2
};
struct mmc_host_ops {
@@ -183,6 +188,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] 125+ messages in thread
* [PATCH v2 05/12] mmc: sdhci: reset sdclk before setting high speed enable
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (3 preceding siblings ...)
2011-03-04 11:32 ` [PATCH v2 04/12] mmc: sd: add support for driver type selection Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-05 4:57 ` Philip Rakity
2011-03-04 11:32 ` [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
` (8 subsequent siblings)
13 siblings, 1 reply; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/host/sdhci.c | 25 ++++++++++++++++++++++---
1 files changed, 22 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 1645687..5d3bb11 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1277,13 +1277,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.
@@ -1294,8 +1293,28 @@ 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;
+
+ /* 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 */
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
}
- }
+ } 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] 125+ messages in thread
* [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (4 preceding siblings ...)
2011-03-04 11:32 ` [PATCH v2 05/12] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-10 8:00 ` subhashj
2011-03-21 6:42 ` Subhash Jadavani
2011-03-04 11:32 ` [PATCH v2 07/12] mmc: sd: set current limit for uhs cards Arindam Nath
` (7 subsequent siblings)
13 siblings, 2 replies; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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. Since MMC_TIMING_UHS_SDR25 is same as
MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag for
UHS SDR25 again. We also 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 set the clock frequency for the
UHS-I mode selected.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
drivers/mmc/host/sdhci.h | 5 +++
include/linux/mmc/card.h | 16 ++++++++
include/linux/mmc/host.h | 4 ++
5 files changed, 146 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index f6a4fab..ec0d8e6 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -464,6 +464,92 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
return 0;
}
+static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
+{
+ unsigned int bus_speed, timing;
+ int err;
+
+ /*
+ * If the host doesn't support any of the UHS-I modes, fallback on
+ * default speed.
+ */
+ if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
+ return 0;
+
+ if (card->host->caps & MMC_CAP_UHS_SDR104) {
+ if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) {
+ bus_speed = UHS_SDR104_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR104;
+ card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
+ } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) {
+ bus_speed = UHS_SDR50_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR50;
+ card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
+ } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) {
+ bus_speed = UHS_DDR50_BUS_SPEED;
+ timing = MMC_TIMING_UHS_DDR50;
+ card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+ } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25) {
+ bus_speed = UHS_SDR25_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR25;
+ card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+ }
+ } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
+ if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
+ (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
+ bus_speed = UHS_SDR50_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR50;
+ card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
+ } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) {
+ bus_speed = UHS_DDR50_BUS_SPEED;
+ timing = MMC_TIMING_UHS_DDR50;
+ card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+ } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25) {
+ bus_speed = UHS_SDR25_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR25;
+ card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+ }
+ } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
+ if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
+ (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
+ (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
+ bus_speed = UHS_DDR50_BUS_SPEED;
+ timing = MMC_TIMING_UHS_DDR50;
+ card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+ } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25) {
+ bus_speed = UHS_SDR25_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR25;
+ card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+ }
+ } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
+ if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
+ (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
+ (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) ||
+ (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
+ bus_speed = UHS_SDR25_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR25;
+ card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+ }
+ }
+
+ err = mmc_sd_switch(card, 1, 0, bus_speed, status);
+ if (err)
+ return err;
+
+ if ((status[16] & 0xF) != bus_speed)
+ printk(KERN_WARNING "%s: Problem setting bus speed mode!\n",
+ mmc_hostname(card->host));
+ else {
+ if (bus_speed) {
+ mmc_set_timing(card->host, timing);
+ mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
+ }
+ }
+
+ return 0;
+}
+
/*
* UHS-I specific initialization procedure
*/
@@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
/* Set the driver strength for the card */
err = sd_select_driver_type(card, status);
+ if (err)
+ goto out;
+
+ /* Set bus speed mode of the card */
+ err = sd_set_bus_speed_mode(card, status);
out:
kfree(status);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 5d3bb11..f127fa2 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
ctrl &= ~SDHCI_CTRL_HISPD;
if (host->version >= SDHCI_SPEC_300) {
- u16 ctrl_2;
+ u16 clk, ctrl_2;
+
+ /* In case of UHS-I modes, set High Speed Enable */
+ if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
+ (ios->timing == MMC_TIMING_UHS_SDR104) ||
+ (ios->timing == MMC_TIMING_UHS_DDR50))
+ ctrl |= SDHCI_CTRL_HISPD;
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
@@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* need to reset SD Clock Enable before changing High
* Speed Enable to avoid generating clock gliches.
*/
- u16 clk;
/* Reset SD Clock Enable */
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
@@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk |= SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
}
+
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+ /* Select Bus Speed Mode for host */
+ if (ios->timing == MMC_TIMING_UHS_SDR25)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+ else if (ios->timing == MMC_TIMING_UHS_SDR50)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+ else if (ios->timing == MMC_TIMING_UHS_SDR104)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+ else if (ios->timing == MMC_TIMING_UHS_DDR50)
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+
+ /* Reset SD Clock Enable */
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+
+ /* Re-enable SD Clock */
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
} else
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index a407b5b..5bf244d 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -150,6 +150,11 @@
#define SDHCI_ACMD12_ERR 0x3C
#define SDHCI_HOST_CONTROL2 0x3E
+#define SDHCI_CTRL_UHS_SDR12 0x0000
+#define SDHCI_CTRL_UHS_SDR25 0x0001
+#define SDHCI_CTRL_UHS_SDR50 0x0002
+#define SDHCI_CTRL_UHS_SDR104 0x0003
+#define SDHCI_CTRL_UHS_DDR50 0x0004
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
#define SDHCI_CTRL_DRV_TYPE_A 0x0010
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 2d7f7a3..0b24c41 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -75,7 +75,23 @@ struct sd_ssr {
struct sd_switch_caps {
unsigned int hs_max_dtr;
+ unsigned int uhs_max_dtr;
+#define UHS_SDR104_MAX_DTR 208000000
+#define UHS_SDR50_MAX_DTR 100000000
+#define UHS_DDR50_MAX_DTR 50000000
+#define UHS_SDR25_MAX_DTR 50000000
unsigned int uhs_bus_mode;
+#define UHS_SDR12_BUS_SPEED 0
+#define UHS_SDR25_BUS_SPEED 1
+#define UHS_SDR50_BUS_SPEED 2
+#define UHS_SDR104_BUS_SPEED 3
+#define UHS_DDR50_BUS_SPEED 4
+
+#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
+#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
+#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
+#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
+#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
unsigned int uhs_drv_type;
#define SD_DRIVER_TYPE_B 0x01
#define SD_DRIVER_TYPE_A 0x02
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index bc2121e..4dfff6d 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -50,6 +50,10 @@ struct mmc_ios {
#define MMC_TIMING_LEGACY 0
#define MMC_TIMING_MMC_HS 1
#define MMC_TIMING_SD_HS 2
+#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
+#define MMC_TIMING_UHS_SDR50 3
+#define MMC_TIMING_UHS_SDR104 4
+#define MMC_TIMING_UHS_DDR50 5
unsigned char ddr; /* dual data rate used */
--
1.7.1
^ permalink raw reply related [flat|nested] 125+ messages in thread
* [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (5 preceding siblings ...)
2011-03-04 11:32 ` [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-09 21:41 ` Philip Rakity
` (3 more replies)
2011-03-04 11:32 ` [PATCH v2 08/12] mmc: sd: report correct speed and capacity of " Arindam Nath
` (6 subsequent siblings)
13 siblings, 4 replies; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
include/linux/mmc/card.h | 9 +++++++++
include/linux/mmc/host.h | 1 +
4 files changed, 79 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ec0d8e6..df98a2c 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -550,6 +550,46 @@ 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)
+{
+ struct mmc_host *host = card->host;
+ int mmc_host_max_current_180, current_limit;
+ int err;
+
+ /* sanity check */
+ if (!host->ops->get_max_current_180)
+ return 0;
+
+ /* Maximum current supported by host at 1.8V */
+ mmc_host_max_current_180 = host->ops->get_max_current_180(host);
+
+ if (mmc_host_max_current_180 >= 800) {
+ if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
+ current_limit = SD_SET_CURRENT_LIMIT_800;
+ else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
+ current_limit = SD_SET_CURRENT_LIMIT_600;
+ else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
+ current_limit = SD_SET_CURRENT_LIMIT_400;
+ } else if (mmc_host_max_current_180 >= 600) {
+ if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
+ current_limit = SD_SET_CURRENT_LIMIT_600;
+ else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
+ current_limit = SD_SET_CURRENT_LIMIT_400;
+ } else if (mmc_host_max_current_180 >= 400)
+ if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
+ current_limit = SD_SET_CURRENT_LIMIT_400;
+
+ 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
*/
@@ -590,6 +630,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 f127fa2..245cc39 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1462,12 +1462,36 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
return -EAGAIN;
}
+static int sdhci_get_max_current_180(struct mmc_host *mmc)
+{
+ struct sdhci_host *host;
+ u32 max_current_caps;
+ unsigned long flags;
+ int max_current_180;
+
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ /* Maximum current is 4 times the register value for 1.8V */
+ max_current_180 = ((max_current_caps & SDHCI_MAX_CURRENT_180_MASK) >>
+ SDHCI_MAX_CURRENT_180_SHIFT) *
+ SDHCI_MAX_CURRENT_MULTIPLIER;
+
+ return max_current_180;
+}
+
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,
+ .get_max_current_180 = sdhci_get_max_current_180,
};
/*****************************************************************************\
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 0b24c41..a6811ae 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -98,6 +98,15 @@ struct sd_switch_caps {
#define SD_DRIVER_TYPE_C 0x04
#define SD_DRIVER_TYPE_D 0x08
unsigned int uhs_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 4dfff6d..e84cd05 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -128,6 +128,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);
+ int (*get_max_current_180)(struct mmc_host *mmc);
};
struct mmc_card;
--
1.7.1
^ permalink raw reply related [flat|nested] 125+ messages in thread
* [PATCH v2 08/12] mmc: sd: report correct speed and capacity of uhs cards
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (6 preceding siblings ...)
2011-03-04 11:32 ` [PATCH v2 07/12] mmc: sd: set current limit for uhs cards Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-10 11:48 ` subhashj
2011-03-04 11:32 ` [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
` (5 subsequent siblings)
13 siblings, 1 reply; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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 can have SDHC or SDXC UHS-I cards, so we need to decide based on
C_SIZE field of CSDv2.0 register. According to Physical Layer spec
v3.01, the minimum value of C_SIZE for SDXC card is 00FFFFh.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/bus.c | 11 ++++++++---
drivers/mmc/core/sd.c | 10 +++++++++-
include/linux/mmc/card.h | 7 +++++++
3 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 63667a8..ceeefa4 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";
@@ -298,7 +302,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_card_ultrahighspeed(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 df98a2c..be01397 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -129,7 +129,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.
@@ -143,6 +143,7 @@ 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);
m = UNSTUFF_BITS(resp, 48, 22);
csd->capacity = (1 + m) << 10;
@@ -922,6 +923,13 @@ 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_card_set_ultrahighspeed(card);
+
+ /* SDXC cards have a minimum C_SIZE of 0x00FFFF */
+ if (card->csd.c_size >= 0xFFFF)
+ mmc_card_set_ext_capacity(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 a6811ae..61459aa 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -29,6 +29,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 */
@@ -151,6 +152,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 */
@@ -193,12 +196,16 @@ struct mmc_card {
#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_card_ultrahighspeed(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_card_set_ultrahighspeed(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] 125+ messages in thread
* [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (7 preceding siblings ...)
2011-03-04 11:32 ` [PATCH v2 08/12] mmc: sd: report correct speed and capacity of " Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-04 18:27 ` Philip Rakity
` (4 more replies)
2011-03-04 11:32 ` [PATCH v2 10/12] mmc: sdhci: enable preset value after " Arindam Nath
` (4 subsequent siblings)
13 siblings, 5 replies; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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. 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 Seletc (bit 7) of Host Control2 register to 0.
Before exiting the tuning procedure, we disable Buffer Read Ready
interrupt.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 4 +
drivers/mmc/host/sdhci.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-
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, 149 insertions(+), 1 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index be01397..1e2d157 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
/* Set current limit for the card */
err = sd_set_current_limit(card, status);
+ /* SPI mode doesn't define CMD19 */
+ if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
+ 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 245cc39..8f4f102 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -39,6 +39,8 @@
#define SDHCI_USE_LEDS_CLASS
#endif
+#define MAX_TUNING_LOOP 40
+
static unsigned int debug_quirks = 0;
static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
@@ -985,7 +987,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);
@@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct mmc_host *mmc)
return max_current_180;
}
+static void sdhci_execute_tuning(struct mmc_host *mmc)
+{
+ struct sdhci_host *host;
+ u16 ctrl;
+ int tuning_loop_counter = MAX_TUNING_LOOP;
+ unsigned long flags;
+ unsigned long timeout;
+
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ 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_SDR104) ||
+ ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
+ (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
+ ctrl |= SDHCI_CTRL_EXEC_TUNING;
+ else {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ /*
+ * As per the Host Controller spec v3.00, tuning command
+ * generates Buffer Read Ready interrupt, so enable that.
+ */
+ sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
+
+ /*
+ * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
+ * of loops reaches 40 times or a timeout of 150ms occurs.
+ */
+ timeout = 150;
+ do {
+ 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;
+ sdhci_send_command(host, &cmd);
+
+ host->cmd = NULL;
+ host->mrq = NULL;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ /* Wait for Buffer Read Ready interrupt */
+ wait_event_interruptible_timeout(host->buf_ready_int,
+ (host->tuning_done == 1),
+ msecs_to_jiffies(50));
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!host->tuning_done) {
+ printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
+ " failed, falling back to fixed sampling"
+ " clock\n");
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+ ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ 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");
+ }
+
+out:
+ sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
@@ -1492,6 +1609,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,
.get_max_current_180 = sdhci_get_max_current_180,
+ .execute_tuning = sdhci_execute_tuning,
};
/*****************************************************************************\
@@ -1703,6 +1821,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
@@ -2126,6 +2254,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;
@@ -2269,6 +2401,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 5bf244d..4746879 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -160,6 +160,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
@@ -187,6 +189,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 e84cd05..651e40b 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -129,6 +129,7 @@ struct mmc_host_ops {
int (*start_signal_voltage_switch)(struct mmc_host *host);
int (*get_max_current_180)(struct mmc_host *mmc);
+ void (*execute_tuning)(struct mmc_host *host);
};
struct mmc_card;
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 612301f..9194f9e 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 282d158..5203b97 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -109,6 +109,7 @@ struct sdhci_host {
#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
+#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
unsigned int version; /* SDHCI spec. version */
@@ -147,6 +148,9 @@ struct sdhci_host {
struct mmc_command *saved_abort_cmd; /* Abort command saved for data error recovery */
+ 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] 125+ messages in thread
* [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (8 preceding siblings ...)
2011-03-04 11:32 ` [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-10 13:23 ` subhashj
2011-03-04 11:32 ` [PATCH v2 11/12] mmc: sdhci: add support for programmable clock mode Arindam Nath
` (3 subsequent siblings)
13 siblings, 1 reply; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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.
We also reset Preset Value Enable when the card is removed from the slot.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 7 +++++++
drivers/mmc/host/sdhci.c | 36 ++++++++++++++++++++++++++++++++++++
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 1e2d157..ae7a771 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -962,6 +962,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
}
}
+ /*
+ * Since initialization is now complete, enable preset
+ * value registers.
+ */
+ if (host->ops->enable_preset_value)
+ host->ops->enable_preset_value(host);
+
host->card = card;
return 0;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 8f4f102..1f6e4ad 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1602,6 +1602,40 @@ out:
spin_unlock_irqrestore(&host->lock, flags);
}
+static void sdhci_enable_preset_value(struct mmc_host *mmc)
+{
+ 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);
+ ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdhci_disable_preset_value(struct sdhci_host *host)
+{
+ u16 ctrl;
+
+ /* Only for Host Controller version >= v3.00 */
+ if (host->version < SDHCI_SPEC_300)
+ return;
+
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
@@ -1610,6 +1644,7 @@ static const struct mmc_host_ops sdhci_ops = {
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.get_max_current_180 = sdhci_get_max_current_180,
.execute_tuning = sdhci_execute_tuning,
+ .enable_preset_value = sdhci_enable_preset_value,
};
/*****************************************************************************\
@@ -1920,6 +1955,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
tasklet_schedule(&host->card_tasklet);
+ sdhci_disable_preset_value(host);
}
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 651e40b..e63e063 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -130,6 +130,7 @@ struct mmc_host_ops {
int (*start_signal_voltage_switch)(struct mmc_host *host);
int (*get_max_current_180)(struct mmc_host *mmc);
void (*execute_tuning)(struct mmc_host *host);
+ void (*enable_preset_value)(struct mmc_host *host);
};
struct mmc_card;
--
1.7.1
^ permalink raw reply related [flat|nested] 125+ messages in thread
* [PATCH v2 11/12] mmc: sdhci: add support for programmable clock mode
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (9 preceding siblings ...)
2011-03-04 11:32 ` [PATCH v2 10/12] mmc: sdhci: enable preset value after " Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-04 11:32 ` [PATCH v2 12/12] mmc: sdhci: add support for retuning mode 1 Arindam Nath
` (2 subsequent siblings)
13 siblings, 0 replies; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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>
---
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 1f6e4ad..2ffb0c4 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1030,8 +1030,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)
@@ -1049,14 +1049,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. */
@@ -1064,10 +1095,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;
@@ -2247,17 +2278,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;
/*
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 4746879..37a8c32 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -102,6 +102,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
@@ -190,6 +191,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 5203b97..4be4022 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -115,6 +115,7 @@ struct sdhci_host {
unsigned int max_clk; /* Max possible freq (MHz) */
unsigned int timeout_clk; /* Timeout freq (KHz) */
+ unsigned int clk_mul; /* Clock Muliplier value */
unsigned int clock; /* Current clock (MHz) */
u8 pwr; /* Current voltage */
--
1.7.1
^ permalink raw reply related [flat|nested] 125+ messages in thread
* [PATCH v2 12/12] mmc: sdhci: add support for retuning mode 1
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (10 preceding siblings ...)
2011-03-04 11:32 ` [PATCH v2 11/12] mmc: sdhci: add support for programmable clock mode Arindam Nath
@ 2011-03-04 11:32 ` Arindam Nath
2011-03-10 13:57 ` subhashj
2011-03-04 15:16 ` [PATCH v2 00/12] add support for host controller v3.00 Chris Ball
2011-03-11 13:13 ` subhashj
13 siblings, 1 reply; 125+ messages in thread
From: Arindam Nath @ 2011-03-04 11:32 UTC (permalink / raw)
To: cjb
Cc: zhangfei.gao, prakity, 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 after successfull UHS-I initialization. Since
retuning mode 1 sets a limit of 4MB on the maximum data length, we set
max_blk_count appropriately. Once the tuning timer expires, we set
SDHCI_NEEDS_RETUNING flag, and if the flag is set, we execute tuning
procedure before sending the next command. We need to restore mmc_request
structure after executing retuning procedure since host->mrq is used
inside the procedure to send CMD19. We also disable and re-enable this
flag during suspend and resume respectively, as per the spec v3.00.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
---
drivers/mmc/core/sd.c | 5 ++
drivers/mmc/host/sdhci.c | 109 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/mmc/host/sdhci.h | 6 ++-
include/linux/mmc/host.h | 1 +
include/linux/mmc/sdhci.h | 6 +++
5 files changed, 124 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ae7a771..1f3cf57 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -641,6 +641,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
card->host->ops->execute_tuning(card->host);
+ /* Initialize and start re-tuning timer */
+ if (!mmc_host_is_spi(card->host) &&
+ card->host->ops->start_retuning_timer)
+ card->host->ops->start_retuning_timer(card->host);
+
out:
kfree(status);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2ffb0c4..8bf0408 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -48,6 +48,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 void sdhci_execute_tuning(struct mmc_host *mmc);
+static void sdhci_tuning_timer(unsigned long data);
static void sdhci_dumpregs(struct sdhci_host *host)
{
@@ -1240,8 +1242,34 @@ 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))) {
+ 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);
+
+ 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);
@@ -1667,6 +1695,26 @@ static void sdhci_disable_preset_value(struct sdhci_host *host)
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
}
+static void sdhci_start_retuning_timer(struct mmc_host *mmc)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (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;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
@@ -1676,6 +1724,7 @@ static const struct mmc_host_ops sdhci_ops = {
.get_max_current_180 = sdhci_get_max_current_180,
.execute_tuning = sdhci_execute_tuning,
.enable_preset_value = sdhci_enable_preset_value,
+ .start_retuning_timer = sdhci_start_retuning_timer,
};
/*****************************************************************************\
@@ -1725,6 +1774,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;
/*
@@ -1799,6 +1852,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 *
@@ -2057,6 +2124,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;
@@ -2098,6 +2174,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;
}
@@ -2353,6 +2435,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
@@ -2488,9 +2585,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)
@@ -2584,6 +2687,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 37a8c32..53d5909 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -190,7 +190,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/host.h b/include/linux/mmc/host.h
index e63e063..09f5d03 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -131,6 +131,7 @@ struct mmc_host_ops {
int (*get_max_current_180)(struct mmc_host *mmc);
void (*execute_tuning)(struct mmc_host *host);
void (*enable_preset_value)(struct mmc_host *host);
+ void (*start_retuning_timer)(struct mmc_host *host);
};
struct mmc_card;
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 4be4022..c6539be 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -110,6 +110,7 @@ struct sdhci_host {
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
+#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
unsigned int version; /* SDHCI spec. version */
@@ -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] 125+ messages in thread
* Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-04 11:32 ` [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
@ 2011-03-04 11:47 ` Wolfram Sang
2011-03-04 11:52 ` Nath, Arindam
2011-03-09 10:44 ` subhashj
` (4 subsequent siblings)
5 siblings, 1 reply; 125+ messages in thread
From: Wolfram Sang @ 2011-03-04 11:47 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, zhangfei.gao, prakity, subhashj, linux-mmc, henry.su,
aaron.lu, anath.amd
[-- Attachment #1: Type: text/plain, Size: 450 bytes --]
> + if (host->version >= SDHCI_SPEC_300)
> + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
Now caps[1] can be uninitialized (didn't the compiler warn about this?)
Maybe
caps[1] = host->version... ? sdhci_readl() : 0
(or simply an else branch)?
Regards,
Wolfram
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-04 11:47 ` Wolfram Sang
@ 2011-03-04 11:52 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-04 11:52 UTC (permalink / raw)
To: Wolfram Sang
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, prakity@marvell.com,
subhashj@codeaurora.org, linux-mmc@vger.kernel.org, Su, Henry,
Lu, Aaron, anath.amd@gmail.com
Hi Wolfram,
> -----Original Message-----
> From: Wolfram Sang [mailto:w.sang@pengutronix.de]
> Sent: Friday, March 04, 2011 5:18 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; Su, Henry; Lu,
> Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
>
> > + if (host->version >= SDHCI_SPEC_300)
> > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
>
> Now caps[1] can be uninitialized (didn't the compiler warn about this?)
Surprisingly, there was no compiler warning on my setup. I will take care of it.
Thanks,
Arindam
> Maybe
>
> caps[1] = host->version... ? sdhci_readl() : 0
>
> (or simply an else branch)?
>
> Regards,
>
> Wolfram
>
> --
> Pengutronix e.K. | Wolfram Sang
> |
> Industrial Linux Solutions | http://www.pengutronix.de/
> |
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 00/12] add support for host controller v3.00
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (11 preceding siblings ...)
2011-03-04 11:32 ` [PATCH v2 12/12] mmc: sdhci: add support for retuning mode 1 Arindam Nath
@ 2011-03-04 15:16 ` Chris Ball
2011-03-04 15:55 ` Nath, Arindam
2011-03-11 13:13 ` subhashj
13 siblings, 1 reply; 125+ messages in thread
From: Chris Ball @ 2011-03-04 15:16 UTC (permalink / raw)
To: Arindam Nath
Cc: zhangfei.gao, prakity, subhashj, linux-mmc, henry.su, aaron.lu,
anath.amd
Hi Arindam,
On Fri, Mar 04 2011, Arindam Nath wrote:
> V2
Thanks very much for sending this! Since you mentioned you aren't
seeing compiler warnings, here's the set I see here:
drivers/mmc/core/sd.c: In function ‘mmc_sd_init_card’:
drivers/mmc/core/sd.c:584:21: warning: ‘current_limit’ may be used
uninitialized in this function [-Wuninitialized]
drivers/mmc/core/sd.c:558:32: note: ‘current_limit’ was declared here
drivers/mmc/core/sd.c:547:18: warning: ‘timing’ may be used
uninitialized in this function [-Wuninitialized]
drivers/mmc/core/sd.c:471:26: note: ‘timing’ was declared here
drivers/mmc/core/sd.c:538:21: warning: ‘bus_speed’ may be used
uninitialized in this function [-Wuninitialized]
drivers/mmc/core/sd.c:471:15: note: ‘bus_speed’ was declared here
drivers/mmc/core/sd.c:456:21: warning: ‘card_set_drv_type’ may be used
uninitialized in this function [-Wuninitialized]
drivers/mmc/core/sd.c:433:25: note: ‘card_set_drv_type’ was declared here
drivers/mmc/core/sd.c:464:22: warning: ‘host_set_drv_type’ may be used
uninitialized in this function [-Wuninitialized]
drivers/mmc/core/sd.c:433:6: note: ‘host_set_drv_type’ was declared here
drivers/mmc/host/sdhci.c: In function ‘sdhci_add_host’:
drivers/mmc/host/sdhci.c:2366:27: warning: ‘caps[1]’ may be used
uninitialized in this function [-Wuninitialized]
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 00/12] add support for host controller v3.00
2011-03-04 15:16 ` [PATCH v2 00/12] add support for host controller v3.00 Chris Ball
@ 2011-03-04 15:55 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-04 15:55 UTC (permalink / raw)
To: Chris Ball
Cc: zhangfei.gao@gmail.com, prakity@marvell.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: Friday, March 04, 2011 8:47 PM
> To: Nath, Arindam
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; Su, Henry; Lu,
> Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 00/12] add support for host controller v3.00
>
> Hi Arindam,
>
> On Fri, Mar 04 2011, Arindam Nath wrote:
> > V2
>
> Thanks very much for sending this! Since you mentioned you aren't
> seeing compiler warnings, here's the set I see here:
>
> drivers/mmc/core/sd.c: In function ‘mmc_sd_init_card’:
> drivers/mmc/core/sd.c:584:21: warning: ‘current_limit’ may be used
> uninitialized in this function [-Wuninitialized]
> drivers/mmc/core/sd.c:558:32: note: ‘current_limit’ was declared here
>
> drivers/mmc/core/sd.c:547:18: warning: ‘timing’ may be used
> uninitialized in this function [-Wuninitialized]
> drivers/mmc/core/sd.c:471:26: note: ‘timing’ was declared here
>
> drivers/mmc/core/sd.c:538:21: warning: ‘bus_speed’ may be used
> uninitialized in this function [-Wuninitialized]
> drivers/mmc/core/sd.c:471:15: note: ‘bus_speed’ was declared here
>
> drivers/mmc/core/sd.c:456:21: warning: ‘card_set_drv_type’ may be used
> uninitialized in this function [-Wuninitialized]
> drivers/mmc/core/sd.c:433:25: note: ‘card_set_drv_type’ was declared
> here
>
> drivers/mmc/core/sd.c:464:22: warning: ‘host_set_drv_type’ may be used
> uninitialized in this function [-Wuninitialized]
> drivers/mmc/core/sd.c:433:6: note: ‘host_set_drv_type’ was declared
> here
>
> drivers/mmc/host/sdhci.c: In function ‘sdhci_add_host’:
> drivers/mmc/host/sdhci.c:2366:27: warning: ‘caps[1]’ may be used
> uninitialized in this function [-Wuninitialized]
Thanks for pointing these out. I will make a note of it, and fix it in the next version.
Regards,
Arindam
>
> --
> Chris Ball <cjb@laptop.org> <http://printf.net/>
> One Laptop Per Child
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-04 11:32 ` [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
@ 2011-03-04 18:27 ` Philip Rakity
2011-03-04 18:48 ` Nath, Arindam
2011-03-04 18:34 ` Philip Rakity
` (3 subsequent siblings)
4 siblings, 1 reply; 125+ messages in thread
From: Philip Rakity @ 2011-03-04 18:27 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On Mar 4, 2011, at 3:32 AM, Arindam Nath wrote:
> Host Controller needs tuning during initialization to operate SDR50
> and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is
> indicated by bit 45 of the Host Controller Capabilities register.
> A new command CMD19 has been defined in the Physical Layer spec
> v3.01 to request the card to send tuning pattern.
>
> We enable Buffer Read Ready interrupt at the very begining of tuning
> procedure, because that is the only interrupt generated by the Host
> Controller during tuning. 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 Seletc (bit 7) of Host Control2 register to 0.
> Before exiting the tuning procedure, we disable Buffer Read Ready
> interrupt.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 4 +
> drivers/mmc/host/sdhci.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-
> 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, 149 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index be01397..1e2d157 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
> /* Set current limit for the card */
> err = sd_set_current_limit(card, status);
>
> + /* SPI mode doesn't define CMD19 */
> + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
> + 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 245cc39..8f4f102 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -39,6 +39,8 @@
> #define SDHCI_USE_LEDS_CLASS
> #endif
>
> +#define MAX_TUNING_LOOP 40
> +
> static unsigned int debug_quirks = 0;
>
> static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
> @@ -985,7 +987,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);
> @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct mmc_host *mmc)
> return max_current_180;
> }
>
> +static void sdhci_execute_tuning(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u16 ctrl;
> + int tuning_loop_counter = MAX_TUNING_LOOP;
> + unsigned long flags;
> + unsigned long timeout;
> +
> + host = mmc_priv(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + 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_SDR104) ||
> + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> + else {
> + spin_unlock_irqrestore(&host->lock, flags);
> + return;
> + }
> +
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + /*
> + * As per the Host Controller spec v3.00, tuning command
> + * generates Buffer Read Ready interrupt, so enable that.
> + */
> + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> +
> + /*
> + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
> + * of loops reaches 40 times or a timeout of 150ms occurs.
> + */
> + timeout = 150;
> + do {
> + 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;
> + sdhci_send_command(host, &cmd);
> +
> + host->cmd = NULL;
> + host->mrq = NULL;
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + /* Wait for Buffer Read Ready interrupt */
> + wait_event_interruptible_timeout(host->buf_ready_int,
> + (host->tuning_done == 1),
> + msecs_to_jiffies(50));
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (!host->tuning_done) {
> + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> + " failed, falling back to fixed sampling"
> + " clock\n");
> + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + 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");
> + }
> +
> +out:
> + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> static const struct mmc_host_ops sdhci_ops = {
> .request = sdhci_request,
> .set_ios = sdhci_set_ios,
> @@ -1492,6 +1609,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,
> .get_max_current_180 = sdhci_get_max_current_180,
> + .execute_tuning = sdhci_execute_tuning,
> };
>
> /*****************************************************************************\
> @@ -1703,6 +1821,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;
> + }
> + }
> +
Add check that you are doing tuning -- if we are doing BUS_TEST width then CMD19 is legal and used for bus width testing
> if (!host->data) {
> /*
> * The "data complete" interrupt is also used to
> @@ -2126,6 +2254,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;
> @@ -2269,6 +2401,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 5bf244d..4746879 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -160,6 +160,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
> @@ -187,6 +189,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 e84cd05..651e40b 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -129,6 +129,7 @@ struct mmc_host_ops {
>
> int (*start_signal_voltage_switch)(struct mmc_host *host);
> int (*get_max_current_180)(struct mmc_host *mmc);
> + void (*execute_tuning)(struct mmc_host *host);
> };
>
> struct mmc_card;
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 612301f..9194f9e 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 282d158..5203b97 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -109,6 +109,7 @@ struct sdhci_host {
> #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
> #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
>
> unsigned int version; /* SDHCI spec. version */
>
> @@ -147,6 +148,9 @@ struct sdhci_host {
>
> struct mmc_command *saved_abort_cmd; /* Abort command saved for data error recovery */
>
> + 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] 125+ messages in thread
* Re: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-04 11:32 ` [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
2011-03-04 18:27 ` Philip Rakity
@ 2011-03-04 18:34 ` Philip Rakity
2011-03-04 18:54 ` Nath, Arindam
2011-03-15 10:32 ` zhangfei gao
` (2 subsequent siblings)
4 siblings, 1 reply; 125+ messages in thread
From: Philip Rakity @ 2011-03-04 18:34 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On Mar 4, 2011, at 3:32 AM, Arindam Nath wrote:
> Host Controller needs tuning during initialization to operate SDR50
> and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is
> indicated by bit 45 of the Host Controller Capabilities register.
> A new command CMD19 has been defined in the Physical Layer spec
> v3.01 to request the card to send tuning pattern.
>
> We enable Buffer Read Ready interrupt at the very begining of tuning
> procedure, because that is the only interrupt generated by the Host
> Controller during tuning. 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 Seletc (bit 7) of Host Control2 register to 0.
> Before exiting the tuning procedure, we disable Buffer Read Ready
> interrupt.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 4 +
> drivers/mmc/host/sdhci.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-
> 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, 149 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index be01397..1e2d157 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
> /* Set current limit for the card */
> err = sd_set_current_limit(card, status);
>
> + /* SPI mode doesn't define CMD19 */
> + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
> + 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 245cc39..8f4f102 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -39,6 +39,8 @@
> #define SDHCI_USE_LEDS_CLASS
> #endif
>
> +#define MAX_TUNING_LOOP 40
> +
> static unsigned int debug_quirks = 0;
>
> static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
> @@ -985,7 +987,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);
> @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct mmc_host *mmc)
> return max_current_180;
> }
>
> +static void sdhci_execute_tuning(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u16 ctrl;
> + int tuning_loop_counter = MAX_TUNING_LOOP;
> + unsigned long flags;
> + unsigned long timeout;
> +
> + host = mmc_priv(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + 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_SDR104) ||
> + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> + else {
> + spin_unlock_irqrestore(&host->lock, flags);
> + return;
> + }
> +
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + /*
> + * As per the Host Controller spec v3.00, tuning command
> + * generates Buffer Read Ready interrupt, so enable that.
> + */
> + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> +
> + /*
> + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
> + * of loops reaches 40 times or a timeout of 150ms occurs.
> + */
> + timeout = 150;
> + do {
> + 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;
> + sdhci_send_command(host, &cmd);
> +
> + host->cmd = NULL;
> + host->mrq = NULL;
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + /* Wait for Buffer Read Ready interrupt */
> + wait_event_interruptible_timeout(host->buf_ready_int,
> + (host->tuning_done == 1),
> + msecs_to_jiffies(50));
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (!host->tuning_done) {
safer to
reread
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
and now clear bits ?
> + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> + " failed, falling back to fixed sampling"
> + " clock\n");
> + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + 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);
msleep is better then mdelay ?
other option is to disable device interrupt
and check every ms if SDHCI_INT_DATA_AVAIL is set
and then enable device interrupt. The controller is supposed to disable all other interrupts when doing tuning.
no need to mask interrupts.
> +
> + /*
> + * 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");
> + }
> +
> +out:
> + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> static const struct mmc_host_ops sdhci_ops = {
> .request = sdhci_request,
> .set_ios = sdhci_set_ios,
> @@ -1492,6 +1609,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,
> .get_max_current_180 = sdhci_get_max_current_180,
> + .execute_tuning = sdhci_execute_tuning,
> };
>
> /*****************************************************************************\
> @@ -1703,6 +1821,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
> @@ -2126,6 +2254,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;
> @@ -2269,6 +2401,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 5bf244d..4746879 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -160,6 +160,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
> @@ -187,6 +189,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 e84cd05..651e40b 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -129,6 +129,7 @@ struct mmc_host_ops {
>
> int (*start_signal_voltage_switch)(struct mmc_host *host);
> int (*get_max_current_180)(struct mmc_host *mmc);
> + void (*execute_tuning)(struct mmc_host *host);
> };
>
> struct mmc_card;
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 612301f..9194f9e 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 282d158..5203b97 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -109,6 +109,7 @@ struct sdhci_host {
> #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
> #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
>
> unsigned int version; /* SDHCI spec. version */
>
> @@ -147,6 +148,9 @@ struct sdhci_host {
>
> struct mmc_command *saved_abort_cmd; /* Abort command saved for data error recovery */
>
> + 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] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-04 18:27 ` Philip Rakity
@ 2011-03-04 18:48 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-04 18:48 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Friday, March 04, 2011 11:58 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
>
> On Mar 4, 2011, at 3:32 AM, Arindam Nath wrote:
>
> > Host Controller needs tuning during initialization to operate SDR50
> > and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is
> > indicated by bit 45 of the Host Controller Capabilities register.
> > A new command CMD19 has been defined in the Physical Layer spec
> > v3.01 to request the card to send tuning pattern.
> >
> > We enable Buffer Read Ready interrupt at the very begining of tuning
> > procedure, because that is the only interrupt generated by the Host
> > Controller during tuning. 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 Seletc (bit 7) of Host Control2 register to 0.
> > Before exiting the tuning procedure, we disable Buffer Read Ready
> > interrupt.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 4 +
> > drivers/mmc/host/sdhci.c | 137
> ++++++++++++++++++++++++++++++++++++++++++++-
> > 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, 149 insertions(+), 1 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index be01397..1e2d157 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
> > /* Set current limit for the card */
> > err = sd_set_current_limit(card, status);
> >
> > + /* SPI mode doesn't define CMD19 */
> > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> >execute_tuning)
> > + 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 245cc39..8f4f102 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -39,6 +39,8 @@
> > #define SDHCI_USE_LEDS_CLASS
> > #endif
> >
> > +#define MAX_TUNING_LOOP 40
> > +
> > static unsigned int debug_quirks = 0;
> >
> > static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data
> *);
> > @@ -985,7 +987,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);
> > @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct
> mmc_host *mmc)
> > return max_current_180;
> > }
> >
> > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u16 ctrl;
> > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > + unsigned long flags;
> > + unsigned long timeout;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + 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_SDR104) ||
> > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > + else {
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return;
> > + }
> > +
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + /*
> > + * As per the Host Controller spec v3.00, tuning command
> > + * generates Buffer Read Ready interrupt, so enable that.
> > + */
> > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > +
> > + /*
> > + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the
> number
> > + * of loops reaches 40 times or a timeout of 150ms occurs.
> > + */
> > + timeout = 150;
> > + do {
> > + 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;
> > + sdhci_send_command(host, &cmd);
> > +
> > + host->cmd = NULL;
> > + host->mrq = NULL;
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + /* Wait for Buffer Read Ready interrupt */
> > + wait_event_interruptible_timeout(host->buf_ready_int,
> > + (host->tuning_done == 1),
> > + msecs_to_jiffies(50));
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + if (!host->tuning_done) {
> > + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> > + " failed, falling back to fixed sampling"
> > + " clock\n");
> > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + 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");
> > + }
> > +
> > +out:
> > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +}
> > +
> > static const struct mmc_host_ops sdhci_ops = {
> > .request = sdhci_request,
> > .set_ios = sdhci_set_ios,
> > @@ -1492,6 +1609,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,
> > .get_max_current_180 = sdhci_get_max_current_180,
> > + .execute_tuning = sdhci_execute_tuning,
> > };
> >
> >
> /**********************************************************************
> *******\
> > @@ -1703,6 +1821,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;
> > + }
> > + }
> > +
>
> Add check that you are doing tuning -- if we are doing BUS_TEST width
> then CMD19 is legal and used for bus width testing
Thanks for the suggestion. Will add a check in next version.
Thanks,
Arindam
>
>
> > if (!host->data) {
> > /*
> > * The "data complete" interrupt is also used to
> > @@ -2126,6 +2254,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;
> > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -160,6 +160,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
> > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> >
> > int (*start_signal_voltage_switch)(struct mmc_host *host);
> > int (*get_max_current_180)(struct mmc_host *mmc);
> > + void (*execute_tuning)(struct mmc_host *host);
> > };
> >
> > struct mmc_card;
> > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> > index 612301f..9194f9e 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 282d158..5203b97 100644
> > --- a/include/linux/mmc/sdhci.h
> > +++ b/include/linux/mmc/sdhci.h
> > @@ -109,6 +109,7 @@ struct sdhci_host {
> > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable
> */
> > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning
> */
> >
> > unsigned int version; /* SDHCI spec. version */
> >
> > @@ -147,6 +148,9 @@ struct sdhci_host {
> >
> > struct mmc_command *saved_abort_cmd; /* Abort command saved
> for data error recovery */
> >
> > + 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] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-04 18:34 ` Philip Rakity
@ 2011-03-04 18:54 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-04 18:54 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Saturday, March 05, 2011 12:04 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
>
> On Mar 4, 2011, at 3:32 AM, Arindam Nath wrote:
>
> > Host Controller needs tuning during initialization to operate SDR50
> > and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is
> > indicated by bit 45 of the Host Controller Capabilities register.
> > A new command CMD19 has been defined in the Physical Layer spec
> > v3.01 to request the card to send tuning pattern.
> >
> > We enable Buffer Read Ready interrupt at the very begining of tuning
> > procedure, because that is the only interrupt generated by the Host
> > Controller during tuning. 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 Seletc (bit 7) of Host Control2 register to 0.
> > Before exiting the tuning procedure, we disable Buffer Read Ready
> > interrupt.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 4 +
> > drivers/mmc/host/sdhci.c | 137
> ++++++++++++++++++++++++++++++++++++++++++++-
> > 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, 149 insertions(+), 1 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index be01397..1e2d157 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
> > /* Set current limit for the card */
> > err = sd_set_current_limit(card, status);
> >
> > + /* SPI mode doesn't define CMD19 */
> > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> >execute_tuning)
> > + 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 245cc39..8f4f102 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -39,6 +39,8 @@
> > #define SDHCI_USE_LEDS_CLASS
> > #endif
> >
> > +#define MAX_TUNING_LOOP 40
> > +
> > static unsigned int debug_quirks = 0;
> >
> > static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data
> *);
> > @@ -985,7 +987,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);
> > @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct
> mmc_host *mmc)
> > return max_current_180;
> > }
> >
> > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u16 ctrl;
> > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > + unsigned long flags;
> > + unsigned long timeout;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + 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_SDR104) ||
> > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > + else {
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return;
> > + }
> > +
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + /*
> > + * As per the Host Controller spec v3.00, tuning command
> > + * generates Buffer Read Ready interrupt, so enable that.
> > + */
> > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > +
> > + /*
> > + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the
> number
> > + * of loops reaches 40 times or a timeout of 150ms occurs.
> > + */
> > + timeout = 150;
> > + do {
> > + 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;
> > + sdhci_send_command(host, &cmd);
> > +
> > + host->cmd = NULL;
> > + host->mrq = NULL;
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + /* Wait for Buffer Read Ready interrupt */
> > + wait_event_interruptible_timeout(host->buf_ready_int,
> > + (host->tuning_done == 1),
> > + msecs_to_jiffies(50));
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + if (!host->tuning_done) {
>
> safer to
> reread
> + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> and now clear bits ?
Agree on this. Will be fixed in next version.
>
> > + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> > + " failed, falling back to fixed sampling"
> > + " clock\n");
> > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + 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);
>
> msleep is better then mdelay ?
The delay in this case is short enough, and also we have acquired spinlock, so I don't think msleep() is a good idea here.
>
> other option is to disable device interrupt
> and check every ms if SDHCI_INT_DATA_AVAIL is set
> and then enable device interrupt. The controller is supposed to
> disable all other interrupts when doing tuning.
> no need to mask interrupts.
The unmask_irqs() at the beginning of function is to enable the controller to generate Buffer Read Ready interrupt, and we just disable it again before leaving the function using mask_irqs().
Thanks,
Arindam
>
> > +
> > + /*
> > + * 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");
> > + }
> > +
> > +out:
> > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +}
> > +
> > static const struct mmc_host_ops sdhci_ops = {
> > .request = sdhci_request,
> > .set_ios = sdhci_set_ios,
> > @@ -1492,6 +1609,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,
> > .get_max_current_180 = sdhci_get_max_current_180,
> > + .execute_tuning = sdhci_execute_tuning,
> > };
> >
> >
> /**********************************************************************
> *******\
> > @@ -1703,6 +1821,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
> > @@ -2126,6 +2254,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;
> > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -160,6 +160,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
> > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> >
> > int (*start_signal_voltage_switch)(struct mmc_host *host);
> > int (*get_max_current_180)(struct mmc_host *mmc);
> > + void (*execute_tuning)(struct mmc_host *host);
> > };
> >
> > struct mmc_card;
> > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> > index 612301f..9194f9e 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 282d158..5203b97 100644
> > --- a/include/linux/mmc/sdhci.h
> > +++ b/include/linux/mmc/sdhci.h
> > @@ -109,6 +109,7 @@ struct sdhci_host {
> > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable
> */
> > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning
> */
> >
> > unsigned int version; /* SDHCI spec. version */
> >
> > @@ -147,6 +148,9 @@ struct sdhci_host {
> >
> > struct mmc_command *saved_abort_cmd; /* Abort command saved
> for data error recovery */
> >
> > + 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] 125+ messages in thread
* Re: [PATCH v2 05/12] mmc: sdhci: reset sdclk before setting high speed enable
2011-03-04 11:32 ` [PATCH v2 05/12] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
@ 2011-03-05 4:57 ` Philip Rakity
2011-03-05 5:07 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Philip Rakity @ 2011-03-05 4:57 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On Mar 4, 2011, at 3:32 AM, 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.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/host/sdhci.c | 25 ++++++++++++++++++++++---
> 1 files changed, 22 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 1645687..5d3bb11 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1277,13 +1277,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.
> @@ -1294,8 +1293,28 @@ 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;
> +
> + /* 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 */
> + clk |= SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
Need to ensure clock is stable before using it ---- use sdhci_set_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] 125+ messages in thread
* RE: [PATCH v2 05/12] mmc: sdhci: reset sdclk before setting high speed enable
2011-03-05 4:57 ` Philip Rakity
@ 2011-03-05 5:07 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-05 5:07 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Saturday, March 05, 2011 10:27 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 05/12] mmc: sdhci: reset sdclk before setting
> high speed enable
>
>
> On Mar 4, 2011, at 3:32 AM, 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.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/host/sdhci.c | 25 ++++++++++++++++++++++---
> > 1 files changed, 22 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 1645687..5d3bb11 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -1277,13 +1277,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.
> > @@ -1294,8 +1293,28 @@ 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;
> > +
> > + /* 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 */
> > + clk |= SDHCI_CLOCK_CARD_EN;
> > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
>
> Need to ensure clock is stable before using it ---- use sdhci_set_clock
Thanks for the suggestion. I will add the modification in next version.
Regards,
Arindam
>
> > }
>
>
>
> > - }
> > + } 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] 125+ messages in thread
* Re: [PATCH v2 04/12] mmc: sd: add support for driver type selection
2011-03-04 11:32 ` [PATCH v2 04/12] mmc: sd: add support for driver type selection Arindam Nath
@ 2011-03-09 5:33 ` Philip Rakity
2011-03-09 8:11 ` Nath, Arindam
2011-03-10 6:57 ` subhashj
1 sibling, 1 reply; 125+ messages in thread
From: Philip Rakity @ 2011-03-09 5:33 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On Mar 4, 2011, at 3:32 AM, 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. We then set the
> appropriate driver type for the card using CMD6 mode 1. As per
> Host Controller spec v3.00, we set driver type for the host only
> if Preset Value Enable in the Host Control2 register is not set.
> SDHCI_HOST_CONTROL has been renamed to SDHCI_HOST_CONTROL1 to
> conform to the spec.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/core.c | 9 +++
> drivers/mmc/core/core.h | 1 +
> drivers/mmc/core/sd.c | 140 +++++++++++++++++++++++++++++++++++++---------
> drivers/mmc/core/sd.h | 3 +-
> drivers/mmc/core/sdio.c | 3 +-
> drivers/mmc/host/sdhci.c | 48 ++++++++++++----
> drivers/mmc/host/sdhci.h | 10 +++-
> include/linux/mmc/card.h | 4 +
> include/linux/mmc/host.h | 8 +++
> 9 files changed, 186 insertions(+), 40 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 6625c05..daa535a 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -947,6 +947,15 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing)
> }
>
> /*
> + * Select appropriate driver type for host.
> + */
> +void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
> +{
> + host->ios.drv_type = drv_type;
> + mmc_set_ios(host);
> +}
> +
> +/*
> * Apply power to the MMC stack. This is a two-stage process.
> * First, we enable power to the card without the clock running.
> * We then wait a bit for the power to stabilise. Finally,
> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> index ca1fdde..6114ca5 100644
> --- a/drivers/mmc/core/core.h
> +++ b/drivers/mmc/core/core.h
> @@ -42,6 +42,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
> unsigned int ddr);
> u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> void mmc_set_timing(struct mmc_host *host, unsigned int timing);
> +void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
>
> static inline void mmc_delay(unsigned int ms)
> {
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index a63956b..f6a4fab 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -426,6 +426,86 @@ out:
> return err;
> }
>
> +static int sd_select_driver_type(struct mmc_card *card, u8 *status)
> +{
> + int host_set_drv_type, card_set_drv_type;
> + 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_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
> + card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> + else if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> + host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> + }
> +
> + err = mmc_sd_switch(card, 1, 2, card_set_drv_type, status);
> + if (err)
> + return err;
> +
> + if ((status[15] & 0xF) != card_set_drv_type)
> + printk(KERN_WARNING "%s: Problem setting driver strength!\n",
> + mmc_hostname(card->host));
> + else
> + mmc_set_driver_type(card->host, host_set_drv_type);
> +
> + return 0;
> +}
warning when compiling above routine for possible uninitialized variables
> ost_set_drv_type, card_set_drv_type;
do you want to change the code to add a else ?
eg
> + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> + host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> + }
else {
card_set_drv_type = MMC_SET_DRIVER_TYPE_D;
host_set_drv_type = MMC_SET_DRIVER_TYPE_D;
}
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 04/12] mmc: sd: add support for driver type selection
2011-03-09 5:33 ` Philip Rakity
@ 2011-03-09 8:11 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-09 8:11 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Wednesday, March 09, 2011 11:04 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 04/12] mmc: sd: add support for driver type
> selection
>
>
> On Mar 4, 2011, at 3:32 AM, 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. We then set the
> > appropriate driver type for the card using CMD6 mode 1. As per
> > Host Controller spec v3.00, we set driver type for the host only
> > if Preset Value Enable in the Host Control2 register is not set.
> > SDHCI_HOST_CONTROL has been renamed to SDHCI_HOST_CONTROL1 to
> > conform to the spec.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/core.c | 9 +++
> > drivers/mmc/core/core.h | 1 +
> > drivers/mmc/core/sd.c | 140
> +++++++++++++++++++++++++++++++++++++---------
> > drivers/mmc/core/sd.h | 3 +-
> > drivers/mmc/core/sdio.c | 3 +-
> > drivers/mmc/host/sdhci.c | 48 ++++++++++++----
> > drivers/mmc/host/sdhci.h | 10 +++-
> > include/linux/mmc/card.h | 4 +
> > include/linux/mmc/host.h | 8 +++
> > 9 files changed, 186 insertions(+), 40 deletions(-)
> >
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index 6625c05..daa535a 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -947,6 +947,15 @@ void mmc_set_timing(struct mmc_host *host,
> unsigned int timing)
> > }
> >
> > /*
> > + * Select appropriate driver type for host.
> > + */
> > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> drv_type)
> > +{
> > + host->ios.drv_type = drv_type;
> > + mmc_set_ios(host);
> > +}
> > +
> > +/*
> > * Apply power to the MMC stack. This is a two-stage process.
> > * First, we enable power to the card without the clock running.
> > * We then wait a bit for the power to stabilise. Finally,
> > diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> > index ca1fdde..6114ca5 100644
> > --- a/drivers/mmc/core/core.h
> > +++ b/drivers/mmc/core/core.h
> > @@ -42,6 +42,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host,
> unsigned int width,
> > unsigned int ddr);
> > u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> > void mmc_set_timing(struct mmc_host *host, unsigned int timing);
> > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> drv_type);
> >
> > static inline void mmc_delay(unsigned int ms)
> > {
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index a63956b..f6a4fab 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -426,6 +426,86 @@ out:
> > return err;
> > }
> >
> > +static int sd_select_driver_type(struct mmc_card *card, u8 *status)
> > +{
> > + int host_set_drv_type, card_set_drv_type;
> > + 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_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
> > + card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > + else if (card->sw_caps.uhs_drv_type &
> SD_DRIVER_TYPE_C)
> > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> > + host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > + }
> > +
> > + err = mmc_sd_switch(card, 1, 2, card_set_drv_type, status);
> > + if (err)
> > + return err;
> > +
> > + if ((status[15] & 0xF) != card_set_drv_type)
> > + printk(KERN_WARNING "%s: Problem setting driver
> strength!\n",
> > + mmc_hostname(card->host));
> > + else
> > + mmc_set_driver_type(card->host, host_set_drv_type);
> > +
> > + return 0;
> > +}
>
> warning when compiling above routine for possible uninitialized
> variables
> > ost_set_drv_type, card_set_drv_type;
Yes, Chris already mentioned about these warnings. These will be fixed in next version of patchset.
>
>
> do you want to change the code to add a else ?
> eg
> > + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> > + host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > + }
>
> else {
> card_set_drv_type = MMC_SET_DRIVER_TYPE_D;
> host_set_drv_type = MMC_SET_DRIVER_TYPE_D;
> }
If none of the if and else-if conditions are met, the controller falls back on default driver type B, so we don't need an else block here.
Thanks,
Arindam
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-04 11:32 ` [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
2011-03-04 11:47 ` Wolfram Sang
@ 2011-03-09 10:44 ` subhashj
2011-03-10 6:30 ` subhashj
2011-03-09 12:45 ` zhangfei gao
` (3 subsequent siblings)
5 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-09 10:44 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
+1. Looks good to me from sd3.0 spec. prospective.
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
> 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 support 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.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 37 ++++++++++-
> drivers/mmc/core/sd_ops.c | 32 ++++++++++
> drivers/mmc/core/sd_ops.h | 1 +
> drivers/mmc/host/sdhci.c | 148
> +++++++++++++++++++++++++++++++++++++++++----
> drivers/mmc/host/sdhci.h | 18 +++++-
> include/linux/mmc/host.h | 10 +++
> include/linux/mmc/sd.h | 1 +
> 7 files changed, 229 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index b3f8a3c..3e82599 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -408,6 +408,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
> @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> ocr, u32 *cid)
> if (!err)
> ocr |= 1 << 30;
>
> - err = mmc_send_app_op_cond(host, ocr, NULL);
> + /* If the host can supply more than 150mA, XPC should be set to
> 1. */
> + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> + MMC_CAP_SET_XPC_180))
> + ocr |= 1 << 28;
> +
> + 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)) {
> + err = mmc_start_voltage_switch(host);
> + if (err)
> + return err;
> + }
> +
> if (mmc_host_is_spi(host))
> err = mmc_send_cid(host, cid);
> else
> @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> }
>
> /*
> + * 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))
> + host->ocr |= (1 << 24);
> +
> + /*
> * Detect and init the card.
> */
> err = mmc_sd_init_card(host, host->ocr, NULL);
> - if (err)
> - goto err;
> + if (err == -EAGAIN) {
> + /*
> + * Retry initialization with S18R set to 0.
> + */
> + host->ocr &= ~(1 << 24);
> + err = mmc_sd_init_card(host, host->ocr, NULL);
> + if (err)
> + goto err;
> + }
>
> mmc_release_host(host);
> err = mmc_add_card(host->card);
> diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> index 797cdb5..8a23e2e 100644
> --- a/drivers/mmc/core/sd_ops.c
> +++ b/drivers/mmc/core/sd_ops.c
> @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card,
> int width)
> return 0;
> }
>
> +int mmc_start_voltage_switch(struct mmc_host *host)
> +{
> + struct mmc_command cmd;
> + int err;
> +
> + BUG_ON(!host);
> +
> + /*
> + * If the host does not provide signal voltage switch procedure,
> we
> + * set S18R to 0, and then retry initialization sequence.
> + */
> + if (!host->ops->start_signal_voltage_switch)
> + return -EAGAIN;
> +
> + 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;
> +
> + err = host->ops->start_signal_voltage_switch(host);
> +
> + return err;
> +}
> +
> int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> {
> struct mmc_command cmd;
> diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> index ffc2305..3cfba59 100644
> --- a/drivers/mmc/core/sd_ops.h
> +++ b/drivers/mmc/core/sd_ops.h
> @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> *scr);
> int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> u8 value, u8 *resp);
> int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> +int mmc_start_voltage_switch(struct mmc_host *host);
>
> #endif
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 8914a25..5487a0b 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -85,6 +85,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",
> @@ -1337,11 +1339,70 @@ out:
> spin_unlock_irqrestore(&host->lock, flags);
> }
>
> +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u8 pwr;
> + u16 clk, ctrl;
> + u32 present_state;
> +
> + host = mmc_priv(mmc);
> +
> + /* Stop SDCLK */
> + host = mmc_priv(mmc);
> + 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_readw(host, SDHCI_HOST_CONTROL2);
> + 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. Stop
> + * power to 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);
> +
> + return -EAGAIN;
> +}
> +
> 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,
> };
>
>
> /**********************************************************************
> *******\
> @@ -1799,7 +1860,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);
> @@ -1822,12 +1885,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);
>
> + if (host->version >= SDHCI_SPEC_300)
> + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> +
> 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;
> @@ -1838,7 +1904,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) &&
> @@ -1892,10 +1959,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;
> @@ -1911,7 +1978,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);
> @@ -1923,7 +1990,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;
>
> /*
> @@ -1950,21 +2017,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)
> @@ -2024,7 +2146,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 223762c..95d70e6 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -69,6 +69,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
> @@ -147,7 +149,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
> @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -117,6 +117,8 @@ struct mmc_host_ops {
>
> /* optional callback for HC quirks */
> void (*init_card)(struct mmc_host *host, struct mmc_card *card);
> +
> + int (*start_signal_voltage_switch)(struct mmc_host *host);
> };
>
> struct mmc_card;
> @@ -173,6 +175,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 178363b..3ba5aa6 100644
> --- a/include/linux/mmc/sd.h
> +++ b/include/linux/mmc/sd.h
> @@ -17,6 +17,7 @@
> /* This is basically the same command as for MMC with some quirks. */
> #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6
> */
> #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7
> */
> +#define SD_SWITCH_VOLTAGE 11 /* ac R1
> */
>
> /* class 10 */
> #define SD_SWITCH 6 /* adtc [31:0] See below R1
> */
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
2011-03-04 11:32 ` [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
@ 2011-03-09 12:22 ` subhashj
2011-03-09 12:55 ` Nath, Arindam
2011-03-15 11:23 ` Subhash Jadavani
1 sibling, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-09 12:22 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
>
> Host Controller v3.00 and later support Auto CMD23 in the Transfer
> Mode register. Since Auto CMD23 can be used with or without DMA,
> and if used with DMA, it should _only_ be ADMA, we check against
> SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
> is set.
>
> A new definition for SDHCI_ARGUMENT2 register has been added in v3.00
> spec, which is the same as SDHCI_DMA_ADDRESS. We program the block
> count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to stop
> multiple block transfers. But during error recovery procedure, we will
> need to send Abort command, so we use a variable saved_abort_cmd to
> save the stop command to be used later.
>
> Two bits are added to SCR register as per the Physical Layer Spec
> v3.01,
> which specify whether the card supports CMD20 and/or CMD23. We use this
> as one of the conditions to decide whether to enable Auto CMD23 or not.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 6 ++++
> drivers/mmc/host/sdhci.c | 69
> +++++++++++++++++++++++++++++++++++++++++---
> drivers/mmc/host/sdhci.h | 2 +
> include/linux/mmc/card.h | 4 ++
> include/linux/mmc/sd.h | 2 +-
> include/linux/mmc/sdhci.h | 2 +
> 6 files changed, 79 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index d18c32b..b3f8a3c 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -188,6 +188,12 @@ 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 (scr->sda_spec3)
> + scr->cmd_support = UNSTUFF_BITS(resp, 32, 2);
> + }
Looks fine.
>
> if (UNSTUFF_BITS(resp, 55, 1))
> card->erased_byte = 0xFF;
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 9e15f41..8914a25 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -25,6 +25,7 @@
>
> #include <linux/mmc/mmc.h>
> #include <linux/mmc/host.h>
> +#include <linux/mmc/card.h>
>
> #include "sdhci.h"
>
> @@ -812,6 +813,30 @@ static void sdhci_prepare_data(struct sdhci_host
> *host, struct mmc_data *data)
> sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
> }
>
> +/*
> + * Does the Host Controller support Auto CMD23?
> + *
> + * There are four preconditions for Auto CMD23 to be supported:
> + * 1. Host Controller v3.00 or later
> + * 2. Card supports CMD23
> + * 3. If DMA is used, it should be ADMA
> + * 4. Only when CMD18 or CMD25 is issued
> + */
> +static int sdhci_host_auto_cmd23_supported(struct sdhci_host *host,
> + struct mmc_request *mrq)
> +{
> + struct mmc_card *card = host->mmc->card;
> +
> + if ((host->version >= SDHCI_SPEC_300) &&
> + card && (card->scr.cmd_support & SD_SCR_CMD23_SUPPORT) &&
> + !(host->flags & SDHCI_USE_SDMA) &&
> + ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
> + (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)))
> + return 1;
> +
> + return 0;
> +}
Just a thought. Shouldn't the name should say *cmd23_required? Because here
you are not just checking the card capability but also checking if current
command requires CMD23 or not.
> +
> static void sdhci_set_transfer_mode(struct sdhci_host *host,
> struct mmc_data *data)
> {
> @@ -825,10 +850,21 @@ static void sdhci_set_transfer_mode(struct
> sdhci_host *host,
> mode = SDHCI_TRNS_BLK_CNT_EN;
> if (data->blocks > 1) {
> if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
> - mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
> - else
> - mode |= SDHCI_TRNS_MULTI;
> + mode |= SDHCI_TRNS_ACMD12;
> + else if (sdhci_host_auto_cmd23_supported(host, host->mrq))
> {
> + /*
> + * Host Controller v3.00 can automatically send
CMD23
> + * before CMD18 or CMD25. For that, we need to
enable
> + * Auto CMD23 in the Transfer Mode register and
> + * program the block count in Argument 2 register.
> + */
> + mode |= SDHCI_TRNS_AUTO_CMD23;
> + sdhci_writel(host, data->blocks, SDHCI_ARGUMENT2);
> + }
> +
> + mode |= SDHCI_TRNS_MULTI;
> }
> +
> if (data->flags & MMC_DATA_READ)
> mode |= SDHCI_TRNS_READ;
> if (host->flags & SDHCI_REQ_USE_DMA)
> @@ -1131,6 +1167,23 @@ static void sdhci_request(struct mmc_host *mmc,
> struct mmc_request *mrq)
> #ifndef SDHCI_USE_LEDS_CLASS
> sdhci_activate_led(host);
> #endif
> + /*
> + * Since the block count for CMD23 has already been specified in
> the
> + * Argument 2 register, we don't need CMD12 to stop multiple
> block
> + * transfers.
> + */
> + if (sdhci_host_auto_cmd23_supported(host, mrq)) {
> + if (mrq->stop) {
> + /*
> + * Save the stop command here to be used during
> + * error recovery
> + */
> + host->saved_abort_cmd = mrq->data->stop;
> + mrq->data->stop = NULL;
> + mrq->stop = NULL;
> + }
> + }
> +
> if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
> if (mrq->stop) {
> mrq->data->stop = NULL;
> @@ -1396,6 +1449,7 @@ static void sdhci_timeout_timer(unsigned long
> data)
>
> if (host->data) {
> host->data->error = -ETIMEDOUT;
> + host->data->stop = host->saved_abort_cmd;
> sdhci_finish_data(host);
> } else {
> if (host->cmd)
> @@ -1534,9 +1588,10 @@ static void sdhci_data_irq(struct sdhci_host
> *host, u32 intmask)
> host->data->error = -EIO;
> }
>
> - if (host->data->error)
> + if (host->data->error) {
> + host->data->stop = host->saved_abort_cmd;
> sdhci_finish_data(host);
> - else {
> + } else {
> if (intmask & (SDHCI_INT_DATA_AVAIL |
> SDHCI_INT_SPACE_AVAIL))
> sdhci_transfer_pio(host);
>
> @@ -1792,6 +1847,10 @@ int sdhci_add_host(struct sdhci_host *host)
> host->flags &= ~SDHCI_USE_ADMA;
> }
>
> + /* If the host can perform ADMA operation, we reset SDMA flag */
> + if (host->flags & SDHCI_USE_ADMA)
> + host->flags &= ~SDHCI_USE_SDMA;
> +
> if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
> if (host->ops->enable_dma) {
> if (host->ops->enable_dma(host)) {
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 6e0969e..223762c 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -25,6 +25,7 @@
> */
>
> #define SDHCI_DMA_ADDRESS 0x00
> +#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
>
> #define SDHCI_BLOCK_SIZE 0x04
> #define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz &
> 0xFFF))
> @@ -37,6 +38,7 @@
> #define SDHCI_TRNS_DMA 0x01
> #define SDHCI_TRNS_BLK_CNT_EN 0x02
> #define SDHCI_TRNS_ACMD12 0x04
> +#define SDHCI_TRNS_AUTO_CMD23 0x08
> #define SDHCI_TRNS_READ 0x10
> #define SDHCI_TRNS_MULTI 0x20
>
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 8ce0827..22b0335 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -58,9 +58,13 @@ 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)
> + unsigned char cmd_support;
> +#define SD_SCR_CMD20_SUPPORT (1<<0)
> +#define SD_SCR_CMD23_SUPPORT (1<<1)
Looks good. I am just curious about: spec says that UHS104 card must support
CMD23. so are you handling this anywhere? Means what if card is detected as
UHS104 card and if it doesn't set this CMD23 bit? Just an error condition.
> };
>
> struct sd_ssr {
> diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
> index 3fd85e0..178363b 100644
> --- a/include/linux/mmc/sd.h
> +++ b/include/linux/mmc/sd.h
> @@ -59,7 +59,7 @@
>
> #define SCR_SPEC_VER_0 0 /* Implements system
specification
> 1.0 - 1.01 */
> #define SCR_SPEC_VER_1 1 /* Implements system
specification
> 1.10 */
> -#define SCR_SPEC_VER_2 2 /* Implements system
specification
> 2.00 */
> +#define SCR_SPEC_VER_2 2 /* Implements system
specification
> 2.00 - 3.0x */
>
> /*
> * SD bus widths
> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> index 83bd9f7..282d158 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -145,6 +145,8 @@ struct sdhci_host {
> unsigned int ocr_avail_sd;
> unsigned int ocr_avail_mmc;
>
> + struct mmc_command *saved_abort_cmd; /* Abort command saved
> for data error recovery */
> +
> unsigned long private[0] ____cacheline_aligned;
> };
> #endif /* __SDHCI_H */
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-04 11:32 ` [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
2011-03-04 11:47 ` Wolfram Sang
2011-03-09 10:44 ` subhashj
@ 2011-03-09 12:45 ` zhangfei gao
2011-03-10 8:11 ` Nath, Arindam
2011-03-15 10:18 ` Subhash Jadavani
` (2 subsequent siblings)
5 siblings, 1 reply; 125+ messages in thread
From: zhangfei gao @ 2011-03-09 12:45 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Fri, Mar 4, 2011 at 6:32 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 support 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.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
The patch can not work with generic card :(
> ---
> drivers/mmc/core/sd.c | 37 ++++++++++-
> drivers/mmc/core/sd_ops.c | 32 ++++++++++
> drivers/mmc/core/sd_ops.h | 1 +
> drivers/mmc/host/sdhci.c | 148 +++++++++++++++++++++++++++++++++++++++++----
> drivers/mmc/host/sdhci.h | 18 +++++-
> include/linux/mmc/host.h | 10 +++
> include/linux/mmc/sd.h | 1 +
> 7 files changed, 229 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index b3f8a3c..3e82599 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -408,6 +408,7 @@ struct device_type sd_type = {
> int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> {
> int err;
> + u32 rocr;
rocr not initialized here, or move to mmc_sd_init_card in following
patch, it may be risky, since dependency.
>
> /*
> * Since we're changing the OCR value, we seem to
> @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> if (!err)
> ocr |= 1 << 30;
>
> - err = mmc_send_app_op_cond(host, ocr, NULL);
> + /* If the host can supply more than 150mA, XPC should be set to 1. */
> + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> + MMC_CAP_SET_XPC_180))
> + ocr |= 1 << 28;
> +
> + 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)) {
if (!mmc_host_is_spi(host) && (rocr & 0x41000000 == 0x41000000))
Currently your patch can not work with general card, for example
rocr=0xc0ff8000 & 0x41000000 = 0x40000000, while what you want is both
two bit are set.
> + err = mmc_start_voltage_switch(host);
> + if (err)
> + return err;
> + }
> +
> if (mmc_host_is_spi(host))
> err = mmc_send_cid(host, cid);
> else
> @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> }
>
> /*
> + * 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))
> + host->ocr |= (1 << 24);
> +
> + /*
> * Detect and init the card.
> */
> err = mmc_sd_init_card(host, host->ocr, NULL);
> - if (err)
> - goto err;
> + if (err == -EAGAIN) {
If other error happen, for example -ETIMEDOUT in current patch for
general card, the following card can not be executed.
kernel panic will happen in mmc_add_card for null pinter, since card
is not allocated.
how about if (err) for safety?
> + /*
> + * Retry initialization with S18R set to 0.
> + */
> + host->ocr &= ~(1 << 24);
> + err = mmc_sd_init_card(host, host->ocr, NULL);
> + if (err)
> + goto err;
> + }
>
> mmc_release_host(host);
> err = mmc_add_card(host->card);
> diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> index 797cdb5..8a23e2e 100644
> --- a/drivers/mmc/core/sd_ops.c
> +++ b/drivers/mmc/core/sd_ops.c
> @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
> return 0;
> }
>
> +int mmc_start_voltage_switch(struct mmc_host *host)
> +{
> + struct mmc_command cmd;
> + int err;
> +
> + BUG_ON(!host);
> +
> + /*
> + * If the host does not provide signal voltage switch procedure, we
> + * set S18R to 0, and then retry initialization sequence.
> + */
> + if (!host->ops->start_signal_voltage_switch)
> + return -EAGAIN;
> +
> + 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;
> +
> + err = host->ops->start_signal_voltage_switch(host);
> +
> + return err;
> +}
> +
> int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> {
> struct mmc_command cmd;
> diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> index ffc2305..3cfba59 100644
> --- a/drivers/mmc/core/sd_ops.h
> +++ b/drivers/mmc/core/sd_ops.h
> @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
> int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> u8 value, u8 *resp);
> int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> +int mmc_start_voltage_switch(struct mmc_host *host);
>
> #endif
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 8914a25..5487a0b 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -85,6 +85,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",
> @@ -1337,11 +1339,70 @@ out:
> spin_unlock_irqrestore(&host->lock, flags);
> }
>
> +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u8 pwr;
> + u16 clk, ctrl;
> + u32 present_state;
> +
> + host = mmc_priv(mmc);
> +
> + /* Stop SDCLK */
> + host = mmc_priv(mmc);
> + 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_readw(host, SDHCI_HOST_CONTROL2);
> + 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. Stop
> + * power to 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);
> +
> + return -EAGAIN;
> +}
> +
> 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,
> };
>
> /*****************************************************************************\
> @@ -1799,7 +1860,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);
> @@ -1822,12 +1885,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);
>
> + if (host->version >= SDHCI_SPEC_300)
> + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> +
> 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;
> @@ -1838,7 +1904,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) &&
> @@ -1892,10 +1959,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;
> @@ -1911,7 +1978,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);
> @@ -1923,7 +1990,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;
>
> /*
> @@ -1950,21 +2017,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)
> @@ -2024,7 +2146,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 223762c..95d70e6 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -69,6 +69,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
> @@ -147,7 +149,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
> @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -117,6 +117,8 @@ struct mmc_host_ops {
>
> /* optional callback for HC quirks */
> void (*init_card)(struct mmc_host *host, struct mmc_card *card);
> +
> + int (*start_signal_voltage_switch)(struct mmc_host *host);
> };
>
> struct mmc_card;
> @@ -173,6 +175,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 178363b..3ba5aa6 100644
> --- a/include/linux/mmc/sd.h
> +++ b/include/linux/mmc/sd.h
> @@ -17,6 +17,7 @@
> /* This is basically the same command as for MMC with some quirks. */
> #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
> #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
> +#define SD_SWITCH_VOLTAGE 11 /* ac R1 */
>
> /* class 10 */
> #define SD_SWITCH 6 /* adtc [31:0] See below R1 */
> --
> 1.7.1
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
2011-03-09 12:22 ` subhashj
@ 2011-03-09 12:55 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-09 12:55 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Wednesday, March 09, 2011 5:52 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
> >
> > Host Controller v3.00 and later support Auto CMD23 in the Transfer
> > Mode register. Since Auto CMD23 can be used with or without DMA,
> > and if used with DMA, it should _only_ be ADMA, we check against
> > SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
> > is set.
> >
> > A new definition for SDHCI_ARGUMENT2 register has been added in v3.00
> > spec, which is the same as SDHCI_DMA_ADDRESS. We program the block
> > count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to stop
> > multiple block transfers. But during error recovery procedure, we
> will
> > need to send Abort command, so we use a variable saved_abort_cmd to
> > save the stop command to be used later.
> >
> > Two bits are added to SCR register as per the Physical Layer Spec
> > v3.01,
> > which specify whether the card supports CMD20 and/or CMD23. We use
> this
> > as one of the conditions to decide whether to enable Auto CMD23 or
> not.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 6 ++++
> > drivers/mmc/host/sdhci.c | 69
> > +++++++++++++++++++++++++++++++++++++++++---
> > drivers/mmc/host/sdhci.h | 2 +
> > include/linux/mmc/card.h | 4 ++
> > include/linux/mmc/sd.h | 2 +-
> > include/linux/mmc/sdhci.h | 2 +
> > 6 files changed, 79 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index d18c32b..b3f8a3c 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -188,6 +188,12 @@ 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 (scr->sda_spec3)
> > + scr->cmd_support = UNSTUFF_BITS(resp, 32, 2);
> > + }
> Looks fine.
>
> >
> > if (UNSTUFF_BITS(resp, 55, 1))
> > card->erased_byte = 0xFF;
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 9e15f41..8914a25 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -25,6 +25,7 @@
> >
> > #include <linux/mmc/mmc.h>
> > #include <linux/mmc/host.h>
> > +#include <linux/mmc/card.h>
> >
> > #include "sdhci.h"
> >
> > @@ -812,6 +813,30 @@ static void sdhci_prepare_data(struct sdhci_host
> > *host, struct mmc_data *data)
> > sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
> > }
> >
> > +/*
> > + * Does the Host Controller support Auto CMD23?
> > + *
> > + * There are four preconditions for Auto CMD23 to be supported:
> > + * 1. Host Controller v3.00 or later
> > + * 2. Card supports CMD23
> > + * 3. If DMA is used, it should be ADMA
> > + * 4. Only when CMD18 or CMD25 is issued
> > + */
> > +static int sdhci_host_auto_cmd23_supported(struct sdhci_host *host,
> > + struct mmc_request *mrq)
> > +{
> > + struct mmc_card *card = host->mmc->card;
> > +
> > + if ((host->version >= SDHCI_SPEC_300) &&
> > + card && (card->scr.cmd_support & SD_SCR_CMD23_SUPPORT) &&
> > + !(host->flags & SDHCI_USE_SDMA) &&
> > + ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
> > + (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)))
> > + return 1;
> > +
> > + return 0;
> > +}
> Just a thought. Shouldn't the name should say *cmd23_required? Because
> here
> you are not just checking the card capability but also checking if
> current
> command requires CMD23 or not.
Yes, *cmd23_required looks good to me. I will change the function name.
>
> > +
> > static void sdhci_set_transfer_mode(struct sdhci_host *host,
> > struct mmc_data *data)
> > {
> > @@ -825,10 +850,21 @@ static void sdhci_set_transfer_mode(struct
> > sdhci_host *host,
> > mode = SDHCI_TRNS_BLK_CNT_EN;
> > if (data->blocks > 1) {
> > if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
> > - mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
> > - else
> > - mode |= SDHCI_TRNS_MULTI;
> > + mode |= SDHCI_TRNS_ACMD12;
> > + else if (sdhci_host_auto_cmd23_supported(host, host->mrq))
> > {
> > + /*
> > + * Host Controller v3.00 can automatically send
> CMD23
> > + * before CMD18 or CMD25. For that, we need to
> enable
> > + * Auto CMD23 in the Transfer Mode register and
> > + * program the block count in Argument 2 register.
> > + */
> > + mode |= SDHCI_TRNS_AUTO_CMD23;
> > + sdhci_writel(host, data->blocks, SDHCI_ARGUMENT2);
> > + }
> > +
> > + mode |= SDHCI_TRNS_MULTI;
> > }
> > +
> > if (data->flags & MMC_DATA_READ)
> > mode |= SDHCI_TRNS_READ;
> > if (host->flags & SDHCI_REQ_USE_DMA)
> > @@ -1131,6 +1167,23 @@ static void sdhci_request(struct mmc_host
> *mmc,
> > struct mmc_request *mrq)
> > #ifndef SDHCI_USE_LEDS_CLASS
> > sdhci_activate_led(host);
> > #endif
> > + /*
> > + * Since the block count for CMD23 has already been specified in
> > the
> > + * Argument 2 register, we don't need CMD12 to stop multiple
> > block
> > + * transfers.
> > + */
> > + if (sdhci_host_auto_cmd23_supported(host, mrq)) {
> > + if (mrq->stop) {
> > + /*
> > + * Save the stop command here to be used during
> > + * error recovery
> > + */
> > + host->saved_abort_cmd = mrq->data->stop;
> > + mrq->data->stop = NULL;
> > + mrq->stop = NULL;
> > + }
> > + }
> > +
> > if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
> > if (mrq->stop) {
> > mrq->data->stop = NULL;
> > @@ -1396,6 +1449,7 @@ static void sdhci_timeout_timer(unsigned long
> > data)
> >
> > if (host->data) {
> > host->data->error = -ETIMEDOUT;
> > + host->data->stop = host->saved_abort_cmd;
> > sdhci_finish_data(host);
> > } else {
> > if (host->cmd)
> > @@ -1534,9 +1588,10 @@ static void sdhci_data_irq(struct sdhci_host
> > *host, u32 intmask)
> > host->data->error = -EIO;
> > }
> >
> > - if (host->data->error)
> > + if (host->data->error) {
> > + host->data->stop = host->saved_abort_cmd;
> > sdhci_finish_data(host);
> > - else {
> > + } else {
> > if (intmask & (SDHCI_INT_DATA_AVAIL |
> > SDHCI_INT_SPACE_AVAIL))
> > sdhci_transfer_pio(host);
> >
> > @@ -1792,6 +1847,10 @@ int sdhci_add_host(struct sdhci_host *host)
> > host->flags &= ~SDHCI_USE_ADMA;
> > }
> >
> > + /* If the host can perform ADMA operation, we reset SDMA flag */
> > + if (host->flags & SDHCI_USE_ADMA)
> > + host->flags &= ~SDHCI_USE_SDMA;
> > +
> > if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
> > if (host->ops->enable_dma) {
> > if (host->ops->enable_dma(host)) {
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index 6e0969e..223762c 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -25,6 +25,7 @@
> > */
> >
> > #define SDHCI_DMA_ADDRESS 0x00
> > +#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
> >
> > #define SDHCI_BLOCK_SIZE 0x04
> > #define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz
> &
> > 0xFFF))
> > @@ -37,6 +38,7 @@
> > #define SDHCI_TRNS_DMA 0x01
> > #define SDHCI_TRNS_BLK_CNT_EN 0x02
> > #define SDHCI_TRNS_ACMD12 0x04
> > +#define SDHCI_TRNS_AUTO_CMD23 0x08
> > #define SDHCI_TRNS_READ 0x10
> > #define SDHCI_TRNS_MULTI 0x20
> >
> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > index 8ce0827..22b0335 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -58,9 +58,13 @@ 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)
> > + unsigned char cmd_support;
> > +#define SD_SCR_CMD20_SUPPORT (1<<0)
> > +#define SD_SCR_CMD23_SUPPORT (1<<1)
> Looks good. I am just curious about: spec says that UHS104 card must
> support
> CMD23. so are you handling this anywhere? Means what if card is
> detected as
> UHS104 card and if it doesn't set this CMD23 bit? Just an error
> condition.
The description from Auto CMD23 Enable in the spec mentions card supporting CMD23 as a _required_ condition before using Auto CMD23. So I assume UHS104 cards should have this bit set for sure.
Thanks,
Arindam
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 03/12] mmc: sd: query function modes for uhs cards
2011-03-04 11:32 ` [PATCH v2 03/12] mmc: sd: query function modes for uhs cards Arindam Nath
@ 2011-03-09 14:08 ` subhashj
2011-03-09 14:31 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-09 14:08 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 03/12] mmc: sd: query function modes for uhs cards
>
> SD cards which conform to Physical Layer Spec v3.01 can support
> additional Bus Speed Modes, Driver Strength, and Current Limit
> other than the default values. We use CMD6 mode 0 to read these
> additional card functions. The values read here will be used
> during UHS-I initialization steps.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 99
> +++++++++++++++++++++++++++++++++++++++-------
> include/linux/mmc/card.h | 3 +
> 2 files changed, 87 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 3e82599..a63956b 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -283,25 +283,94 @@ static int mmc_read_switch(struct mmc_card *card)
> return -ENOMEM;
> }
>
> - 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 ((err != -EINVAL)
> - && (err != -ENOSYS)
> - && (err != -EFAULT))
> + if (card->scr.sda_spec3) {
> + /* First find out the supported Bus Speed Modes. */
> + err = mmc_sd_switch(card, 0, 0, 1, status);
Reading function-group-1 status applies for both SD3.0 and SD2.0 cards as
well. With card->scr.sda_spec check, you are duplicating the same code in
else part of it as well. You can optimize it to only one call to
mmc_sd_switch() to read function-group-1.
> + 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 "
> + "Bus Speed modes.\n",
> + mmc_hostname(card->host));
> + err = 0;
> +
> goto out;
> + }
>
> - printk(KER N_WARNING "%s: problem reading switch "
> - "capabilities, performance might suffer.\n",
> - mmc_hostname(card->host));
> - err = 0;
> + card->sw_caps.uhs_bus_mode = status[13];
Is adding uhs* prefix is appropriate? If card supports SD3.0 spec means card
have to support UHS modes? Can we have cards (SDXC?) which sets SD3.0 spec
bit in SCR but does not support any of the UHS modes. So in this case
putting uhs* prefix might not be appropriate.
> +
> + /* 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;
> - }
> + goto out;
> + }
>
> - if (status[13] & 0x02)
> - card->sw_caps.hs_max_dtr = 50000000;
> + card->sw_caps.uhs_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.uhs_curr_limit = status[7];
> + } else {
> + 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 ((err != -EINVAL)
> + && (err != -ENOSYS)
> + && (err != -EFAULT))
> + goto out;
> +
> + printk(KERN_WARNING "%s: problem reading switch "
> + "capabilities, performance might suffer.\n",
> + mmc_hostname(card->host));
> + err = 0;
> + goto out;
> + }
> +
> + 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 22b0335..7080f22 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -75,6 +75,9 @@ struct sd_ssr {
>
> struct sd_switch_caps {
> unsigned int hs_max_dtr;
> + unsigned int uhs_bus_mode;
> + unsigned int uhs_drv_type;
> + unsigned int uhs_curr_limit;
> };
>
> struct sdio_cccr {
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 03/12] mmc: sd: query function modes for uhs cards
2011-03-09 14:08 ` subhashj
@ 2011-03-09 14:31 ` Nath, Arindam
2011-03-09 18:04 ` subhashj
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-09 14:31 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Wednesday, March 09, 2011 7:39 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 03/12] mmc: sd: query function modes for uhs
> cards
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 03/12] mmc: sd: query function modes for uhs cards
> >
> > SD cards which conform to Physical Layer Spec v3.01 can support
> > additional Bus Speed Modes, Driver Strength, and Current Limit
> > other than the default values. We use CMD6 mode 0 to read these
> > additional card functions. The values read here will be used
> > during UHS-I initialization steps.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 99
> > +++++++++++++++++++++++++++++++++++++++-------
> > include/linux/mmc/card.h | 3 +
> > 2 files changed, 87 insertions(+), 15 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index 3e82599..a63956b 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -283,25 +283,94 @@ static int mmc_read_switch(struct mmc_card
> *card)
> > return -ENOMEM;
> > }
> >
> > - 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 ((err != -EINVAL)
> > - && (err != -ENOSYS)
> > - && (err != -EFAULT))
> > + if (card->scr.sda_spec3) {
> > + /* First find out the supported Bus Speed Modes. */
> > + err = mmc_sd_switch(card, 0, 0, 1, status);
> Reading function-group-1 status applies for both SD3.0 and SD2.0 cards
> as
> well. With card->scr.sda_spec check, you are duplicating the same code
> in
> else part of it as well. You can optimize it to only one call to
> mmc_sd_switch() to read function-group-1.
Yes, there is a duplication of the code. I will try to minimize it in the next version.
>
> > + 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 "
> > + "Bus Speed modes.\n",
> > + mmc_hostname(card->host));
> > + err = 0;
> > +
> > goto out;
> > + }
> >
> > - printk(KER N_WARNING "%s: problem reading switch "
> > - "capabilities, performance might suffer.\n",
> > - mmc_hostname(card->host));
> > - err = 0;
> > + card->sw_caps.uhs_bus_mode = status[13];
> Is adding uhs* prefix is appropriate? If card supports SD3.0 spec means
> card
> have to support UHS modes? Can we have cards (SDXC?) which sets SD3.0
> spec
> bit in SCR but does not support any of the UHS modes. So in this case
> putting uhs* prefix might not be appropriate.
As per Physical Layer Spec v3.01, UHS-I can be applied to SDHC and SDXC cards. Cards which support UHS-I should have sda_spec3 set in the SCR register. Since we need to set the data rate depending on the exact UHS-I speed mode, I have prefixed the variable with uhs_ to make things clear.
Thanks,
Arindam
>
>
> > +
> > + /* 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;
> > - }
> > + goto out;
> > + }
> >
> > - if (status[13] & 0x02)
> > - card->sw_caps.hs_max_dtr = 50000000;
> > + card->sw_caps.uhs_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.uhs_curr_limit = status[7];
> > + } else {
> > + 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 ((err != -EINVAL)
> > + && (err != -ENOSYS)
> > + && (err != -EFAULT))
> > + goto out;
> > +
> > + printk(KERN_WARNING "%s: problem reading switch "
> > + "capabilities, performance might suffer.\n",
> > + mmc_hostname(card->host));
> > + err = 0;
> > + goto out;
> > + }
> > +
> > + 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 22b0335..7080f22 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -75,6 +75,9 @@ struct sd_ssr {
> >
> > struct sd_switch_caps {
> > unsigned int hs_max_dtr;
> > + unsigned int uhs_bus_mode;
> > + unsigned int uhs_drv_type;
> > + unsigned int uhs_curr_limit;
> > };
> >
> > struct sdio_cccr {
> > --
> > 1.7.1
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 03/12] mmc: sd: query function modes for uhs cards
2011-03-09 14:31 ` Nath, Arindam
@ 2011-03-09 18:04 ` subhashj
2011-03-09 18:16 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-09 18:04 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Wednesday, March 09, 2011 8:01 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 03/12] mmc: sd: query function modes for uhs
> cards
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Wednesday, March 09, 2011 7:39 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 03/12] mmc: sd: query function modes for uhs
> > cards
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 03/12] mmc: sd: query function modes for uhs
> cards
> > >
> > > SD cards which conform to Physical Layer Spec v3.01 can support
> > > additional Bus Speed Modes, Driver Strength, and Current Limit
> > > other than the default values. We use CMD6 mode 0 to read these
> > > additional card functions. The values read here will be used
> > > during UHS-I initialization steps.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/sd.c | 99
> > > +++++++++++++++++++++++++++++++++++++++-------
> > > include/linux/mmc/card.h | 3 +
> > > 2 files changed, 87 insertions(+), 15 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index 3e82599..a63956b 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -283,25 +283,94 @@ static int mmc_read_switch(struct mmc_card
> > *card)
> > > return -ENOMEM;
> > > }
> > >
> > > - 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 ((err != -EINVAL)
> > > - && (err != -ENOSYS)
> > > - && (err != -EFAULT))
> > > + if (card->scr.sda_spec3) {
> > > + /* First find out the supported Bus Speed Modes. */
> > > + err = mmc_sd_switch(card, 0, 0, 1, status);
> > Reading function-group-1 status applies for both SD3.0 and SD2.0
> cards
> > as
> > well. With card->scr.sda_spec check, you are duplicating the same
> code
> > in
> > else part of it as well. You can optimize it to only one call to
> > mmc_sd_switch() to read function-group-1.
>
> Yes, there is a duplication of the code. I will try to minimize it in
> the next version.
>
> >
> > > + 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 "
> > > + "Bus Speed modes.\n",
> > > + mmc_hostname(card->host));
> > > + err = 0;
> > > +
> > > goto out;
> > > + }
> > >
> > > - printk(KER N_WARNING "%s: problem reading switch "
> > > - "capabilities, performance might suffer.\n",
> > > - mmc_hostname(card->host));
> > > - err = 0;
> > > + card->sw_caps.uhs_bus_mode = status[13];
> > Is adding uhs* prefix is appropriate? If card supports SD3.0 spec
> means
> > card
> > have to support UHS modes? Can we have cards (SDXC?) which sets SD3.0
> > spec
> > bit in SCR but does not support any of the UHS modes. So in this case
> > putting uhs* prefix might not be appropriate.
>
> As per Physical Layer Spec v3.01, UHS-I can be applied to SDHC and SDXC
> cards. Cards which support UHS-I should have sda_spec3 set in the SCR
> register. Since we need to set the data rate depending on the exact
> UHS-I speed mode, I have prefixed the variable with uhs_ to make things
> clear.
UHS-I speed mode support is not a must condition to set SD_SPEC3 bit to 1 in
SCR register:
This is what the spec says:
. Essential conditions to indicate Version 3.00 Card (SD_SPEC=2 and
SD_SPEC3=1)
(1) The card shall support CMD6
(2) The card shall support CMD8
(3) The card shall support CMD42
(4) User area capacity shall be up to 2GB (SDSC) or 32GB (SDHC)
User area capacity shall be more than or equal to 32GB and up to 2TB (SDXC)
(5) Speed Class shall be supported (SDHC or SDXC)
Supporting UHS-I mode is an optional conditional.
>
> Thanks,
> Arindam
>
> >
> >
> > > +
> > > + /* 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;
> > > - }
> > > + goto out;
> > > + }
> > >
> > > - if (status[13] & 0x02)
> > > - card->sw_caps.hs_max_dtr = 50000000;
> > > + card->sw_caps.uhs_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.uhs_curr_limit = status[7];
> > > + } else {
> > > + 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 ((err != -EINVAL)
> > > + && (err != -ENOSYS)
> > > + && (err != -EFAULT))
> > > + goto out;
> > > +
> > > + printk(KERN_WARNING "%s: problem reading switch "
> > > + "capabilities, performance might suffer.\n",
> > > + mmc_hostname(card->host));
> > > + err = 0;
> > > + goto out;
> > > + }
> > > +
> > > + 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 22b0335..7080f22 100644
> > > --- a/include/linux/mmc/card.h
> > > +++ b/include/linux/mmc/card.h
> > > @@ -75,6 +75,9 @@ struct sd_ssr {
> > >
> > > struct sd_switch_caps {
> > > unsigned int hs_max_dtr;
> > > + unsigned int uhs_bus_mode;
> > > + unsigned int uhs_drv_type;
> > > + unsigned int uhs_curr_limit;
> > > };
> > >
> > > struct sdio_cccr {
> > > --
> > > 1.7.1
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 03/12] mmc: sd: query function modes for uhs cards
2011-03-09 18:04 ` subhashj
@ 2011-03-09 18:16 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-09 18:16 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Wednesday, March 09, 2011 11:35 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 03/12] mmc: sd: query function modes for uhs
> cards
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Wednesday, March 09, 2011 8:01 PM
> > To: subhashj@codeaurora.org; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 03/12] mmc: sd: query function modes for uhs
> > cards
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > Sent: Wednesday, March 09, 2011 7:39 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 03/12] mmc: sd: query function modes for uhs
> > > cards
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > To: cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > henry.su@amd.com;
> > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > Subject: [PATCH v2 03/12] mmc: sd: query function modes for uhs
> > cards
> > > >
> > > > SD cards which conform to Physical Layer Spec v3.01 can support
> > > > additional Bus Speed Modes, Driver Strength, and Current Limit
> > > > other than the default values. We use CMD6 mode 0 to read these
> > > > additional card functions. The values read here will be used
> > > > during UHS-I initialization steps.
> > > >
> > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > ---
> > > > drivers/mmc/core/sd.c | 99
> > > > +++++++++++++++++++++++++++++++++++++++-------
> > > > include/linux/mmc/card.h | 3 +
> > > > 2 files changed, 87 insertions(+), 15 deletions(-)
> > > >
> > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > index 3e82599..a63956b 100644
> > > > --- a/drivers/mmc/core/sd.c
> > > > +++ b/drivers/mmc/core/sd.c
> > > > @@ -283,25 +283,94 @@ static int mmc_read_switch(struct mmc_card
> > > *card)
> > > > return -ENOMEM;
> > > > }
> > > >
> > > > - 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 ((err != -EINVAL)
> > > > - && (err != -ENOSYS)
> > > > - && (err != -EFAULT))
> > > > + if (card->scr.sda_spec3) {
> > > > + /* First find out the supported Bus Speed Modes. */
> > > > + err = mmc_sd_switch(card, 0, 0, 1, status);
> > > Reading function-group-1 status applies for both SD3.0 and SD2.0
> > cards
> > > as
> > > well. With card->scr.sda_spec check, you are duplicating the same
> > code
> > > in
> > > else part of it as well. You can optimize it to only one call to
> > > mmc_sd_switch() to read function-group-1.
> >
> > Yes, there is a duplication of the code. I will try to minimize it in
> > the next version.
> >
> > >
> > > > + 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 "
> > > > + "Bus Speed modes.\n",
> > > > + mmc_hostname(card->host));
> > > > + err = 0;
> > > > +
> > > > goto out;
> > > > + }
> > > >
> > > > - printk(KER N_WARNING "%s: problem reading switch "
> > > > - "capabilities, performance might suffer.\n",
> > > > - mmc_hostname(card->host));
> > > > - err = 0;
> > > > + card->sw_caps.uhs_bus_mode = status[13];
> > > Is adding uhs* prefix is appropriate? If card supports SD3.0 spec
> > means
> > > card
> > > have to support UHS modes? Can we have cards (SDXC?) which sets
> SD3.0
> > > spec
> > > bit in SCR but does not support any of the UHS modes. So in this
> case
> > > putting uhs* prefix might not be appropriate.
> >
> > As per Physical Layer Spec v3.01, UHS-I can be applied to SDHC and
> SDXC
> > cards. Cards which support UHS-I should have sda_spec3 set in the SCR
> > register. Since we need to set the data rate depending on the exact
> > UHS-I speed mode, I have prefixed the variable with uhs_ to make
> things
> > clear.
>
> UHS-I speed mode support is not a must condition to set SD_SPEC3 bit to
> 1 in
> SCR register:
> This is what the spec says:
> . Essential conditions to indicate Version 3.00 Card (SD_SPEC=2 and
> SD_SPEC3=1)
> (1) The card shall support CMD6
> (2) The card shall support CMD8
> (3) The card shall support CMD42
> (4) User area capacity shall be up to 2GB (SDSC) or 32GB (SDHC)
> User area capacity shall be more than or equal to 32GB and up to 2TB
> (SDXC)
> (5) Speed Class shall be supported (SDHC or SDXC)
>
> Supporting UHS-I mode is an optional conditional.
Thanks for the clarification. I will change the variable names as sd3_*.
Arindam
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > >
> > > > +
> > > > + /* 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;
> > > > - }
> > > > + goto out;
> > > > + }
> > > >
> > > > - if (status[13] & 0x02)
> > > > - card->sw_caps.hs_max_dtr = 50000000;
> > > > + card->sw_caps.uhs_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.uhs_curr_limit = status[7];
> > > > + } else {
> > > > + 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 ((err != -EINVAL)
> > > > + && (err != -ENOSYS)
> > > > + && (err != -EFAULT))
> > > > + goto out;
> > > > +
> > > > + printk(KERN_WARNING "%s: problem reading switch
> "
> > > > + "capabilities, performance might
> suffer.\n",
> > > > + mmc_hostname(card->host));
> > > > + err = 0;
> > > > + goto out;
> > > > + }
> > > > +
> > > > + 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 22b0335..7080f22 100644
> > > > --- a/include/linux/mmc/card.h
> > > > +++ b/include/linux/mmc/card.h
> > > > @@ -75,6 +75,9 @@ struct sd_ssr {
> > > >
> > > > struct sd_switch_caps {
> > > > unsigned int hs_max_dtr;
> > > > + unsigned int uhs_bus_mode;
> > > > + unsigned int uhs_drv_type;
> > > > + unsigned int uhs_curr_limit;
> > > > };
> > > >
> > > > struct sdio_cccr {
> > > > --
> > > > 1.7.1
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-04 11:32 ` [PATCH v2 07/12] mmc: sd: set current limit for uhs cards Arindam Nath
@ 2011-03-09 21:41 ` Philip Rakity
2011-03-10 3:12 ` Nath, Arindam
2011-03-10 8:16 ` subhashj
` (2 subsequent siblings)
3 siblings, 1 reply; 125+ messages in thread
From: Philip Rakity @ 2011-03-09 21:41 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On Mar 4, 2011, at 3:32 AM, 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.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> include/linux/mmc/card.h | 9 +++++++++
> include/linux/mmc/host.h | 1 +
> 4 files changed, 79 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index ec0d8e6..df98a2c 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -550,6 +550,46 @@ 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)
> +{
> + struct mmc_host *host = card->host;
> + int mmc_host_max_current_180, current_limit;
> + int err;
> +
> + /* sanity check */
> + if (!host->ops->get_max_current_180)
> + return 0;
> +
> + /* Maximum current supported by host at 1.8V */
> + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
> +
> + if (mmc_host_max_current_180 >= 800) {
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
> + current_limit = SD_SET_CURRENT_LIMIT_800;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> + current_limit = SD_SET_CURRENT_LIMIT_600;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> + } else if (mmc_host_max_current_180 >= 600) {
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> + current_limit = SD_SET_CURRENT_LIMIT_600;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> + } else if (mmc_host_max_current_180 >= 400)
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> +
> + 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;
> +}
> +
warning on current_limit may not be initialized.
> /*
> * UHS-I specific initialization procedure
> */
> @@ -590,6 +630,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 f127fa2..245cc39 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1462,12 +1462,36 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> return -EAGAIN;
> }
>
> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u32 max_current_caps;
> + unsigned long flags;
> + int max_current_180;
> +
> + host = mmc_priv(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + /* Maximum current is 4 times the register value for 1.8V */
> + max_current_180 = ((max_current_caps & SDHCI_MAX_CURRENT_180_MASK) >>
> + SDHCI_MAX_CURRENT_180_SHIFT) *
> + SDHCI_MAX_CURRENT_MULTIPLIER;
> +
> + return max_current_180;
> +}
> +
> 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,
> + .get_max_current_180 = sdhci_get_max_current_180,
> };
>
> /*****************************************************************************\
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 0b24c41..a6811ae 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -98,6 +98,15 @@ struct sd_switch_caps {
> #define SD_DRIVER_TYPE_C 0x04
> #define SD_DRIVER_TYPE_D 0x08
> unsigned int uhs_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 4dfff6d..e84cd05 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -128,6 +128,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);
> + int (*get_max_current_180)(struct mmc_host *mmc);
> };
>
> struct mmc_card;
> --
> 1.7.1
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-09 21:41 ` Philip Rakity
@ 2011-03-10 3:12 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 3:12 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Thursday, March 10, 2011 3:11 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>
>
> On Mar 4, 2011, at 3:32 AM, 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.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 45
> +++++++++++++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> > include/linux/mmc/card.h | 9 +++++++++
> > include/linux/mmc/host.h | 1 +
> > 4 files changed, 79 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index ec0d8e6..df98a2c 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -550,6 +550,46 @@ 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)
> > +{
> > + struct mmc_host *host = card->host;
> > + int mmc_host_max_current_180, current_limit;
> > + int err;
> > +
> > + /* sanity check */
> > + if (!host->ops->get_max_current_180)
> > + return 0;
> > +
> > + /* Maximum current supported by host at 1.8V */
> > + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
> > +
> > + if (mmc_host_max_current_180 >= 800) {
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
> > + current_limit = SD_SET_CURRENT_LIMIT_800;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> > + current_limit = SD_SET_CURRENT_LIMIT_600;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > + } else if (mmc_host_max_current_180 >= 600) {
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> > + current_limit = SD_SET_CURRENT_LIMIT_600;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > + } else if (mmc_host_max_current_180 >= 400)
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > +
> > + 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;
> > +}
> > +
>
> warning on current_limit may not be initialized.
Thanks. Already pointed out by Chris. Will be fixed.
Arindam
>
> > /*
> > * UHS-I specific initialization procedure
> > */
> > @@ -590,6 +630,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 f127fa2..245cc39 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -1462,12 +1462,36 @@ static int
> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > return -EAGAIN;
> > }
> >
> > +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u32 max_current_caps;
> > + unsigned long flags;
> > + int max_current_180;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + /* Maximum current is 4 times the register value for 1.8V */
> > + max_current_180 = ((max_current_caps &
> SDHCI_MAX_CURRENT_180_MASK) >>
> > + SDHCI_MAX_CURRENT_180_SHIFT) *
> > + SDHCI_MAX_CURRENT_MULTIPLIER;
> > +
> > + return max_current_180;
> > +}
> > +
> > 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,
> > + .get_max_current_180 = sdhci_get_max_current_180,
> > };
> >
> >
> /**********************************************************************
> *******\
> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > index 0b24c41..a6811ae 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -98,6 +98,15 @@ struct sd_switch_caps {
> > #define SD_DRIVER_TYPE_C 0x04
> > #define SD_DRIVER_TYPE_D 0x08
> > unsigned int uhs_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 4dfff6d..e84cd05 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -128,6 +128,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);
> > + int (*get_max_current_180)(struct mmc_host *mmc);
> > };
> >
> > struct mmc_card;
> > --
> > 1.7.1
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-09 10:44 ` subhashj
@ 2011-03-10 6:30 ` subhashj
2011-03-10 8:05 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 6:30 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of subhashj@codeaurora.org
> Sent: Wednesday, March 09, 2011 4:15 PM
> To: 'Arindam Nath'; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; henry.su@amd.com; aaron.lu@amd.com;
> anath.amd@gmail.com
> Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
> +1. Looks good to me from sd3.0 spec. prospective.
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> > switch procedure
> >
> > 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 support 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.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 37 ++++++++++-
> > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> > drivers/mmc/core/sd_ops.h | 1 +
> > drivers/mmc/host/sdhci.c | 148
> > +++++++++++++++++++++++++++++++++++++++++----
> > drivers/mmc/host/sdhci.h | 18 +++++-
> > include/linux/mmc/host.h | 10 +++
> > include/linux/mmc/sd.h | 1 +
> > 7 files changed, 229 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index b3f8a3c..3e82599 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -408,6 +408,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
> > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> > ocr, u32 *cid)
> > if (!err)
> > ocr |= 1 << 30;
> >
> > - err = mmc_send_app_op_cond(host, ocr, NULL);
> > + /* If the host can supply more than 150mA, XPC should be set to
> > 1. */
> > + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > + MMC_CAP_SET_XPC_180))
> > + ocr |= 1 << 28;
> > +
> > + 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)) {
> > + err = mmc_start_voltage_switch(host);
> > + if (err)
> > + return err;
> > + }
> > +
> > if (mmc_host_is_spi(host))
> > err = mmc_send_cid(host, cid);
> > else
> > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> > }
> >
> > /*
> > + * 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))
> > + host->ocr |= (1 << 24);
> > +
> > + /*
> > * Detect and init the card.
> > */
> > err = mmc_sd_init_card(host, host->ocr, NULL);
> > - if (err)
> > - goto err;
> > + if (err == -EAGAIN) {
> > + /*
> > + * Retry initialization with S18R set to 0.
> > + */
> > + host->ocr &= ~(1 << 24);
> > + err = mmc_sd_init_card(host, host->ocr, NULL);
> > + if (err)
> > + goto err;
> > + }
Just a thought. Shouldn't we do setting of this b24 bit of OCR in
mmc_sd_get_cid() function itself and if mmc_send_app_op_cond() returns error
then reset b24 bit and retry mmc_send_app_op_cond() command.
> >
> > mmc_release_host(host);
> > err = mmc_add_card(host->card);
> > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > index 797cdb5..8a23e2e 100644
> > --- a/drivers/mmc/core/sd_ops.c
> > +++ b/drivers/mmc/core/sd_ops.c
> > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card,
> > int width)
> > return 0;
> > }
> >
> > +int mmc_start_voltage_switch(struct mmc_host *host)
> > +{
> > + struct mmc_command cmd;
> > + int err;
> > +
> > + BUG_ON(!host);
> > +
> > + /*
> > + * If the host does not provide signal voltage switch procedure,
> > we
> > + * set S18R to 0, and then retry initialization sequence.
> > + */
> > + if (!host->ops->start_signal_voltage_switch)
> > + return -EAGAIN;
> > +
> > + 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;
> > +
> > + err = host->ops->start_signal_voltage_switch(host);
> > +
> > + return err;
> > +}
> > +
> > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> > {
> > struct mmc_command cmd;
> > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > index ffc2305..3cfba59 100644
> > --- a/drivers/mmc/core/sd_ops.h
> > +++ b/drivers/mmc/core/sd_ops.h
> > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> > *scr);
> > int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> > u8 value, u8 *resp);
> > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > +int mmc_start_voltage_switch(struct mmc_host *host);
> >
> > #endif
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 8914a25..5487a0b 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -85,6 +85,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",
> > @@ -1337,11 +1339,70 @@ out:
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> >
> > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u8 pwr;
> > + u16 clk, ctrl;
> > + u32 present_state;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + /* Stop SDCLK */
> > + host = mmc_priv(mmc);
> > + 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_readw(host, SDHCI_HOST_CONTROL2);
> > + 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. Stop
> > + * power to 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);
> > +
> > + return -EAGAIN;
> > +}
> > +
> > 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,
> > };
> >
> >
> >
> /**********************************************************************
> > *******\
> > @@ -1799,7 +1860,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);
> > @@ -1822,12 +1885,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);
> >
> > + if (host->version >= SDHCI_SPEC_300)
> > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> > +
> > 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;
> > @@ -1838,7 +1904,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) &&
> > @@ -1892,10 +1959,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;
> > @@ -1911,7 +1978,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);
> > @@ -1923,7 +1990,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;
> >
> > /*
> > @@ -1950,21 +2017,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)
> > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -69,6 +69,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
> > @@ -147,7 +149,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
> > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> >
> > /* optional callback for HC quirks */
> > void (*init_card)(struct mmc_host *host, struct mmc_card *card);
> > +
> > + int (*start_signal_voltage_switch)(struct mmc_host *host);
> > };
> >
> > struct mmc_card;
> > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> > --- a/include/linux/mmc/sd.h
> > +++ b/include/linux/mmc/sd.h
> > @@ -17,6 +17,7 @@
> > /* This is basically the same command as for MMC with some quirks.
> */
> > #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6
> > */
> > #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7
> > */
> > +#define SD_SWITCH_VOLTAGE 11 /* ac R1
> > */
> >
> > /* class 10 */
> > #define SD_SWITCH 6 /* adtc [31:0] See below R1
> > */
> > --
> > 1.7.1
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 04/12] mmc: sd: add support for driver type selection
2011-03-04 11:32 ` [PATCH v2 04/12] mmc: sd: add support for driver type selection Arindam Nath
2011-03-09 5:33 ` Philip Rakity
@ 2011-03-10 6:57 ` subhashj
2011-03-10 8:31 ` Nath, Arindam
1 sibling, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 6:57 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 04/12] mmc: sd: add support for driver type
> selection
>
> 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. We then set the
> appropriate driver type for the card using CMD6 mode 1. As per
> Host Controller spec v3.00, we set driver type for the host only
> if Preset Value Enable in the Host Control2 register is not set.
> SDHCI_HOST_CONTROL has been renamed to SDHCI_HOST_CONTROL1 to
> conform to the spec.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/core.c | 9 +++
> drivers/mmc/core/core.h | 1 +
> drivers/mmc/core/sd.c | 140 +++++++++++++++++++++++++++++++++++++-
> --------
> drivers/mmc/core/sd.h | 3 +-
> drivers/mmc/core/sdio.c | 3 +-
> drivers/mmc/host/sdhci.c | 48 ++++++++++++----
> drivers/mmc/host/sdhci.h | 10 +++-
> include/linux/mmc/card.h | 4 +
> include/linux/mmc/host.h | 8 +++
> 9 files changed, 186 insertions(+), 40 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 6625c05..daa535a 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -947,6 +947,15 @@ void mmc_set_timing(struct mmc_host *host,
> unsigned int timing)
> }
>
> /*
> + * Select appropriate driver type for host.
> + */
> +void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
> +{
> + host->ios.drv_type = drv_type;
> + mmc_set_ios(host);
> +}
> +
> +/*
> * Apply power to the MMC stack. This is a two-stage process.
> * First, we enable power to the card without the clock running.
> * We then wait a bit for the power to stabilise. Finally,
> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> index ca1fdde..6114ca5 100644
> --- a/drivers/mmc/core/core.h
> +++ b/drivers/mmc/core/core.h
> @@ -42,6 +42,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host,
> unsigned int width,
> unsigned int ddr);
> u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> void mmc_set_timing(struct mmc_host *host, unsigned int timing);
> +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> drv_type);
>
> static inline void mmc_delay(unsigned int ms)
> {
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index a63956b..f6a4fab 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -426,6 +426,86 @@ out:
> return err;
> }
>
> +static int sd_select_driver_type(struct mmc_card *card, u8 *status)
> +{
> + int host_set_drv_type, card_set_drv_type;
Why you need to use *set* in name? Just host_drv_type & card_drv_type is not
enough?
> + 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_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
> + card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> + else if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
Why you are combining TYPE_C under CAP_*_TYPE_A?
> + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> + host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> + }
Again TYPE_C checking here? Also why are you skiping TYPE_D here?
> +
> + err = mmc_sd_switch(card, 1, 2, card_set_drv_type, status);
> + if (err)
> + return err;
> +
> + if ((status[15] & 0xF) != card_set_drv_type)
> + printk(KERN_WARNING "%s: Problem setting driver
> strength!\n",
> + mmc_hostname(card->host));
> + else
> + mmc_set_driver_type(card->host, host_set_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;
We can combine both these check under single "if" checking.
> +
> + err = -EIO;
Is this really required? I don't think initializing "err" is needed here.
> +
> + 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;
> + }
You may want to allocate memory only after bus width setting is successful.
> +
> + /* 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);
Actually why are you allocating memory for "status" response in
mmc_sd_init_uhs_card() function. You need "status" only in
sd_select_driver_type() function. So you should be allocating memory in
sd_select_driver_type() function only. here you just say "
sd_select_driver_type(card);"
> +
> +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],
> @@ -474,10 +554,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
> @@ -502,7 +582,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr,
> u32 *cid)
> MMC_CAP_SET_XPC_180))
> ocr |= 1 << 28;
>
> - err = mmc_send_app_op_cond(host, ocr, &rocr);
> + err = mmc_send_app_op_cond(host, ocr, rocr);
> if (err)
> return err;
>
> @@ -510,7 +590,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr,
> u32 *cid)
> * 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)) {
> + if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x41000000)) {
You are not doing NULL check for "rocr" in mmc_send_app_op_cond() but you
are doing it here. Is it needed? If "rocr" is NULL then
mmc_send_app_op_cond() itself will fail.
Also, you may need to check b31 (busy bit) of rocr because spec says this:
"Section:4.2.3.1 CCS (Bit 30) and S18A (Bit 24) are valid when Busy (Bit 31)
is set to 1."
> err = mmc_start_voltage_switch(host);
> if (err)
> return err;
> @@ -643,11 +723,12 @@ static int mmc_sd_init_card(struct mmc_host
> *host, u32 ocr,
> struct mmc_card *card;
> int err;
> u32 cid[4];
> + u32 rocr;
>
> 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;
>
> @@ -700,30 +781,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 & 0x01000000) {
Instead of 0x01000000, you can use (1 << 24) to be more readable.
> + 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 5c4a54d..6d16684 100644
> --- a/drivers/mmc/core/sdio.c
> +++ b/drivers/mmc/core/sdio.c
> @@ -364,7 +364,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 5487a0b..1645687 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -63,7 +63,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));
> @@ -216,18 +216,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
> @@ -786,14 +786,14 @@ static void sdhci_prepare_data(struct sdhci_host
> *host, struct mmc_data *data)
> * 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)) {
> @@ -1252,7 +1252,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)
> @@ -1265,10 +1265,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)
> @@ -1277,7 +1277,25 @@ 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.
> + */
> + 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,
> @@ -2037,6 +2055,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 95d70e6..a407b5b 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -72,7 +72,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
> @@ -151,6 +151,11 @@
>
> #define SDHCI_HOST_CONTROL2 0x3E
> #define SDHCI_CTRL_VDD_180 0x0008
> +#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
> @@ -174,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 7080f22..2d7f7a3 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -77,6 +77,10 @@ struct sd_switch_caps {
> unsigned int hs_max_dtr;
> unsigned int uhs_bus_mode;
> unsigned int uhs_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 uhs_curr_limit;
> };
>
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index ad7daa3..bc2121e 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 drv_type; /* driver type (A, C, D) */
> +
> +#define MMC_SET_DRIVER_TYPE_A 1
> +#define MMC_SET_DRIVER_TYPE_C 2
*SET* is not required in name. again why are we ignoring TYPE D here?
> };
>
> struct mmc_host_ops {
> @@ -183,6 +188,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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-04 11:32 ` [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
@ 2011-03-10 8:00 ` subhashj
2011-03-10 8:36 ` Nath, Arindam
2011-03-21 6:42 ` Subhash Jadavani
1 sibling, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 8:00 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode
> selection
>
> 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. Since MMC_TIMING_UHS_SDR25 is same as
> MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag for
> UHS SDR25 again. We also 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 set the clock frequency for the
> UHS-I mode selected.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 91
> ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> drivers/mmc/host/sdhci.h | 5 +++
> include/linux/mmc/card.h | 16 ++++++++
> include/linux/mmc/host.h | 4 ++
> 5 files changed, 146 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index f6a4fab..ec0d8e6 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -464,6 +464,92 @@ static int sd_select_driver_type(struct mmc_card
> *card, u8 *status)
> return 0;
> }
>
> +static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
> +{
> + unsigned int bus_speed, timing;
> + int err;
> +
> + /*
> + * If the host doesn't support any of the UHS-I modes, fallback
> on
> + * default speed.
> + */
> + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
> + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
> + return 0;
> +
> + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> + if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) {
> + bus_speed = UHS_SDR104_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR104;
> + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)
> {
> + bus_speed = UHS_SDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR50;
> + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> {
> + bus_speed = UHS_DDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_DDR50;
> + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> {
> + bus_speed = UHS_SDR25_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR25;
> + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> + }
> + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
You can combine both modes with "or": if (card->sw_caps.uhs_bus_mode &
(SD_MODE_UHS_SDR104 | SD_MODE_UHS_SDR50)). This can be done for many of the
below checks.
> + bus_speed = UHS_SDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR50;
> + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> {
> + bus_speed = UHS_DDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_DDR50;
> + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> {
> + bus_speed = UHS_SDR25_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR25;
> + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> + }
> + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> + bus_speed = UHS_DDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_DDR50;
> + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> {
> + bus_speed = UHS_SDR25_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR25;
> + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> + }
> + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> + bus_speed = UHS_SDR25_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR25;
> + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> + }
> + }
> +
> + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
For SDR12 mode, what will be the bus_speed?
> + 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 {
For SDR12 mode, let's say you are setting the bus_speed =
UHS_SDR12_BUS_SPEED (which is 0). Then for SDR12 mode there won't be a clock
setting as you are checking if(buspeed). Is this what you want? How will you
set the clock to 25 Mhz for SDR12 mode?
> + if (bus_speed) {
> + mmc_set_timing(card->host, timing);
> + mmc_set_clock(card->host,
card->sw_caps.uhs_max_dtr);
> + }
> + }
> +
> + return 0;
> +}
> +
> /*
> * UHS-I specific initialization procedure
> */
> @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
>
> /* Set the driver strength for the card */
> err = sd_select_driver_type(card, status);
> + if (err)
> + goto out;
> +
> + /* Set bus speed mode of the card */
> + err = sd_set_bus_speed_mode(card, status);
>
> out:
> kfree(status);
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 5d3bb11..f127fa2 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct mmc_host *mmc,
> struct mmc_ios *ios)
> ctrl &= ~SDHCI_CTRL_HISPD;
>
> if (host->version >= SDHCI_SPEC_300) {
> - u16 ctrl_2;
> + u16 clk, ctrl_2;
> +
> + /* In case of UHS-I modes, set High Speed Enable */
> + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> + (ios->timing == MMC_TIMING_UHS_DDR50))
> + ctrl |= SDHCI_CTRL_HISPD;
>
> ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct mmc_host *mmc,
> struct mmc_ios *ios)
> * need to reset SD Clock Enable before changing
High
> * Speed Enable to avoid generating clock gliches.
> */
> - u16 clk;
>
> /* Reset SD Clock Enable */
> clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct mmc_host *mmc,
> struct mmc_ios *ios)
> clk |= SDHCI_CLOCK_CARD_EN;
> sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> }
> +
> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +
> + /* Select Bus Speed Mode for host */
> + if (ios->timing == MMC_TIMING_UHS_SDR25)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> + else if (ios->timing == MMC_TIMING_UHS_SDR104)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> +
> + /* Reset SD Clock Enable */
> + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> + clk &= ~SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> +
> + /* Re-enable SD Clock */
> + clk |= SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> } else
> sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
>
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index a407b5b..5bf244d 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -150,6 +150,11 @@
> #define SDHCI_ACMD12_ERR 0x3C
>
> #define SDHCI_HOST_CONTROL2 0x3E
> +#define SDHCI_CTRL_UHS_SDR12 0x0000
> +#define SDHCI_CTRL_UHS_SDR25 0x0001
> +#define SDHCI_CTRL_UHS_SDR50 0x0002
> +#define SDHCI_CTRL_UHS_SDR104 0x0003
> +#define SDHCI_CTRL_UHS_DDR50 0x0004
> #define SDHCI_CTRL_VDD_180 0x0008
> #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 2d7f7a3..0b24c41 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -75,7 +75,23 @@ struct sd_ssr {
>
> struct sd_switch_caps {
> unsigned int hs_max_dtr;
> + unsigned int uhs_max_dtr;
> +#define UHS_SDR104_MAX_DTR 208000000
> +#define UHS_SDR50_MAX_DTR 100000000
> +#define UHS_DDR50_MAX_DTR 50000000
> +#define UHS_SDR25_MAX_DTR 50000000
> unsigned int uhs_bus_mode;
> +#define UHS_SDR12_BUS_SPEED 0
> +#define UHS_SDR25_BUS_SPEED 1
> +#define UHS_SDR50_BUS_SPEED 2
> +#define UHS_SDR104_BUS_SPEED 3
> +#define UHS_DDR50_BUS_SPEED 4
> +
> +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> unsigned int uhs_drv_type;
> #define SD_DRIVER_TYPE_B 0x01
> #define SD_DRIVER_TYPE_A 0x02
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index bc2121e..4dfff6d 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -50,6 +50,10 @@ struct mmc_ios {
> #define MMC_TIMING_LEGACY 0
> #define MMC_TIMING_MMC_HS 1
> #define MMC_TIMING_SD_HS 2
> +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> +#define MMC_TIMING_UHS_SDR50 3
> +#define MMC_TIMING_UHS_SDR104 4
> +#define MMC_TIMING_UHS_DDR50 5
>
> unsigned char ddr; /* dual data rate used */
>
> --
> 1.7.1
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-10 6:30 ` subhashj
@ 2011-03-10 8:05 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 8:05 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 12:00 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of subhashj@codeaurora.org
> > Sent: Wednesday, March 09, 2011 4:15 PM
> > To: 'Arindam Nath'; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; henry.su@amd.com; aaron.lu@amd.com;
> > anath.amd@gmail.com
> > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> > switch procedure
> >
> > +1. Looks good to me from sd3.0 spec. prospective.
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> > > switch procedure
> > >
> > > 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 support 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.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/sd.c | 37 ++++++++++-
> > > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> > > drivers/mmc/core/sd_ops.h | 1 +
> > > drivers/mmc/host/sdhci.c | 148
> > > +++++++++++++++++++++++++++++++++++++++++----
> > > drivers/mmc/host/sdhci.h | 18 +++++-
> > > include/linux/mmc/host.h | 10 +++
> > > include/linux/mmc/sd.h | 1 +
> > > 7 files changed, 229 insertions(+), 18 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index b3f8a3c..3e82599 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -408,6 +408,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
> > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> > > ocr, u32 *cid)
> > > if (!err)
> > > ocr |= 1 << 30;
> > >
> > > - err = mmc_send_app_op_cond(host, ocr, NULL);
> > > + /* If the host can supply more than 150mA, XPC should be set to
> > > 1. */
> > > + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > > + MMC_CAP_SET_XPC_180))
> > > + ocr |= 1 << 28;
> > > +
> > > + 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)) {
> > > + err = mmc_start_voltage_switch(host);
> > > + if (err)
> > > + return err;
> > > + }
> > > +
> > > if (mmc_host_is_spi(host))
> > > err = mmc_send_cid(host, cid);
> > > else
> > > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> > > }
> > >
> > > /*
> > > + * 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))
> > > + host->ocr |= (1 << 24);
> > > +
> > > + /*
> > > * Detect and init the card.
> > > */
> > > err = mmc_sd_init_card(host, host->ocr, NULL);
> > > - if (err)
> > > - goto err;
> > > + if (err == -EAGAIN) {
> > > + /*
> > > + * Retry initialization with S18R set to 0.
> > > + */
> > > + host->ocr &= ~(1 << 24);
> > > + err = mmc_sd_init_card(host, host->ocr, NULL);
> > > + if (err)
> > > + goto err;
> > > + }
>
> Just a thought. Shouldn't we do setting of this b24 bit of OCR in
> mmc_sd_get_cid() function itself and if mmc_send_app_op_cond() returns
> error
> then reset b24 bit and retry mmc_send_app_op_cond() command.
This makes sense as per the spec. I will change it.
Thanks,
Arindam
>
>
> > >
> > > mmc_release_host(host);
> > > err = mmc_add_card(host->card);
> > > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > > index 797cdb5..8a23e2e 100644
> > > --- a/drivers/mmc/core/sd_ops.c
> > > +++ b/drivers/mmc/core/sd_ops.c
> > > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card
> *card,
> > > int width)
> > > return 0;
> > > }
> > >
> > > +int mmc_start_voltage_switch(struct mmc_host *host)
> > > +{
> > > + struct mmc_command cmd;
> > > + int err;
> > > +
> > > + BUG_ON(!host);
> > > +
> > > + /*
> > > + * If the host does not provide signal voltage switch procedure,
> > > we
> > > + * set S18R to 0, and then retry initialization sequence.
> > > + */
> > > + if (!host->ops->start_signal_voltage_switch)
> > > + return -EAGAIN;
> > > +
> > > + 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;
> > > +
> > > + err = host->ops->start_signal_voltage_switch(host);
> > > +
> > > + return err;
> > > +}
> > > +
> > > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32
> *rocr)
> > > {
> > > struct mmc_command cmd;
> > > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > > index ffc2305..3cfba59 100644
> > > --- a/drivers/mmc/core/sd_ops.h
> > > +++ b/drivers/mmc/core/sd_ops.h
> > > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> > > *scr);
> > > int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> > > u8 value, u8 *resp);
> > > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > > +int mmc_start_voltage_switch(struct mmc_host *host);
> > >
> > > #endif
> > >
> > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > index 8914a25..5487a0b 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -85,6 +85,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",
> > > @@ -1337,11 +1339,70 @@ out:
> > > spin_unlock_irqrestore(&host->lock, flags);
> > > }
> > >
> > > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > > +{
> > > + struct sdhci_host *host;
> > > + u8 pwr;
> > > + u16 clk, ctrl;
> > > + u32 present_state;
> > > +
> > > + host = mmc_priv(mmc);
> > > +
> > > + /* Stop SDCLK */
> > > + host = mmc_priv(mmc);
> > > + 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_readw(host, SDHCI_HOST_CONTROL2);
> > > + 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. Stop
> > > + * power to 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);
> > > +
> > > + return -EAGAIN;
> > > +}
> > > +
> > > 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,
> > > };
> > >
> > >
> > >
> >
> /**********************************************************************
> > > *******\
> > > @@ -1799,7 +1860,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);
> > > @@ -1822,12 +1885,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);
> > >
> > > + if (host->version >= SDHCI_SPEC_300)
> > > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> > > +
> > > 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;
> > > @@ -1838,7 +1904,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) &&
> > > @@ -1892,10 +1959,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;
> > > @@ -1911,7 +1978,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);
> > > @@ -1923,7 +1990,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;
> > >
> > > /*
> > > @@ -1950,21 +2017,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)
> > > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> > > --- a/drivers/mmc/host/sdhci.h
> > > +++ b/drivers/mmc/host/sdhci.h
> > > @@ -69,6 +69,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
> > > @@ -147,7 +149,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
> > > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> > > --- a/include/linux/mmc/host.h
> > > +++ b/include/linux/mmc/host.h
> > > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> > >
> > > /* optional callback for HC quirks */
> > > void (*init_card)(struct mmc_host *host, struct mmc_card *card);
> > > +
> > > + int (*start_signal_voltage_switch)(struct mmc_host *host);
> > > };
> > >
> > > struct mmc_card;
> > > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> > > --- a/include/linux/mmc/sd.h
> > > +++ b/include/linux/mmc/sd.h
> > > @@ -17,6 +17,7 @@
> > > /* This is basically the same command as for MMC with some quirks.
> > */
> > > #define SD_SEND_RELATIVE_ADDR 3 /* bcr
> R6
> > > */
> > > #define SD_SEND_IF_COND 8 /* bcr [11:0] See below
> R7
> > > */
> > > +#define SD_SWITCH_VOLTAGE 11 /* ac
> R1
> > > */
> > >
> > > /* class 10 */
> > > #define SD_SWITCH 6 /* adtc [31:0] See below
> R1
> > > */
> > > --
> > > 1.7.1
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-09 12:45 ` zhangfei gao
@ 2011-03-10 8:11 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 8:11 UTC (permalink / raw)
To: zhangfei gao
Cc: cjb@laptop.org, prakity@marvell.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Zhangfei,
> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Wednesday, March 09, 2011 6:15 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; prakity@marvell.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
> On Fri, Mar 4, 2011 at 6:32 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 support 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.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>
> The patch can not work with generic card :(
>
> > ---
> > drivers/mmc/core/sd.c | 37 ++++++++++-
> > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> > drivers/mmc/core/sd_ops.h | 1 +
> > drivers/mmc/host/sdhci.c | 148
> +++++++++++++++++++++++++++++++++++++++++----
> > drivers/mmc/host/sdhci.h | 18 +++++-
> > include/linux/mmc/host.h | 10 +++
> > include/linux/mmc/sd.h | 1 +
> > 7 files changed, 229 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index b3f8a3c..3e82599 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -408,6 +408,7 @@ struct device_type sd_type = {
> > int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> > {
> > int err;
> > + u32 rocr;
> rocr not initialized here, or move to mmc_sd_init_card in following
> patch, it may be risky, since dependency.
I will move rocr to mmc_sd_get_cid().
>
> >
> > /*
> > * Since we're changing the OCR value, we seem to
> > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> ocr, u32 *cid)
> > if (!err)
> > ocr |= 1 << 30;
> >
> > - err = mmc_send_app_op_cond(host, ocr, NULL);
> > + /* If the host can supply more than 150mA, XPC should be set
> to 1. */
> > + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > + MMC_CAP_SET_XPC_180))
> > + ocr |= 1 << 28;
> > +
> > + 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)) {
>
> if (!mmc_host_is_spi(host) && (rocr & 0x41000000 == 0x41000000))
> Currently your patch can not work with general card, for example
> rocr=0xc0ff8000 & 0x41000000 = 0x40000000, while what you want is both
> two bit are set.
Good catch. I will fix this. Thanks.
>
> > + err = mmc_start_voltage_switch(host);
> > + if (err)
> > + return err;
> > + }
> > +
> > if (mmc_host_is_spi(host))
> > err = mmc_send_cid(host, cid);
> > else
> > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> > }
> >
> > /*
> > + * 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))
> > + host->ocr |= (1 << 24);
> > +
> > + /*
> > * Detect and init the card.
> > */
> > err = mmc_sd_init_card(host, host->ocr, NULL);
> > - if (err)
> > - goto err;
> > + if (err == -EAGAIN) {
> If other error happen, for example -ETIMEDOUT in current patch for
> general card, the following card can not be executed.
> kernel panic will happen in mmc_add_card for null pinter, since card
> is not allocated.
> how about if (err) for safety?
Yes, I will make the check more generic, or probably we won't need to once I move rocr to mmc_sd_get_cid().
Thanks,
Arindam
>
> > + /*
> > + * Retry initialization with S18R set to 0.
> > + */
> > + host->ocr &= ~(1 << 24);
> > + err = mmc_sd_init_card(host, host->ocr, NULL);
> > + if (err)
> > + goto err;
> > + }
> >
> > mmc_release_host(host);
> > err = mmc_add_card(host->card);
> > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > index 797cdb5..8a23e2e 100644
> > --- a/drivers/mmc/core/sd_ops.c
> > +++ b/drivers/mmc/core/sd_ops.c
> > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card,
> int width)
> > return 0;
> > }
> >
> > +int mmc_start_voltage_switch(struct mmc_host *host)
> > +{
> > + struct mmc_command cmd;
> > + int err;
> > +
> > + BUG_ON(!host);
> > +
> > + /*
> > + * If the host does not provide signal voltage switch
> procedure, we
> > + * set S18R to 0, and then retry initialization sequence.
> > + */
> > + if (!host->ops->start_signal_voltage_switch)
> > + return -EAGAIN;
> > +
> > + 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;
> > +
> > + err = host->ops->start_signal_voltage_switch(host);
> > +
> > + return err;
> > +}
> > +
> > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> > {
> > struct mmc_command cmd;
> > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > index ffc2305..3cfba59 100644
> > --- a/drivers/mmc/core/sd_ops.h
> > +++ b/drivers/mmc/core/sd_ops.h
> > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> *scr);
> > int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> > u8 value, u8 *resp);
> > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > +int mmc_start_voltage_switch(struct mmc_host *host);
> >
> > #endif
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 8914a25..5487a0b 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -85,6 +85,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",
> > @@ -1337,11 +1339,70 @@ out:
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> >
> > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u8 pwr;
> > + u16 clk, ctrl;
> > + u32 present_state;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + /* Stop SDCLK */
> > + host = mmc_priv(mmc);
> > + 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_readw(host, SDHCI_HOST_CONTROL2);
> > + 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. Stop
> > + * power to 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);
> > +
> > + return -EAGAIN;
> > +}
> > +
> > 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,
> > };
> >
> >
> /*********************************************************************
> ********\
> > @@ -1799,7 +1860,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);
> > @@ -1822,12 +1885,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);
> >
> > + if (host->version >= SDHCI_SPEC_300)
> > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> > +
> > 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;
> > @@ -1838,7 +1904,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) &&
> > @@ -1892,10 +1959,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;
> > @@ -1911,7 +1978,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);
> > @@ -1923,7 +1990,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;
> >
> > /*
> > @@ -1950,21 +2017,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)
> > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -69,6 +69,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
> > @@ -147,7 +149,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
> > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> >
> > /* optional callback for HC quirks */
> > void (*init_card)(struct mmc_host *host, struct mmc_card
> *card);
> > +
> > + int (*start_signal_voltage_switch)(struct mmc_host
> *host);
> > };
> >
> > struct mmc_card;
> > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> > --- a/include/linux/mmc/sd.h
> > +++ b/include/linux/mmc/sd.h
> > @@ -17,6 +17,7 @@
> > /* This is basically the same command as for MMC with some quirks.
> */
> > #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6
> */
> > #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7
> */
> > +#define SD_SWITCH_VOLTAGE 11 /* ac R1
> */
> >
> > /* class 10 */
> > #define SD_SWITCH 6 /* adtc [31:0] See below R1
> */
> > --
> > 1.7.1
> >
> >
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-04 11:32 ` [PATCH v2 07/12] mmc: sd: set current limit for uhs cards Arindam Nath
2011-03-09 21:41 ` Philip Rakity
@ 2011-03-10 8:16 ` subhashj
2011-03-10 8:43 ` Nath, Arindam
2011-03-16 14:26 ` Philip Rakity
2011-03-21 7:43 ` Subhash Jadavani
3 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 8:16 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>
> 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.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 45
> +++++++++++++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> include/linux/mmc/card.h | 9 +++++++++
> include/linux/mmc/host.h | 1 +
> 4 files changed, 79 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index ec0d8e6..df98a2c 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -550,6 +550,46 @@ 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)
> +{
> + struct mmc_host *host = card->host;
> + int mmc_host_max_current_180, current_limit;
> + int err;
> +
> + /* sanity check */
> + if (!host->ops->get_max_current_180)
> + return 0;
> +
> + /* Maximum current supported by host at 1.8V */
> + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
I don't think this should be mmc_ops. Max. current limit supported by host
should be one of the MMC_CAP*.
In your case you can read the "SDHCI_MAX_CURRENT" register during the probe
and can set the appropriate MMC_CAP* from below:
#define MMC_CAP_MAX_CURRENT_200 (1 << xx)
#define MMC_CAP_MAX_CURRENT_400 (1 << (xx+1))
#define MMC_CAP_MAX_CURRENT_600 (1 << (xx+2))
#define MMC_CAP_MAX_CURRENT_800 (1 << (xx+3))
> +
> + if (mmc_host_max_current_180 >= 800) {
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
> + current_limit = SD_SET_CURRENT_LIMIT_800;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> + current_limit = SD_SET_CURRENT_LIMIT_600;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> + } else if (mmc_host_max_current_180 >= 600) {
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> + current_limit = SD_SET_CURRENT_LIMIT_600;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> + } else if (mmc_host_max_current_180 >= 400)
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> +
What will be value of current_limit if (mmc_host_max_current_180 < 400)
> + 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
> */
> @@ -590,6 +630,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 f127fa2..245cc39 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1462,12 +1462,36 @@ static int
> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> return -EAGAIN;
> }
>
> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u32 max_current_caps;
> + unsigned long flags;
> + int max_current_180;
> +
> + host = mmc_priv(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + /* Maximum current is 4 times the register value for 1.8V */
> + max_current_180 = ((max_current_caps &
> SDHCI_MAX_CURRENT_180_MASK) >>
> + SDHCI_MAX_CURRENT_180_SHIFT) *
> + SDHCI_MAX_CURRENT_MULTIPLIER;
> +
> + return max_current_180;
> +}
> +
> 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,
> + .get_max_current_180 = sdhci_get_max_current_180,
> };
>
>
> /**********************************************************************
> *******\
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 0b24c41..a6811ae 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -98,6 +98,15 @@ struct sd_switch_caps {
> #define SD_DRIVER_TYPE_C 0x04
> #define SD_DRIVER_TYPE_D 0x08
> unsigned int uhs_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 4dfff6d..e84cd05 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -128,6 +128,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);
> + int (*get_max_current_180)(struct mmc_host *mmc);
> };
>
> struct mmc_card;
> --
> 1.7.1
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 04/12] mmc: sd: add support for driver type selection
2011-03-10 6:57 ` subhashj
@ 2011-03-10 8:31 ` Nath, Arindam
2011-03-10 10:28 ` subhashj
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 8:31 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 12:27 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> selection
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 04/12] mmc: sd: add support for driver type
> > selection
> >
> > 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. We then set the
> > appropriate driver type for the card using CMD6 mode 1. As per
> > Host Controller spec v3.00, we set driver type for the host only
> > if Preset Value Enable in the Host Control2 register is not set.
> > SDHCI_HOST_CONTROL has been renamed to SDHCI_HOST_CONTROL1 to
> > conform to the spec.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/core.c | 9 +++
> > drivers/mmc/core/core.h | 1 +
> > drivers/mmc/core/sd.c | 140
> +++++++++++++++++++++++++++++++++++++-
> > --------
> > drivers/mmc/core/sd.h | 3 +-
> > drivers/mmc/core/sdio.c | 3 +-
> > drivers/mmc/host/sdhci.c | 48 ++++++++++++----
> > drivers/mmc/host/sdhci.h | 10 +++-
> > include/linux/mmc/card.h | 4 +
> > include/linux/mmc/host.h | 8 +++
> > 9 files changed, 186 insertions(+), 40 deletions(-)
> >
> > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > index 6625c05..daa535a 100644
> > --- a/drivers/mmc/core/core.c
> > +++ b/drivers/mmc/core/core.c
> > @@ -947,6 +947,15 @@ void mmc_set_timing(struct mmc_host *host,
> > unsigned int timing)
> > }
> >
> > /*
> > + * Select appropriate driver type for host.
> > + */
> > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> drv_type)
> > +{
> > + host->ios.drv_type = drv_type;
> > + mmc_set_ios(host);
> > +}
> > +
> > +/*
> > * Apply power to the MMC stack. This is a two-stage process.
> > * First, we enable power to the card without the clock running.
> > * We then wait a bit for the power to stabilise. Finally,
> > diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> > index ca1fdde..6114ca5 100644
> > --- a/drivers/mmc/core/core.h
> > +++ b/drivers/mmc/core/core.h
> > @@ -42,6 +42,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host,
> > unsigned int width,
> > unsigned int ddr);
> > u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> > void mmc_set_timing(struct mmc_host *host, unsigned int timing);
> > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> > drv_type);
> >
> > static inline void mmc_delay(unsigned int ms)
> > {
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index a63956b..f6a4fab 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -426,6 +426,86 @@ out:
> > return err;
> > }
> >
> > +static int sd_select_driver_type(struct mmc_card *card, u8 *status)
> > +{
> > + int host_set_drv_type, card_set_drv_type;
>
> Why you need to use *set* in name? Just host_drv_type & card_drv_type
> is not
> enough?
I wanted to emphasize on the fact that we are setting the values for host and card both. I don't have issues removing the *set* part.
>
> > + 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_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
> > + card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > + else if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
>
> Why you are combining TYPE_C under CAP_*_TYPE_A?
Both the host and card can support multiple driver types, so we need to decide the appropriate setting for them based on their capabilities.
>
> > + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> > + host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > + }
>
> Again TYPE_C checking here? Also why are you skiping TYPE_D here?
TYPE_C checking for the same reason as above. I could not find a scenario where I can put TYPE_D into, so there is not checking. Probably you can suggest something?
>
> > +
> > + err = mmc_sd_switch(card, 1, 2, card_set_drv_type, status);
> > + if (err)
> > + return err;
> > +
> > + if ((status[15] & 0xF) != card_set_drv_type)
> > + printk(KERN_WARNING "%s: Problem setting driver
> > strength!\n",
> > + mmc_hostname(card->host));
> > + else
> > + mmc_set_driver_type(card->host, host_set_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;
>
> We can combine both these check under single "if" checking.
The code snippet is a replica of the code already present in mmc_sd_switch_hs() and mmc_read_switch(), so I too followed the same checking. I can combine them into a single condition if you want.
>
> > +
> > + err = -EIO;
>
> Is this really required? I don't think initializing "err" is needed
> here.
Again a replica of the functions mentioned above. Let me know if you want this removed.
>
> > +
> > + 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;
> > + }
>
> You may want to allocate memory only after bus width setting is
> successful.
Yes, good suggestion. I will incorporate this change.
>
> > +
> > + /* 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);
>
> Actually why are you allocating memory for "status" response in
> mmc_sd_init_uhs_card() function. You need "status" only in
> sd_select_driver_type() function. So you should be allocating memory in
> sd_select_driver_type() function only. here you just say "
> sd_select_driver_type(card);"
Please check the later patches. "status" is used to store return statuses from *bus_speed_mode() and *current_limit() as well. So I think it makes sense to allocate memory for "status" at the beginning of the function, and use it later.
>
> > +
> > +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],
> > @@ -474,10 +554,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
> > @@ -502,7 +582,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> ocr,
> > u32 *cid)
> > MMC_CAP_SET_XPC_180))
> > ocr |= 1 << 28;
> >
> > - err = mmc_send_app_op_cond(host, ocr, &rocr);
> > + err = mmc_send_app_op_cond(host, ocr, rocr);
> > if (err)
> > return err;
> >
> > @@ -510,7 +590,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> ocr,
> > u32 *cid)
> > * 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)) {
> > + if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x41000000)) {
>
> You are not doing NULL check for "rocr" in mmc_send_app_op_cond() but
> you
> are doing it here. Is it needed? If "rocr" is NULL then
> mmc_send_app_op_cond() itself will fail.
I am actually checking rocr for NULL before returning from mmc_send_app_op_cond(). Can you please check the function again to make sure?
> Also, you may need to check b31 (busy bit) of rocr because spec says
> this:
> "Section:4.2.3.1 CCS (Bit 30) and S18A (Bit 24) are valid when Busy
> (Bit 31)
> is set to 1."
The busy bit (bit 31) checking is already performed inside mmc_send_app_op_cond(), cmd.resp[0] & MMC_CARD_BUSY.
>
> > err = mmc_start_voltage_switch(host);
> > if (err)
> > return err;
> > @@ -643,11 +723,12 @@ static int mmc_sd_init_card(struct mmc_host
> > *host, u32 ocr,
> > struct mmc_card *card;
> > int err;
> > u32 cid[4];
> > + u32 rocr;
> >
> > 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;
> >
> > @@ -700,30 +781,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 & 0x01000000) {
>
> Instead of 0x01000000, you can use (1 << 24) to be more readable.
I am okay with your suggestion. Will change it.
Thanks,
Arindam
>
> > + 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 5c4a54d..6d16684 100644
> > --- a/drivers/mmc/core/sdio.c
> > +++ b/drivers/mmc/core/sdio.c
> > @@ -364,7 +364,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 5487a0b..1645687 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -63,7 +63,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));
> > @@ -216,18 +216,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
> > @@ -786,14 +786,14 @@ static void sdhci_prepare_data(struct
> sdhci_host
> > *host, struct mmc_data *data)
> > * 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)) {
> > @@ -1252,7 +1252,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)
> > @@ -1265,10 +1265,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)
> > @@ -1277,7 +1277,25 @@ 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.
> > + */
> > + 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,
> > @@ -2037,6 +2055,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 95d70e6..a407b5b 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -72,7 +72,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
> > @@ -151,6 +151,11 @@
> >
> > #define SDHCI_HOST_CONTROL2 0x3E
> > #define SDHCI_CTRL_VDD_180 0x0008
> > +#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
> > @@ -174,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 7080f22..2d7f7a3 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -77,6 +77,10 @@ struct sd_switch_caps {
> > unsigned int hs_max_dtr;
> > unsigned int uhs_bus_mode;
> > unsigned int uhs_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 uhs_curr_limit;
> > };
> >
> > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > index ad7daa3..bc2121e 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 drv_type; /* driver type (A, C, D) */
> > +
> > +#define MMC_SET_DRIVER_TYPE_A 1
> > +#define MMC_SET_DRIVER_TYPE_C 2
>
> *SET* is not required in name. again why are we ignoring TYPE D here?
>
> > };
> >
> > struct mmc_host_ops {
> > @@ -183,6 +188,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
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-10 8:00 ` subhashj
@ 2011-03-10 8:36 ` Nath, Arindam
2011-03-10 10:07 ` subhashj
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 8:36 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 1:30 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
>
>
> > -----Original Message-----
> > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> > Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode
> > selection
> >
> > 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. Since MMC_TIMING_UHS_SDR25 is same as
> > MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag for
> > UHS SDR25 again. We also 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 set the clock frequency for the
> > UHS-I mode selected.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 91
> > ++++++++++++++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > drivers/mmc/host/sdhci.h | 5 +++
> > include/linux/mmc/card.h | 16 ++++++++
> > include/linux/mmc/host.h | 4 ++
> > 5 files changed, 146 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index f6a4fab..ec0d8e6 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -464,6 +464,92 @@ static int sd_select_driver_type(struct mmc_card
> > *card, u8 *status)
> > return 0;
> > }
> >
> > +static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
> > +{
> > + unsigned int bus_speed, timing;
> > + int err;
> > +
> > + /*
> > + * If the host doesn't support any of the UHS-I modes, fallback
> > on
> > + * default speed.
> > + */
> > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
> > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
> > + return 0;
> > +
> > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > + if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) {
> > + bus_speed = UHS_SDR104_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR104;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)
> > {
> > + bus_speed = UHS_SDR50_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR50;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> > {
> > + bus_speed = UHS_DDR50_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_DDR50;
> > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > {
> > + bus_speed = UHS_SDR25_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR25;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > + }
> > + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
>
> You can combine both modes with "or": if (card->sw_caps.uhs_bus_mode &
> (SD_MODE_UHS_SDR104 | SD_MODE_UHS_SDR50)). This can be done for many of
> the
> below checks.
Thanks. Yes, it can be combined into a single check. Will do so.
>
> > + bus_speed = UHS_SDR50_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR50;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> > {
> > + bus_speed = UHS_DDR50_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_DDR50;
> > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > {
> > + bus_speed = UHS_SDR25_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR25;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > + }
> > + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > + bus_speed = UHS_DDR50_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_DDR50;
> > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > {
> > + bus_speed = UHS_SDR25_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR25;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > + }
> > + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > + bus_speed = UHS_SDR25_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR25;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > + }
> > + }
> > +
> > + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
>
> For SDR12 mode, what will be the bus_speed?
Since for SDR12, the bus speed and clock freq are same as the default speed, I don't think we need to set them explicitly in case any of the above conditions fail. Please correct me if wrong.
>
>
> > + 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 {
>
> For SDR12 mode, let's say you are setting the bus_speed =
> UHS_SDR12_BUS_SPEED (which is 0). Then for SDR12 mode there won't be a
> clock
> setting as you are checking if(buspeed). Is this what you want? How
> will you
> set the clock to 25 Mhz for SDR12 mode?
Please see the above comment.
Thanks,
Arindam
>
> > + if (bus_speed) {
> > + mmc_set_timing(card->host, timing);
> > + mmc_set_clock(card->host,
> card->sw_caps.uhs_max_dtr);
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > /*
> > * UHS-I specific initialization procedure
> > */
> > @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> > *card)
> >
> > /* Set the driver strength for the card */
> > err = sd_select_driver_type(card, status);
> > + if (err)
> > + goto out;
> > +
> > + /* Set bus speed mode of the card */
> > + err = sd_set_bus_speed_mode(card, status);
> >
> > out:
> > kfree(status);
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 5d3bb11..f127fa2 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct mmc_host
> *mmc,
> > struct mmc_ios *ios)
> > ctrl &= ~SDHCI_CTRL_HISPD;
> >
> > if (host->version >= SDHCI_SPEC_300) {
> > - u16 ctrl_2;
> > + u16 clk, ctrl_2;
> > +
> > + /* In case of UHS-I modes, set High Speed Enable */
> > + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> > + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> > + (ios->timing == MMC_TIMING_UHS_DDR50))
> > + ctrl |= SDHCI_CTRL_HISPD;
> >
> > ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct mmc_host *mmc,
> > struct mmc_ios *ios)
> > * need to reset SD Clock Enable before changing
> High
> > * Speed Enable to avoid generating clock gliches.
> > */
> > - u16 clk;
> >
> > /* Reset SD Clock Enable */
> > clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct mmc_host
> *mmc,
> > struct mmc_ios *ios)
> > clk |= SDHCI_CLOCK_CARD_EN;
> > sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > }
> > +
> > + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > +
> > + /* Select Bus Speed Mode for host */
> > + if (ios->timing == MMC_TIMING_UHS_SDR25)
> > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > + else if (ios->timing == MMC_TIMING_UHS_SDR104)
> > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> > + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > +
> > + /* Reset SD Clock Enable */
> > + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > +
> > + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> > +
> > + /* Re-enable SD Clock */
> > + clk |= SDHCI_CLOCK_CARD_EN;
> > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > } else
> > sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
> >
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index a407b5b..5bf244d 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -150,6 +150,11 @@
> > #define SDHCI_ACMD12_ERR 0x3C
> >
> > #define SDHCI_HOST_CONTROL2 0x3E
> > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > #define SDHCI_CTRL_VDD_180 0x0008
> > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > index 2d7f7a3..0b24c41 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -75,7 +75,23 @@ struct sd_ssr {
> >
> > struct sd_switch_caps {
> > unsigned int hs_max_dtr;
> > + unsigned int uhs_max_dtr;
> > +#define UHS_SDR104_MAX_DTR 208000000
> > +#define UHS_SDR50_MAX_DTR 100000000
> > +#define UHS_DDR50_MAX_DTR 50000000
> > +#define UHS_SDR25_MAX_DTR 50000000
> > unsigned int uhs_bus_mode;
> > +#define UHS_SDR12_BUS_SPEED 0
> > +#define UHS_SDR25_BUS_SPEED 1
> > +#define UHS_SDR50_BUS_SPEED 2
> > +#define UHS_SDR104_BUS_SPEED 3
> > +#define UHS_DDR50_BUS_SPEED 4
> > +
> > +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> > +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> > +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> > +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> > +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> > unsigned int uhs_drv_type;
> > #define SD_DRIVER_TYPE_B 0x01
> > #define SD_DRIVER_TYPE_A 0x02
> > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > index bc2121e..4dfff6d 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -50,6 +50,10 @@ struct mmc_ios {
> > #define MMC_TIMING_LEGACY 0
> > #define MMC_TIMING_MMC_HS 1
> > #define MMC_TIMING_SD_HS 2
> > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > +#define MMC_TIMING_UHS_SDR50 3
> > +#define MMC_TIMING_UHS_SDR104 4
> > +#define MMC_TIMING_UHS_DDR50 5
> >
> > unsigned char ddr; /* dual data rate used */
> >
> > --
> > 1.7.1
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-10 8:16 ` subhashj
@ 2011-03-10 8:43 ` Nath, Arindam
2011-03-10 9:45 ` subhashj
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 8:43 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 1:46 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>
>
>
> > -----Original Message-----
> > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> > Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
> >
> > 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.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 45
> > +++++++++++++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> > include/linux/mmc/card.h | 9 +++++++++
> > include/linux/mmc/host.h | 1 +
> > 4 files changed, 79 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index ec0d8e6..df98a2c 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -550,6 +550,46 @@ 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)
> > +{
> > + struct mmc_host *host = card->host;
> > + int mmc_host_max_current_180, current_limit;
> > + int err;
> > +
> > + /* sanity check */
> > + if (!host->ops->get_max_current_180)
> > + return 0;
> > +
> > + /* Maximum current supported by host at 1.8V */
> > + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
>
> I don't think this should be mmc_ops. Max. current limit supported by
> host
> should be one of the MMC_CAP*.
> In your case you can read the "SDHCI_MAX_CURRENT" register during the
> probe
> and can set the appropriate MMC_CAP* from below:
> #define MMC_CAP_MAX_CURRENT_200 (1 << xx)
> #define MMC_CAP_MAX_CURRENT_400 (1 << (xx+1))
> #define MMC_CAP_MAX_CURRENT_600 (1 << (xx+2))
> #define MMC_CAP_MAX_CURRENT_800 (1 << (xx+3))
Good suggestion. Thanks. I will make the changes.
>
> > +
> > + if (mmc_host_max_current_180 >= 800) {
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
> > + current_limit = SD_SET_CURRENT_LIMIT_800;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> > + current_limit = SD_SET_CURRENT_LIMIT_600;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > + } else if (mmc_host_max_current_180 >= 600) {
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> > + current_limit = SD_SET_CURRENT_LIMIT_600;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > + } else if (mmc_host_max_current_180 >= 400)
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > +
>
> What will be value of current_limit if (mmc_host_max_current_180 < 400)
I referred to table 4-11 of Physical Layer specv3.01. < 400mA should fall under the default current limit.
Thanks,
Arindam
>
> > + 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
> > */
> > @@ -590,6 +630,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 f127fa2..245cc39 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -1462,12 +1462,36 @@ static int
> > sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > return -EAGAIN;
> > }
> >
> > +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u32 max_current_caps;
> > + unsigned long flags;
> > + int max_current_180;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + /* Maximum current is 4 times the register value for 1.8V */
> > + max_current_180 = ((max_current_caps &
> > SDHCI_MAX_CURRENT_180_MASK) >>
> > + SDHCI_MAX_CURRENT_180_SHIFT) *
> > + SDHCI_MAX_CURRENT_MULTIPLIER;
> > +
> > + return max_current_180;
> > +}
> > +
> > 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,
> > + .get_max_current_180 = sdhci_get_max_current_180,
> > };
> >
> >
> >
> /**********************************************************************
> > *******\
> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > index 0b24c41..a6811ae 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -98,6 +98,15 @@ struct sd_switch_caps {
> > #define SD_DRIVER_TYPE_C 0x04
> > #define SD_DRIVER_TYPE_D 0x08
> > unsigned int uhs_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 4dfff6d..e84cd05 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -128,6 +128,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);
> > + int (*get_max_current_180)(struct mmc_host *mmc);
> > };
> >
> > struct mmc_card;
> > --
> > 1.7.1
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-10 8:43 ` Nath, Arindam
@ 2011-03-10 9:45 ` subhashj
0 siblings, 0 replies; 125+ messages in thread
From: subhashj @ 2011-03-10 9:45 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Thursday, March 10, 2011 2:13 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Thursday, March 10, 2011 1:46 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs
> cards
> >
> >
> >
> > > -----Original Message-----
> > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> Arindam
> > > Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
> > >
> > > 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.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/sd.c | 45
> > > +++++++++++++++++++++++++++++++++++++++++++++
> > > drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> > > include/linux/mmc/card.h | 9 +++++++++
> > > include/linux/mmc/host.h | 1 +
> > > 4 files changed, 79 insertions(+), 0 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index ec0d8e6..df98a2c 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -550,6 +550,46 @@ 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)
> > > +{
> > > + struct mmc_host *host = card->host;
> > > + int mmc_host_max_current_180, current_limit;
> > > + int err;
> > > +
> > > + /* sanity check */
> > > + if (!host->ops->get_max_current_180)
> > > + return 0;
> > > +
> > > + /* Maximum current supported by host at 1.8V */
> > > + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
> >
> > I don't think this should be mmc_ops. Max. current limit supported by
> > host
> > should be one of the MMC_CAP*.
> > In your case you can read the "SDHCI_MAX_CURRENT" register during the
> > probe
> > and can set the appropriate MMC_CAP* from below:
> > #define MMC_CAP_MAX_CURRENT_200 (1 << xx)
> > #define MMC_CAP_MAX_CURRENT_400 (1 << (xx+1))
> > #define MMC_CAP_MAX_CURRENT_600 (1 << (xx+2))
> > #define MMC_CAP_MAX_CURRENT_800 (1 << (xx+3))
>
> Good suggestion. Thanks. I will make the changes.
>
> >
> > > +
> > > + if (mmc_host_max_current_180 >= 800) {
> > > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
> > > + current_limit = SD_SET_CURRENT_LIMIT_800;
> > > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> > > + current_limit = SD_SET_CURRENT_LIMIT_600;
> > > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > > + } else if (mmc_host_max_current_180 >= 600) {
> > > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> > > + current_limit = SD_SET_CURRENT_LIMIT_600;
> > > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > > + } else if (mmc_host_max_current_180 >= 400)
> > > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > > +
> >
> > What will be value of current_limit if (mmc_host_max_current_180 <
> 400)
>
> I referred to table 4-11 of Physical Layer specv3.01. < 400mA should
> fall under the default current limit.
Ok. this is fine.
>
> Thanks,
> Arindam
>
> >
> > > + 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
> > > */
> > > @@ -590,6 +630,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 f127fa2..245cc39 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -1462,12 +1462,36 @@ static int
> > > sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > > return -EAGAIN;
> > > }
> > >
> > > +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> > > +{
> > > + struct sdhci_host *host;
> > > + u32 max_current_caps;
> > > + unsigned long flags;
> > > + int max_current_180;
> > > +
> > > + host = mmc_priv(mmc);
> > > +
> > > + spin_lock_irqsave(&host->lock, flags);
> > > +
> > > + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> > > +
> > > + spin_unlock_irqrestore(&host->lock, flags);
> > > +
> > > + /* Maximum current is 4 times the register value for 1.8V */
> > > + max_current_180 = ((max_current_caps &
> > > SDHCI_MAX_CURRENT_180_MASK) >>
> > > + SDHCI_MAX_CURRENT_180_SHIFT) *
> > > + SDHCI_MAX_CURRENT_MULTIPLIER;
> > > +
> > > + return max_current_180;
> > > +}
> > > +
> > > 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,
> > > + .get_max_current_180 = sdhci_get_max_current_180,
> > > };
> > >
> > >
> > >
> >
> /**********************************************************************
> > > *******\
> > > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > > index 0b24c41..a6811ae 100644
> > > --- a/include/linux/mmc/card.h
> > > +++ b/include/linux/mmc/card.h
> > > @@ -98,6 +98,15 @@ struct sd_switch_caps {
> > > #define SD_DRIVER_TYPE_C 0x04
> > > #define SD_DRIVER_TYPE_D 0x08
> > > unsigned int uhs_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 4dfff6d..e84cd05 100644
> > > --- a/include/linux/mmc/host.h
> > > +++ b/include/linux/mmc/host.h
> > > @@ -128,6 +128,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);
> > > + int (*get_max_current_180)(struct mmc_host *mmc);
> > > };
> > >
> > > struct mmc_card;
> > > --
> > > 1.7.1
> >
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-10 8:36 ` Nath, Arindam
@ 2011-03-10 10:07 ` subhashj
2011-03-10 10:15 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 10:07 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Thursday, March 10, 2011 2:06 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Thursday, March 10, 2011 1:30 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode selection
> >
> >
> >
> > > -----Original Message-----
> > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> Arindam
> > > Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode
> > > selection
> > >
> > > 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. Since MMC_TIMING_UHS_SDR25 is same as
> > > MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag
> for
> > > UHS SDR25 again. We also 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 set the clock frequency for the
> > > UHS-I mode selected.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/sd.c | 91
> > > ++++++++++++++++++++++++++++++++++++++++++++++
> > > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > > drivers/mmc/host/sdhci.h | 5 +++
> > > include/linux/mmc/card.h | 16 ++++++++
> > > include/linux/mmc/host.h | 4 ++
> > > 5 files changed, 146 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index f6a4fab..ec0d8e6 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -464,6 +464,92 @@ static int sd_select_driver_type(struct
> mmc_card
> > > *card, u8 *status)
> > > return 0;
> > > }
> > >
> > > +static int sd_set_bus_speed_mode(struct mmc_card *card, u8
> *status)
> > > +{
> > > + unsigned int bus_speed, timing;
> > > + int err;
> > > +
> > > + /*
> > > + * If the host doesn't support any of the UHS-I modes, fallback
> > > on
> > > + * default speed.
> > > + */
> > > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
> > > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
> > > + return 0;
> > > +
> > > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > > + if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) {
> > > + bus_speed = UHS_SDR104_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR104;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)
> > > {
> > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR50;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> > > {
> > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_DDR50;
> > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > > {
> > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR25;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > + }
> > > + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> >
> > You can combine both modes with "or": if (card->sw_caps.uhs_bus_mode
> &
> > (SD_MODE_UHS_SDR104 | SD_MODE_UHS_SDR50)). This can be done for many
> of
> > the
> > below checks.
>
> Thanks. Yes, it can be combined into a single check. Will do so.
>
> >
> > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR50;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> > > {
> > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_DDR50;
> > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > > {
> > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR25;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > + }
> > > + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_DDR50;
> > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > > {
> > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR25;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > + }
> > > + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR25;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > + }
> > > + }
> > > +
> > > + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> >
> > For SDR12 mode, what will be the bus_speed?
>
> Since for SDR12, the bus speed and clock freq are same as the default
> speed, I don't think we need to set them explicitly in case any of the
> above conditions fail. Please correct me if wrong.
But if you see this function, your bus_speed is not initialized if CAPS is
only set to SDR12 and you are calling mmc_sd_switch(..., bus_speed, ...).
Also as SDR12 is the default access mode, you may not want to call
mmc_sd_switch() itself.
>
> >
> >
> > > + 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 {
> >
> > For SDR12 mode, let's say you are setting the bus_speed =
> > UHS_SDR12_BUS_SPEED (which is 0). Then for SDR12 mode there won't be
> a
> > clock
> > setting as you are checking if(buspeed). Is this what you want? How
> > will you
> > set the clock to 25 Mhz for SDR12 mode?
>
> Please see the above comment.
>
> Thanks,
> Arindam
>
> >
> > > + if (bus_speed) {
> > > + mmc_set_timing(card->host, timing);
> > > + mmc_set_clock(card->host,
> > card->sw_caps.uhs_max_dtr);
> > > + }
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > /*
> > > * UHS-I specific initialization procedure
> > > */
> > > @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct
> mmc_card
> > > *card)
> > >
> > > /* Set the driver strength for the card */
> > > err = sd_select_driver_type(card, status);
> > > + if (err)
> > > + goto out;
> > > +
> > > + /* Set bus speed mode of the card */
> > > + err = sd_set_bus_speed_mode(card, status);
> > >
> > > out:
> > > kfree(status);
> > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > index 5d3bb11..f127fa2 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct mmc_host
> > *mmc,
> > > struct mmc_ios *ios)
> > > ctrl &= ~SDHCI_CTRL_HISPD;
> > >
> > > if (host->version >= SDHCI_SPEC_300) {
> > > - u16 ctrl_2;
> > > + u16 clk, ctrl_2;
> > > +
> > > + /* In case of UHS-I modes, set High Speed Enable */
> > > + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> > > + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> > > + (ios->timing == MMC_TIMING_UHS_DDR50))
> > > + ctrl |= SDHCI_CTRL_HISPD;
> > >
> > > ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> > > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct mmc_host
> *mmc,
> > > struct mmc_ios *ios)
> > > * need to reset SD Clock Enable before changing
> > High
> > > * Speed Enable to avoid generating clock gliches.
> > > */
> > > - u16 clk;
> > >
> > > /* Reset SD Clock Enable */
> > > clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct mmc_host
> > *mmc,
> > > struct mmc_ios *ios)
> > > clk |= SDHCI_CLOCK_CARD_EN;
> > > sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > }
> > > +
> > > + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > +
> > > + /* Select Bus Speed Mode for host */
> > > + if (ios->timing == MMC_TIMING_UHS_SDR25)
> > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > > + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > > + else if (ios->timing == MMC_TIMING_UHS_SDR104)
> > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> > > + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> > > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > > +
> > > + /* Reset SD Clock Enable */
> > > + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > +
> > > + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> > > +
> > > + /* Re-enable SD Clock */
> > > + clk |= SDHCI_CLOCK_CARD_EN;
> > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > } else
> > > sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
> > >
> > > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > > index a407b5b..5bf244d 100644
> > > --- a/drivers/mmc/host/sdhci.h
> > > +++ b/drivers/mmc/host/sdhci.h
> > > @@ -150,6 +150,11 @@
> > > #define SDHCI_ACMD12_ERR 0x3C
> > >
> > > #define SDHCI_HOST_CONTROL2 0x3E
> > > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > > #define SDHCI_CTRL_VDD_180 0x0008
> > > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > > index 2d7f7a3..0b24c41 100644
> > > --- a/include/linux/mmc/card.h
> > > +++ b/include/linux/mmc/card.h
> > > @@ -75,7 +75,23 @@ struct sd_ssr {
> > >
> > > struct sd_switch_caps {
> > > unsigned int hs_max_dtr;
> > > + unsigned int uhs_max_dtr;
> > > +#define UHS_SDR104_MAX_DTR 208000000
> > > +#define UHS_SDR50_MAX_DTR 100000000
> > > +#define UHS_DDR50_MAX_DTR 50000000
> > > +#define UHS_SDR25_MAX_DTR 50000000
> > > unsigned int uhs_bus_mode;
> > > +#define UHS_SDR12_BUS_SPEED 0
> > > +#define UHS_SDR25_BUS_SPEED 1
> > > +#define UHS_SDR50_BUS_SPEED 2
> > > +#define UHS_SDR104_BUS_SPEED 3
> > > +#define UHS_DDR50_BUS_SPEED 4
> > > +
> > > +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> > > +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> > > +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> > > +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> > > +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> > > unsigned int uhs_drv_type;
> > > #define SD_DRIVER_TYPE_B 0x01
> > > #define SD_DRIVER_TYPE_A 0x02
> > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > > index bc2121e..4dfff6d 100644
> > > --- a/include/linux/mmc/host.h
> > > +++ b/include/linux/mmc/host.h
> > > @@ -50,6 +50,10 @@ struct mmc_ios {
> > > #define MMC_TIMING_LEGACY 0
> > > #define MMC_TIMING_MMC_HS 1
> > > #define MMC_TIMING_SD_HS 2
> > > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > > +#define MMC_TIMING_UHS_SDR50 3
> > > +#define MMC_TIMING_UHS_SDR104 4
> > > +#define MMC_TIMING_UHS_DDR50 5
> > >
> > > unsigned char ddr; /* dual data rate used */
> > >
> > > --
> > > 1.7.1
> >
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-10 10:07 ` subhashj
@ 2011-03-10 10:15 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 10:15 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 3:38 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
>
>
> > -----Original Message-----
> > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > Sent: Thursday, March 10, 2011 2:06 PM
> > To: subhashj@codeaurora.org; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode selection
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > Sent: Thursday, March 10, 2011 1:30 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> speed
> > > mode selection
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> > Arindam
> > > > Nath
> > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > To: cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > henry.su@amd.com;
> > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode
> > > > selection
> > > >
> > > > 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. Since MMC_TIMING_UHS_SDR25 is same as
> > > > MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag
> > for
> > > > UHS SDR25 again. We also 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 set the clock frequency for
> the
> > > > UHS-I mode selected.
> > > >
> > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > ---
> > > > drivers/mmc/core/sd.c | 91
> > > > ++++++++++++++++++++++++++++++++++++++++++++++
> > > > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > > > drivers/mmc/host/sdhci.h | 5 +++
> > > > include/linux/mmc/card.h | 16 ++++++++
> > > > include/linux/mmc/host.h | 4 ++
> > > > 5 files changed, 146 insertions(+), 2 deletions(-)
> > > >
> > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > index f6a4fab..ec0d8e6 100644
> > > > --- a/drivers/mmc/core/sd.c
> > > > +++ b/drivers/mmc/core/sd.c
> > > > @@ -464,6 +464,92 @@ static int sd_select_driver_type(struct
> > mmc_card
> > > > *card, u8 *status)
> > > > return 0;
> > > > }
> > > >
> > > > +static int sd_set_bus_speed_mode(struct mmc_card *card, u8
> > *status)
> > > > +{
> > > > + unsigned int bus_speed, timing;
> > > > + int err;
> > > > +
> > > > + /*
> > > > + * If the host doesn't support any of the UHS-I modes,
> fallback
> > > > on
> > > > + * default speed.
> > > > + */
> > > > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 |
> MMC_CAP_UHS_SDR25 |
> > > > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> MMC_CAP_UHS_DDR50)))
> > > > + return 0;
> > > > +
> > > > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > > > + if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)
> {
> > > > + bus_speed = UHS_SDR104_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR104;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR50)
> > > > {
> > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_DDR50)
> > > > {
> > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR25)
> > > > {
> > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > > + }
> > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> > > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50))
> {
> > >
> > > You can combine both modes with "or": if (card-
> >sw_caps.uhs_bus_mode
> > &
> > > (SD_MODE_UHS_SDR104 | SD_MODE_UHS_SDR50)). This can be done for
> many
> > of
> > > the
> > > below checks.
> >
> > Thanks. Yes, it can be combined into a single check. Will do so.
> >
> > >
> > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_DDR50)
> > > > {
> > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR25)
> > > > {
> > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > > + }
> > > > + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> > > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50))
> {
> > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR25)
> > > > {
> > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > > + }
> > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> > > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25))
> {
> > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > > + }
> > > > + }
> > > > +
> > > > + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> > >
> > > For SDR12 mode, what will be the bus_speed?
> >
> > Since for SDR12, the bus speed and clock freq are same as the default
> > speed, I don't think we need to set them explicitly in case any of
> the
> > above conditions fail. Please correct me if wrong.
>
> But if you see this function, your bus_speed is not initialized if CAPS
> is
> only set to SDR12 and you are calling mmc_sd_switch(..., bus_speed,
> ...).
Yes, I should have initialized bus_speed to 0 in the beginning of the function, this also should have lead to a warning during compilation (which unfortunately I did not).
> Also as SDR12 is the default access mode, you may not want to call
> mmc_sd_switch() itself.
Yes, this should be fine. I will make the change.
Thanks,
Arindam
>
> >
> > >
> > >
> > > > + 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 {
> > >
> > > For SDR12 mode, let's say you are setting the bus_speed =
> > > UHS_SDR12_BUS_SPEED (which is 0). Then for SDR12 mode there won't
> be
> > a
> > > clock
> > > setting as you are checking if(buspeed). Is this what you want? How
> > > will you
> > > set the clock to 25 Mhz for SDR12 mode?
> >
> > Please see the above comment.
> >
> > Thanks,
> > Arindam
> >
> > >
> > > > + if (bus_speed) {
> > > > + mmc_set_timing(card->host, timing);
> > > > + mmc_set_clock(card->host,
> > > card->sw_caps.uhs_max_dtr);
> > > > + }
> > > > + }
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > /*
> > > > * UHS-I specific initialization procedure
> > > > */
> > > > @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct
> > mmc_card
> > > > *card)
> > > >
> > > > /* Set the driver strength for the card */
> > > > err = sd_select_driver_type(card, status);
> > > > + if (err)
> > > > + goto out;
> > > > +
> > > > + /* Set bus speed mode of the card */
> > > > + err = sd_set_bus_speed_mode(card, status);
> > > >
> > > > out:
> > > > kfree(status);
> > > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > > index 5d3bb11..f127fa2 100644
> > > > --- a/drivers/mmc/host/sdhci.c
> > > > +++ b/drivers/mmc/host/sdhci.c
> > > > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct mmc_host
> > > *mmc,
> > > > struct mmc_ios *ios)
> > > > ctrl &= ~SDHCI_CTRL_HISPD;
> > > >
> > > > if (host->version >= SDHCI_SPEC_300) {
> > > > - u16 ctrl_2;
> > > > + u16 clk, ctrl_2;
> > > > +
> > > > + /* In case of UHS-I modes, set High Speed Enable */
> > > > + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> > > > + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> > > > + (ios->timing == MMC_TIMING_UHS_DDR50))
> > > > + ctrl |= SDHCI_CTRL_HISPD;
> > > >
> > > > ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> > > > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct mmc_host
> > *mmc,
> > > > struct mmc_ios *ios)
> > > > * need to reset SD Clock Enable before
> changing
> > > High
> > > > * Speed Enable to avoid generating clock
> gliches.
> > > > */
> > > > - u16 clk;
> > > >
> > > > /* Reset SD Clock Enable */
> > > > clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct mmc_host
> > > *mmc,
> > > > struct mmc_ios *ios)
> > > > clk |= SDHCI_CLOCK_CARD_EN;
> > > > sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > > }
> > > > +
> > > > + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > +
> > > > + /* Select Bus Speed Mode for host */
> > > > + if (ios->timing == MMC_TIMING_UHS_SDR25)
> > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > > > + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > > > + else if (ios->timing == MMC_TIMING_UHS_SDR104)
> > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> > > > + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> > > > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > > > +
> > > > + /* Reset SD Clock Enable */
> > > > + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > > +
> > > > + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> > > > +
> > > > + /* Re-enable SD Clock */
> > > > + clk |= SDHCI_CLOCK_CARD_EN;
> > > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > > } else
> > > > sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
> > > >
> > > > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > > > index a407b5b..5bf244d 100644
> > > > --- a/drivers/mmc/host/sdhci.h
> > > > +++ b/drivers/mmc/host/sdhci.h
> > > > @@ -150,6 +150,11 @@
> > > > #define SDHCI_ACMD12_ERR 0x3C
> > > >
> > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > > > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > > > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > > > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > > > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > > > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > > > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > > > index 2d7f7a3..0b24c41 100644
> > > > --- a/include/linux/mmc/card.h
> > > > +++ b/include/linux/mmc/card.h
> > > > @@ -75,7 +75,23 @@ struct sd_ssr {
> > > >
> > > > struct sd_switch_caps {
> > > > unsigned int hs_max_dtr;
> > > > + unsigned int uhs_max_dtr;
> > > > +#define UHS_SDR104_MAX_DTR 208000000
> > > > +#define UHS_SDR50_MAX_DTR 100000000
> > > > +#define UHS_DDR50_MAX_DTR 50000000
> > > > +#define UHS_SDR25_MAX_DTR 50000000
> > > > unsigned int uhs_bus_mode;
> > > > +#define UHS_SDR12_BUS_SPEED 0
> > > > +#define UHS_SDR25_BUS_SPEED 1
> > > > +#define UHS_SDR50_BUS_SPEED 2
> > > > +#define UHS_SDR104_BUS_SPEED 3
> > > > +#define UHS_DDR50_BUS_SPEED 4
> > > > +
> > > > +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> > > > +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> > > > +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> > > > +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> > > > +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> > > > unsigned int uhs_drv_type;
> > > > #define SD_DRIVER_TYPE_B 0x01
> > > > #define SD_DRIVER_TYPE_A 0x02
> > > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > > > index bc2121e..4dfff6d 100644
> > > > --- a/include/linux/mmc/host.h
> > > > +++ b/include/linux/mmc/host.h
> > > > @@ -50,6 +50,10 @@ struct mmc_ios {
> > > > #define MMC_TIMING_LEGACY 0
> > > > #define MMC_TIMING_MMC_HS 1
> > > > #define MMC_TIMING_SD_HS 2
> > > > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > > > +#define MMC_TIMING_UHS_SDR50 3
> > > > +#define MMC_TIMING_UHS_SDR104 4
> > > > +#define MMC_TIMING_UHS_DDR50 5
> > > >
> > > > unsigned char ddr; /* dual data rate used
> */
> > > >
> > > > --
> > > > 1.7.1
> > >
> > >
> >
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 04/12] mmc: sd: add support for driver type selection
2011-03-10 8:31 ` Nath, Arindam
@ 2011-03-10 10:28 ` subhashj
2011-03-10 10:44 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 10:28 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Thursday, March 10, 2011 2:02 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> selection
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Thursday, March 10, 2011 12:27 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> > selection
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 04/12] mmc: sd: add support for driver type
> > > selection
> > >
> > > 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. We then set the
> > > appropriate driver type for the card using CMD6 mode 1. As per
> > > Host Controller spec v3.00, we set driver type for the host only
> > > if Preset Value Enable in the Host Control2 register is not set.
> > > SDHCI_HOST_CONTROL has been renamed to SDHCI_HOST_CONTROL1 to
> > > conform to the spec.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/core.c | 9 +++
> > > drivers/mmc/core/core.h | 1 +
> > > drivers/mmc/core/sd.c | 140
> > +++++++++++++++++++++++++++++++++++++-
> > > --------
> > > drivers/mmc/core/sd.h | 3 +-
> > > drivers/mmc/core/sdio.c | 3 +-
> > > drivers/mmc/host/sdhci.c | 48 ++++++++++++----
> > > drivers/mmc/host/sdhci.h | 10 +++-
> > > include/linux/mmc/card.h | 4 +
> > > include/linux/mmc/host.h | 8 +++
> > > 9 files changed, 186 insertions(+), 40 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > > index 6625c05..daa535a 100644
> > > --- a/drivers/mmc/core/core.c
> > > +++ b/drivers/mmc/core/core.c
> > > @@ -947,6 +947,15 @@ void mmc_set_timing(struct mmc_host *host,
> > > unsigned int timing)
> > > }
> > >
> > > /*
> > > + * Select appropriate driver type for host.
> > > + */
> > > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> > drv_type)
> > > +{
> > > + host->ios.drv_type = drv_type;
> > > + mmc_set_ios(host);
> > > +}
> > > +
> > > +/*
> > > * Apply power to the MMC stack. This is a two-stage process.
> > > * First, we enable power to the card without the clock running.
> > > * We then wait a bit for the power to stabilise. Finally,
> > > diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> > > index ca1fdde..6114ca5 100644
> > > --- a/drivers/mmc/core/core.h
> > > +++ b/drivers/mmc/core/core.h
> > > @@ -42,6 +42,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host,
> > > unsigned int width,
> > > unsigned int ddr);
> > > u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> > > void mmc_set_timing(struct mmc_host *host, unsigned int timing);
> > > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> > > drv_type);
> > >
> > > static inline void mmc_delay(unsigned int ms)
> > > {
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index a63956b..f6a4fab 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -426,6 +426,86 @@ out:
> > > return err;
> > > }
> > >
> > > +static int sd_select_driver_type(struct mmc_card *card, u8
> *status)
> > > +{
> > > + int host_set_drv_type, card_set_drv_type;
> >
> > Why you need to use *set* in name? Just host_drv_type & card_drv_type
> > is not
> > enough?
>
> I wanted to emphasize on the fact that we are setting the values for
> host and card both. I don't have issues removing the *set* part.
I don't think you need to use *set*.
>
> >
> > > + 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_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
> > > + card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > > + else if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> > > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> >
> > Why you are combining TYPE_C under CAP_*_TYPE_A?
>
> Both the host and card can support multiple driver types, so we need to
> decide the appropriate setting for them based on their capabilities.
Agreed but here you are first checking that host is capable of TYPE_A or
not. Then if it's capable of TYPE_A that doesn't guarantee that host is
capable of TYPE_C as well even if card supports both TYPE_A and TYPE_C.
>
> >
> > > + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> > > + host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> > > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > > + }
> >
> > Again TYPE_C checking here? Also why are you skiping TYPE_D here?
>
> TYPE_C checking for the same reason as above. I could not find a
> scenario where I can put TYPE_D into, so there is not checking.
> Probably you can suggest something?
Will following simple check is not enough?
if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) {
host_set_drv_type = MMC_SET_DRIVER_TYPE_A;
if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
} else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
} else if (card->host->caps & MMC_CAP_DRIVER_TYPE_D) {
host_set_drv_type = MMC_SET_DRIVER_TYPE_D;
if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_D)
card_set_drv_type = MMC_SET_DRIVER_TYPE_D;
} else {
return 0; /* default = TYPE_B */
}
>
> >
> > > +
> > > + err = mmc_sd_switch(card, 1, 2, card_set_drv_type, status);
> > > + if (err)
> > > + return err;
> > > +
> > > + if ((status[15] & 0xF) != card_set_drv_type)
> > > + printk(KERN_WARNING "%s: Problem setting driver
> > > strength!\n",
> > > + mmc_hostname(card->host));
> > > + else
> > > + mmc_set_driver_type(card->host, host_set_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;
> >
> > We can combine both these check under single "if" checking.
>
> The code snippet is a replica of the code already present in
> mmc_sd_switch_hs() and mmc_read_switch(), so I too followed the same
> checking. I can combine them into a single condition if you want.
Ok. it's up to you in this case. If it looks by having 2 different
statements then it's fine.
>
> >
> > > +
> > > + err = -EIO;
> >
> > Is this really required? I don't think initializing "err" is needed
> > here.
>
> Again a replica of the functions mentioned above. Let me know if you
> want this removed.
This is not needed here. You can remove it.
>
> >
> > > +
> > > + 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;
> > > + }
> >
> > You may want to allocate memory only after bus width setting is
> > successful.
>
> Yes, good suggestion. I will incorporate this change.
>
> >
> > > +
> > > + /* 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);
> >
> > Actually why are you allocating memory for "status" response in
> > mmc_sd_init_uhs_card() function. You need "status" only in
> > sd_select_driver_type() function. So you should be allocating memory
> in
> > sd_select_driver_type() function only. here you just say "
> > sd_select_driver_type(card);"
>
> Please check the later patches. "status" is used to store return
> statuses from *bus_speed_mode() and *current_limit() as well. So I
> think it makes sense to allocate memory for "status" at the beginning
> of the function, and use it later.
Sorry. I had not checked the later patches. Yes, allocating here is proper.
As same status is being used for other switch settings as well.
>
> >
> > > +
> > > +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],
> > > @@ -474,10 +554,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
> > > @@ -502,7 +582,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> > ocr,
> > > u32 *cid)
> > > MMC_CAP_SET_XPC_180))
> > > ocr |= 1 << 28;
> > >
> > > - err = mmc_send_app_op_cond(host, ocr, &rocr);
> > > + err = mmc_send_app_op_cond(host, ocr, rocr);
> > > if (err)
> > > return err;
> > >
> > > @@ -510,7 +590,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> > ocr,
> > > u32 *cid)
> > > * 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)) {
> > > + if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x41000000)) {
> >
> > You are not doing NULL check for "rocr" in mmc_send_app_op_cond() but
> > you
> > are doing it here. Is it needed? If "rocr" is NULL then
> > mmc_send_app_op_cond() itself will fail.
>
> I am actually checking rocr for NULL before returning from
> mmc_send_app_op_cond(). Can you please check the function again to make
> sure?
Yes. It's fine. ignore this.
>
> > Also, you may need to check b31 (busy bit) of rocr because spec says
> > this:
> > "Section:4.2.3.1 CCS (Bit 30) and S18A (Bit 24) are valid when Busy
> > (Bit 31)
> > is set to 1."
>
> The busy bit (bit 31) checking is already performed inside
> mmc_send_app_op_cond(), cmd.resp[0] & MMC_CARD_BUSY.
Ok. fine.
>
> >
> > > err = mmc_start_voltage_switch(host);
> > > if (err)
> > > return err;
> > > @@ -643,11 +723,12 @@ static int mmc_sd_init_card(struct mmc_host
> > > *host, u32 ocr,
> > > struct mmc_card *card;
> > > int err;
> > > u32 cid[4];
> > > + u32 rocr;
> > >
> > > 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;
> > >
> > > @@ -700,30 +781,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 & 0x01000000) {
> >
> > Instead of 0x01000000, you can use (1 << 24) to be more readable.
>
> I am okay with your suggestion. Will change it.
>
> Thanks,
> Arindam
>
> >
> > > + 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 5c4a54d..6d16684 100644
> > > --- a/drivers/mmc/core/sdio.c
> > > +++ b/drivers/mmc/core/sdio.c
> > > @@ -364,7 +364,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 5487a0b..1645687 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -63,7 +63,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));
> > > @@ -216,18 +216,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
> > > @@ -786,14 +786,14 @@ static void sdhci_prepare_data(struct
> > sdhci_host
> > > *host, struct mmc_data *data)
> > > * 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)) {
> > > @@ -1252,7 +1252,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)
> > > @@ -1265,10 +1265,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)
> > > @@ -1277,7 +1277,25 @@ 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.
> > > + */
> > > + 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,
> > > @@ -2037,6 +2055,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 95d70e6..a407b5b 100644
> > > --- a/drivers/mmc/host/sdhci.h
> > > +++ b/drivers/mmc/host/sdhci.h
> > > @@ -72,7 +72,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
> > > @@ -151,6 +151,11 @@
> > >
> > > #define SDHCI_HOST_CONTROL2 0x3E
> > > #define SDHCI_CTRL_VDD_180 0x0008
> > > +#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
> > > @@ -174,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 7080f22..2d7f7a3 100644
> > > --- a/include/linux/mmc/card.h
> > > +++ b/include/linux/mmc/card.h
> > > @@ -77,6 +77,10 @@ struct sd_switch_caps {
> > > unsigned int hs_max_dtr;
> > > unsigned int uhs_bus_mode;
> > > unsigned int uhs_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 uhs_curr_limit;
> > > };
> > >
> > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > > index ad7daa3..bc2121e 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 drv_type; /* driver type (A, C,
> D) */
> > > +
> > > +#define MMC_SET_DRIVER_TYPE_A 1
> > > +#define MMC_SET_DRIVER_TYPE_C 2
> >
> > *SET* is not required in name. again why are we ignoring TYPE D here?
> >
> > > };
> > >
> > > struct mmc_host_ops {
> > > @@ -183,6 +188,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
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 04/12] mmc: sd: add support for driver type selection
2011-03-10 10:28 ` subhashj
@ 2011-03-10 10:44 ` Nath, Arindam
2011-03-10 11:25 ` subhashj
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 10:44 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 3:58 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> selection
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Thursday, March 10, 2011 2:02 PM
> > To: subhashj@codeaurora.org; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> > selection
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > Sent: Thursday, March 10, 2011 12:27 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> > > selection
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > To: cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > henry.su@amd.com;
> > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > Subject: [PATCH v2 04/12] mmc: sd: add support for driver type
> > > > selection
> > > >
> > > > 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. We then set the
> > > > appropriate driver type for the card using CMD6 mode 1. As per
> > > > Host Controller spec v3.00, we set driver type for the host only
> > > > if Preset Value Enable in the Host Control2 register is not set.
> > > > SDHCI_HOST_CONTROL has been renamed to SDHCI_HOST_CONTROL1 to
> > > > conform to the spec.
> > > >
> > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > ---
> > > > drivers/mmc/core/core.c | 9 +++
> > > > drivers/mmc/core/core.h | 1 +
> > > > drivers/mmc/core/sd.c | 140
> > > +++++++++++++++++++++++++++++++++++++-
> > > > --------
> > > > drivers/mmc/core/sd.h | 3 +-
> > > > drivers/mmc/core/sdio.c | 3 +-
> > > > drivers/mmc/host/sdhci.c | 48 ++++++++++++----
> > > > drivers/mmc/host/sdhci.h | 10 +++-
> > > > include/linux/mmc/card.h | 4 +
> > > > include/linux/mmc/host.h | 8 +++
> > > > 9 files changed, 186 insertions(+), 40 deletions(-)
> > > >
> > > > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > > > index 6625c05..daa535a 100644
> > > > --- a/drivers/mmc/core/core.c
> > > > +++ b/drivers/mmc/core/core.c
> > > > @@ -947,6 +947,15 @@ void mmc_set_timing(struct mmc_host *host,
> > > > unsigned int timing)
> > > > }
> > > >
> > > > /*
> > > > + * Select appropriate driver type for host.
> > > > + */
> > > > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> > > drv_type)
> > > > +{
> > > > + host->ios.drv_type = drv_type;
> > > > + mmc_set_ios(host);
> > > > +}
> > > > +
> > > > +/*
> > > > * Apply power to the MMC stack. This is a two-stage process.
> > > > * First, we enable power to the card without the clock running.
> > > > * We then wait a bit for the power to stabilise. Finally,
> > > > diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> > > > index ca1fdde..6114ca5 100644
> > > > --- a/drivers/mmc/core/core.h
> > > > +++ b/drivers/mmc/core/core.h
> > > > @@ -42,6 +42,7 @@ void mmc_set_bus_width_ddr(struct mmc_host
> *host,
> > > > unsigned int width,
> > > > unsigned int ddr);
> > > > u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> > > > void mmc_set_timing(struct mmc_host *host, unsigned int timing);
> > > > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> > > > drv_type);
> > > >
> > > > static inline void mmc_delay(unsigned int ms)
> > > > {
> > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > index a63956b..f6a4fab 100644
> > > > --- a/drivers/mmc/core/sd.c
> > > > +++ b/drivers/mmc/core/sd.c
> > > > @@ -426,6 +426,86 @@ out:
> > > > return err;
> > > > }
> > > >
> > > > +static int sd_select_driver_type(struct mmc_card *card, u8
> > *status)
> > > > +{
> > > > + int host_set_drv_type, card_set_drv_type;
> > >
> > > Why you need to use *set* in name? Just host_drv_type &
> card_drv_type
> > > is not
> > > enough?
> >
> > I wanted to emphasize on the fact that we are setting the values for
> > host and card both. I don't have issues removing the *set* part.
>
> I don't think you need to use *set*.
Okay with me. I will remove *set*.
>
> >
> > >
> > > > + 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_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > > > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
> > > > + card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > > > + else if (card->sw_caps.uhs_drv_type &
> SD_DRIVER_TYPE_C)
> > > > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > >
> > > Why you are combining TYPE_C under CAP_*_TYPE_A?
> >
> > Both the host and card can support multiple driver types, so we need
> to
> > decide the appropriate setting for them based on their capabilities.
>
> Agreed but here you are first checking that host is capable of TYPE_A
> or
> not. Then if it's capable of TYPE_A that doesn't guarantee that host is
> capable of TYPE_C as well even if card supports both TYPE_A and TYPE_C.
Please refer to section 6.7.1.2 of the Physical Layer spec v3.01. As per the description, it seemed to me like, since Driver Type C is weaker than Driver Type A, a host with Type A can still drive a card of Type C. I might have misunderstood the spec, so please correct me if wrong.
>
> >
> > >
> > > > + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> > > > + host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > > > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> > > > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > > > + }
> > >
> > > Again TYPE_C checking here? Also why are you skiping TYPE_D here?
> >
> > TYPE_C checking for the same reason as above. I could not find a
> > scenario where I can put TYPE_D into, so there is not checking.
> > Probably you can suggest something?
>
> Will following simple check is not enough?
>
> if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) {
> host_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
> card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_D) {
> host_set_drv_type = MMC_SET_DRIVER_TYPE_D;
> if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_D)
> card_set_drv_type = MMC_SET_DRIVER_TYPE_D;
> } else {
> return 0; /* default = TYPE_B */
> }
>
>
>
> >
> > >
> > > > +
> > > > + err = mmc_sd_switch(card, 1, 2, card_set_drv_type, status);
> > > > + if (err)
> > > > + return err;
> > > > +
> > > > + if ((status[15] & 0xF) != card_set_drv_type)
> > > > + printk(KERN_WARNING "%s: Problem setting driver
> > > > strength!\n",
> > > > + mmc_hostname(card->host));
> > > > + else
> > > > + mmc_set_driver_type(card->host, host_set_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;
> > >
> > > We can combine both these check under single "if" checking.
> >
> > The code snippet is a replica of the code already present in
> > mmc_sd_switch_hs() and mmc_read_switch(), so I too followed the same
> > checking. I can combine them into a single condition if you want.
>
> Ok. it's up to you in this case. If it looks by having 2 different
> statements then it's fine.
>
> >
> > >
> > > > +
> > > > + err = -EIO;
> > >
> > > Is this really required? I don't think initializing "err" is needed
> > > here.
> >
> > Again a replica of the functions mentioned above. Let me know if you
> > want this removed.
>
> This is not needed here. You can remove it.
Will remove it then. Thanks.
>
> >
> > >
> > > > +
> > > > + 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;
> > > > + }
> > >
> > > You may want to allocate memory only after bus width setting is
> > > successful.
> >
> > Yes, good suggestion. I will incorporate this change.
> >
> > >
> > > > +
> > > > + /* 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);
> > >
> > > Actually why are you allocating memory for "status" response in
> > > mmc_sd_init_uhs_card() function. You need "status" only in
> > > sd_select_driver_type() function. So you should be allocating
> memory
> > in
> > > sd_select_driver_type() function only. here you just say "
> > > sd_select_driver_type(card);"
> >
> > Please check the later patches. "status" is used to store return
> > statuses from *bus_speed_mode() and *current_limit() as well. So I
> > think it makes sense to allocate memory for "status" at the beginning
> > of the function, and use it later.
>
> Sorry. I had not checked the later patches. Yes, allocating here is
> proper.
> As same status is being used for other switch settings as well.
>
>
> >
> > >
> > > > +
> > > > +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],
> > > > @@ -474,10 +554,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
> > > > @@ -502,7 +582,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> > > ocr,
> > > > u32 *cid)
> > > > MMC_CAP_SET_XPC_180))
> > > > ocr |= 1 << 28;
> > > >
> > > > - err = mmc_send_app_op_cond(host, ocr, &rocr);
> > > > + err = mmc_send_app_op_cond(host, ocr, rocr);
> > > > if (err)
> > > > return err;
> > > >
> > > > @@ -510,7 +590,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> > > ocr,
> > > > u32 *cid)
> > > > * 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)) {
> > > > + if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x41000000)) {
> > >
> > > You are not doing NULL check for "rocr" in mmc_send_app_op_cond()
> but
> > > you
> > > are doing it here. Is it needed? If "rocr" is NULL then
> > > mmc_send_app_op_cond() itself will fail.
> >
> > I am actually checking rocr for NULL before returning from
> > mmc_send_app_op_cond(). Can you please check the function again to
> make
> > sure?
> Yes. It's fine. ignore this.
>
>
> >
> > > Also, you may need to check b31 (busy bit) of rocr because spec
> says
> > > this:
> > > "Section:4.2.3.1 CCS (Bit 30) and S18A (Bit 24) are valid when Busy
> > > (Bit 31)
> > > is set to 1."
> >
> > The busy bit (bit 31) checking is already performed inside
> > mmc_send_app_op_cond(), cmd.resp[0] & MMC_CARD_BUSY.
>
> Ok. fine.
>
> >
> > >
> > > > err = mmc_start_voltage_switch(host);
> > > > if (err)
> > > > return err;
> > > > @@ -643,11 +723,12 @@ static int mmc_sd_init_card(struct mmc_host
> > > > *host, u32 ocr,
> > > > struct mmc_card *card;
> > > > int err;
> > > > u32 cid[4];
> > > > + u32 rocr;
> > > >
> > > > 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;
> > > >
> > > > @@ -700,30 +781,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 & 0x01000000) {
> > >
> > > Instead of 0x01000000, you can use (1 << 24) to be more readable.
> >
> > I am okay with your suggestion. Will change it.
> >
> > Thanks,
> > Arindam
> >
> > >
> > > > + 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 5c4a54d..6d16684 100644
> > > > --- a/drivers/mmc/core/sdio.c
> > > > +++ b/drivers/mmc/core/sdio.c
> > > > @@ -364,7 +364,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 5487a0b..1645687 100644
> > > > --- a/drivers/mmc/host/sdhci.c
> > > > +++ b/drivers/mmc/host/sdhci.c
> > > > @@ -63,7 +63,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));
> > > > @@ -216,18 +216,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
> > > > @@ -786,14 +786,14 @@ static void sdhci_prepare_data(struct
> > > sdhci_host
> > > > *host, struct mmc_data *data)
> > > > * 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)) {
> > > > @@ -1252,7 +1252,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)
> > > > @@ -1265,10 +1265,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)
> > > > @@ -1277,7 +1277,25 @@ 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.
> > > > + */
> > > > + 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,
> > > > @@ -2037,6 +2055,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 95d70e6..a407b5b 100644
> > > > --- a/drivers/mmc/host/sdhci.h
> > > > +++ b/drivers/mmc/host/sdhci.h
> > > > @@ -72,7 +72,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
> > > > @@ -151,6 +151,11 @@
> > > >
> > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > +#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
> > > > @@ -174,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 7080f22..2d7f7a3 100644
> > > > --- a/include/linux/mmc/card.h
> > > > +++ b/include/linux/mmc/card.h
> > > > @@ -77,6 +77,10 @@ struct sd_switch_caps {
> > > > unsigned int hs_max_dtr;
> > > > unsigned int uhs_bus_mode;
> > > > unsigned int uhs_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 uhs_curr_limit;
> > > > };
> > > >
> > > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > > > index ad7daa3..bc2121e 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 drv_type; /* driver type (A, C,
> > D) */
> > > > +
> > > > +#define MMC_SET_DRIVER_TYPE_A 1
> > > > +#define MMC_SET_DRIVER_TYPE_C 2
> > >
> > > *SET* is not required in name. again why are we ignoring TYPE D
> here?
> > >
> > > > };
> > > >
> > > > struct mmc_host_ops {
> > > > @@ -183,6 +188,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
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 04/12] mmc: sd: add support for driver type selection
2011-03-10 10:44 ` Nath, Arindam
@ 2011-03-10 11:25 ` subhashj
2011-03-10 11:34 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 11:25 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Thursday, March 10, 2011 4:15 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> selection
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Thursday, March 10, 2011 3:58 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> > selection
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > Sent: Thursday, March 10, 2011 2:02 PM
> > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> > > selection
> > >
> > > Hi Subhash,
> > >
> > >
> > > > -----Original Message-----
> > > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > > Sent: Thursday, March 10, 2011 12:27 PM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver
> type
> > > > selection
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > To: cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > henry.su@amd.com;
> > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > Subject: [PATCH v2 04/12] mmc: sd: add support for driver type
> > > > > selection
> > > > >
> > > > > 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. We then set the
> > > > > appropriate driver type for the card using CMD6 mode 1. As per
> > > > > Host Controller spec v3.00, we set driver type for the host
> only
> > > > > if Preset Value Enable in the Host Control2 register is not
> set.
> > > > > SDHCI_HOST_CONTROL has been renamed to SDHCI_HOST_CONTROL1 to
> > > > > conform to the spec.
> > > > >
> > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > ---
> > > > > drivers/mmc/core/core.c | 9 +++
> > > > > drivers/mmc/core/core.h | 1 +
> > > > > drivers/mmc/core/sd.c | 140
> > > > +++++++++++++++++++++++++++++++++++++-
> > > > > --------
> > > > > drivers/mmc/core/sd.h | 3 +-
> > > > > drivers/mmc/core/sdio.c | 3 +-
> > > > > drivers/mmc/host/sdhci.c | 48 ++++++++++++----
> > > > > drivers/mmc/host/sdhci.h | 10 +++-
> > > > > include/linux/mmc/card.h | 4 +
> > > > > include/linux/mmc/host.h | 8 +++
> > > > > 9 files changed, 186 insertions(+), 40 deletions(-)
> > > > >
> > > > > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> > > > > index 6625c05..daa535a 100644
> > > > > --- a/drivers/mmc/core/core.c
> > > > > +++ b/drivers/mmc/core/core.c
> > > > > @@ -947,6 +947,15 @@ void mmc_set_timing(struct mmc_host *host,
> > > > > unsigned int timing)
> > > > > }
> > > > >
> > > > > /*
> > > > > + * Select appropriate driver type for host.
> > > > > + */
> > > > > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> > > > drv_type)
> > > > > +{
> > > > > + host->ios.drv_type = drv_type;
> > > > > + mmc_set_ios(host);
> > > > > +}
> > > > > +
> > > > > +/*
> > > > > * Apply power to the MMC stack. This is a two-stage process.
> > > > > * First, we enable power to the card without the clock
> running.
> > > > > * We then wait a bit for the power to stabilise. Finally,
> > > > > diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> > > > > index ca1fdde..6114ca5 100644
> > > > > --- a/drivers/mmc/core/core.h
> > > > > +++ b/drivers/mmc/core/core.h
> > > > > @@ -42,6 +42,7 @@ void mmc_set_bus_width_ddr(struct mmc_host
> > *host,
> > > > > unsigned int width,
> > > > > unsigned int ddr);
> > > > > u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> > > > > void mmc_set_timing(struct mmc_host *host, unsigned int
> timing);
> > > > > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> > > > > drv_type);
> > > > >
> > > > > static inline void mmc_delay(unsigned int ms)
> > > > > {
> > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > > index a63956b..f6a4fab 100644
> > > > > --- a/drivers/mmc/core/sd.c
> > > > > +++ b/drivers/mmc/core/sd.c
> > > > > @@ -426,6 +426,86 @@ out:
> > > > > return err;
> > > > > }
> > > > >
> > > > > +static int sd_select_driver_type(struct mmc_card *card, u8
> > > *status)
> > > > > +{
> > > > > + int host_set_drv_type, card_set_drv_type;
> > > >
> > > > Why you need to use *set* in name? Just host_drv_type &
> > card_drv_type
> > > > is not
> > > > enough?
> > >
> > > I wanted to emphasize on the fact that we are setting the values
> for
> > > host and card both. I don't have issues removing the *set* part.
> >
> > I don't think you need to use *set*.
>
> Okay with me. I will remove *set*.
>
> >
> > >
> > > >
> > > > > + 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_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > > > > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
> > > > > + card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > > > > + else if (card->sw_caps.uhs_drv_type &
> > SD_DRIVER_TYPE_C)
> > > > > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > > >
> > > > Why you are combining TYPE_C under CAP_*_TYPE_A?
> > >
> > > Both the host and card can support multiple driver types, so we
> need
> > to
> > > decide the appropriate setting for them based on their
> capabilities.
> >
> > Agreed but here you are first checking that host is capable of TYPE_A
> > or
> > not. Then if it's capable of TYPE_A that doesn't guarantee that host
> is
> > capable of TYPE_C as well even if card supports both TYPE_A and
> TYPE_C.
>
> Please refer to section 6.7.1.2 of the Physical Layer spec v3.01. As
> per the description, it seemed to me like, since Driver Type C is
> weaker than Driver Type A, a host with Type A can still drive a card of
> Type C. I might have misunderstood the spec, so please correct me if
> wrong.
So this is what you are assuming: A > B > C > D. with above check you are
ignoring TYPE_B then. Let's say you check host CAP TYPE_A and if it supports
then you check if card supports TYPE_A or not. If it's not then you are
directly jumping to TYPE_C. but according to spec TYPE_B is stronger than
TYPE_C. Please check the section: 6.7.1.5 in spec. it says this: "whenever a
stronger driver than type B is needed, driver type A can be selected and
whenever a weaker driver than type B is needed, driver type C can be
selected "
>
> >
> > >
> > > >
> > > > > + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> > > > > + host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > > > > + if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> > > > > + card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > > > > + }
> > > >
> > > > Again TYPE_C checking here? Also why are you skiping TYPE_D here?
> > >
> > > TYPE_C checking for the same reason as above. I could not find a
> > > scenario where I can put TYPE_D into, so there is not checking.
> > > Probably you can suggest something?
> >
> > Will following simple check is not enough?
> >
> > if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) {
> > host_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
> > card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> > host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> > card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_D) {
> > host_set_drv_type = MMC_SET_DRIVER_TYPE_D;
> > if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_D)
> > card_set_drv_type = MMC_SET_DRIVER_TYPE_D;
> > } else {
> > return 0; /* default = TYPE_B */
> > }
> >
> >
> >
> > >
> > > >
> > > > > +
> > > > > + err = mmc_sd_switch(card, 1, 2, card_set_drv_type, status);
> > > > > + if (err)
> > > > > + return err;
> > > > > +
> > > > > + if ((status[15] & 0xF) != card_set_drv_type)
> > > > > + printk(KERN_WARNING "%s: Problem setting driver
> > > > > strength!\n",
> > > > > + mmc_hostname(card->host));
> > > > > + else
> > > > > + mmc_set_driver_type(card->host, host_set_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;
> > > >
> > > > We can combine both these check under single "if" checking.
> > >
> > > The code snippet is a replica of the code already present in
> > > mmc_sd_switch_hs() and mmc_read_switch(), so I too followed the
> same
> > > checking. I can combine them into a single condition if you want.
> >
> > Ok. it's up to you in this case. If it looks by having 2 different
> > statements then it's fine.
> >
> > >
> > > >
> > > > > +
> > > > > + err = -EIO;
> > > >
> > > > Is this really required? I don't think initializing "err" is
> needed
> > > > here.
> > >
> > > Again a replica of the functions mentioned above. Let me know if
> you
> > > want this removed.
> >
> > This is not needed here. You can remove it.
>
> Will remove it then. Thanks.
>
> >
> > >
> > > >
> > > > > +
> > > > > + 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;
> > > > > + }
> > > >
> > > > You may want to allocate memory only after bus width setting is
> > > > successful.
> > >
> > > Yes, good suggestion. I will incorporate this change.
> > >
> > > >
> > > > > +
> > > > > + /* 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);
> > > >
> > > > Actually why are you allocating memory for "status" response in
> > > > mmc_sd_init_uhs_card() function. You need "status" only in
> > > > sd_select_driver_type() function. So you should be allocating
> > memory
> > > in
> > > > sd_select_driver_type() function only. here you just say "
> > > > sd_select_driver_type(card);"
> > >
> > > Please check the later patches. "status" is used to store return
> > > statuses from *bus_speed_mode() and *current_limit() as well. So I
> > > think it makes sense to allocate memory for "status" at the
> beginning
> > > of the function, and use it later.
> >
> > Sorry. I had not checked the later patches. Yes, allocating here is
> > proper.
> > As same status is being used for other switch settings as well.
> >
> >
> > >
> > > >
> > > > > +
> > > > > +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],
> > > > > @@ -474,10 +554,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
> > > > > @@ -502,7 +582,7 @@ int mmc_sd_get_cid(struct mmc_host *host,
> u32
> > > > ocr,
> > > > > u32 *cid)
> > > > > MMC_CAP_SET_XPC_180))
> > > > > ocr |= 1 << 28;
> > > > >
> > > > > - err = mmc_send_app_op_cond(host, ocr, &rocr);
> > > > > + err = mmc_send_app_op_cond(host, ocr, rocr);
> > > > > if (err)
> > > > > return err;
> > > > >
> > > > > @@ -510,7 +590,7 @@ int mmc_sd_get_cid(struct mmc_host *host,
> u32
> > > > ocr,
> > > > > u32 *cid)
> > > > > * 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)) {
> > > > > + if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x41000000))
> {
> > > >
> > > > You are not doing NULL check for "rocr" in mmc_send_app_op_cond()
> > but
> > > > you
> > > > are doing it here. Is it needed? If "rocr" is NULL then
> > > > mmc_send_app_op_cond() itself will fail.
> > >
> > > I am actually checking rocr for NULL before returning from
> > > mmc_send_app_op_cond(). Can you please check the function again to
> > make
> > > sure?
> > Yes. It's fine. ignore this.
> >
> >
> > >
> > > > Also, you may need to check b31 (busy bit) of rocr because spec
> > says
> > > > this:
> > > > "Section:4.2.3.1 CCS (Bit 30) and S18A (Bit 24) are valid when
> Busy
> > > > (Bit 31)
> > > > is set to 1."
> > >
> > > The busy bit (bit 31) checking is already performed inside
> > > mmc_send_app_op_cond(), cmd.resp[0] & MMC_CARD_BUSY.
> >
> > Ok. fine.
> >
> > >
> > > >
> > > > > err = mmc_start_voltage_switch(host);
> > > > > if (err)
> > > > > return err;
> > > > > @@ -643,11 +723,12 @@ static int mmc_sd_init_card(struct
> mmc_host
> > > > > *host, u32 ocr,
> > > > > struct mmc_card *card;
> > > > > int err;
> > > > > u32 cid[4];
> > > > > + u32 rocr;
> > > > >
> > > > > 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;
> > > > >
> > > > > @@ -700,30 +781,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 & 0x01000000) {
> > > >
> > > > Instead of 0x01000000, you can use (1 << 24) to be more readable.
> > >
> > > I am okay with your suggestion. Will change it.
> > >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > > > + 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 5c4a54d..6d16684 100644
> > > > > --- a/drivers/mmc/core/sdio.c
> > > > > +++ b/drivers/mmc/core/sdio.c
> > > > > @@ -364,7 +364,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 5487a0b..1645687 100644
> > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > @@ -63,7 +63,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));
> > > > > @@ -216,18 +216,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
> > > > > @@ -786,14 +786,14 @@ static void sdhci_prepare_data(struct
> > > > sdhci_host
> > > > > *host, struct mmc_data *data)
> > > > > * 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)) {
> > > > > @@ -1252,7 +1252,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)
> > > > > @@ -1265,10 +1265,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)
> > > > > @@ -1277,7 +1277,25 @@ 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.
> > > > > + */
> > > > > + 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,
> > > > > @@ -2037,6 +2055,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 95d70e6..a407b5b 100644
> > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > @@ -72,7 +72,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
> > > > > @@ -151,6 +151,11 @@
> > > > >
> > > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > > +#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
> > > > > @@ -174,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 7080f22..2d7f7a3 100644
> > > > > --- a/include/linux/mmc/card.h
> > > > > +++ b/include/linux/mmc/card.h
> > > > > @@ -77,6 +77,10 @@ struct sd_switch_caps {
> > > > > unsigned int hs_max_dtr;
> > > > > unsigned int uhs_bus_mode;
> > > > > unsigned int uhs_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 uhs_curr_limit;
> > > > > };
> > > > >
> > > > > diff --git a/include/linux/mmc/host.h
> b/include/linux/mmc/host.h
> > > > > index ad7daa3..bc2121e 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 drv_type; /* driver type (A,
> C,
> > > D) */
> > > > > +
> > > > > +#define MMC_SET_DRIVER_TYPE_A 1
> > > > > +#define MMC_SET_DRIVER_TYPE_C 2
> > > >
> > > > *SET* is not required in name. again why are we ignoring TYPE D
> > here?
> > > >
> > > > > };
> > > > >
> > > > > struct mmc_host_ops {
> > > > > @@ -183,6 +188,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
> > > > >
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe
> linux-
> > > mmc"
> > > > in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > info.html
> > > >
> > >
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 04/12] mmc: sd: add support for driver type selection
2011-03-10 11:25 ` subhashj
@ 2011-03-10 11:34 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 11:34 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 4:56 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> selection
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Thursday, March 10, 2011 4:15 PM
> > To: subhashj@codeaurora.org; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> > selection
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > Sent: Thursday, March 10, 2011 3:58 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver type
> > > selection
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > Sent: Thursday, March 10, 2011 2:02 PM
> > > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver
> type
> > > > selection
> > > >
> > > > Hi Subhash,
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > > > Sent: Thursday, March 10, 2011 12:27 PM
> > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 04/12] mmc: sd: add support for driver
> > type
> > > > > selection
> > > > >
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > To: cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > henry.su@amd.com;
> > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > Subject: [PATCH v2 04/12] mmc: sd: add support for driver
> type
> > > > > > selection
> > > > > >
> > > > > > 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. We then set the
> > > > > > appropriate driver type for the card using CMD6 mode 1. As
> per
> > > > > > Host Controller spec v3.00, we set driver type for the host
> > only
> > > > > > if Preset Value Enable in the Host Control2 register is not
> > set.
> > > > > > SDHCI_HOST_CONTROL has been renamed to SDHCI_HOST_CONTROL1 to
> > > > > > conform to the spec.
> > > > > >
> > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > ---
> > > > > > drivers/mmc/core/core.c | 9 +++
> > > > > > drivers/mmc/core/core.h | 1 +
> > > > > > drivers/mmc/core/sd.c | 140
> > > > > +++++++++++++++++++++++++++++++++++++-
> > > > > > --------
> > > > > > drivers/mmc/core/sd.h | 3 +-
> > > > > > drivers/mmc/core/sdio.c | 3 +-
> > > > > > drivers/mmc/host/sdhci.c | 48 ++++++++++++----
> > > > > > drivers/mmc/host/sdhci.h | 10 +++-
> > > > > > include/linux/mmc/card.h | 4 +
> > > > > > include/linux/mmc/host.h | 8 +++
> > > > > > 9 files changed, 186 insertions(+), 40 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/mmc/core/core.c
> b/drivers/mmc/core/core.c
> > > > > > index 6625c05..daa535a 100644
> > > > > > --- a/drivers/mmc/core/core.c
> > > > > > +++ b/drivers/mmc/core/core.c
> > > > > > @@ -947,6 +947,15 @@ void mmc_set_timing(struct mmc_host
> *host,
> > > > > > unsigned int timing)
> > > > > > }
> > > > > >
> > > > > > /*
> > > > > > + * Select appropriate driver type for host.
> > > > > > + */
> > > > > > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> > > > > drv_type)
> > > > > > +{
> > > > > > + host->ios.drv_type = drv_type;
> > > > > > + mmc_set_ios(host);
> > > > > > +}
> > > > > > +
> > > > > > +/*
> > > > > > * Apply power to the MMC stack. This is a two-stage
> process.
> > > > > > * First, we enable power to the card without the clock
> > running.
> > > > > > * We then wait a bit for the power to stabilise. Finally,
> > > > > > diff --git a/drivers/mmc/core/core.h
> b/drivers/mmc/core/core.h
> > > > > > index ca1fdde..6114ca5 100644
> > > > > > --- a/drivers/mmc/core/core.h
> > > > > > +++ b/drivers/mmc/core/core.h
> > > > > > @@ -42,6 +42,7 @@ void mmc_set_bus_width_ddr(struct mmc_host
> > > *host,
> > > > > > unsigned int width,
> > > > > > unsigned int ddr);
> > > > > > u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
> > > > > > void mmc_set_timing(struct mmc_host *host, unsigned int
> > timing);
> > > > > > +void mmc_set_driver_type(struct mmc_host *host, unsigned int
> > > > > > drv_type);
> > > > > >
> > > > > > static inline void mmc_delay(unsigned int ms)
> > > > > > {
> > > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > > > index a63956b..f6a4fab 100644
> > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > @@ -426,6 +426,86 @@ out:
> > > > > > return err;
> > > > > > }
> > > > > >
> > > > > > +static int sd_select_driver_type(struct mmc_card *card, u8
> > > > *status)
> > > > > > +{
> > > > > > + int host_set_drv_type, card_set_drv_type;
> > > > >
> > > > > Why you need to use *set* in name? Just host_drv_type &
> > > card_drv_type
> > > > > is not
> > > > > enough?
> > > >
> > > > I wanted to emphasize on the fact that we are setting the values
> > for
> > > > host and card both. I don't have issues removing the *set* part.
> > >
> > > I don't think you need to use *set*.
> >
> > Okay with me. I will remove *set*.
> >
> > >
> > > >
> > > > >
> > > > > > + 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_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > > > > > + if (card->sw_caps.uhs_drv_type &
> SD_DRIVER_TYPE_A)
> > > > > > + card_set_drv_type =
> MMC_SET_DRIVER_TYPE_A;
> > > > > > + else if (card->sw_caps.uhs_drv_type &
> > > SD_DRIVER_TYPE_C)
> > > > > > + card_set_drv_type =
> MMC_SET_DRIVER_TYPE_C;
> > > > >
> > > > > Why you are combining TYPE_C under CAP_*_TYPE_A?
> > > >
> > > > Both the host and card can support multiple driver types, so we
> > need
> > > to
> > > > decide the appropriate setting for them based on their
> > capabilities.
> > >
> > > Agreed but here you are first checking that host is capable of
> TYPE_A
> > > or
> > > not. Then if it's capable of TYPE_A that doesn't guarantee that
> host
> > is
> > > capable of TYPE_C as well even if card supports both TYPE_A and
> > TYPE_C.
> >
> > Please refer to section 6.7.1.2 of the Physical Layer spec v3.01. As
> > per the description, it seemed to me like, since Driver Type C is
> > weaker than Driver Type A, a host with Type A can still drive a card
> of
> > Type C. I might have misunderstood the spec, so please correct me if
> > wrong.
>
> So this is what you are assuming: A > B > C > D. with above check you
> are
> ignoring TYPE_B then. Let's say you check host CAP TYPE_A and if it
> supports
> then you check if card supports TYPE_A or not. If it's not then you are
> directly jumping to TYPE_C. but according to spec TYPE_B is stronger
> than
> TYPE_C. Please check the section: 6.7.1.5 in spec. it says this:
> "whenever a
> stronger driver than type B is needed, driver type A can be selected
> and
> whenever a weaker driver than type B is needed, driver type C can be
> selected "
Thanks for the correction. I will add checking for type B as well.
Arindam
>
> >
> > >
> > > >
> > > > >
> > > > > > + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> > > > > > + host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > > > > > + if (card->sw_caps.uhs_drv_type &
> SD_DRIVER_TYPE_C)
> > > > > > + card_set_drv_type =
> MMC_SET_DRIVER_TYPE_C;
> > > > > > + }
> > > > >
> > > > > Again TYPE_C checking here? Also why are you skiping TYPE_D
> here?
> > > >
> > > > TYPE_C checking for the same reason as above. I could not find a
> > > > scenario where I can put TYPE_D into, so there is not checking.
> > > > Probably you can suggest something?
> > >
> > > Will following simple check is not enough?
> > >
> > > if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) {
> > > host_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > > if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_A)
> > > card_set_drv_type = MMC_SET_DRIVER_TYPE_A;
> > > } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
> > > host_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > > if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_C)
> > > card_set_drv_type = MMC_SET_DRIVER_TYPE_C;
> > > } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_D) {
> > > host_set_drv_type = MMC_SET_DRIVER_TYPE_D;
> > > if (card->sw_caps.uhs_drv_type & SD_DRIVER_TYPE_D)
> > > card_set_drv_type = MMC_SET_DRIVER_TYPE_D;
> > > } else {
> > > return 0; /* default = TYPE_B */
> > > }
> > >
> > >
> > >
> > > >
> > > > >
> > > > > > +
> > > > > > + err = mmc_sd_switch(card, 1, 2, card_set_drv_type,
> status);
> > > > > > + if (err)
> > > > > > + return err;
> > > > > > +
> > > > > > + if ((status[15] & 0xF) != card_set_drv_type)
> > > > > > + printk(KERN_WARNING "%s: Problem setting driver
> > > > > > strength!\n",
> > > > > > + mmc_hostname(card->host));
> > > > > > + else
> > > > > > + mmc_set_driver_type(card->host,
> host_set_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;
> > > > >
> > > > > We can combine both these check under single "if" checking.
> > > >
> > > > The code snippet is a replica of the code already present in
> > > > mmc_sd_switch_hs() and mmc_read_switch(), so I too followed the
> > same
> > > > checking. I can combine them into a single condition if you want.
> > >
> > > Ok. it's up to you in this case. If it looks by having 2 different
> > > statements then it's fine.
> > >
> > > >
> > > > >
> > > > > > +
> > > > > > + err = -EIO;
> > > > >
> > > > > Is this really required? I don't think initializing "err" is
> > needed
> > > > > here.
> > > >
> > > > Again a replica of the functions mentioned above. Let me know if
> > you
> > > > want this removed.
> > >
> > > This is not needed here. You can remove it.
> >
> > Will remove it then. Thanks.
> >
> > >
> > > >
> > > > >
> > > > > > +
> > > > > > + 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;
> > > > > > + }
> > > > >
> > > > > You may want to allocate memory only after bus width setting is
> > > > > successful.
> > > >
> > > > Yes, good suggestion. I will incorporate this change.
> > > >
> > > > >
> > > > > > +
> > > > > > + /* 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);
> > > > >
> > > > > Actually why are you allocating memory for "status" response in
> > > > > mmc_sd_init_uhs_card() function. You need "status" only in
> > > > > sd_select_driver_type() function. So you should be allocating
> > > memory
> > > > in
> > > > > sd_select_driver_type() function only. here you just say "
> > > > > sd_select_driver_type(card);"
> > > >
> > > > Please check the later patches. "status" is used to store return
> > > > statuses from *bus_speed_mode() and *current_limit() as well. So
> I
> > > > think it makes sense to allocate memory for "status" at the
> > beginning
> > > > of the function, and use it later.
> > >
> > > Sorry. I had not checked the later patches. Yes, allocating here is
> > > proper.
> > > As same status is being used for other switch settings as well.
> > >
> > >
> > > >
> > > > >
> > > > > > +
> > > > > > +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],
> > > > > > @@ -474,10 +554,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
> > > > > > @@ -502,7 +582,7 @@ int mmc_sd_get_cid(struct mmc_host *host,
> > u32
> > > > > ocr,
> > > > > > u32 *cid)
> > > > > > MMC_CAP_SET_XPC_180))
> > > > > > ocr |= 1 << 28;
> > > > > >
> > > > > > - err = mmc_send_app_op_cond(host, ocr, &rocr);
> > > > > > + err = mmc_send_app_op_cond(host, ocr, rocr);
> > > > > > if (err)
> > > > > > return err;
> > > > > >
> > > > > > @@ -510,7 +590,7 @@ int mmc_sd_get_cid(struct mmc_host *host,
> > u32
> > > > > ocr,
> > > > > > u32 *cid)
> > > > > > * 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)) {
> > > > > > + if (!mmc_host_is_spi(host) && rocr && (*rocr &
> 0x41000000))
> > {
> > > > >
> > > > > You are not doing NULL check for "rocr" in
> mmc_send_app_op_cond()
> > > but
> > > > > you
> > > > > are doing it here. Is it needed? If "rocr" is NULL then
> > > > > mmc_send_app_op_cond() itself will fail.
> > > >
> > > > I am actually checking rocr for NULL before returning from
> > > > mmc_send_app_op_cond(). Can you please check the function again
> to
> > > make
> > > > sure?
> > > Yes. It's fine. ignore this.
> > >
> > >
> > > >
> > > > > Also, you may need to check b31 (busy bit) of rocr because spec
> > > says
> > > > > this:
> > > > > "Section:4.2.3.1 CCS (Bit 30) and S18A (Bit 24) are valid when
> > Busy
> > > > > (Bit 31)
> > > > > is set to 1."
> > > >
> > > > The busy bit (bit 31) checking is already performed inside
> > > > mmc_send_app_op_cond(), cmd.resp[0] & MMC_CARD_BUSY.
> > >
> > > Ok. fine.
> > >
> > > >
> > > > >
> > > > > > err = mmc_start_voltage_switch(host);
> > > > > > if (err)
> > > > > > return err;
> > > > > > @@ -643,11 +723,12 @@ static int mmc_sd_init_card(struct
> > mmc_host
> > > > > > *host, u32 ocr,
> > > > > > struct mmc_card *card;
> > > > > > int err;
> > > > > > u32 cid[4];
> > > > > > + u32 rocr;
> > > > > >
> > > > > > 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;
> > > > > >
> > > > > > @@ -700,30 +781,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 & 0x01000000) {
> > > > >
> > > > > Instead of 0x01000000, you can use (1 << 24) to be more
> readable.
> > > >
> > > > I am okay with your suggestion. Will change it.
> > > >
> > > > Thanks,
> > > > Arindam
> > > >
> > > > >
> > > > > > + 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 5c4a54d..6d16684 100644
> > > > > > --- a/drivers/mmc/core/sdio.c
> > > > > > +++ b/drivers/mmc/core/sdio.c
> > > > > > @@ -364,7 +364,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 5487a0b..1645687 100644
> > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > @@ -63,7 +63,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));
> > > > > > @@ -216,18 +216,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
> > > > > > @@ -786,14 +786,14 @@ static void sdhci_prepare_data(struct
> > > > > sdhci_host
> > > > > > *host, struct mmc_data *data)
> > > > > > * 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)) {
> > > > > > @@ -1252,7 +1252,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)
> > > > > > @@ -1265,10 +1265,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)
> > > > > > @@ -1277,7 +1277,25 @@ 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.
> > > > > > + */
> > > > > > + 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,
> > > > > > @@ -2037,6 +2055,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 95d70e6..a407b5b 100644
> > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > @@ -72,7 +72,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
> > > > > > @@ -151,6 +151,11 @@
> > > > > >
> > > > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > > > +#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
> > > > > > @@ -174,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 7080f22..2d7f7a3 100644
> > > > > > --- a/include/linux/mmc/card.h
> > > > > > +++ b/include/linux/mmc/card.h
> > > > > > @@ -77,6 +77,10 @@ struct sd_switch_caps {
> > > > > > unsigned int hs_max_dtr;
> > > > > > unsigned int uhs_bus_mode;
> > > > > > unsigned int uhs_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 uhs_curr_limit;
> > > > > > };
> > > > > >
> > > > > > diff --git a/include/linux/mmc/host.h
> > b/include/linux/mmc/host.h
> > > > > > index ad7daa3..bc2121e 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 drv_type; /* driver type
> (A,
> > C,
> > > > D) */
> > > > > > +
> > > > > > +#define MMC_SET_DRIVER_TYPE_A 1
> > > > > > +#define MMC_SET_DRIVER_TYPE_C 2
> > > > >
> > > > > *SET* is not required in name. again why are we ignoring TYPE D
> > > here?
> > > > >
> > > > > > };
> > > > > >
> > > > > > struct mmc_host_ops {
> > > > > > @@ -183,6 +188,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
> > > > > >
> > > > > > --
> > > > > > To unsubscribe from this list: send the line "unsubscribe
> > linux-
> > > > mmc"
> > > > > in
> > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > info.html
> > > > >
> > > >
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 08/12] mmc: sd: report correct speed and capacity of uhs cards
2011-03-04 11:32 ` [PATCH v2 08/12] mmc: sd: report correct speed and capacity of " Arindam Nath
@ 2011-03-10 11:48 ` subhashj
2011-03-10 13:28 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 11:48 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 08/12] mmc: sd: report correct speed and capacity of
> uhs cards
>
> 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 can have SDHC or SDXC UHS-I cards, so we need to decide based on
> C_SIZE field of CSDv2.0 register. According to Physical Layer spec
> v3.01, the minimum value of C_SIZE for SDXC card is 00FFFFh.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/bus.c | 11 ++++++++---
> drivers/mmc/core/sd.c | 10 +++++++++-
> include/linux/mmc/card.h | 7 +++++++
> 3 files changed, 24 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> index 63667a8..ceeefa4 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";
> @@ -298,7 +302,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_card_ultrahighspeed(card) ? "ultra high speed "
:
we should use "sd" prefix before ultrahighspeed. Also can't we use name as
ush instead of ultrahighspeed? What about mmc_card_sd_ush().
Also with the USH, SD card's DDR50 mode also falls under UHS mode. And we
have DDR mode for MMC card as well (mmc_card_ddr_mode()).
> + (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 df98a2c..be01397 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -129,7 +129,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.
> @@ -143,6 +143,7 @@ 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);
>
> m = UNSTUFF_BITS(resp, 48, 22);
> csd->capacity = (1 + m) << 10;
> @@ -922,6 +923,13 @@ 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_card_set_ultrahighspeed(card);
> +
> + /* SDXC cards have a minimum C_SIZE of 0x00FFFF */
> + if (card->csd.c_size >= 0xFFFF)
> + mmc_card_set_ext_capacity(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 a6811ae..61459aa 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -29,6 +29,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 */
> @@ -151,6 +152,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 */
> @@ -193,12 +196,16 @@ struct mmc_card {
> #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_card_ultrahighspeed(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_card_set_ultrahighspeed(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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-04 11:32 ` [PATCH v2 10/12] mmc: sdhci: enable preset value after " Arindam Nath
@ 2011-03-10 13:23 ` subhashj
2011-03-10 13:30 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 13:23 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> initialization
>
> According to the Host Controller spec v3.00, setting Preset Value
> Enable
> in the Host Control2 register lets SDCLK Frequency Select, Clock
> Generator
> Select and Driver Strength Select to be set automatically by the Host
> Controller based on the UHS-I mode set. This patch enables this
> feature.
> We also reset Preset Value Enable when the card is removed from the
> slot.
What does this preset_value means? Is this defined in SD3.01 spec? I think
it looks like specific to your controller (sdhci). If it's specific to your
controller then this new mmc_ops should not be added.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 7 +++++++
> drivers/mmc/host/sdhci.c | 36 ++++++++++++++++++++++++++++++++++++
> 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 1e2d157..ae7a771 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -962,6 +962,13 @@ static int mmc_sd_init_card(struct mmc_host *host,
> u32 ocr,
> }
> }
>
> + /*
> + * Since initialization is now complete, enable preset
> + * value registers.
> + */
> + if (host->ops->enable_preset_value)
> + host->ops->enable_preset_value(host);
> +
> host->card = card;
> return 0;
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 8f4f102..1f6e4ad 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1602,6 +1602,40 @@ out:
> spin_unlock_irqrestore(&host->lock, flags);
> }
>
> +static void sdhci_enable_preset_value(struct mmc_host *mmc)
> +{
> + 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);
> + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> +static void sdhci_disable_preset_value(struct sdhci_host *host)
> +{
> + u16 ctrl;
> +
> + /* Only for Host Controller version >= v3.00 */
> + if (host->version < SDHCI_SPEC_300)
> + return;
> +
> + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +}
> +
> static const struct mmc_host_ops sdhci_ops = {
> .request = sdhci_request,
> .set_ios = sdhci_set_ios,
> @@ -1610,6 +1644,7 @@ static const struct mmc_host_ops sdhci_ops = {
> .start_signal_voltage_switch =
> sdhci_start_signal_voltage_switch,
> .get_max_current_180 = sdhci_get_max_current_180,
> .execute_tuning = sdhci_execute_tuning,
> + .enable_preset_value = sdhci_enable_preset_value,
> };
>
>
> /**********************************************************************
> *******\
> @@ -1920,6 +1955,7 @@ static irqreturn_t sdhci_irq(int irq, void
> *dev_id)
> sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
> SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
> tasklet_schedule(&host->card_tasklet);
> + sdhci_disable_preset_value(host);
> }
>
> intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 651e40b..e63e063 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -130,6 +130,7 @@ struct mmc_host_ops {
> int (*start_signal_voltage_switch)(struct mmc_host *host);
> int (*get_max_current_180)(struct mmc_host *mmc);
> void (*execute_tuning)(struct mmc_host *host);
> + void (*enable_preset_value)(struct mmc_host *host);
> };
>
> struct mmc_card;
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 08/12] mmc: sd: report correct speed and capacity of uhs cards
2011-03-10 11:48 ` subhashj
@ 2011-03-10 13:28 ` Nath, Arindam
2011-03-10 13:41 ` subhashj
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 13:28 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 5:18 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 08/12] mmc: sd: report correct speed and
> capacity of uhs cards
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 08/12] mmc: sd: report correct speed and capacity
> of
> > uhs cards
> >
> > 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 can have SDHC or SDXC UHS-I cards, so we need to decide based on
> > C_SIZE field of CSDv2.0 register. According to Physical Layer spec
> > v3.01, the minimum value of C_SIZE for SDXC card is 00FFFFh.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/bus.c | 11 ++++++++---
> > drivers/mmc/core/sd.c | 10 +++++++++-
> > include/linux/mmc/card.h | 7 +++++++
> > 3 files changed, 24 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> > index 63667a8..ceeefa4 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";
> > @@ -298,7 +302,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_card_ultrahighspeed(card) ? "ultra high speed "
> :
>
> we should use "sd" prefix before ultrahighspeed. Also can't we use name
> as
> ush instead of ultrahighspeed? What about mmc_card_sd_ush().
> Also with the USH, SD card's DDR50 mode also falls under UHS mode. And
> we
> have DDR mode for MMC card as well (mmc_card_ddr_mode()).
I wanted the name to be similar as *_highspeed(), so *_ultrahighspeed(). How about mmc_sd_card_ultrahighspeed() or mmc_sd_card_uhs()?
Thanks,
Arindam
>
> > + (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 df98a2c..be01397 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -129,7 +129,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.
> > @@ -143,6 +143,7 @@ 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);
> >
> > m = UNSTUFF_BITS(resp, 48, 22);
> > csd->capacity = (1 + m) << 10;
> > @@ -922,6 +923,13 @@ 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_card_set_ultrahighspeed(card);
> > +
> > + /* SDXC cards have a minimum C_SIZE of 0x00FFFF */
> > + if (card->csd.c_size >= 0xFFFF)
> > + mmc_card_set_ext_capacity(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 a6811ae..61459aa 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -29,6 +29,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 */
> > @@ -151,6 +152,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 */
> > @@ -193,12 +196,16 @@ struct mmc_card {
> > #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_card_ultrahighspeed(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_card_set_ultrahighspeed(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
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-10 13:23 ` subhashj
@ 2011-03-10 13:30 ` Nath, Arindam
2011-03-10 13:45 ` subhashj
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 13:30 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 6:54 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> initialization
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> > initialization
> >
> > According to the Host Controller spec v3.00, setting Preset Value
> > Enable
> > in the Host Control2 register lets SDCLK Frequency Select, Clock
> > Generator
> > Select and Driver Strength Select to be set automatically by the Host
> > Controller based on the UHS-I mode set. This patch enables this
> > feature.
> > We also reset Preset Value Enable when the card is removed from the
> > slot.
>
> What does this preset_value means? Is this defined in SD3.01 spec? I
> think
> it looks like specific to your controller (sdhci). If it's specific to
> your
> controller then this new mmc_ops should not be added.
Preset Value Enable is very much part of the standard SD Host Controller spec v3.00. Please refer to Host Control 2 register.
Thanks,
Arindam
>
>
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 7 +++++++
> > drivers/mmc/host/sdhci.c | 36 ++++++++++++++++++++++++++++++++++++
> > 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 1e2d157..ae7a771 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -962,6 +962,13 @@ static int mmc_sd_init_card(struct mmc_host
> *host,
> > u32 ocr,
> > }
> > }
> >
> > + /*
> > + * Since initialization is now complete, enable preset
> > + * value registers.
> > + */
> > + if (host->ops->enable_preset_value)
> > + host->ops->enable_preset_value(host);
> > +
> > host->card = card;
> > return 0;
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 8f4f102..1f6e4ad 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -1602,6 +1602,40 @@ out:
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> >
> > +static void sdhci_enable_preset_value(struct mmc_host *mmc)
> > +{
> > + 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);
> > + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +}
> > +
> > +static void sdhci_disable_preset_value(struct sdhci_host *host)
> > +{
> > + u16 ctrl;
> > +
> > + /* Only for Host Controller version >= v3.00 */
> > + if (host->version < SDHCI_SPEC_300)
> > + return;
> > +
> > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +}
> > +
> > static const struct mmc_host_ops sdhci_ops = {
> > .request = sdhci_request,
> > .set_ios = sdhci_set_ios,
> > @@ -1610,6 +1644,7 @@ static const struct mmc_host_ops sdhci_ops = {
> > .start_signal_voltage_switch =
> > sdhci_start_signal_voltage_switch,
> > .get_max_current_180 = sdhci_get_max_current_180,
> > .execute_tuning = sdhci_execute_tuning,
> > + .enable_preset_value = sdhci_enable_preset_value,
> > };
> >
> >
> >
> /**********************************************************************
> > *******\
> > @@ -1920,6 +1955,7 @@ static irqreturn_t sdhci_irq(int irq, void
> > *dev_id)
> > sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
> > SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
> > tasklet_schedule(&host->card_tasklet);
> > + sdhci_disable_preset_value(host);
> > }
> >
> > intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
> > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > index 651e40b..e63e063 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -130,6 +130,7 @@ struct mmc_host_ops {
> > int (*start_signal_voltage_switch)(struct mmc_host *host);
> > int (*get_max_current_180)(struct mmc_host *mmc);
> > void (*execute_tuning)(struct mmc_host *host);
> > + void (*enable_preset_value)(struct mmc_host *host);
> > };
> >
> > struct mmc_card;
> > --
> > 1.7.1
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 08/12] mmc: sd: report correct speed and capacity of uhs cards
2011-03-10 13:28 ` Nath, Arindam
@ 2011-03-10 13:41 ` subhashj
0 siblings, 0 replies; 125+ messages in thread
From: subhashj @ 2011-03-10 13:41 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Thursday, March 10, 2011 6:58 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 08/12] mmc: sd: report correct speed and
> capacity of uhs cards
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Thursday, March 10, 2011 5:18 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 08/12] mmc: sd: report correct speed and
> > capacity of uhs cards
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 08/12] mmc: sd: report correct speed and
> capacity
> > of
> > > uhs cards
> > >
> > > 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 can have SDHC or SDXC UHS-I cards, so we need to decide based on
> > > C_SIZE field of CSDv2.0 register. According to Physical Layer spec
> > > v3.01, the minimum value of C_SIZE for SDXC card is 00FFFFh.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/bus.c | 11 ++++++++---
> > > drivers/mmc/core/sd.c | 10 +++++++++-
> > > include/linux/mmc/card.h | 7 +++++++
> > > 3 files changed, 24 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> > > index 63667a8..ceeefa4 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";
> > > @@ -298,7 +302,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_card_ultrahighspeed(card) ? "ultra high speed "
> > :
> >
> > we should use "sd" prefix before ultrahighspeed. Also can't we use
> name
> > as
> > ush instead of ultrahighspeed? What about mmc_card_sd_ush().
> > Also with the USH, SD card's DDR50 mode also falls under UHS mode.
> And
> > we
> > have DDR mode for MMC card as well (mmc_card_ddr_mode()).
>
> I wanted the name to be similar as *_highspeed(), so
> *_ultrahighspeed(). How about mmc_sd_card_ultrahighspeed() or
> mmc_sd_card_uhs()?
Yes, mmc_sd_card_uhs() looks fine.
>
> Thanks,
> Arindam
>
> >
> > > + (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 df98a2c..be01397 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -129,7 +129,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.
> > > @@ -143,6 +143,7 @@ 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);
> > >
> > > m = UNSTUFF_BITS(resp, 48, 22);
> > > csd->capacity = (1 + m) << 10;
> > > @@ -922,6 +923,13 @@ 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_card_set_ultrahighspeed(card);
> > > +
> > > + /* SDXC cards have a minimum C_SIZE of 0x00FFFF */
> > > + if (card->csd.c_size >= 0xFFFF)
> > > + mmc_card_set_ext_capacity(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 a6811ae..61459aa 100644
> > > --- a/include/linux/mmc/card.h
> > > +++ b/include/linux/mmc/card.h
> > > @@ -29,6 +29,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 */
> > > @@ -151,6 +152,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 */
> > > @@ -193,12 +196,16 @@ struct mmc_card {
> > > #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_card_ultrahighspeed(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_card_set_ultrahighspeed(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
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-10 13:30 ` Nath, Arindam
@ 2011-03-10 13:45 ` subhashj
2011-03-10 13:49 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 13:45 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Thursday, March 10, 2011 7:01 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> initialization
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Thursday, March 10, 2011 6:54 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> uhs
> > initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> > > initialization
> > >
> > > According to the Host Controller spec v3.00, setting Preset Value
> > > Enable
> > > in the Host Control2 register lets SDCLK Frequency Select, Clock
> > > Generator
> > > Select and Driver Strength Select to be set automatically by the
> Host
> > > Controller based on the UHS-I mode set. This patch enables this
> > > feature.
> > > We also reset Preset Value Enable when the card is removed from the
> > > slot.
> >
> > What does this preset_value means? Is this defined in SD3.01 spec? I
> > think
> > it looks like specific to your controller (sdhci). If it's specific
> to
> > your
> > controller then this new mmc_ops should not be added.
>
> Preset Value Enable is very much part of the standard SD Host
> Controller spec v3.00. Please refer to Host Control 2 register.
I don't think all controllers should be compliant to SD host controller
spec. I don't think controller on our chips has something like this.
>
> Thanks,
> Arindam
>
> >
> >
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/sd.c | 7 +++++++
> > > drivers/mmc/host/sdhci.c | 36
> ++++++++++++++++++++++++++++++++++++
> > > 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 1e2d157..ae7a771 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -962,6 +962,13 @@ static int mmc_sd_init_card(struct mmc_host
> > *host,
> > > u32 ocr,
> > > }
> > > }
> > >
> > > + /*
> > > + * Since initialization is now complete, enable preset
> > > + * value registers.
> > > + */
> > > + if (host->ops->enable_preset_value)
> > > + host->ops->enable_preset_value(host);
> > > +
> > > host->card = card;
> > > return 0;
> > >
> > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > index 8f4f102..1f6e4ad 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -1602,6 +1602,40 @@ out:
> > > spin_unlock_irqrestore(&host->lock, flags);
> > > }
> > >
> > > +static void sdhci_enable_preset_value(struct mmc_host *mmc)
> > > +{
> > > + 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);
> > > + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > +
> > > + spin_unlock_irqrestore(&host->lock, flags);
> > > +}
> > > +
> > > +static void sdhci_disable_preset_value(struct sdhci_host *host)
> > > +{
> > > + u16 ctrl;
> > > +
> > > + /* Only for Host Controller version >= v3.00 */
> > > + if (host->version < SDHCI_SPEC_300)
> > > + return;
> > > +
> > > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > +}
> > > +
> > > static const struct mmc_host_ops sdhci_ops = {
> > > .request = sdhci_request,
> > > .set_ios = sdhci_set_ios,
> > > @@ -1610,6 +1644,7 @@ static const struct mmc_host_ops sdhci_ops =
> {
> > > .start_signal_voltage_switch =
> > > sdhci_start_signal_voltage_switch,
> > > .get_max_current_180 = sdhci_get_max_current_180,
> > > .execute_tuning = sdhci_execute_tuning,
> > > + .enable_preset_value = sdhci_enable_preset_value,
> > > };
> > >
> > >
> > >
> >
> /**********************************************************************
> > > *******\
> > > @@ -1920,6 +1955,7 @@ static irqreturn_t sdhci_irq(int irq, void
> > > *dev_id)
> > > sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
> > > SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
> > > tasklet_schedule(&host->card_tasklet);
> > > + sdhci_disable_preset_value(host);
> > > }
> > >
> > > intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
> > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > > index 651e40b..e63e063 100644
> > > --- a/include/linux/mmc/host.h
> > > +++ b/include/linux/mmc/host.h
> > > @@ -130,6 +130,7 @@ struct mmc_host_ops {
> > > int (*start_signal_voltage_switch)(struct mmc_host *host);
> > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > void (*execute_tuning)(struct mmc_host *host);
> > > + void (*enable_preset_value)(struct mmc_host *host);
> > > };
> > >
> > > struct mmc_card;
> > > --
> > > 1.7.1
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-10 13:45 ` subhashj
@ 2011-03-10 13:49 ` Nath, Arindam
2011-03-10 14:03 ` subhashj
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 13:49 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 7:15 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> initialization
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Thursday, March 10, 2011 7:01 PM
> > To: subhashj@codeaurora.org; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> uhs
> > initialization
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > Sent: Thursday, March 10, 2011 6:54 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> > uhs
> > > initialization
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > To: cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > henry.su@amd.com;
> > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > Subject: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> uhs
> > > > initialization
> > > >
> > > > According to the Host Controller spec v3.00, setting Preset Value
> > > > Enable
> > > > in the Host Control2 register lets SDCLK Frequency Select, Clock
> > > > Generator
> > > > Select and Driver Strength Select to be set automatically by the
> > Host
> > > > Controller based on the UHS-I mode set. This patch enables this
> > > > feature.
> > > > We also reset Preset Value Enable when the card is removed from
> the
> > > > slot.
> > >
> > > What does this preset_value means? Is this defined in SD3.01 spec?
> I
> > > think
> > > it looks like specific to your controller (sdhci). If it's specific
> > to
> > > your
> > > controller then this new mmc_ops should not be added.
> >
> > Preset Value Enable is very much part of the standard SD Host
> > Controller spec v3.00. Please refer to Host Control 2 register.
>
> I don't think all controllers should be compliant to SD host controller
> spec. I don't think controller on our chips has something like this.
But from the spec it looks as if controllers conforming to v3.00 should provide this feature. There is no other bit anywhere else in the spec which can indicate support for Preset Value Enable except for the controller version number.
Thanks,
Arindam
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > >
> > > >
> > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > ---
> > > > drivers/mmc/core/sd.c | 7 +++++++
> > > > drivers/mmc/host/sdhci.c | 36
> > ++++++++++++++++++++++++++++++++++++
> > > > 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 1e2d157..ae7a771 100644
> > > > --- a/drivers/mmc/core/sd.c
> > > > +++ b/drivers/mmc/core/sd.c
> > > > @@ -962,6 +962,13 @@ static int mmc_sd_init_card(struct mmc_host
> > > *host,
> > > > u32 ocr,
> > > > }
> > > > }
> > > >
> > > > + /*
> > > > + * Since initialization is now complete, enable preset
> > > > + * value registers.
> > > > + */
> > > > + if (host->ops->enable_preset_value)
> > > > + host->ops->enable_preset_value(host);
> > > > +
> > > > host->card = card;
> > > > return 0;
> > > >
> > > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > > index 8f4f102..1f6e4ad 100644
> > > > --- a/drivers/mmc/host/sdhci.c
> > > > +++ b/drivers/mmc/host/sdhci.c
> > > > @@ -1602,6 +1602,40 @@ out:
> > > > spin_unlock_irqrestore(&host->lock, flags);
> > > > }
> > > >
> > > > +static void sdhci_enable_preset_value(struct mmc_host *mmc)
> > > > +{
> > > > + 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);
> > > > + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > +
> > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > +}
> > > > +
> > > > +static void sdhci_disable_preset_value(struct sdhci_host *host)
> > > > +{
> > > > + u16 ctrl;
> > > > +
> > > > + /* Only for Host Controller version >= v3.00 */
> > > > + if (host->version < SDHCI_SPEC_300)
> > > > + return;
> > > > +
> > > > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > +}
> > > > +
> > > > static const struct mmc_host_ops sdhci_ops = {
> > > > .request = sdhci_request,
> > > > .set_ios = sdhci_set_ios,
> > > > @@ -1610,6 +1644,7 @@ static const struct mmc_host_ops sdhci_ops
> =
> > {
> > > > .start_signal_voltage_switch =
> > > > sdhci_start_signal_voltage_switch,
> > > > .get_max_current_180 = sdhci_get_max_current_180,
> > > > .execute_tuning = sdhci_execute_tuning,
> > > > + .enable_preset_value = sdhci_enable_preset_value,
> > > > };
> > > >
> > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > *******\
> > > > @@ -1920,6 +1955,7 @@ static irqreturn_t sdhci_irq(int irq, void
> > > > *dev_id)
> > > > sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
> > > > SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
> > > > tasklet_schedule(&host->card_tasklet);
> > > > + sdhci_disable_preset_value(host);
> > > > }
> > > >
> > > > intmask &= ~(SDHCI_INT_CARD_INSERT |
> SDHCI_INT_CARD_REMOVE);
> > > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > > > index 651e40b..e63e063 100644
> > > > --- a/include/linux/mmc/host.h
> > > > +++ b/include/linux/mmc/host.h
> > > > @@ -130,6 +130,7 @@ struct mmc_host_ops {
> > > > int (*start_signal_voltage_switch)(struct mmc_host
> *host);
> > > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > > void (*execute_tuning)(struct mmc_host *host);
> > > > + void (*enable_preset_value)(struct mmc_host *host);
> > > > };
> > > >
> > > > struct mmc_card;
> > > > --
> > > > 1.7.1
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 12/12] mmc: sdhci: add support for retuning mode 1
2011-03-04 11:32 ` [PATCH v2 12/12] mmc: sdhci: add support for retuning mode 1 Arindam Nath
@ 2011-03-10 13:57 ` subhashj
2011-03-10 14:00 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 13:57 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 12/12] mmc: sdhci: add support for retuning mode 1
>
> 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 after successfull UHS-I initialization. Since
> retuning mode 1 sets a limit of 4MB on the maximum data length, we set
> max_blk_count appropriately. Once the tuning timer expires, we set
> SDHCI_NEEDS_RETUNING flag, and if the flag is set, we execute tuning
> procedure before sending the next command. We need to restore
> mmc_request
> structure after executing retuning procedure since host->mrq is used
> inside the procedure to send CMD19. We also disable and re-enable this
> flag during suspend and resume respectively, as per the spec v3.00.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 5 ++
> drivers/mmc/host/sdhci.c | 109
> ++++++++++++++++++++++++++++++++++++++++++++-
> drivers/mmc/host/sdhci.h | 6 ++-
> include/linux/mmc/host.h | 1 +
> include/linux/mmc/sdhci.h | 6 +++
> 5 files changed, 124 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index ae7a771..1f3cf57 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -641,6 +641,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
> if (!mmc_host_is_spi(card->host) && card->host->ops-
> >execute_tuning)
> card->host->ops->execute_tuning(card->host);
>
> + /* Initialize and start re-tuning timer */
> + if (!mmc_host_is_spi(card->host) &&
> + card->host->ops->start_retuning_timer)
> + card->host->ops->start_retuning_timer(card->host);
Why you need extra mmc_ops for this? You can start the retuning timer when
first time your driver's "execute_tuning" ops gets executed.
> +
> out:
> kfree(status);
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 2ffb0c4..8bf0408 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -48,6 +48,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 void sdhci_execute_tuning(struct mmc_host *mmc);
> +static void sdhci_tuning_timer(unsigned long data);
>
> static void sdhci_dumpregs(struct sdhci_host *host)
> {
> @@ -1240,8 +1242,34 @@ 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))) {
> + 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);
> +
> + 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);
> @@ -1667,6 +1695,26 @@ static void sdhci_disable_preset_value(struct
> sdhci_host *host)
> sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> }
>
> +static void sdhci_start_retuning_timer(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + unsigned long flags;
> +
> + host = mmc_priv(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (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;
> + }
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> static const struct mmc_host_ops sdhci_ops = {
> .request = sdhci_request,
> .set_ios = sdhci_set_ios,
> @@ -1676,6 +1724,7 @@ static const struct mmc_host_ops sdhci_ops = {
> .get_max_current_180 = sdhci_get_max_current_180,
> .execute_tuning = sdhci_execute_tuning,
> .enable_preset_value = sdhci_enable_preset_value,
> + .start_retuning_timer = sdhci_start_retuning_timer,
> };
>
>
> /**********************************************************************
> *******\
> @@ -1725,6 +1774,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;
>
> /*
> @@ -1799,6 +1852,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
> *
> @@ -2057,6 +2124,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;
> @@ -2098,6 +2174,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;
> }
>
> @@ -2353,6 +2435,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
> @@ -2488,9 +2585,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)
> @@ -2584,6 +2687,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 37a8c32..53d5909 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -190,7 +190,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/host.h b/include/linux/mmc/host.h
> index e63e063..09f5d03 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -131,6 +131,7 @@ struct mmc_host_ops {
> int (*get_max_current_180)(struct mmc_host *mmc);
> void (*execute_tuning)(struct mmc_host *host);
> void (*enable_preset_value)(struct mmc_host *host);
> + void (*start_retuning_timer)(struct mmc_host *host);
> };
>
> struct mmc_card;
> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> index 4be4022..c6539be 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -110,6 +110,7 @@ struct sdhci_host {
> #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> #define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
> +#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
>
> unsigned int version; /* SDHCI spec. version */
>
> @@ -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] 125+ messages in thread
* RE: [PATCH v2 12/12] mmc: sdhci: add support for retuning mode 1
2011-03-10 13:57 ` subhashj
@ 2011-03-10 14:00 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 14:00 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 7:28 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 12/12] mmc: sdhci: add support for retuning mode
> 1
>
>
>
> > -----Original Message-----
> > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> > Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 12/12] mmc: sdhci: add support for retuning mode 1
> >
> > 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 after successfull UHS-I initialization. Since
> > retuning mode 1 sets a limit of 4MB on the maximum data length, we
> set
> > max_blk_count appropriately. Once the tuning timer expires, we set
> > SDHCI_NEEDS_RETUNING flag, and if the flag is set, we execute tuning
> > procedure before sending the next command. We need to restore
> > mmc_request
> > structure after executing retuning procedure since host->mrq is used
> > inside the procedure to send CMD19. We also disable and re-enable
> this
> > flag during suspend and resume respectively, as per the spec v3.00.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 5 ++
> > drivers/mmc/host/sdhci.c | 109
> > ++++++++++++++++++++++++++++++++++++++++++++-
> > drivers/mmc/host/sdhci.h | 6 ++-
> > include/linux/mmc/host.h | 1 +
> > include/linux/mmc/sdhci.h | 6 +++
> > 5 files changed, 124 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index ae7a771..1f3cf57 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -641,6 +641,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> > *card)
> > if (!mmc_host_is_spi(card->host) && card->host->ops-
> > >execute_tuning)
> > card->host->ops->execute_tuning(card->host);
> >
> > + /* Initialize and start re-tuning timer */
> > + if (!mmc_host_is_spi(card->host) &&
> > + card->host->ops->start_retuning_timer)
> > + card->host->ops->start_retuning_timer(card->host);
>
> Why you need extra mmc_ops for this? You can start the retuning timer
> when
> first time your driver's "execute_tuning" ops gets executed.
Thanks for the comment. I agree with you. Will change it for next version.
Arindam
>
> > +
> > out:
> > kfree(status);
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 2ffb0c4..8bf0408 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -48,6 +48,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 void sdhci_execute_tuning(struct mmc_host *mmc);
> > +static void sdhci_tuning_timer(unsigned long data);
> >
> > static void sdhci_dumpregs(struct sdhci_host *host)
> > {
> > @@ -1240,8 +1242,34 @@ 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))) {
> > + 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);
> > +
> > + 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);
> > @@ -1667,6 +1695,26 @@ static void sdhci_disable_preset_value(struct
> > sdhci_host *host)
> > sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > }
> >
> > +static void sdhci_start_retuning_timer(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + unsigned long flags;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + if (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;
> > + }
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +}
> > +
> > static const struct mmc_host_ops sdhci_ops = {
> > .request = sdhci_request,
> > .set_ios = sdhci_set_ios,
> > @@ -1676,6 +1724,7 @@ static const struct mmc_host_ops sdhci_ops = {
> > .get_max_current_180 = sdhci_get_max_current_180,
> > .execute_tuning = sdhci_execute_tuning,
> > .enable_preset_value = sdhci_enable_preset_value,
> > + .start_retuning_timer = sdhci_start_retuning_timer,
> > };
> >
> >
> >
> /**********************************************************************
> > *******\
> > @@ -1725,6 +1774,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;
> >
> > /*
> > @@ -1799,6 +1852,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
> > *
> > @@ -2057,6 +2124,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;
> > @@ -2098,6 +2174,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;
> > }
> >
> > @@ -2353,6 +2435,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
> > @@ -2488,9 +2585,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)
> > @@ -2584,6 +2687,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 37a8c32..53d5909 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -190,7 +190,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/host.h b/include/linux/mmc/host.h
> > index e63e063..09f5d03 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -131,6 +131,7 @@ struct mmc_host_ops {
> > int (*get_max_current_180)(struct mmc_host *mmc);
> > void (*execute_tuning)(struct mmc_host *host);
> > void (*enable_preset_value)(struct mmc_host *host);
> > + void (*start_retuning_timer)(struct mmc_host *host);
> > };
> >
> > struct mmc_card;
> > diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> > index 4be4022..c6539be 100644
> > --- a/include/linux/mmc/sdhci.h
> > +++ b/include/linux/mmc/sdhci.h
> > @@ -110,6 +110,7 @@ struct sdhci_host {
> > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> > #define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning
> */
> > +#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning
> */
> >
> > unsigned int version; /* SDHCI spec. version */
> >
> > @@ -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] 125+ messages in thread
* RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-10 13:49 ` Nath, Arindam
@ 2011-03-10 14:03 ` subhashj
2011-03-10 14:07 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 14:03 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Thursday, March 10, 2011 7:20 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> initialization
>
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Thursday, March 10, 2011 7:15 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> uhs
> > initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > Sent: Thursday, March 10, 2011 7:01 PM
> > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> > uhs
> > > initialization
> > >
> > > Hi Subhash,
> > >
> > >
> > > > -----Original Message-----
> > > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > > Sent: Thursday, March 10, 2011 6:54 PM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> after
> > > uhs
> > > > initialization
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > To: cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > henry.su@amd.com;
> > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > Subject: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> > uhs
> > > > > initialization
> > > > >
> > > > > According to the Host Controller spec v3.00, setting Preset
> Value
> > > > > Enable
> > > > > in the Host Control2 register lets SDCLK Frequency Select,
> Clock
> > > > > Generator
> > > > > Select and Driver Strength Select to be set automatically by
> the
> > > Host
> > > > > Controller based on the UHS-I mode set. This patch enables this
> > > > > feature.
> > > > > We also reset Preset Value Enable when the card is removed from
> > the
> > > > > slot.
> > > >
> > > > What does this preset_value means? Is this defined in SD3.01
> spec?
> > I
> > > > think
> > > > it looks like specific to your controller (sdhci). If it's
> specific
> > > to
> > > > your
> > > > controller then this new mmc_ops should not be added.
> > >
> > > Preset Value Enable is very much part of the standard SD Host
> > > Controller spec v3.00. Please refer to Host Control 2 register.
> >
> > I don't think all controllers should be compliant to SD host
> controller
> > spec. I don't think controller on our chips has something like this.
>
> But from the spec it looks as if controllers conforming to v3.00 should
> provide this feature. There is no other bit anywhere else in the spec
> which can indicate support for Preset Value Enable except for the
> controller version number.
Actually, I am not aware about this controller spec v3.00. can you please
point me to the link for this spec (if it's available)?
>
> Thanks,
> Arindam
>
> >
> > >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > >
> > > > >
> > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > ---
> > > > > drivers/mmc/core/sd.c | 7 +++++++
> > > > > drivers/mmc/host/sdhci.c | 36
> > > ++++++++++++++++++++++++++++++++++++
> > > > > 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 1e2d157..ae7a771 100644
> > > > > --- a/drivers/mmc/core/sd.c
> > > > > +++ b/drivers/mmc/core/sd.c
> > > > > @@ -962,6 +962,13 @@ static int mmc_sd_init_card(struct
> mmc_host
> > > > *host,
> > > > > u32 ocr,
> > > > > }
> > > > > }
> > > > >
> > > > > + /*
> > > > > + * Since initialization is now complete, enable preset
> > > > > + * value registers.
> > > > > + */
> > > > > + if (host->ops->enable_preset_value)
> > > > > + host->ops->enable_preset_value(host);
> > > > > +
> > > > > host->card = card;
> > > > > return 0;
> > > > >
> > > > > diff --git a/drivers/mmc/host/sdhci.c
> b/drivers/mmc/host/sdhci.c
> > > > > index 8f4f102..1f6e4ad 100644
> > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > @@ -1602,6 +1602,40 @@ out:
> > > > > spin_unlock_irqrestore(&host->lock, flags);
> > > > > }
> > > > >
> > > > > +static void sdhci_enable_preset_value(struct mmc_host *mmc)
> > > > > +{
> > > > > + 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);
> > > > > + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > +
> > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > +}
> > > > > +
> > > > > +static void sdhci_disable_preset_value(struct sdhci_host
> *host)
> > > > > +{
> > > > > + u16 ctrl;
> > > > > +
> > > > > + /* Only for Host Controller version >= v3.00 */
> > > > > + if (host->version < SDHCI_SPEC_300)
> > > > > + return;
> > > > > +
> > > > > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > > + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > +}
> > > > > +
> > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > .request = sdhci_request,
> > > > > .set_ios = sdhci_set_ios,
> > > > > @@ -1610,6 +1644,7 @@ static const struct mmc_host_ops
> sdhci_ops
> > =
> > > {
> > > > > .start_signal_voltage_switch =
> > > > > sdhci_start_signal_voltage_switch,
> > > > > .get_max_current_180 = sdhci_get_max_current_180,
> > > > > .execute_tuning = sdhci_execute_tuning,
> > > > > + .enable_preset_value = sdhci_enable_preset_value,
> > > > > };
> > > > >
> > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > *******\
> > > > > @@ -1920,6 +1955,7 @@ static irqreturn_t sdhci_irq(int irq,
> void
> > > > > *dev_id)
> > > > > sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT
|
> > > > > SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
> > > > > tasklet_schedule(&host->card_tasklet);
> > > > > + sdhci_disable_preset_value(host);
> > > > > }
> > > > >
> > > > > intmask &= ~(SDHCI_INT_CARD_INSERT |
> > SDHCI_INT_CARD_REMOVE);
> > > > > diff --git a/include/linux/mmc/host.h
> b/include/linux/mmc/host.h
> > > > > index 651e40b..e63e063 100644
> > > > > --- a/include/linux/mmc/host.h
> > > > > +++ b/include/linux/mmc/host.h
> > > > > @@ -130,6 +130,7 @@ struct mmc_host_ops {
> > > > > int (*start_signal_voltage_switch)(struct mmc_host
> > *host);
> > > > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > > > void (*execute_tuning)(struct mmc_host *host);
> > > > > + void (*enable_preset_value)(struct mmc_host *host);
> > > > > };
> > > > >
> > > > > struct mmc_card;
> > > > > --
> > > > > 1.7.1
> > > > >
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe
> linux-
> > > mmc"
> > > > in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > info.html
> > > >
> > >
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-10 14:03 ` subhashj
@ 2011-03-10 14:07 ` Nath, Arindam
2011-03-10 14:12 ` subhashj
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 14:07 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 7:34 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> initialization
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Thursday, March 10, 2011 7:20 PM
> > To: subhashj@codeaurora.org; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> uhs
> > initialization
> >
> > > -----Original Message-----
> > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > Sent: Thursday, March 10, 2011 7:15 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> > uhs
> > > initialization
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > Sent: Thursday, March 10, 2011 7:01 PM
> > > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> after
> > > uhs
> > > > initialization
> > > >
> > > > Hi Subhash,
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > > > Sent: Thursday, March 10, 2011 6:54 PM
> > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > after
> > > > uhs
> > > > > initialization
> > > > >
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > To: cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > henry.su@amd.com;
> > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > Subject: [PATCH v2 10/12] mmc: sdhci: enable preset value
> after
> > > uhs
> > > > > > initialization
> > > > > >
> > > > > > According to the Host Controller spec v3.00, setting Preset
> > Value
> > > > > > Enable
> > > > > > in the Host Control2 register lets SDCLK Frequency Select,
> > Clock
> > > > > > Generator
> > > > > > Select and Driver Strength Select to be set automatically by
> > the
> > > > Host
> > > > > > Controller based on the UHS-I mode set. This patch enables
> this
> > > > > > feature.
> > > > > > We also reset Preset Value Enable when the card is removed
> from
> > > the
> > > > > > slot.
> > > > >
> > > > > What does this preset_value means? Is this defined in SD3.01
> > spec?
> > > I
> > > > > think
> > > > > it looks like specific to your controller (sdhci). If it's
> > specific
> > > > to
> > > > > your
> > > > > controller then this new mmc_ops should not be added.
> > > >
> > > > Preset Value Enable is very much part of the standard SD Host
> > > > Controller spec v3.00. Please refer to Host Control 2 register.
> > >
> > > I don't think all controllers should be compliant to SD host
> > controller
> > > spec. I don't think controller on our chips has something like
> this.
> >
> > But from the spec it looks as if controllers conforming to v3.00
> should
> > provide this feature. There is no other bit anywhere else in the spec
> > which can indicate support for Preset Value Enable except for the
> > controller version number.
>
> Actually, I am not aware about this controller spec v3.00. can you
> please
> point me to the link for this spec (if it's available)?
The spec is named "SD Host Controller Standard Specification Ver3.00". I think it can be downloaded from sdcard.org, but you will need to pay for the license.
Thanks,
Arindam
>
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > > >
> > > > Thanks,
> > > > Arindam
> > > >
> > > > >
> > > > >
> > > > > >
> > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > ---
> > > > > > drivers/mmc/core/sd.c | 7 +++++++
> > > > > > drivers/mmc/host/sdhci.c | 36
> > > > ++++++++++++++++++++++++++++++++++++
> > > > > > 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 1e2d157..ae7a771 100644
> > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > @@ -962,6 +962,13 @@ static int mmc_sd_init_card(struct
> > mmc_host
> > > > > *host,
> > > > > > u32 ocr,
> > > > > > }
> > > > > > }
> > > > > >
> > > > > > + /*
> > > > > > + * Since initialization is now complete, enable preset
> > > > > > + * value registers.
> > > > > > + */
> > > > > > + if (host->ops->enable_preset_value)
> > > > > > + host->ops->enable_preset_value(host);
> > > > > > +
> > > > > > host->card = card;
> > > > > > return 0;
> > > > > >
> > > > > > diff --git a/drivers/mmc/host/sdhci.c
> > b/drivers/mmc/host/sdhci.c
> > > > > > index 8f4f102..1f6e4ad 100644
> > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > @@ -1602,6 +1602,40 @@ out:
> > > > > > spin_unlock_irqrestore(&host->lock, flags);
> > > > > > }
> > > > > >
> > > > > > +static void sdhci_enable_preset_value(struct mmc_host *mmc)
> > > > > > +{
> > > > > > + 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);
> > > > > > + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > +
> > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > +}
> > > > > > +
> > > > > > +static void sdhci_disable_preset_value(struct sdhci_host
> > *host)
> > > > > > +{
> > > > > > + u16 ctrl;
> > > > > > +
> > > > > > + /* Only for Host Controller version >= v3.00 */
> > > > > > + if (host->version < SDHCI_SPEC_300)
> > > > > > + return;
> > > > > > +
> > > > > > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > > > + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > +}
> > > > > > +
> > > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > > .request = sdhci_request,
> > > > > > .set_ios = sdhci_set_ios,
> > > > > > @@ -1610,6 +1644,7 @@ static const struct mmc_host_ops
> > sdhci_ops
> > > =
> > > > {
> > > > > > .start_signal_voltage_switch =
> > > > > > sdhci_start_signal_voltage_switch,
> > > > > > .get_max_current_180 = sdhci_get_max_current_180,
> > > > > > .execute_tuning = sdhci_execute_tuning,
> > > > > > + .enable_preset_value = sdhci_enable_preset_value,
> > > > > > };
> > > > > >
> > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > > *******\
> > > > > > @@ -1920,6 +1955,7 @@ static irqreturn_t sdhci_irq(int irq,
> > void
> > > > > > *dev_id)
> > > > > > sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT
> |
> > > > > > SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
> > > > > > tasklet_schedule(&host->card_tasklet);
> > > > > > + sdhci_disable_preset_value(host);
> > > > > > }
> > > > > >
> > > > > > intmask &= ~(SDHCI_INT_CARD_INSERT |
> > > SDHCI_INT_CARD_REMOVE);
> > > > > > diff --git a/include/linux/mmc/host.h
> > b/include/linux/mmc/host.h
> > > > > > index 651e40b..e63e063 100644
> > > > > > --- a/include/linux/mmc/host.h
> > > > > > +++ b/include/linux/mmc/host.h
> > > > > > @@ -130,6 +130,7 @@ struct mmc_host_ops {
> > > > > > int (*start_signal_voltage_switch)(struct mmc_host
> > > *host);
> > > > > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > > > > void (*execute_tuning)(struct mmc_host *host);
> > > > > > + void (*enable_preset_value)(struct mmc_host *host);
> > > > > > };
> > > > > >
> > > > > > struct mmc_card;
> > > > > > --
> > > > > > 1.7.1
> > > > > >
> > > > > > --
> > > > > > To unsubscribe from this list: send the line "unsubscribe
> > linux-
> > > > mmc"
> > > > > in
> > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > info.html
> > > > >
> > > >
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-10 14:07 ` Nath, Arindam
@ 2011-03-10 14:12 ` subhashj
2011-03-10 14:15 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 14:12 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Thursday, March 10, 2011 7:38 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> initialization
>
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Thursday, March 10, 2011 7:34 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> uhs
> > initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > Sent: Thursday, March 10, 2011 7:20 PM
> > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> > uhs
> > > initialization
> > >
> > > > -----Original Message-----
> > > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > > Sent: Thursday, March 10, 2011 7:15 PM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> after
> > > uhs
> > > > initialization
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > Sent: Thursday, March 10, 2011 7:01 PM
> > > > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > after
> > > > uhs
> > > > > initialization
> > > > >
> > > > > Hi Subhash,
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: subhashj@codeaurora.org
> [mailto:subhashj@codeaurora.org]
> > > > > > Sent: Thursday, March 10, 2011 6:54 PM
> > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > > after
> > > > > uhs
> > > > > > initialization
> > > > > >
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > To: cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > henry.su@amd.com;
> > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > Subject: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > after
> > > > uhs
> > > > > > > initialization
> > > > > > >
> > > > > > > According to the Host Controller spec v3.00, setting Preset
> > > Value
> > > > > > > Enable
> > > > > > > in the Host Control2 register lets SDCLK Frequency Select,
> > > Clock
> > > > > > > Generator
> > > > > > > Select and Driver Strength Select to be set automatically
> by
> > > the
> > > > > Host
> > > > > > > Controller based on the UHS-I mode set. This patch enables
> > this
> > > > > > > feature.
> > > > > > > We also reset Preset Value Enable when the card is removed
> > from
> > > > the
> > > > > > > slot.
> > > > > >
> > > > > > What does this preset_value means? Is this defined in SD3.01
> > > spec?
> > > > I
> > > > > > think
> > > > > > it looks like specific to your controller (sdhci). If it's
> > > specific
> > > > > to
> > > > > > your
> > > > > > controller then this new mmc_ops should not be added.
> > > > >
> > > > > Preset Value Enable is very much part of the standard SD Host
> > > > > Controller spec v3.00. Please refer to Host Control 2 register.
> > > >
> > > > I don't think all controllers should be compliant to SD host
> > > controller
> > > > spec. I don't think controller on our chips has something like
> > this.
> > >
> > > But from the spec it looks as if controllers conforming to v3.00
> > should
> > > provide this feature. There is no other bit anywhere else in the
> spec
> > > which can indicate support for Preset Value Enable except for the
> > > controller version number.
> >
> > Actually, I am not aware about this controller spec v3.00. can you
> > please
> > point me to the link for this spec (if it's available)?
>
> The spec is named "SD Host Controller Standard Specification Ver3.00".
> I think it can be downloaded from sdcard.org, but you will need to pay
> for the license.
Thanks. I just read from here:
http://www.sdcard.org/developers/tech/host_controller/. I think this spec is
just recommendation for implementing Host controller Hw.
It doesn't mean all Host controllers on all different chipset should have
same register interface as mentioned by the spec. I don't think controller
on our chipset complies to this. Controller just complies to provide all SD
Physical layer spec v3.00/v3.01.
>
> Thanks,
> Arindam
>
> >
> >
> > >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > > >
> > > > > Thanks,
> > > > > Arindam
> > > > >
> > > > > >
> > > > > >
> > > > > > >
> > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > ---
> > > > > > > drivers/mmc/core/sd.c | 7 +++++++
> > > > > > > drivers/mmc/host/sdhci.c | 36
> > > > > ++++++++++++++++++++++++++++++++++++
> > > > > > > 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 1e2d157..ae7a771 100644
> > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > @@ -962,6 +962,13 @@ static int mmc_sd_init_card(struct
> > > mmc_host
> > > > > > *host,
> > > > > > > u32 ocr,
> > > > > > > }
> > > > > > > }
> > > > > > >
> > > > > > > + /*
> > > > > > > + * Since initialization is now complete, enable
> preset
> > > > > > > + * value registers.
> > > > > > > + */
> > > > > > > + if (host->ops->enable_preset_value)
> > > > > > > + host->ops->enable_preset_value(host);
> > > > > > > +
> > > > > > > host->card = card;
> > > > > > > return 0;
> > > > > > >
> > > > > > > diff --git a/drivers/mmc/host/sdhci.c
> > > b/drivers/mmc/host/sdhci.c
> > > > > > > index 8f4f102..1f6e4ad 100644
> > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > @@ -1602,6 +1602,40 @@ out:
> > > > > > > spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > }
> > > > > > >
> > > > > > > +static void sdhci_enable_preset_value(struct mmc_host
> *mmc)
> > > > > > > +{
> > > > > > > + 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);
> > > > > > > + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > +
> > > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > +}
> > > > > > > +
> > > > > > > +static void sdhci_disable_preset_value(struct sdhci_host
> > > *host)
> > > > > > > +{
> > > > > > > + u16 ctrl;
> > > > > > > +
> > > > > > > + /* Only for Host Controller version >= v3.00 */
> > > > > > > + if (host->version < SDHCI_SPEC_300)
> > > > > > > + return;
> > > > > > > +
> > > > > > > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > > > > + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > +}
> > > > > > > +
> > > > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > > > .request = sdhci_request,
> > > > > > > .set_ios = sdhci_set_ios,
> > > > > > > @@ -1610,6 +1644,7 @@ static const struct mmc_host_ops
> > > sdhci_ops
> > > > =
> > > > > {
> > > > > > > .start_signal_voltage_switch =
> > > > > > > sdhci_start_signal_voltage_switch,
> > > > > > > .get_max_current_180 =
> sdhci_get_max_current_180,
> > > > > > > .execute_tuning = sdhci_execute_tuning,
> > > > > > > + .enable_preset_value =
> sdhci_enable_preset_value,
> > > > > > > };
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > > > *******\
> > > > > > > @@ -1920,6 +1955,7 @@ static irqreturn_t sdhci_irq(int irq,
> > > void
> > > > > > > *dev_id)
> > > > > > > sdhci_writel(host, intmask &
> (SDHCI_INT_CARD_INSERT
> > |
> > > > > > > SDHCI_INT_CARD_REMOVE),
> SDHCI_INT_STATUS);
> > > > > > > tasklet_schedule(&host->card_tasklet);
> > > > > > > + sdhci_disable_preset_value(host);
> > > > > > > }
> > > > > > >
> > > > > > > intmask &= ~(SDHCI_INT_CARD_INSERT |
> > > > SDHCI_INT_CARD_REMOVE);
> > > > > > > diff --git a/include/linux/mmc/host.h
> > > b/include/linux/mmc/host.h
> > > > > > > index 651e40b..e63e063 100644
> > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > @@ -130,6 +130,7 @@ struct mmc_host_ops {
> > > > > > > int (*start_signal_voltage_switch)(struct mmc_host
> > > > *host);
> > > > > > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > > > > > void (*execute_tuning)(struct mmc_host *host);
> > > > > > > + void (*enable_preset_value)(struct mmc_host *host);
> > > > > > > };
> > > > > > >
> > > > > > > struct mmc_card;
> > > > > > > --
> > > > > > > 1.7.1
> > > > > > >
> > > > > > > --
> > > > > > > To unsubscribe from this list: send the line "unsubscribe
> > > linux-
> > > > > mmc"
> > > > > > in
> > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > > info.html
> > > > > >
> > > > >
> > > > >
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe
> linux-
> > > mmc"
> > > > in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > info.html
> > > >
> > >
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-10 14:12 ` subhashj
@ 2011-03-10 14:15 ` Nath, Arindam
2011-03-10 14:19 ` subhashj
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 14:15 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 7:43 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> initialization
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Thursday, March 10, 2011 7:38 PM
> > To: subhashj@codeaurora.org; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> uhs
> > initialization
> >
> > > -----Original Message-----
> > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > Sent: Thursday, March 10, 2011 7:34 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> > uhs
> > > initialization
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > Sent: Thursday, March 10, 2011 7:20 PM
> > > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> after
> > > uhs
> > > > initialization
> > > >
> > > > > -----Original Message-----
> > > > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > > > Sent: Thursday, March 10, 2011 7:15 PM
> > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > after
> > > > uhs
> > > > > initialization
> > > > >
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > Sent: Thursday, March 10, 2011 7:01 PM
> > > > > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > > after
> > > > > uhs
> > > > > > initialization
> > > > > >
> > > > > > Hi Subhash,
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: subhashj@codeaurora.org
> > [mailto:subhashj@codeaurora.org]
> > > > > > > Sent: Thursday, March 10, 2011 6:54 PM
> > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > > > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset
> value
> > > > after
> > > > > > uhs
> > > > > > > initialization
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > > -----Original Message-----
> > > > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > > To: cjb@laptop.org
> > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > > henry.su@amd.com;
> > > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > > Subject: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > > after
> > > > > uhs
> > > > > > > > initialization
> > > > > > > >
> > > > > > > > According to the Host Controller spec v3.00, setting
> Preset
> > > > Value
> > > > > > > > Enable
> > > > > > > > in the Host Control2 register lets SDCLK Frequency
> Select,
> > > > Clock
> > > > > > > > Generator
> > > > > > > > Select and Driver Strength Select to be set automatically
> > by
> > > > the
> > > > > > Host
> > > > > > > > Controller based on the UHS-I mode set. This patch
> enables
> > > this
> > > > > > > > feature.
> > > > > > > > We also reset Preset Value Enable when the card is
> removed
> > > from
> > > > > the
> > > > > > > > slot.
> > > > > > >
> > > > > > > What does this preset_value means? Is this defined in
> SD3.01
> > > > spec?
> > > > > I
> > > > > > > think
> > > > > > > it looks like specific to your controller (sdhci). If it's
> > > > specific
> > > > > > to
> > > > > > > your
> > > > > > > controller then this new mmc_ops should not be added.
> > > > > >
> > > > > > Preset Value Enable is very much part of the standard SD Host
> > > > > > Controller spec v3.00. Please refer to Host Control 2
> register.
> > > > >
> > > > > I don't think all controllers should be compliant to SD host
> > > > controller
> > > > > spec. I don't think controller on our chips has something like
> > > this.
> > > >
> > > > But from the spec it looks as if controllers conforming to v3.00
> > > should
> > > > provide this feature. There is no other bit anywhere else in the
> > spec
> > > > which can indicate support for Preset Value Enable except for the
> > > > controller version number.
> > >
> > > Actually, I am not aware about this controller spec v3.00. can you
> > > please
> > > point me to the link for this spec (if it's available)?
> >
> > The spec is named "SD Host Controller Standard Specification
> Ver3.00".
> > I think it can be downloaded from sdcard.org, but you will need to
> pay
> > for the license.
>
>
> Thanks. I just read from here:
> http://www.sdcard.org/developers/tech/host_controller/. I think this
> spec is
> just recommendation for implementing Host controller Hw.
> It doesn't mean all Host controllers on all different chipset should
> have
> same register interface as mentioned by the spec. I don't think
> controller
> on our chipset complies to this. Controller just complies to provide
> all SD
> Physical layer spec v3.00/v3.01.
In that case I think it is safe enough to check the version of Host Controller for v3.00 before enabling Preset Value.
Thanks,
Arindam
>
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > >
> > > >
> > > > Thanks,
> > > > Arindam
> > > >
> > > > >
> > > > > >
> > > > > > Thanks,
> > > > > > Arindam
> > > > > >
> > > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > > ---
> > > > > > > > drivers/mmc/core/sd.c | 7 +++++++
> > > > > > > > drivers/mmc/host/sdhci.c | 36
> > > > > > ++++++++++++++++++++++++++++++++++++
> > > > > > > > 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 1e2d157..ae7a771 100644
> > > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > > @@ -962,6 +962,13 @@ static int mmc_sd_init_card(struct
> > > > mmc_host
> > > > > > > *host,
> > > > > > > > u32 ocr,
> > > > > > > > }
> > > > > > > > }
> > > > > > > >
> > > > > > > > + /*
> > > > > > > > + * Since initialization is now complete, enable
> > preset
> > > > > > > > + * value registers.
> > > > > > > > + */
> > > > > > > > + if (host->ops->enable_preset_value)
> > > > > > > > + host->ops->enable_preset_value(host);
> > > > > > > > +
> > > > > > > > host->card = card;
> > > > > > > > return 0;
> > > > > > > >
> > > > > > > > diff --git a/drivers/mmc/host/sdhci.c
> > > > b/drivers/mmc/host/sdhci.c
> > > > > > > > index 8f4f102..1f6e4ad 100644
> > > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > > @@ -1602,6 +1602,40 @@ out:
> > > > > > > > spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > > }
> > > > > > > >
> > > > > > > > +static void sdhci_enable_preset_value(struct mmc_host
> > *mmc)
> > > > > > > > +{
> > > > > > > > + 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);
> > > > > > > > + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > > +
> > > > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static void sdhci_disable_preset_value(struct sdhci_host
> > > > *host)
> > > > > > > > +{
> > > > > > > > + u16 ctrl;
> > > > > > > > +
> > > > > > > > + /* Only for Host Controller version >= v3.00 */
> > > > > > > > + if (host->version < SDHCI_SPEC_300)
> > > > > > > > + return;
> > > > > > > > +
> > > > > > > > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > > > > > + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > > > > .request = sdhci_request,
> > > > > > > > .set_ios = sdhci_set_ios,
> > > > > > > > @@ -1610,6 +1644,7 @@ static const struct mmc_host_ops
> > > > sdhci_ops
> > > > > =
> > > > > > {
> > > > > > > > .start_signal_voltage_switch =
> > > > > > > > sdhci_start_signal_voltage_switch,
> > > > > > > > .get_max_current_180 =
> > sdhci_get_max_current_180,
> > > > > > > > .execute_tuning = sdhci_execute_tuning,
> > > > > > > > + .enable_preset_value =
> > sdhci_enable_preset_value,
> > > > > > > > };
> > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > > > > *******\
> > > > > > > > @@ -1920,6 +1955,7 @@ static irqreturn_t sdhci_irq(int
> irq,
> > > > void
> > > > > > > > *dev_id)
> > > > > > > > sdhci_writel(host, intmask &
> > (SDHCI_INT_CARD_INSERT
> > > |
> > > > > > > > SDHCI_INT_CARD_REMOVE),
> > SDHCI_INT_STATUS);
> > > > > > > > tasklet_schedule(&host->card_tasklet);
> > > > > > > > + sdhci_disable_preset_value(host);
> > > > > > > > }
> > > > > > > >
> > > > > > > > intmask &= ~(SDHCI_INT_CARD_INSERT |
> > > > > SDHCI_INT_CARD_REMOVE);
> > > > > > > > diff --git a/include/linux/mmc/host.h
> > > > b/include/linux/mmc/host.h
> > > > > > > > index 651e40b..e63e063 100644
> > > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > > @@ -130,6 +130,7 @@ struct mmc_host_ops {
> > > > > > > > int (*start_signal_voltage_switch)(struct mmc_host
> > > > > *host);
> > > > > > > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > > > > > > void (*execute_tuning)(struct mmc_host *host);
> > > > > > > > + void (*enable_preset_value)(struct mmc_host *host);
> > > > > > > > };
> > > > > > > >
> > > > > > > > struct mmc_card;
> > > > > > > > --
> > > > > > > > 1.7.1
> > > > > > > >
> > > > > > > > --
> > > > > > > > To unsubscribe from this list: send the line "unsubscribe
> > > > linux-
> > > > > > mmc"
> > > > > > > in
> > > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > > > info.html
> > > > > > >
> > > > > >
> > > > > >
> > > > > > --
> > > > > > To unsubscribe from this list: send the line "unsubscribe
> > linux-
> > > > mmc"
> > > > > in
> > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > info.html
> > > > >
> > > >
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-10 14:15 ` Nath, Arindam
@ 2011-03-10 14:19 ` subhashj
2011-03-10 14:24 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-10 14:19 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Thursday, March 10, 2011 7:46 PM
> To: subhashj@codeaurora.org; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> initialization
>
>
>
> > -----Original Message-----
> > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > Sent: Thursday, March 10, 2011 7:43 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> uhs
> > initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > Sent: Thursday, March 10, 2011 7:38 PM
> > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> > uhs
> > > initialization
> > >
> > > > -----Original Message-----
> > > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > > Sent: Thursday, March 10, 2011 7:34 PM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> after
> > > uhs
> > > > initialization
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > Sent: Thursday, March 10, 2011 7:20 PM
> > > > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > after
> > > > uhs
> > > > > initialization
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: subhashj@codeaurora.org
> [mailto:subhashj@codeaurora.org]
> > > > > > Sent: Thursday, March 10, 2011 7:15 PM
> > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > > after
> > > > > uhs
> > > > > > initialization
> > > > > >
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > > Sent: Thursday, March 10, 2011 7:01 PM
> > > > > > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > > > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset
> value
> > > > after
> > > > > > uhs
> > > > > > > initialization
> > > > > > >
> > > > > > > Hi Subhash,
> > > > > > >
> > > > > > >
> > > > > > > > -----Original Message-----
> > > > > > > > From: subhashj@codeaurora.org
> > > [mailto:subhashj@codeaurora.org]
> > > > > > > > Sent: Thursday, March 10, 2011 6:54 PM
> > > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > anath.amd@gmail.com
> > > > > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset
> > value
> > > > > after
> > > > > > > uhs
> > > > > > > > initialization
> > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > > -----Original Message-----
> > > > > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-
> mmc-
> > > > > > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > > > To: cjb@laptop.org
> > > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > > > henry.su@amd.com;
> > > > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > > > Subject: [PATCH v2 10/12] mmc: sdhci: enable preset
> value
> > > > after
> > > > > > uhs
> > > > > > > > > initialization
> > > > > > > > >
> > > > > > > > > According to the Host Controller spec v3.00, setting
> > Preset
> > > > > Value
> > > > > > > > > Enable
> > > > > > > > > in the Host Control2 register lets SDCLK Frequency
> > Select,
> > > > > Clock
> > > > > > > > > Generator
> > > > > > > > > Select and Driver Strength Select to be set
> automatically
> > > by
> > > > > the
> > > > > > > Host
> > > > > > > > > Controller based on the UHS-I mode set. This patch
> > enables
> > > > this
> > > > > > > > > feature.
> > > > > > > > > We also reset Preset Value Enable when the card is
> > removed
> > > > from
> > > > > > the
> > > > > > > > > slot.
> > > > > > > >
> > > > > > > > What does this preset_value means? Is this defined in
> > SD3.01
> > > > > spec?
> > > > > > I
> > > > > > > > think
> > > > > > > > it looks like specific to your controller (sdhci). If
> it's
> > > > > specific
> > > > > > > to
> > > > > > > > your
> > > > > > > > controller then this new mmc_ops should not be added.
> > > > > > >
> > > > > > > Preset Value Enable is very much part of the standard SD
> Host
> > > > > > > Controller spec v3.00. Please refer to Host Control 2
> > register.
> > > > > >
> > > > > > I don't think all controllers should be compliant to SD host
> > > > > controller
> > > > > > spec. I don't think controller on our chips has something
> like
> > > > this.
> > > > >
> > > > > But from the spec it looks as if controllers conforming to
> v3.00
> > > > should
> > > > > provide this feature. There is no other bit anywhere else in
> the
> > > spec
> > > > > which can indicate support for Preset Value Enable except for
> the
> > > > > controller version number.
> > > >
> > > > Actually, I am not aware about this controller spec v3.00. can
> you
> > > > please
> > > > point me to the link for this spec (if it's available)?
> > >
> > > The spec is named "SD Host Controller Standard Specification
> > Ver3.00".
> > > I think it can be downloaded from sdcard.org, but you will need to
> > pay
> > > for the license.
> >
> >
> > Thanks. I just read from here:
> > http://www.sdcard.org/developers/tech/host_controller/. I think this
> > spec is
> > just recommendation for implementing Host controller Hw.
> > It doesn't mean all Host controllers on all different chipset should
> > have
> > same register interface as mentioned by the spec. I don't think
> > controller
> > on our chipset complies to this. Controller just complies to provide
> > all SD
> > Physical layer spec v3.00/v3.01.
>
> In that case I think it is safe enough to check the version of Host
> Controller for v3.00 before enabling Preset Value.
I don't think controller on our chipset is compliant to any of the host
controller spec (2.0 or 3.0).
>
> Thanks,
> Arindam
>
> >
> >
> > >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > >
> > > > >
> > > > > Thanks,
> > > > > Arindam
> > > > >
> > > > > >
> > > > > > >
> > > > > > > Thanks,
> > > > > > > Arindam
> > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > >
> > > > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > > > ---
> > > > > > > > > drivers/mmc/core/sd.c | 7 +++++++
> > > > > > > > > drivers/mmc/host/sdhci.c | 36
> > > > > > > ++++++++++++++++++++++++++++++++++++
> > > > > > > > > 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 1e2d157..ae7a771 100644
> > > > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > > > @@ -962,6 +962,13 @@ static int mmc_sd_init_card(struct
> > > > > mmc_host
> > > > > > > > *host,
> > > > > > > > > u32 ocr,
> > > > > > > > > }
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > > + /*
> > > > > > > > > + * Since initialization is now complete, enable
> > > preset
> > > > > > > > > + * value registers.
> > > > > > > > > + */
> > > > > > > > > + if (host->ops->enable_preset_value)
> > > > > > > > > + host->ops->enable_preset_value(host);
> > > > > > > > > +
> > > > > > > > > host->card = card;
> > > > > > > > > return 0;
> > > > > > > > >
> > > > > > > > > diff --git a/drivers/mmc/host/sdhci.c
> > > > > b/drivers/mmc/host/sdhci.c
> > > > > > > > > index 8f4f102..1f6e4ad 100644
> > > > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > > > @@ -1602,6 +1602,40 @@ out:
> > > > > > > > > spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > > +static void sdhci_enable_preset_value(struct mmc_host
> > > *mmc)
> > > > > > > > > +{
> > > > > > > > > + 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);
> > > > > > > > > + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > > > +
> > > > > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static void sdhci_disable_preset_value(struct
> sdhci_host
> > > > > *host)
> > > > > > > > > +{
> > > > > > > > > + u16 ctrl;
> > > > > > > > > +
> > > > > > > > > + /* Only for Host Controller version >= v3.00 */
> > > > > > > > > + if (host->version < SDHCI_SPEC_300)
> > > > > > > > > + return;
> > > > > > > > > +
> > > > > > > > > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > > > > > > + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > > > > > .request = sdhci_request,
> > > > > > > > > .set_ios = sdhci_set_ios,
> > > > > > > > > @@ -1610,6 +1644,7 @@ static const struct mmc_host_ops
> > > > > sdhci_ops
> > > > > > =
> > > > > > > {
> > > > > > > > > .start_signal_voltage_switch =
> > > > > > > > > sdhci_start_signal_voltage_switch,
> > > > > > > > > .get_max_current_180 =
> > > sdhci_get_max_current_180,
> > > > > > > > > .execute_tuning =
sdhci_execute_tuning,
> > > > > > > > > + .enable_preset_value =
> > > sdhci_enable_preset_value,
> > > > > > > > > };
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > > > > > *******\
> > > > > > > > > @@ -1920,6 +1955,7 @@ static irqreturn_t sdhci_irq(int
> > irq,
> > > > > void
> > > > > > > > > *dev_id)
> > > > > > > > > sdhci_writel(host, intmask &
> > > (SDHCI_INT_CARD_INSERT
> > > > |
> > > > > > > > > SDHCI_INT_CARD_REMOVE),
> > > SDHCI_INT_STATUS);
> > > > > > > > > tasklet_schedule(&host->card_tasklet);
> > > > > > > > > + sdhci_disable_preset_value(host);
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > > intmask &= ~(SDHCI_INT_CARD_INSERT |
> > > > > > SDHCI_INT_CARD_REMOVE);
> > > > > > > > > diff --git a/include/linux/mmc/host.h
> > > > > b/include/linux/mmc/host.h
> > > > > > > > > index 651e40b..e63e063 100644
> > > > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > > > @@ -130,6 +130,7 @@ struct mmc_host_ops {
> > > > > > > > > int (*start_signal_voltage_switch)(struct
mmc_host
> > > > > > *host);
> > > > > > > > > int (*get_max_current_180)(struct mmc_host
*mmc);
> > > > > > > > > void (*execute_tuning)(struct mmc_host *host);
> > > > > > > > > + void (*enable_preset_value)(struct mmc_host
*host);
> > > > > > > > > };
> > > > > > > > >
> > > > > > > > > struct mmc_card;
> > > > > > > > > --
> > > > > > > > > 1.7.1
> > > > > > > > >
> > > > > > > > > --
> > > > > > > > > To unsubscribe from this list: send the line
> "unsubscribe
> > > > > linux-
> > > > > > > mmc"
> > > > > > > > in
> > > > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > > > More majordomo info at
> http://vger.kernel.org/majordomo-
> > > > > > info.html
> > > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > --
> > > > > > > To unsubscribe from this list: send the line "unsubscribe
> > > linux-
> > > > > mmc"
> > > > > > in
> > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > > info.html
> > > > > >
> > > > >
> > > > >
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe
> linux-
> > > mmc"
> > > > in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > info.html
> > > >
> > >
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs initialization
2011-03-10 14:19 ` subhashj
@ 2011-03-10 14:24 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-10 14:24 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 10, 2011 7:50 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after uhs
> initialization
>
>
>
> > -----Original Message-----
> > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > Sent: Thursday, March 10, 2011 7:46 PM
> > To: subhashj@codeaurora.org; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> uhs
> > initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > Sent: Thursday, March 10, 2011 7:43 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value after
> > uhs
> > > initialization
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > Sent: Thursday, March 10, 2011 7:38 PM
> > > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> after
> > > uhs
> > > > initialization
> > > >
> > > > > -----Original Message-----
> > > > > From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> > > > > Sent: Thursday, March 10, 2011 7:34 PM
> > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > after
> > > > uhs
> > > > > initialization
> > > > >
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > Sent: Thursday, March 10, 2011 7:20 PM
> > > > > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset value
> > > after
> > > > > uhs
> > > > > > initialization
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: subhashj@codeaurora.org
> > [mailto:subhashj@codeaurora.org]
> > > > > > > Sent: Thursday, March 10, 2011 7:15 PM
> > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > > > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset
> value
> > > > after
> > > > > > uhs
> > > > > > > initialization
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > > -----Original Message-----
> > > > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > > > Sent: Thursday, March 10, 2011 7:01 PM
> > > > > > > > To: subhashj@codeaurora.org; cjb@laptop.org
> > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > anath.amd@gmail.com
> > > > > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset
> > value
> > > > > after
> > > > > > > uhs
> > > > > > > > initialization
> > > > > > > >
> > > > > > > > Hi Subhash,
> > > > > > > >
> > > > > > > >
> > > > > > > > > -----Original Message-----
> > > > > > > > > From: subhashj@codeaurora.org
> > > > [mailto:subhashj@codeaurora.org]
> > > > > > > > > Sent: Thursday, March 10, 2011 6:54 PM
> > > > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > > anath.amd@gmail.com
> > > > > > > > > Subject: RE: [PATCH v2 10/12] mmc: sdhci: enable preset
> > > value
> > > > > > after
> > > > > > > > uhs
> > > > > > > > > initialization
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > > -----Original Message-----
> > > > > > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-
> > mmc-
> > > > > > > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > > > > To: cjb@laptop.org
> > > > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > > > > henry.su@amd.com;
> > > > > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > > > > Subject: [PATCH v2 10/12] mmc: sdhci: enable preset
> > value
> > > > > after
> > > > > > > uhs
> > > > > > > > > > initialization
> > > > > > > > > >
> > > > > > > > > > According to the Host Controller spec v3.00, setting
> > > Preset
> > > > > > Value
> > > > > > > > > > Enable
> > > > > > > > > > in the Host Control2 register lets SDCLK Frequency
> > > Select,
> > > > > > Clock
> > > > > > > > > > Generator
> > > > > > > > > > Select and Driver Strength Select to be set
> > automatically
> > > > by
> > > > > > the
> > > > > > > > Host
> > > > > > > > > > Controller based on the UHS-I mode set. This patch
> > > enables
> > > > > this
> > > > > > > > > > feature.
> > > > > > > > > > We also reset Preset Value Enable when the card is
> > > removed
> > > > > from
> > > > > > > the
> > > > > > > > > > slot.
> > > > > > > > >
> > > > > > > > > What does this preset_value means? Is this defined in
> > > SD3.01
> > > > > > spec?
> > > > > > > I
> > > > > > > > > think
> > > > > > > > > it looks like specific to your controller (sdhci). If
> > it's
> > > > > > specific
> > > > > > > > to
> > > > > > > > > your
> > > > > > > > > controller then this new mmc_ops should not be added.
> > > > > > > >
> > > > > > > > Preset Value Enable is very much part of the standard SD
> > Host
> > > > > > > > Controller spec v3.00. Please refer to Host Control 2
> > > register.
> > > > > > >
> > > > > > > I don't think all controllers should be compliant to SD
> host
> > > > > > controller
> > > > > > > spec. I don't think controller on our chips has something
> > like
> > > > > this.
> > > > > >
> > > > > > But from the spec it looks as if controllers conforming to
> > v3.00
> > > > > should
> > > > > > provide this feature. There is no other bit anywhere else in
> > the
> > > > spec
> > > > > > which can indicate support for Preset Value Enable except for
> > the
> > > > > > controller version number.
> > > > >
> > > > > Actually, I am not aware about this controller spec v3.00. can
> > you
> > > > > please
> > > > > point me to the link for this spec (if it's available)?
> > > >
> > > > The spec is named "SD Host Controller Standard Specification
> > > Ver3.00".
> > > > I think it can be downloaded from sdcard.org, but you will need
> to
> > > pay
> > > > for the license.
> > >
> > >
> > > Thanks. I just read from here:
> > > http://www.sdcard.org/developers/tech/host_controller/. I think
> this
> > > spec is
> > > just recommendation for implementing Host controller Hw.
> > > It doesn't mean all Host controllers on all different chipset
> should
> > > have
> > > same register interface as mentioned by the spec. I don't think
> > > controller
> > > on our chipset complies to this. Controller just complies to
> provide
> > > all SD
> > > Physical layer spec v3.00/v3.01.
> >
> > In that case I think it is safe enough to check the version of Host
> > Controller for v3.00 before enabling Preset Value.
>
> I don't think controller on our chipset is compliant to any of the host
> controller spec (2.0 or 3.0).
But in that case the Host Controller Version Register, offset FEh, should actually tell us the conformance level of the HC. Please correct if wrong.
Thanks,
Arindam
>
>
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > >
> > > >
> > > > Thanks,
> > > > Arindam
> > > >
> > > > >
> > > > >
> > > > > >
> > > > > > Thanks,
> > > > > > Arindam
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > Thanks,
> > > > > > > > Arindam
> > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > > > > ---
> > > > > > > > > > drivers/mmc/core/sd.c | 7 +++++++
> > > > > > > > > > drivers/mmc/host/sdhci.c | 36
> > > > > > > > ++++++++++++++++++++++++++++++++++++
> > > > > > > > > > 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 1e2d157..ae7a771 100644
> > > > > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > > > > @@ -962,6 +962,13 @@ static int
> mmc_sd_init_card(struct
> > > > > > mmc_host
> > > > > > > > > *host,
> > > > > > > > > > u32 ocr,
> > > > > > > > > > }
> > > > > > > > > > }
> > > > > > > > > >
> > > > > > > > > > + /*
> > > > > > > > > > + * Since initialization is now complete, enable
> > > > preset
> > > > > > > > > > + * value registers.
> > > > > > > > > > + */
> > > > > > > > > > + if (host->ops->enable_preset_value)
> > > > > > > > > > + host->ops->enable_preset_value(host);
> > > > > > > > > > +
> > > > > > > > > > host->card = card;
> > > > > > > > > > return 0;
> > > > > > > > > >
> > > > > > > > > > diff --git a/drivers/mmc/host/sdhci.c
> > > > > > b/drivers/mmc/host/sdhci.c
> > > > > > > > > > index 8f4f102..1f6e4ad 100644
> > > > > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > > > > @@ -1602,6 +1602,40 @@ out:
> > > > > > > > > > spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > > > > }
> > > > > > > > > >
> > > > > > > > > > +static void sdhci_enable_preset_value(struct
> mmc_host
> > > > *mmc)
> > > > > > > > > > +{
> > > > > > > > > > + 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);
> > > > > > > > > > + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > > > > +
> > > > > > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void sdhci_disable_preset_value(struct
> > sdhci_host
> > > > > > *host)
> > > > > > > > > > +{
> > > > > > > > > > + u16 ctrl;
> > > > > > > > > > +
> > > > > > > > > > + /* Only for Host Controller version >= v3.00 */
> > > > > > > > > > + if (host->version < SDHCI_SPEC_300)
> > > > > > > > > > + return;
> > > > > > > > > > +
> > > > > > > > > > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > > > > > > > + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> > > > > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > > > > > > .request = sdhci_request,
> > > > > > > > > > .set_ios = sdhci_set_ios,
> > > > > > > > > > @@ -1610,6 +1644,7 @@ static const struct
> mmc_host_ops
> > > > > > sdhci_ops
> > > > > > > =
> > > > > > > > {
> > > > > > > > > > .start_signal_voltage_switch =
> > > > > > > > > > sdhci_start_signal_voltage_switch,
> > > > > > > > > > .get_max_current_180 =
> > > > sdhci_get_max_current_180,
> > > > > > > > > > .execute_tuning =
> sdhci_execute_tuning,
> > > > > > > > > > + .enable_preset_value =
> > > > sdhci_enable_preset_value,
> > > > > > > > > > };
> > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > > > > > > *******\
> > > > > > > > > > @@ -1920,6 +1955,7 @@ static irqreturn_t
> sdhci_irq(int
> > > irq,
> > > > > > void
> > > > > > > > > > *dev_id)
> > > > > > > > > > sdhci_writel(host, intmask &
> > > > (SDHCI_INT_CARD_INSERT
> > > > > |
> > > > > > > > > > SDHCI_INT_CARD_REMOVE),
> > > > SDHCI_INT_STATUS);
> > > > > > > > > > tasklet_schedule(&host->card_tasklet);
> > > > > > > > > > + sdhci_disable_preset_value(host);
> > > > > > > > > > }
> > > > > > > > > >
> > > > > > > > > > intmask &= ~(SDHCI_INT_CARD_INSERT |
> > > > > > > SDHCI_INT_CARD_REMOVE);
> > > > > > > > > > diff --git a/include/linux/mmc/host.h
> > > > > > b/include/linux/mmc/host.h
> > > > > > > > > > index 651e40b..e63e063 100644
> > > > > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > > > > @@ -130,6 +130,7 @@ struct mmc_host_ops {
> > > > > > > > > > int (*start_signal_voltage_switch)(struct
> mmc_host
> > > > > > > *host);
> > > > > > > > > > int (*get_max_current_180)(struct mmc_host
> *mmc);
> > > > > > > > > > void (*execute_tuning)(struct mmc_host *host);
> > > > > > > > > > + void (*enable_preset_value)(struct mmc_host
> *host);
> > > > > > > > > > };
> > > > > > > > > >
> > > > > > > > > > struct mmc_card;
> > > > > > > > > > --
> > > > > > > > > > 1.7.1
> > > > > > > > > >
> > > > > > > > > > --
> > > > > > > > > > To unsubscribe from this list: send the line
> > "unsubscribe
> > > > > > linux-
> > > > > > > > mmc"
> > > > > > > > > in
> > > > > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > > > > More majordomo info at
> > http://vger.kernel.org/majordomo-
> > > > > > > info.html
> > > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > --
> > > > > > > > To unsubscribe from this list: send the line "unsubscribe
> > > > linux-
> > > > > > mmc"
> > > > > > > in
> > > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > > > info.html
> > > > > > >
> > > > > >
> > > > > >
> > > > > > --
> > > > > > To unsubscribe from this list: send the line "unsubscribe
> > linux-
> > > > mmc"
> > > > > in
> > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > info.html
> > > > >
> > > >
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 00/12] add support for host controller v3.00
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
` (12 preceding siblings ...)
2011-03-04 15:16 ` [PATCH v2 00/12] add support for host controller v3.00 Chris Ball
@ 2011-03-11 13:13 ` subhashj
2011-03-11 15:29 ` Nath, Arindam
13 siblings, 1 reply; 125+ messages in thread
From: subhashj @ 2011-03-11 13:13 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
Arindam,
I am not sure whether you run the "./scripts/checkpatch.pl" script for your
patch or not? When I run this script on your patch, it throws following
warnings.
You might want to correct these warnings in your next patches.
$ ./scripts/checkpatch.pl 0001-mmc-SD3.0-patches.patch
WARNING: line over 80 characters
#765: FILE: include/linux/mmc/card.h:155:
+#define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra
high speed mode */
WARNING: line over 80 characters
#830: FILE: include/linux/mmc/host.h:191:
+#define MMC_CAP_UHS_SDR12 (1 << 15) /* Host supports UHS SDR12
mode */
WARNING: line over 80 characters
#831: FILE: include/linux/mmc/host.h:192:
+#define MMC_CAP_UHS_SDR25 (1 << 16) /* Host supports UHS SDR25
mode */
WARNING: line over 80 characters
#832: FILE: include/linux/mmc/host.h:193:
+#define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50
mode */
WARNING: line over 80 characters
#833: FILE: include/linux/mmc/host.h:194:
+#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104
mode */
WARNING: line over 80 characters
#834: FILE: include/linux/mmc/host.h:195:
+#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50
mode */
WARNING: line over 80 characters
#835: FILE: include/linux/mmc/host.h:196:
+#define MMC_CAP_SET_XPC_330 (1 << 20) /* Host supports >150mA
current at 3.3V */
WARNING: line over 80 characters
#836: FILE: include/linux/mmc/host.h:197:
+#define MMC_CAP_SET_XPC_300 (1 << 21) /* Host supports >150mA
current at 3.0V */
WARNING: line over 80 characters
#837: FILE: include/linux/mmc/host.h:198:
+#define MMC_CAP_SET_XPC_180 (1 << 22) /* Host supports >150mA
current at 1.8V */
WARNING: line over 80 characters
#838: FILE: include/linux/mmc/host.h:199:
+#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type
A */
WARNING: line over 80 characters
#839: FILE: include/linux/mmc/host.h:200:
+#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type
C */
WARNING: line over 80 characters
#840: FILE: include/linux/mmc/host.h:201:
+#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type
D */
WARNING: line over 80 characters
#873: FILE: include/linux/mmc/sd.h:63:
+#define SCR_SPEC_VER_2 2 /* Implements system specification
2.00 - 3.0x */
total: 0 errors, 13 warnings, 778 lines checked
Regards,
Subhash
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 00/12] add support for host controller v3.00
>
> 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 (12):
> [PATCH 01/12] mmc: sdhci: add support for auto CMD23
> [PATCH 02/12] mmc: sd: add support for signal voltage switch
> procedure
> [PATCH 03/12] mmc: sd: query function modes for uhs cards
> [PATCH 04/12] mmc: sd: add support for driver type selection
> [PATCH 05/12] mmc: sdhci: reset sdclk before setting high speed
> enable
> [PATCH 06/12] mmc: sd: add support for uhs bus speed mode selection
> [PATCH 07/12] mmc: sd: set current limit for uhs cards
> [PATCH 08/12] mmc: sd: report correct speed and capacity of uhs cards
> [PATCH 09/12] mmc: sd: add support for tuning during uhs
> initialization
> [PATCH 10/12] mmc: sdhci: enable preset value after uhs
> initialization
> [PATCH 11/12] mmc: sdhci: add support for programmable clock mode
> [PATCH 12/12] mmc: sdhci: add support for retuning mode 1
>
> drivers/mmc/core/bus.c | 11 +-
> drivers/mmc/core/core.c | 9 +
> drivers/mmc/core/core.h | 1 +
> drivers/mmc/core/sd.c | 436 +++++++++++++++++++++++++---
> drivers/mmc/core/sd.h | 3 +-
> drivers/mmc/core/sd_ops.c | 32 ++
> drivers/mmc/core/sd_ops.h | 1 +
> drivers/mmc/core/sdio.c | 3 +-
> drivers/mmc/host/sdhci.c | 697
> ++++++++++++++++++++++++++++++++++++++++++---
> drivers/mmc/host/sdhci.h | 45 +++-
> include/linux/mmc/card.h | 43 +++
> include/linux/mmc/host.h | 26 ++
> include/linux/mmc/mmc.h | 1 +
> include/linux/mmc/sd.h | 3 +-
> include/linux/mmc/sdhci.h | 13 +
> 15 files changed, 1228 insertions(+), 96 deletions(-)
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 00/12] add support for host controller v3.00
2011-03-11 13:13 ` subhashj
@ 2011-03-11 15:29 ` Nath, Arindam
2011-03-11 16:06 ` Chris Ball
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-11 15:29 UTC (permalink / raw)
To: subhashj@codeaurora.org, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
In fact I do run checkpatch.pl on my patches before submitting. The warnings if you notice are all from the header files reporting lines over 80 characters. I did not pay attention to these since if you look into the header files, you will find that lines exceeding 80 characters are common in them. So I am really not sure whether they need to be fixed.
Thanks,
Arindam
> -----Original Message-----
> From: subhashj@codeaurora.org [mailto:subhashj@codeaurora.org]
> Sent: Friday, March 11, 2011 6:43 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 00/12] add support for host controller v3.00
>
> Arindam,
>
> I am not sure whether you run the "./scripts/checkpatch.pl" script for
> your
> patch or not? When I run this script on your patch, it throws following
> warnings.
> You might want to correct these warnings in your next patches.
>
> $ ./scripts/checkpatch.pl 0001-mmc-SD3.0-patches.patch
> WARNING: line over 80 characters
> #765: FILE: include/linux/mmc/card.h:155:
> +#define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in
> ultra
> high speed mode */
>
> WARNING: line over 80 characters
> #830: FILE: include/linux/mmc/host.h:191:
> +#define MMC_CAP_UHS_SDR12 (1 << 15) /* Host supports UHS
> SDR12
> mode */
>
> WARNING: line over 80 characters
> #831: FILE: include/linux/mmc/host.h:192:
> +#define MMC_CAP_UHS_SDR25 (1 << 16) /* Host supports UHS
> SDR25
> mode */
>
> WARNING: line over 80 characters
> #832: FILE: include/linux/mmc/host.h:193:
> +#define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS
> SDR50
> mode */
>
> WARNING: line over 80 characters
> #833: FILE: include/linux/mmc/host.h:194:
> +#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS
> SDR104
> mode */
>
> WARNING: line over 80 characters
> #834: FILE: include/linux/mmc/host.h:195:
> +#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS
> DDR50
> mode */
>
> WARNING: line over 80 characters
> #835: FILE: include/linux/mmc/host.h:196:
> +#define MMC_CAP_SET_XPC_330 (1 << 20) /* Host supports >150mA
> current at 3.3V */
>
> WARNING: line over 80 characters
> #836: FILE: include/linux/mmc/host.h:197:
> +#define MMC_CAP_SET_XPC_300 (1 << 21) /* Host supports >150mA
> current at 3.0V */
>
> WARNING: line over 80 characters
> #837: FILE: include/linux/mmc/host.h:198:
> +#define MMC_CAP_SET_XPC_180 (1 << 22) /* Host supports >150mA
> current at 1.8V */
>
> WARNING: line over 80 characters
> #838: FILE: include/linux/mmc/host.h:199:
> +#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver
> Type
> A */
>
> WARNING: line over 80 characters
> #839: FILE: include/linux/mmc/host.h:200:
> +#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver
> Type
> C */
>
> WARNING: line over 80 characters
> #840: FILE: include/linux/mmc/host.h:201:
> +#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver
> Type
> D */
>
> WARNING: line over 80 characters
> #873: FILE: include/linux/mmc/sd.h:63:
> +#define SCR_SPEC_VER_2 2 /* Implements system
> specification
> 2.00 - 3.0x */
>
> total: 0 errors, 13 warnings, 778 lines checked
>
>
> Regards,
> Subhash
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 00/12] add support for host controller v3.00
> >
> > 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 (12):
> > [PATCH 01/12] mmc: sdhci: add support for auto CMD23
> > [PATCH 02/12] mmc: sd: add support for signal voltage switch
> > procedure
> > [PATCH 03/12] mmc: sd: query function modes for uhs cards
> > [PATCH 04/12] mmc: sd: add support for driver type selection
> > [PATCH 05/12] mmc: sdhci: reset sdclk before setting high speed
> > enable
> > [PATCH 06/12] mmc: sd: add support for uhs bus speed mode selection
> > [PATCH 07/12] mmc: sd: set current limit for uhs cards
> > [PATCH 08/12] mmc: sd: report correct speed and capacity of uhs
> cards
> > [PATCH 09/12] mmc: sd: add support for tuning during uhs
> > initialization
> > [PATCH 10/12] mmc: sdhci: enable preset value after uhs
> > initialization
> > [PATCH 11/12] mmc: sdhci: add support for programmable clock mode
> > [PATCH 12/12] mmc: sdhci: add support for retuning mode 1
> >
> > drivers/mmc/core/bus.c | 11 +-
> > drivers/mmc/core/core.c | 9 +
> > drivers/mmc/core/core.h | 1 +
> > drivers/mmc/core/sd.c | 436 +++++++++++++++++++++++++---
> > drivers/mmc/core/sd.h | 3 +-
> > drivers/mmc/core/sd_ops.c | 32 ++
> > drivers/mmc/core/sd_ops.h | 1 +
> > drivers/mmc/core/sdio.c | 3 +-
> > drivers/mmc/host/sdhci.c | 697
> > ++++++++++++++++++++++++++++++++++++++++++---
> > drivers/mmc/host/sdhci.h | 45 +++-
> > include/linux/mmc/card.h | 43 +++
> > include/linux/mmc/host.h | 26 ++
> > include/linux/mmc/mmc.h | 1 +
> > include/linux/mmc/sd.h | 3 +-
> > include/linux/mmc/sdhci.h | 13 +
> > 15 files changed, 1228 insertions(+), 96 deletions(-)
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 00/12] add support for host controller v3.00
2011-03-11 15:29 ` Nath, Arindam
@ 2011-03-11 16:06 ` Chris Ball
0 siblings, 0 replies; 125+ messages in thread
From: Chris Ball @ 2011-03-11 16:06 UTC (permalink / raw)
To: Nath, Arindam
Cc: subhashj@codeaurora.org, zhangfei.gao@gmail.com,
prakity@marvell.com, linux-mmc@vger.kernel.org, Su, Henry,
Lu, Aaron, anath.amd@gmail.com
Hi,
On Fri, Mar 11 2011, Nath, Arindam wrote:
> In fact I do run checkpatch.pl on my patches before submitting. The
> warnings if you notice are all from the header files reporting lines
> over 80 characters. I did not pay attention to these since if you look
> into the header files, you will find that lines exceeding 80
> characters are common in them. So I am really not sure whether they
> need to be fixed.
I agree, don't worry about these in this case.
Thanks,
- Chris.
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-04 11:32 ` [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
` (2 preceding siblings ...)
2011-03-09 12:45 ` zhangfei gao
@ 2011-03-15 10:18 ` Subhash Jadavani
2011-03-15 10:32 ` Nath, Arindam
2011-03-16 21:39 ` Philip Rakity
2011-03-24 10:52 ` zhangfei gao
5 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-15 10:18 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
Arindam,
During voltage switch, voltage level for CLK, DATA and CMD pads/pins should
be changed from 3.3v to 1.8v. for this SD controller has to modify few bits
in their controller registers and also have to set the voltage level of a
regulator which is powering those pad/pins.
Now with you current patch, you are calling start_signal_voltage_switch
mmc_ops when you want to switch from 3.3v to 1.8v and each controller driver
would then perform above operation in their handler (modifying relevant
controller register and setting the voltage level) along with SD3.0 spec
requirements.
Now let's say after all above initialization and some data transfer with
UHS-I card, card is removed from the slot. Now if I insert the SDHC/SDSC
card (with SDv2.0 support) then how will controller undone the above voltage
switch operations (means changing pad/ping voltage level back from 1.8v to
3.3v)?
To avoid above issue, we can have one new entry in mmc_ios which specifies
the voltage level required for CLK, DATA, CMD pins (0 = 3.3v, 1 = 1.8v).
This entry would be set once start_signal_voltage_switch ops returns
success. So now when card is removed and new card is inserted, at the start
of initialization we should make sure that set_ios() gets called with this
entry reset (0) which would make sure that CLK, DATA and CMD pads/pins
voltage level back to 3.3v.
Regards,
Subhash
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
> 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 support 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.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 37 ++++++++++-
> drivers/mmc/core/sd_ops.c | 32 ++++++++++
> drivers/mmc/core/sd_ops.h | 1 +
> drivers/mmc/host/sdhci.c | 148
> +++++++++++++++++++++++++++++++++++++++++----
> drivers/mmc/host/sdhci.h | 18 +++++-
> include/linux/mmc/host.h | 10 +++
> include/linux/mmc/sd.h | 1 +
> 7 files changed, 229 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index b3f8a3c..3e82599 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -408,6 +408,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
> @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> ocr, u32 *cid)
> if (!err)
> ocr |= 1 << 30;
>
> - err = mmc_send_app_op_cond(host, ocr, NULL);
> + /* If the host can supply more than 150mA, XPC should be set to
> 1. */
> + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> + MMC_CAP_SET_XPC_180))
> + ocr |= 1 << 28;
> +
> + 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)) {
> + err = mmc_start_voltage_switch(host);
> + if (err)
> + return err;
> + }
> +
> if (mmc_host_is_spi(host))
> err = mmc_send_cid(host, cid);
> else
> @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> }
>
> /*
> + * 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))
> + host->ocr |= (1 << 24);
> +
> + /*
> * Detect and init the card.
> */
> err = mmc_sd_init_card(host, host->ocr, NULL);
> - if (err)
> - goto err;
> + if (err == -EAGAIN) {
> + /*
> + * Retry initialization with S18R set to 0.
> + */
> + host->ocr &= ~(1 << 24);
> + err = mmc_sd_init_card(host, host->ocr, NULL);
> + if (err)
> + goto err;
> + }
>
> mmc_release_host(host);
> err = mmc_add_card(host->card);
> diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> index 797cdb5..8a23e2e 100644
> --- a/drivers/mmc/core/sd_ops.c
> +++ b/drivers/mmc/core/sd_ops.c
> @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card,
> int width)
> return 0;
> }
>
> +int mmc_start_voltage_switch(struct mmc_host *host)
> +{
> + struct mmc_command cmd;
> + int err;
> +
> + BUG_ON(!host);
> +
> + /*
> + * If the host does not provide signal voltage switch procedure,
> we
> + * set S18R to 0, and then retry initialization sequence.
> + */
> + if (!host->ops->start_signal_voltage_switch)
> + return -EAGAIN;
> +
> + 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;
> +
> + err = host->ops->start_signal_voltage_switch(host);
> +
> + return err;
> +}
> +
> int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> {
> struct mmc_command cmd;
> diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> index ffc2305..3cfba59 100644
> --- a/drivers/mmc/core/sd_ops.h
> +++ b/drivers/mmc/core/sd_ops.h
> @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> *scr);
> int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> u8 value, u8 *resp);
> int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> +int mmc_start_voltage_switch(struct mmc_host *host);
>
> #endif
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 8914a25..5487a0b 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -85,6 +85,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",
> @@ -1337,11 +1339,70 @@ out:
> spin_unlock_irqrestore(&host->lock, flags);
> }
>
> +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u8 pwr;
> + u16 clk, ctrl;
> + u32 present_state;
> +
> + host = mmc_priv(mmc);
> +
> + /* Stop SDCLK */
> + host = mmc_priv(mmc);
> + 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_readw(host, SDHCI_HOST_CONTROL2);
> + 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. Stop
> + * power to 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);
> +
> + return -EAGAIN;
> +}
> +
> 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,
> };
>
>
> /**********************************************************************
> *******\
> @@ -1799,7 +1860,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);
> @@ -1822,12 +1885,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);
>
> + if (host->version >= SDHCI_SPEC_300)
> + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> +
> 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;
> @@ -1838,7 +1904,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) &&
> @@ -1892,10 +1959,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;
> @@ -1911,7 +1978,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);
> @@ -1923,7 +1990,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;
>
> /*
> @@ -1950,21 +2017,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)
> @@ -2024,7 +2146,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 223762c..95d70e6 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -69,6 +69,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
> @@ -147,7 +149,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
> @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -117,6 +117,8 @@ struct mmc_host_ops {
>
> /* optional callback for HC quirks */
> void (*init_card)(struct mmc_host *host, struct mmc_card *card);
> +
> + int (*start_signal_voltage_switch)(struct mmc_host *host);
> };
>
> struct mmc_card;
> @@ -173,6 +175,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 178363b..3ba5aa6 100644
> --- a/include/linux/mmc/sd.h
> +++ b/include/linux/mmc/sd.h
> @@ -17,6 +17,7 @@
> /* This is basically the same command as for MMC with some quirks. */
> #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6
> */
> #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7
> */
> +#define SD_SWITCH_VOLTAGE 11 /* ac R1
> */
>
> /* class 10 */
> #define SD_SWITCH 6 /* adtc [31:0] See below R1
> */
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-04 11:32 ` [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
2011-03-04 18:27 ` Philip Rakity
2011-03-04 18:34 ` Philip Rakity
@ 2011-03-15 10:32 ` zhangfei gao
2011-03-15 10:43 ` Nath, Arindam
2011-03-21 9:43 ` Subhash Jadavani
2011-03-21 10:58 ` Subhash Jadavani
4 siblings, 1 reply; 125+ messages in thread
From: zhangfei gao @ 2011-03-15 10:32 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Fri, Mar 4, 2011 at 6:32 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. 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 Seletc (bit 7) of Host Control2 register to 0.
> Before exiting the tuning procedure, we disable Buffer Read Ready
> interrupt.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 4 +
> drivers/mmc/host/sdhci.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-
> 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, 149 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index be01397..1e2d157 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
> /* Set current limit for the card */
> err = sd_set_current_limit(card, status);
>
> + /* SPI mode doesn't define CMD19 */
> + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
> + 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 245cc39..8f4f102 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -39,6 +39,8 @@
> #define SDHCI_USE_LEDS_CLASS
> #endif
>
> +#define MAX_TUNING_LOOP 40
> +
> static unsigned int debug_quirks = 0;
>
> static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
> @@ -985,7 +987,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);
> @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct mmc_host *mmc)
> return max_current_180;
> }
>
> +static void sdhci_execute_tuning(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u16 ctrl;
> + int tuning_loop_counter = MAX_TUNING_LOOP;
> + unsigned long flags;
> + unsigned long timeout;
> +
> + host = mmc_priv(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + 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_SDR104) ||
> + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> + else {
> + spin_unlock_irqrestore(&host->lock, flags);
> + return;
> + }
> +
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + /*
> + * As per the Host Controller spec v3.00, tuning command
> + * generates Buffer Read Ready interrupt, so enable that.
> + */
> + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> +
> + /*
> + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
> + * of loops reaches 40 times or a timeout of 150ms occurs.
> + */
> + timeout = 150;
> + do {
> + 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;
Tune may fail since SDHCI_TRNS_DMA is set in SDHCI_TRANSFER_MODE,
SDHCI_TRNS_DMA required to be cleared before send_command, such as
sdhci_writew(host, 0x12, SDHCI_TRANSFER_MODE);
> + sdhci_send_command(host, &cmd);
> +
> + host->cmd = NULL;
> + host->mrq = NULL;
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + /* Wait for Buffer Read Ready interrupt */
> + wait_event_interruptible_timeout(host->buf_ready_int,
> + (host->tuning_done == 1),
> + msecs_to_jiffies(50));
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (!host->tuning_done) {
> + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> + " failed, falling back to fixed sampling"
> + " clock\n");
> + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + 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");
> + }
> +
> +out:
> + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> static const struct mmc_host_ops sdhci_ops = {
> .request = sdhci_request,
> .set_ios = sdhci_set_ios,
> @@ -1492,6 +1609,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,
> .get_max_current_180 = sdhci_get_max_current_180,
> + .execute_tuning = sdhci_execute_tuning,
> };
>
> /*****************************************************************************\
> @@ -1703,6 +1821,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
> @@ -2126,6 +2254,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;
> @@ -2269,6 +2401,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 5bf244d..4746879 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -160,6 +160,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
> @@ -187,6 +189,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 e84cd05..651e40b 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -129,6 +129,7 @@ struct mmc_host_ops {
>
> int (*start_signal_voltage_switch)(struct mmc_host *host);
> int (*get_max_current_180)(struct mmc_host *mmc);
> + void (*execute_tuning)(struct mmc_host *host);
> };
>
> struct mmc_card;
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 612301f..9194f9e 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 282d158..5203b97 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -109,6 +109,7 @@ struct sdhci_host {
> #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
> #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
>
> unsigned int version; /* SDHCI spec. version */
>
> @@ -147,6 +148,9 @@ struct sdhci_host {
>
> struct mmc_command *saved_abort_cmd; /* Abort command saved for data error recovery */
>
> + 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] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-15 10:18 ` Subhash Jadavani
@ 2011-03-15 10:32 ` Nath, Arindam
2011-03-15 11:18 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-15 10:32 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Tuesday, March 15, 2011 3:48 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
> Arindam,
>
> During voltage switch, voltage level for CLK, DATA and CMD pads/pins
> should
> be changed from 3.3v to 1.8v. for this SD controller has to modify few
> bits
> in their controller registers and also have to set the voltage level of
> a
> regulator which is powering those pad/pins.
>
> Now with you current patch, you are calling start_signal_voltage_switch
> mmc_ops when you want to switch from 3.3v to 1.8v and each controller
> driver
> would then perform above operation in their handler (modifying relevant
> controller register and setting the voltage level) along with SD3.0
> spec
> requirements.
>
> Now let's say after all above initialization and some data transfer
> with
> UHS-I card, card is removed from the slot. Now if I insert the
> SDHC/SDSC
> card (with SDv2.0 support) then how will controller undone the above
> voltage
> switch operations (means changing pad/ping voltage level back from 1.8v
> to
> 3.3v)?
>
> To avoid above issue, we can have one new entry in mmc_ios which
> specifies
> the voltage level required for CLK, DATA, CMD pins (0 = 3.3v, 1 =
> 1.8v).
> This entry would be set once start_signal_voltage_switch ops returns
> success. So now when card is removed and new card is inserted, at the
> start
> of initialization we should make sure that set_ios() gets called with
> this
> entry reset (0) which would make sure that CLK, DATA and CMD pads/pins
> voltage level back to 3.3v.
Rather than following the procedure mentioned above, can we simply not reset 1.8V signaling enable in the Host Control2 register when a card is removed? Will that serve the purpose?
Thanks,
Arindam
>
> Regards,
> Subhash
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> > switch procedure
> >
> > 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 support 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.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 37 ++++++++++-
> > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> > drivers/mmc/core/sd_ops.h | 1 +
> > drivers/mmc/host/sdhci.c | 148
> > +++++++++++++++++++++++++++++++++++++++++----
> > drivers/mmc/host/sdhci.h | 18 +++++-
> > include/linux/mmc/host.h | 10 +++
> > include/linux/mmc/sd.h | 1 +
> > 7 files changed, 229 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index b3f8a3c..3e82599 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -408,6 +408,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
> > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> > ocr, u32 *cid)
> > if (!err)
> > ocr |= 1 << 30;
> >
> > - err = mmc_send_app_op_cond(host, ocr, NULL);
> > + /* If the host can supply more than 150mA, XPC should be set to
> > 1. */
> > + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > + MMC_CAP_SET_XPC_180))
> > + ocr |= 1 << 28;
> > +
> > + 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)) {
> > + err = mmc_start_voltage_switch(host);
> > + if (err)
> > + return err;
> > + }
> > +
> > if (mmc_host_is_spi(host))
> > err = mmc_send_cid(host, cid);
> > else
> > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> > }
> >
> > /*
> > + * 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))
> > + host->ocr |= (1 << 24);
> > +
> > + /*
> > * Detect and init the card.
> > */
> > err = mmc_sd_init_card(host, host->ocr, NULL);
> > - if (err)
> > - goto err;
> > + if (err == -EAGAIN) {
> > + /*
> > + * Retry initialization with S18R set to 0.
> > + */
> > + host->ocr &= ~(1 << 24);
> > + err = mmc_sd_init_card(host, host->ocr, NULL);
> > + if (err)
> > + goto err;
> > + }
> >
> > mmc_release_host(host);
> > err = mmc_add_card(host->card);
> > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > index 797cdb5..8a23e2e 100644
> > --- a/drivers/mmc/core/sd_ops.c
> > +++ b/drivers/mmc/core/sd_ops.c
> > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card,
> > int width)
> > return 0;
> > }
> >
> > +int mmc_start_voltage_switch(struct mmc_host *host)
> > +{
> > + struct mmc_command cmd;
> > + int err;
> > +
> > + BUG_ON(!host);
> > +
> > + /*
> > + * If the host does not provide signal voltage switch procedure,
> > we
> > + * set S18R to 0, and then retry initialization sequence.
> > + */
> > + if (!host->ops->start_signal_voltage_switch)
> > + return -EAGAIN;
> > +
> > + 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;
> > +
> > + err = host->ops->start_signal_voltage_switch(host);
> > +
> > + return err;
> > +}
> > +
> > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> > {
> > struct mmc_command cmd;
> > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > index ffc2305..3cfba59 100644
> > --- a/drivers/mmc/core/sd_ops.h
> > +++ b/drivers/mmc/core/sd_ops.h
> > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> > *scr);
> > int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> > u8 value, u8 *resp);
> > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > +int mmc_start_voltage_switch(struct mmc_host *host);
> >
> > #endif
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 8914a25..5487a0b 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -85,6 +85,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",
> > @@ -1337,11 +1339,70 @@ out:
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> >
> > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u8 pwr;
> > + u16 clk, ctrl;
> > + u32 present_state;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + /* Stop SDCLK */
> > + host = mmc_priv(mmc);
> > + 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_readw(host, SDHCI_HOST_CONTROL2);
> > + 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. Stop
> > + * power to 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);
> > +
> > + return -EAGAIN;
> > +}
> > +
> > 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,
> > };
> >
> >
> >
> /**********************************************************************
> > *******\
> > @@ -1799,7 +1860,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);
> > @@ -1822,12 +1885,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);
> >
> > + if (host->version >= SDHCI_SPEC_300)
> > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> > +
> > 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;
> > @@ -1838,7 +1904,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) &&
> > @@ -1892,10 +1959,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;
> > @@ -1911,7 +1978,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);
> > @@ -1923,7 +1990,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;
> >
> > /*
> > @@ -1950,21 +2017,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)
> > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -69,6 +69,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
> > @@ -147,7 +149,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
> > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> >
> > /* optional callback for HC quirks */
> > void (*init_card)(struct mmc_host *host, struct mmc_card *card);
> > +
> > + int (*start_signal_voltage_switch)(struct mmc_host *host);
> > };
> >
> > struct mmc_card;
> > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> > --- a/include/linux/mmc/sd.h
> > +++ b/include/linux/mmc/sd.h
> > @@ -17,6 +17,7 @@
> > /* This is basically the same command as for MMC with some quirks.
> */
> > #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6
> > */
> > #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7
> > */
> > +#define SD_SWITCH_VOLTAGE 11 /* ac R1
> > */
> >
> > /* class 10 */
> > #define SD_SWITCH 6 /* adtc [31:0] See below R1
> > */
> > --
> > 1.7.1
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-15 10:32 ` zhangfei gao
@ 2011-03-15 10:43 ` Nath, Arindam
2011-03-16 2:51 ` zhangfei gao
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-15 10:43 UTC (permalink / raw)
To: zhangfei gao
Cc: cjb@laptop.org, prakity@marvell.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Zhangfei,
> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Tuesday, March 15, 2011 4:02 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; prakity@marvell.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
> On Fri, Mar 4, 2011 at 6:32 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. 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 Seletc (bit 7) of Host Control2 register to 0.
> > Before exiting the tuning procedure, we disable Buffer Read Ready
> > interrupt.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 4 +
> > drivers/mmc/host/sdhci.c | 137
> ++++++++++++++++++++++++++++++++++++++++++++-
> > 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, 149 insertions(+), 1 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index be01397..1e2d157 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
> > /* Set current limit for the card */
> > err = sd_set_current_limit(card, status);
> >
> > + /* SPI mode doesn't define CMD19 */
> > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> >execute_tuning)
> > + 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 245cc39..8f4f102 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -39,6 +39,8 @@
> > #define SDHCI_USE_LEDS_CLASS
> > #endif
> >
> > +#define MAX_TUNING_LOOP 40
> > +
> > static unsigned int debug_quirks = 0;
> >
> > static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data
> *);
> > @@ -985,7 +987,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);
> > @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct
> mmc_host *mmc)
> > return max_current_180;
> > }
> >
> > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u16 ctrl;
> > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > + unsigned long flags;
> > + unsigned long timeout;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + 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_SDR104) ||
> > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > + else {
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return;
> > + }
> > +
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + /*
> > + * As per the Host Controller spec v3.00, tuning command
> > + * generates Buffer Read Ready interrupt, so enable that.
> > + */
> > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > +
> > + /*
> > + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or
> the number
> > + * of loops reaches 40 times or a timeout of 150ms occurs.
> > + */
> > + timeout = 150;
> > + do {
> > + 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;
>
> Tune may fail since SDHCI_TRNS_DMA is set in SDHCI_TRANSFER_MODE,
> SDHCI_TRNS_DMA required to be cleared before send_command, such as
> sdhci_writew(host, 0x12, SDHCI_TRANSFER_MODE);
Can you please tell me why and under what condition can tuning fail when SDHCI_TRANS_DMA is set? Since cmd->data is set to NULL inside sdhci_execute_tuning(), sdhci_set_transfer_mode() will return at the very beginning of the function before setting SDHCI_TRNS_DMA. Moreover, as per the Host Controller v3.00 spec, during tuning procedure, the host controller does not generate interrupts other than Buffer Read Ready.
Thanks,
Arindam
>
> > + sdhci_send_command(host, &cmd);
> > +
> > + host->cmd = NULL;
> > + host->mrq = NULL;
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + /* Wait for Buffer Read Ready interrupt */
> > + wait_event_interruptible_timeout(host->buf_ready_int,
> > + (host->tuning_done == 1),
> > + msecs_to_jiffies(50));
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + if (!host->tuning_done) {
> > + printk(KERN_INFO DRIVER_NAME ": Tuning
> procedure"
> > + " failed, falling back to fixed
> sampling"
> > + " clock\n");
> > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > + sdhci_writew(host, ctrl,
> SDHCI_HOST_CONTROL2);
> > +
> > + 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");
> > + }
> > +
> > +out:
> > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +}
> > +
> > static const struct mmc_host_ops sdhci_ops = {
> > .request = sdhci_request,
> > .set_ios = sdhci_set_ios,
> > @@ -1492,6 +1609,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,
> > .get_max_current_180 = sdhci_get_max_current_180,
> > + .execute_tuning = sdhci_execute_tuning,
> > };
> >
> >
> /*********************************************************************
> ********\
> > @@ -1703,6 +1821,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
> > @@ -2126,6 +2254,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;
> > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -160,6 +160,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
> > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> >
> > int (*start_signal_voltage_switch)(struct mmc_host *host);
> > int (*get_max_current_180)(struct mmc_host *mmc);
> > + void (*execute_tuning)(struct mmc_host *host);
> > };
> >
> > struct mmc_card;
> > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> > index 612301f..9194f9e 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 282d158..5203b97 100644
> > --- a/include/linux/mmc/sdhci.h
> > +++ b/include/linux/mmc/sdhci.h
> > @@ -109,6 +109,7 @@ struct sdhci_host {
> > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
> > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning
> */
> >
> > unsigned int version; /* SDHCI spec. version */
> >
> > @@ -147,6 +148,9 @@ struct sdhci_host {
> >
> > struct mmc_command *saved_abort_cmd; /* Abort command
> saved for data error recovery */
> >
> > + 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] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-15 10:32 ` Nath, Arindam
@ 2011-03-15 11:18 ` Subhash Jadavani
2011-03-15 11:28 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-15 11:18 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Tuesday, March 15, 2011 4:03 PM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Tuesday, March 15, 2011 3:48 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> > switch procedure
> >
> > Arindam,
> >
> > During voltage switch, voltage level for CLK, DATA and CMD pads/pins
> > should
> > be changed from 3.3v to 1.8v. for this SD controller has to modify
> few
> > bits
> > in their controller registers and also have to set the voltage level
> of
> > a
> > regulator which is powering those pad/pins.
> >
> > Now with you current patch, you are calling
> start_signal_voltage_switch
> > mmc_ops when you want to switch from 3.3v to 1.8v and each controller
> > driver
> > would then perform above operation in their handler (modifying
> relevant
> > controller register and setting the voltage level) along with SD3.0
> > spec
> > requirements.
> >
> > Now let's say after all above initialization and some data transfer
> > with
> > UHS-I card, card is removed from the slot. Now if I insert the
> > SDHC/SDSC
> > card (with SDv2.0 support) then how will controller undone the above
> > voltage
> > switch operations (means changing pad/ping voltage level back from
> 1.8v
> > to
> > 3.3v)?
> >
> > To avoid above issue, we can have one new entry in mmc_ios which
> > specifies
> > the voltage level required for CLK, DATA, CMD pins (0 = 3.3v, 1 =
> > 1.8v).
> > This entry would be set once start_signal_voltage_switch ops returns
> > success. So now when card is removed and new card is inserted, at the
> > start
> > of initialization we should make sure that set_ios() gets called with
> > this
> > entry reset (0) which would make sure that CLK, DATA and CMD
> pads/pins
> > voltage level back to 3.3v.
>
> Rather than following the procedure mentioned above, can we simply not
> reset 1.8V signaling enable in the Host Control2 register when a card
> is removed? Will that serve the purpose?
How will the host controller driver comes to know that card is removed
(assuming that there is no hardware based card detection available)? And I
don't think host controller driver itself should take decision to revert
back from 1.8v to 3.3v until core layer tells it do so.
Regarding clearing the bit in Host Control2 register, it's specific to your
controller. Not all controllers/platform will have same bits and also some
controller might have to do something extra than setting few bits in
controller registers. In our case for switching from 3.3v to 1.8v, other
than setting one of the controller register bit, we also need to set the
voltage level of a regulator which is powering these interface pins CMD,
DATA, CLK.
>
> Thanks,
> Arindam
>
> >
> > Regards,
> > Subhash
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> > > switch procedure
> > >
> > > 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 support 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.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/sd.c | 37 ++++++++++-
> > > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> > > drivers/mmc/core/sd_ops.h | 1 +
> > > drivers/mmc/host/sdhci.c | 148
> > > +++++++++++++++++++++++++++++++++++++++++----
> > > drivers/mmc/host/sdhci.h | 18 +++++-
> > > include/linux/mmc/host.h | 10 +++
> > > include/linux/mmc/sd.h | 1 +
> > > 7 files changed, 229 insertions(+), 18 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index b3f8a3c..3e82599 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -408,6 +408,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
> > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> > > ocr, u32 *cid)
> > > if (!err)
> > > ocr |= 1 << 30;
> > >
> > > - err = mmc_send_app_op_cond(host, ocr, NULL);
> > > + /* If the host can supply more than 150mA, XPC should be set to
> > > 1. */
> > > + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > > + MMC_CAP_SET_XPC_180))
> > > + ocr |= 1 << 28;
> > > +
> > > + 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)) {
> > > + err = mmc_start_voltage_switch(host);
> > > + if (err)
> > > + return err;
> > > + }
> > > +
> > > if (mmc_host_is_spi(host))
> > > err = mmc_send_cid(host, cid);
> > > else
> > > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> > > }
> > >
> > > /*
> > > + * 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))
> > > + host->ocr |= (1 << 24);
> > > +
> > > + /*
> > > * Detect and init the card.
> > > */
> > > err = mmc_sd_init_card(host, host->ocr, NULL);
> > > - if (err)
> > > - goto err;
> > > + if (err == -EAGAIN) {
> > > + /*
> > > + * Retry initialization with S18R set to 0.
> > > + */
> > > + host->ocr &= ~(1 << 24);
> > > + err = mmc_sd_init_card(host, host->ocr, NULL);
> > > + if (err)
> > > + goto err;
> > > + }
> > >
> > > mmc_release_host(host);
> > > err = mmc_add_card(host->card);
> > > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > > index 797cdb5..8a23e2e 100644
> > > --- a/drivers/mmc/core/sd_ops.c
> > > +++ b/drivers/mmc/core/sd_ops.c
> > > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card
> *card,
> > > int width)
> > > return 0;
> > > }
> > >
> > > +int mmc_start_voltage_switch(struct mmc_host *host)
> > > +{
> > > + struct mmc_command cmd;
> > > + int err;
> > > +
> > > + BUG_ON(!host);
> > > +
> > > + /*
> > > + * If the host does not provide signal voltage switch
> procedure,
> > > we
> > > + * set S18R to 0, and then retry initialization sequence.
> > > + */
> > > + if (!host->ops->start_signal_voltage_switch)
> > > + return -EAGAIN;
> > > +
> > > + 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;
> > > +
> > > + err = host->ops->start_signal_voltage_switch(host);
> > > +
> > > + return err;
> > > +}
> > > +
> > > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32
> *rocr)
> > > {
> > > struct mmc_command cmd;
> > > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > > index ffc2305..3cfba59 100644
> > > --- a/drivers/mmc/core/sd_ops.h
> > > +++ b/drivers/mmc/core/sd_ops.h
> > > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> > > *scr);
> > > int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> > > u8 value, u8 *resp);
> > > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > > +int mmc_start_voltage_switch(struct mmc_host *host);
> > >
> > > #endif
> > >
> > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > index 8914a25..5487a0b 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -85,6 +85,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",
> > > @@ -1337,11 +1339,70 @@ out:
> > > spin_unlock_irqrestore(&host->lock, flags);
> > > }
> > >
> > > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > > +{
> > > + struct sdhci_host *host;
> > > + u8 pwr;
> > > + u16 clk, ctrl;
> > > + u32 present_state;
> > > +
> > > + host = mmc_priv(mmc);
> > > +
> > > + /* Stop SDCLK */
> > > + host = mmc_priv(mmc);
> > > + 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_readw(host, SDHCI_HOST_CONTROL2);
> > > + 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. Stop
> > > + * power to 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);
> > > +
> > > + return -EAGAIN;
> > > +}
> > > +
> > > 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,
> > > };
> > >
> > >
> > >
> >
> /**********************************************************************
> > > *******\
> > > @@ -1799,7 +1860,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);
> > > @@ -1822,12 +1885,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);
> > >
> > > + if (host->version >= SDHCI_SPEC_300)
> > > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> > > +
> > > 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;
> > > @@ -1838,7 +1904,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) &&
> > > @@ -1892,10 +1959,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;
> > > @@ -1911,7 +1978,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);
> > > @@ -1923,7 +1990,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;
> > >
> > > /*
> > > @@ -1950,21 +2017,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)
> > > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> > > --- a/drivers/mmc/host/sdhci.h
> > > +++ b/drivers/mmc/host/sdhci.h
> > > @@ -69,6 +69,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
> > > @@ -147,7 +149,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
> > > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> > > --- a/include/linux/mmc/host.h
> > > +++ b/include/linux/mmc/host.h
> > > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> > >
> > > /* optional callback for HC quirks */
> > > void (*init_card)(struct mmc_host *host, struct mmc_card
> *card);
> > > +
> > > + int (*start_signal_voltage_switch)(struct mmc_host *host);
> > > };
> > >
> > > struct mmc_card;
> > > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> > > --- a/include/linux/mmc/sd.h
> > > +++ b/include/linux/mmc/sd.h
> > > @@ -17,6 +17,7 @@
> > > /* This is basically the same command as for MMC with some quirks.
> > */
> > > #define SD_SEND_RELATIVE_ADDR 3 /* bcr
> R6
> > > */
> > > #define SD_SEND_IF_COND 8 /* bcr [11:0] See below
> R7
> > > */
> > > +#define SD_SWITCH_VOLTAGE 11 /* ac
> R1
> > > */
> > >
> > > /* class 10 */
> > > #define SD_SWITCH 6 /* adtc [31:0] See below
> R1
> > > */
> > > --
> > > 1.7.1
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
2011-03-04 11:32 ` [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
2011-03-09 12:22 ` subhashj
@ 2011-03-15 11:23 ` Subhash Jadavani
2011-03-15 11:35 ` Nath, Arindam
1 sibling, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-15 11:23 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
Arindam,
Capability to send CMD23 automatically is something specific to your
controller.
CMD23 (SET_BLOCK_COUNT) is a new command added in SD3.0 spec and mandatory
for SDR104 mode. Why are you not adding this command in core/block layer
before sending multi byte read command to host controller driver?
Regards,
Subhash
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
>
> Host Controller v3.00 and later support Auto CMD23 in the Transfer
> Mode register. Since Auto CMD23 can be used with or without DMA,
> and if used with DMA, it should _only_ be ADMA, we check against
> SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
> is set.
>
> A new definition for SDHCI_ARGUMENT2 register has been added in v3.00
> spec, which is the same as SDHCI_DMA_ADDRESS. We program the block
> count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to stop
> multiple block transfers. But during error recovery procedure, we will
> need to send Abort command, so we use a variable saved_abort_cmd to
> save the stop command to be used later.
>
> Two bits are added to SCR register as per the Physical Layer Spec
> v3.01,
> which specify whether the card supports CMD20 and/or CMD23. We use this
> as one of the conditions to decide whether to enable Auto CMD23 or not.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 6 ++++
> drivers/mmc/host/sdhci.c | 69
> +++++++++++++++++++++++++++++++++++++++++---
> drivers/mmc/host/sdhci.h | 2 +
> include/linux/mmc/card.h | 4 ++
> include/linux/mmc/sd.h | 2 +-
> include/linux/mmc/sdhci.h | 2 +
> 6 files changed, 79 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index d18c32b..b3f8a3c 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -188,6 +188,12 @@ 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 (scr->sda_spec3)
> + scr->cmd_support = UNSTUFF_BITS(resp, 32, 2);
> + }
>
> if (UNSTUFF_BITS(resp, 55, 1))
> card->erased_byte = 0xFF;
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 9e15f41..8914a25 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -25,6 +25,7 @@
>
> #include <linux/mmc/mmc.h>
> #include <linux/mmc/host.h>
> +#include <linux/mmc/card.h>
>
> #include "sdhci.h"
>
> @@ -812,6 +813,30 @@ static void sdhci_prepare_data(struct sdhci_host
> *host, struct mmc_data *data)
> sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
> }
>
> +/*
> + * Does the Host Controller support Auto CMD23?
> + *
> + * There are four preconditions for Auto CMD23 to be supported:
> + * 1. Host Controller v3.00 or later
> + * 2. Card supports CMD23
> + * 3. If DMA is used, it should be ADMA
> + * 4. Only when CMD18 or CMD25 is issued
> + */
> +static int sdhci_host_auto_cmd23_supported(struct sdhci_host *host,
> + struct mmc_request *mrq)
> +{
> + struct mmc_card *card = host->mmc->card;
> +
> + if ((host->version >= SDHCI_SPEC_300) &&
> + card && (card->scr.cmd_support & SD_SCR_CMD23_SUPPORT) &&
> + !(host->flags & SDHCI_USE_SDMA) &&
> + ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
> + (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)))
> + return 1;
> +
> + return 0;
> +}
> +
> static void sdhci_set_transfer_mode(struct sdhci_host *host,
> struct mmc_data *data)
> {
> @@ -825,10 +850,21 @@ static void sdhci_set_transfer_mode(struct
> sdhci_host *host,
> mode = SDHCI_TRNS_BLK_CNT_EN;
> if (data->blocks > 1) {
> if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
> - mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
> - else
> - mode |= SDHCI_TRNS_MULTI;
> + mode |= SDHCI_TRNS_ACMD12;
> + else if (sdhci_host_auto_cmd23_supported(host, host->mrq))
> {
> + /*
> + * Host Controller v3.00 can automatically send
CMD23
> + * before CMD18 or CMD25. For that, we need to
enable
> + * Auto CMD23 in the Transfer Mode register and
> + * program the block count in Argument 2 register.
> + */
> + mode |= SDHCI_TRNS_AUTO_CMD23;
> + sdhci_writel(host, data->blocks, SDHCI_ARGUMENT2);
> + }
> +
> + mode |= SDHCI_TRNS_MULTI;
> }
> +
> if (data->flags & MMC_DATA_READ)
> mode |= SDHCI_TRNS_READ;
> if (host->flags & SDHCI_REQ_USE_DMA)
> @@ -1131,6 +1167,23 @@ static void sdhci_request(struct mmc_host *mmc,
> struct mmc_request *mrq)
> #ifndef SDHCI_USE_LEDS_CLASS
> sdhci_activate_led(host);
> #endif
> + /*
> + * Since the block count for CMD23 has already been specified in
> the
> + * Argument 2 register, we don't need CMD12 to stop multiple
> block
> + * transfers.
> + */
> + if (sdhci_host_auto_cmd23_supported(host, mrq)) {
> + if (mrq->stop) {
> + /*
> + * Save the stop command here to be used during
> + * error recovery
> + */
> + host->saved_abort_cmd = mrq->data->stop;
> + mrq->data->stop = NULL;
> + mrq->stop = NULL;
> + }
> + }
> +
> if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
> if (mrq->stop) {
> mrq->data->stop = NULL;
> @@ -1396,6 +1449,7 @@ static void sdhci_timeout_timer(unsigned long
> data)
>
> if (host->data) {
> host->data->error = -ETIMEDOUT;
> + host->data->stop = host->saved_abort_cmd;
> sdhci_finish_data(host);
> } else {
> if (host->cmd)
> @@ -1534,9 +1588,10 @@ static void sdhci_data_irq(struct sdhci_host
> *host, u32 intmask)
> host->data->error = -EIO;
> }
>
> - if (host->data->error)
> + if (host->data->error) {
> + host->data->stop = host->saved_abort_cmd;
> sdhci_finish_data(host);
> - else {
> + } else {
> if (intmask & (SDHCI_INT_DATA_AVAIL |
> SDHCI_INT_SPACE_AVAIL))
> sdhci_transfer_pio(host);
>
> @@ -1792,6 +1847,10 @@ int sdhci_add_host(struct sdhci_host *host)
> host->flags &= ~SDHCI_USE_ADMA;
> }
>
> + /* If the host can perform ADMA operation, we reset SDMA flag */
> + if (host->flags & SDHCI_USE_ADMA)
> + host->flags &= ~SDHCI_USE_SDMA;
> +
> if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
> if (host->ops->enable_dma) {
> if (host->ops->enable_dma(host)) {
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 6e0969e..223762c 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -25,6 +25,7 @@
> */
>
> #define SDHCI_DMA_ADDRESS 0x00
> +#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
>
> #define SDHCI_BLOCK_SIZE 0x04
> #define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz &
> 0xFFF))
> @@ -37,6 +38,7 @@
> #define SDHCI_TRNS_DMA 0x01
> #define SDHCI_TRNS_BLK_CNT_EN 0x02
> #define SDHCI_TRNS_ACMD12 0x04
> +#define SDHCI_TRNS_AUTO_CMD23 0x08
> #define SDHCI_TRNS_READ 0x10
> #define SDHCI_TRNS_MULTI 0x20
>
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 8ce0827..22b0335 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -58,9 +58,13 @@ 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)
> + unsigned char cmd_support;
> +#define SD_SCR_CMD20_SUPPORT (1<<0)
> +#define SD_SCR_CMD23_SUPPORT (1<<1)
> };
>
> struct sd_ssr {
> diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
> index 3fd85e0..178363b 100644
> --- a/include/linux/mmc/sd.h
> +++ b/include/linux/mmc/sd.h
> @@ -59,7 +59,7 @@
>
> #define SCR_SPEC_VER_0 0 /* Implements system
specification
> 1.0 - 1.01 */
> #define SCR_SPEC_VER_1 1 /* Implements system
specification
> 1.10 */
> -#define SCR_SPEC_VER_2 2 /* Implements system
specification
> 2.00 */
> +#define SCR_SPEC_VER_2 2 /* Implements system
specification
> 2.00 - 3.0x */
>
> /*
> * SD bus widths
> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> index 83bd9f7..282d158 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -145,6 +145,8 @@ struct sdhci_host {
> unsigned int ocr_avail_sd;
> unsigned int ocr_avail_mmc;
>
> + struct mmc_command *saved_abort_cmd; /* Abort command saved
> for data error recovery */
> +
> unsigned long private[0] ____cacheline_aligned;
> };
> #endif /* __SDHCI_H */
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-15 11:18 ` Subhash Jadavani
@ 2011-03-15 11:28 ` Nath, Arindam
2011-03-15 11:58 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-15 11:28 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Tuesday, March 15, 2011 4:49 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Tuesday, March 15, 2011 4:03 PM
> > To: Subhash Jadavani; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> > switch procedure
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > Sent: Tuesday, March 15, 2011 3:48 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
> voltage
> > > switch procedure
> > >
> > > Arindam,
> > >
> > > During voltage switch, voltage level for CLK, DATA and CMD
> pads/pins
> > > should
> > > be changed from 3.3v to 1.8v. for this SD controller has to modify
> > few
> > > bits
> > > in their controller registers and also have to set the voltage
> level
> > of
> > > a
> > > regulator which is powering those pad/pins.
> > >
> > > Now with you current patch, you are calling
> > start_signal_voltage_switch
> > > mmc_ops when you want to switch from 3.3v to 1.8v and each
> controller
> > > driver
> > > would then perform above operation in their handler (modifying
> > relevant
> > > controller register and setting the voltage level) along with SD3.0
> > > spec
> > > requirements.
> > >
> > > Now let's say after all above initialization and some data transfer
> > > with
> > > UHS-I card, card is removed from the slot. Now if I insert the
> > > SDHC/SDSC
> > > card (with SDv2.0 support) then how will controller undone the
> above
> > > voltage
> > > switch operations (means changing pad/ping voltage level back from
> > 1.8v
> > > to
> > > 3.3v)?
> > >
> > > To avoid above issue, we can have one new entry in mmc_ios which
> > > specifies
> > > the voltage level required for CLK, DATA, CMD pins (0 = 3.3v, 1 =
> > > 1.8v).
> > > This entry would be set once start_signal_voltage_switch ops
> returns
> > > success. So now when card is removed and new card is inserted, at
> the
> > > start
> > > of initialization we should make sure that set_ios() gets called
> with
> > > this
> > > entry reset (0) which would make sure that CLK, DATA and CMD
> > pads/pins
> > > voltage level back to 3.3v.
> >
> > Rather than following the procedure mentioned above, can we simply
> not
> > reset 1.8V signaling enable in the Host Control2 register when a card
> > is removed? Will that serve the purpose?
>
> How will the host controller driver comes to know that card is removed
> (assuming that there is no hardware based card detection available)?
Correct me if wrong, but I think bit 7 of Normal Interrupt Status Register can inform regarding the card removal event.
> And I
> don't think host controller driver itself should take decision to
> revert
> back from 1.8v to 3.3v until core layer tells it do so.
>
> Regarding clearing the bit in Host Control2 register, it's specific to
> your
> controller. Not all controllers/platform will have same bits
As part of signal voltage switch procedure, we set 1.8V signaling enable in the Host Control2 register. So the same bit can be reset to set the signaling voltage back to 3.3V.
and also
> some
> controller might have to do something extra than setting few bits in
> controller registers. In our case for switching from 3.3v to 1.8v,
> other
> than setting one of the controller register bit, we also need to set
> the
> voltage level of a regulator which is powering these interface pins
> CMD,
> DATA, CLK.
In that case I will modify the code to add an entry into mmc_ios as per your suggestion.
Thanks,
Arindam
>
>
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > > Regards,
> > > Subhash
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > To: cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > henry.su@amd.com;
> > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > Subject: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> > > > switch procedure
> > > >
> > > > 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 support 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.
> > > >
> > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > ---
> > > > drivers/mmc/core/sd.c | 37 ++++++++++-
> > > > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> > > > drivers/mmc/core/sd_ops.h | 1 +
> > > > drivers/mmc/host/sdhci.c | 148
> > > > +++++++++++++++++++++++++++++++++++++++++----
> > > > drivers/mmc/host/sdhci.h | 18 +++++-
> > > > include/linux/mmc/host.h | 10 +++
> > > > include/linux/mmc/sd.h | 1 +
> > > > 7 files changed, 229 insertions(+), 18 deletions(-)
> > > >
> > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > index b3f8a3c..3e82599 100644
> > > > --- a/drivers/mmc/core/sd.c
> > > > +++ b/drivers/mmc/core/sd.c
> > > > @@ -408,6 +408,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
> > > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host,
> u32
> > > > ocr, u32 *cid)
> > > > if (!err)
> > > > ocr |= 1 << 30;
> > > >
> > > > - err = mmc_send_app_op_cond(host, ocr, NULL);
> > > > + /* If the host can supply more than 150mA, XPC should be set
> to
> > > > 1. */
> > > > + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > > > + MMC_CAP_SET_XPC_180))
> > > > + ocr |= 1 << 28;
> > > > +
> > > > + 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)) {
> > > > + err = mmc_start_voltage_switch(host);
> > > > + if (err)
> > > > + return err;
> > > > + }
> > > > +
> > > > if (mmc_host_is_spi(host))
> > > > err = mmc_send_cid(host, cid);
> > > > else
> > > > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> > > > }
> > > >
> > > > /*
> > > > + * 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))
> > > > + host->ocr |= (1 << 24);
> > > > +
> > > > + /*
> > > > * Detect and init the card.
> > > > */
> > > > err = mmc_sd_init_card(host, host->ocr, NULL);
> > > > - if (err)
> > > > - goto err;
> > > > + if (err == -EAGAIN) {
> > > > + /*
> > > > + * Retry initialization with S18R set to 0.
> > > > + */
> > > > + host->ocr &= ~(1 << 24);
> > > > + err = mmc_sd_init_card(host, host->ocr, NULL);
> > > > + if (err)
> > > > + goto err;
> > > > + }
> > > >
> > > > mmc_release_host(host);
> > > > err = mmc_add_card(host->card);
> > > > diff --git a/drivers/mmc/core/sd_ops.c
> b/drivers/mmc/core/sd_ops.c
> > > > index 797cdb5..8a23e2e 100644
> > > > --- a/drivers/mmc/core/sd_ops.c
> > > > +++ b/drivers/mmc/core/sd_ops.c
> > > > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card
> > *card,
> > > > int width)
> > > > return 0;
> > > > }
> > > >
> > > > +int mmc_start_voltage_switch(struct mmc_host *host)
> > > > +{
> > > > + struct mmc_command cmd;
> > > > + int err;
> > > > +
> > > > + BUG_ON(!host);
> > > > +
> > > > + /*
> > > > + * If the host does not provide signal voltage switch
> > procedure,
> > > > we
> > > > + * set S18R to 0, and then retry initialization sequence.
> > > > + */
> > > > + if (!host->ops->start_signal_voltage_switch)
> > > > + return -EAGAIN;
> > > > +
> > > > + 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;
> > > > +
> > > > + err = host->ops->start_signal_voltage_switch(host);
> > > > +
> > > > + return err;
> > > > +}
> > > > +
> > > > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32
> > *rocr)
> > > > {
> > > > struct mmc_command cmd;
> > > > diff --git a/drivers/mmc/core/sd_ops.h
> b/drivers/mmc/core/sd_ops.h
> > > > index ffc2305..3cfba59 100644
> > > > --- a/drivers/mmc/core/sd_ops.h
> > > > +++ b/drivers/mmc/core/sd_ops.h
> > > > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> > > > *scr);
> > > > int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> > > > u8 value, u8 *resp);
> > > > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > > > +int mmc_start_voltage_switch(struct mmc_host *host);
> > > >
> > > > #endif
> > > >
> > > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > > index 8914a25..5487a0b 100644
> > > > --- a/drivers/mmc/host/sdhci.c
> > > > +++ b/drivers/mmc/host/sdhci.c
> > > > @@ -85,6 +85,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",
> > > > @@ -1337,11 +1339,70 @@ out:
> > > > spin_unlock_irqrestore(&host->lock, flags);
> > > > }
> > > >
> > > > +static int sdhci_start_signal_voltage_switch(struct mmc_host
> *mmc)
> > > > +{
> > > > + struct sdhci_host *host;
> > > > + u8 pwr;
> > > > + u16 clk, ctrl;
> > > > + u32 present_state;
> > > > +
> > > > + host = mmc_priv(mmc);
> > > > +
> > > > + /* Stop SDCLK */
> > > > + host = mmc_priv(mmc);
> > > > + 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_readw(host, SDHCI_HOST_CONTROL2);
> > > > + 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. Stop
> > > > + * power to 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);
> > > > +
> > > > + return -EAGAIN;
> > > > +}
> > > > +
> > > > 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,
> > > > };
> > > >
> > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > *******\
> > > > @@ -1799,7 +1860,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);
> > > > @@ -1822,12 +1885,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);
> > > >
> > > > + if (host->version >= SDHCI_SPEC_300)
> > > > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> > > > +
> > > > 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;
> > > > @@ -1838,7 +1904,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) &&
> > > > @@ -1892,10 +1959,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;
> > > > @@ -1911,7 +1978,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);
> > > > @@ -1923,7 +1990,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;
> > > >
> > > > /*
> > > > @@ -1950,21 +2017,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)
> > > > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> > > > --- a/drivers/mmc/host/sdhci.h
> > > > +++ b/drivers/mmc/host/sdhci.h
> > > > @@ -69,6 +69,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
> > > > @@ -147,7 +149,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
> > > > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> > > > --- a/include/linux/mmc/host.h
> > > > +++ b/include/linux/mmc/host.h
> > > > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> > > >
> > > > /* optional callback for HC quirks */
> > > > void (*init_card)(struct mmc_host *host, struct mmc_card
> > *card);
> > > > +
> > > > + int (*start_signal_voltage_switch)(struct mmc_host
> *host);
> > > > };
> > > >
> > > > struct mmc_card;
> > > > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> > > > --- a/include/linux/mmc/sd.h
> > > > +++ b/include/linux/mmc/sd.h
> > > > @@ -17,6 +17,7 @@
> > > > /* This is basically the same command as for MMC with some
> quirks.
> > > */
> > > > #define SD_SEND_RELATIVE_ADDR 3 /* bcr
> > R6
> > > > */
> > > > #define SD_SEND_IF_COND 8 /* bcr [11:0] See below
> > R7
> > > > */
> > > > +#define SD_SWITCH_VOLTAGE 11 /* ac
> > R1
> > > > */
> > > >
> > > > /* class 10 */
> > > > #define SD_SWITCH 6 /* adtc [31:0] See below
> > R1
> > > > */
> > > > --
> > > > 1.7.1
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
2011-03-15 11:23 ` Subhash Jadavani
@ 2011-03-15 11:35 ` Nath, Arindam
2011-03-15 11:52 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-15 11:35 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Tuesday, March 15, 2011 4:54 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
>
> Arindam,
>
> Capability to send CMD23 automatically is something specific to your
> controller.
Auto CMD23 is different from ACMD23. Auto CMD23 is a feature added into Host Controller Spec v3.00, and the code follows the procedure to check the 4 preconditions required to enable Host to send CMD23 automatically. I had similar discussion on the community with Arnd few weeks back, and we agreed that this should be part of controller specific code.
Thanks,
Arindam
> CMD23 (SET_BLOCK_COUNT) is a new command added in SD3.0 spec and
> mandatory
> for SDR104 mode. Why are you not adding this command in core/block
> layer
> before sending multi byte read command to host controller driver?
>
> Regards,
> Subhash
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
> >
> > Host Controller v3.00 and later support Auto CMD23 in the Transfer
> > Mode register. Since Auto CMD23 can be used with or without DMA,
> > and if used with DMA, it should _only_ be ADMA, we check against
> > SDHCI_USE_SDMA not being set. This flag is reset when SDHCI_USE_ADMA
> > is set.
> >
> > A new definition for SDHCI_ARGUMENT2 register has been added in v3.00
> > spec, which is the same as SDHCI_DMA_ADDRESS. We program the block
> > count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to stop
> > multiple block transfers. But during error recovery procedure, we
> will
> > need to send Abort command, so we use a variable saved_abort_cmd to
> > save the stop command to be used later.
> >
> > Two bits are added to SCR register as per the Physical Layer Spec
> > v3.01,
> > which specify whether the card supports CMD20 and/or CMD23. We use
> this
> > as one of the conditions to decide whether to enable Auto CMD23 or
> not.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 6 ++++
> > drivers/mmc/host/sdhci.c | 69
> > +++++++++++++++++++++++++++++++++++++++++---
> > drivers/mmc/host/sdhci.h | 2 +
> > include/linux/mmc/card.h | 4 ++
> > include/linux/mmc/sd.h | 2 +-
> > include/linux/mmc/sdhci.h | 2 +
> > 6 files changed, 79 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index d18c32b..b3f8a3c 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -188,6 +188,12 @@ 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 (scr->sda_spec3)
> > + scr->cmd_support = UNSTUFF_BITS(resp, 32, 2);
> > + }
> >
> > if (UNSTUFF_BITS(resp, 55, 1))
> > card->erased_byte = 0xFF;
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 9e15f41..8914a25 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -25,6 +25,7 @@
> >
> > #include <linux/mmc/mmc.h>
> > #include <linux/mmc/host.h>
> > +#include <linux/mmc/card.h>
> >
> > #include "sdhci.h"
> >
> > @@ -812,6 +813,30 @@ static void sdhci_prepare_data(struct sdhci_host
> > *host, struct mmc_data *data)
> > sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
> > }
> >
> > +/*
> > + * Does the Host Controller support Auto CMD23?
> > + *
> > + * There are four preconditions for Auto CMD23 to be supported:
> > + * 1. Host Controller v3.00 or later
> > + * 2. Card supports CMD23
> > + * 3. If DMA is used, it should be ADMA
> > + * 4. Only when CMD18 or CMD25 is issued
> > + */
> > +static int sdhci_host_auto_cmd23_supported(struct sdhci_host *host,
> > + struct mmc_request *mrq)
> > +{
> > + struct mmc_card *card = host->mmc->card;
> > +
> > + if ((host->version >= SDHCI_SPEC_300) &&
> > + card && (card->scr.cmd_support & SD_SCR_CMD23_SUPPORT) &&
> > + !(host->flags & SDHCI_USE_SDMA) &&
> > + ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
> > + (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)))
> > + return 1;
> > +
> > + return 0;
> > +}
> > +
> > static void sdhci_set_transfer_mode(struct sdhci_host *host,
> > struct mmc_data *data)
> > {
> > @@ -825,10 +850,21 @@ static void sdhci_set_transfer_mode(struct
> > sdhci_host *host,
> > mode = SDHCI_TRNS_BLK_CNT_EN;
> > if (data->blocks > 1) {
> > if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
> > - mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
> > - else
> > - mode |= SDHCI_TRNS_MULTI;
> > + mode |= SDHCI_TRNS_ACMD12;
> > + else if (sdhci_host_auto_cmd23_supported(host, host->mrq))
> > {
> > + /*
> > + * Host Controller v3.00 can automatically send
> CMD23
> > + * before CMD18 or CMD25. For that, we need to
> enable
> > + * Auto CMD23 in the Transfer Mode register and
> > + * program the block count in Argument 2 register.
> > + */
> > + mode |= SDHCI_TRNS_AUTO_CMD23;
> > + sdhci_writel(host, data->blocks, SDHCI_ARGUMENT2);
> > + }
> > +
> > + mode |= SDHCI_TRNS_MULTI;
> > }
> > +
> > if (data->flags & MMC_DATA_READ)
> > mode |= SDHCI_TRNS_READ;
> > if (host->flags & SDHCI_REQ_USE_DMA)
> > @@ -1131,6 +1167,23 @@ static void sdhci_request(struct mmc_host
> *mmc,
> > struct mmc_request *mrq)
> > #ifndef SDHCI_USE_LEDS_CLASS
> > sdhci_activate_led(host);
> > #endif
> > + /*
> > + * Since the block count for CMD23 has already been specified in
> > the
> > + * Argument 2 register, we don't need CMD12 to stop multiple
> > block
> > + * transfers.
> > + */
> > + if (sdhci_host_auto_cmd23_supported(host, mrq)) {
> > + if (mrq->stop) {
> > + /*
> > + * Save the stop command here to be used during
> > + * error recovery
> > + */
> > + host->saved_abort_cmd = mrq->data->stop;
> > + mrq->data->stop = NULL;
> > + mrq->stop = NULL;
> > + }
> > + }
> > +
> > if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
> > if (mrq->stop) {
> > mrq->data->stop = NULL;
> > @@ -1396,6 +1449,7 @@ static void sdhci_timeout_timer(unsigned long
> > data)
> >
> > if (host->data) {
> > host->data->error = -ETIMEDOUT;
> > + host->data->stop = host->saved_abort_cmd;
> > sdhci_finish_data(host);
> > } else {
> > if (host->cmd)
> > @@ -1534,9 +1588,10 @@ static void sdhci_data_irq(struct sdhci_host
> > *host, u32 intmask)
> > host->data->error = -EIO;
> > }
> >
> > - if (host->data->error)
> > + if (host->data->error) {
> > + host->data->stop = host->saved_abort_cmd;
> > sdhci_finish_data(host);
> > - else {
> > + } else {
> > if (intmask & (SDHCI_INT_DATA_AVAIL |
> > SDHCI_INT_SPACE_AVAIL))
> > sdhci_transfer_pio(host);
> >
> > @@ -1792,6 +1847,10 @@ int sdhci_add_host(struct sdhci_host *host)
> > host->flags &= ~SDHCI_USE_ADMA;
> > }
> >
> > + /* If the host can perform ADMA operation, we reset SDMA flag */
> > + if (host->flags & SDHCI_USE_ADMA)
> > + host->flags &= ~SDHCI_USE_SDMA;
> > +
> > if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
> > if (host->ops->enable_dma) {
> > if (host->ops->enable_dma(host)) {
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index 6e0969e..223762c 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -25,6 +25,7 @@
> > */
> >
> > #define SDHCI_DMA_ADDRESS 0x00
> > +#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
> >
> > #define SDHCI_BLOCK_SIZE 0x04
> > #define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz
> &
> > 0xFFF))
> > @@ -37,6 +38,7 @@
> > #define SDHCI_TRNS_DMA 0x01
> > #define SDHCI_TRNS_BLK_CNT_EN 0x02
> > #define SDHCI_TRNS_ACMD12 0x04
> > +#define SDHCI_TRNS_AUTO_CMD23 0x08
> > #define SDHCI_TRNS_READ 0x10
> > #define SDHCI_TRNS_MULTI 0x20
> >
> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > index 8ce0827..22b0335 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -58,9 +58,13 @@ 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)
> > + unsigned char cmd_support;
> > +#define SD_SCR_CMD20_SUPPORT (1<<0)
> > +#define SD_SCR_CMD23_SUPPORT (1<<1)
> > };
> >
> > struct sd_ssr {
> > diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
> > index 3fd85e0..178363b 100644
> > --- a/include/linux/mmc/sd.h
> > +++ b/include/linux/mmc/sd.h
> > @@ -59,7 +59,7 @@
> >
> > #define SCR_SPEC_VER_0 0 /* Implements system
> specification
> > 1.0 - 1.01 */
> > #define SCR_SPEC_VER_1 1 /* Implements system
> specification
> > 1.10 */
> > -#define SCR_SPEC_VER_2 2 /* Implements system
> specification
> > 2.00 */
> > +#define SCR_SPEC_VER_2 2 /* Implements system
> specification
> > 2.00 - 3.0x */
> >
> > /*
> > * SD bus widths
> > diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> > index 83bd9f7..282d158 100644
> > --- a/include/linux/mmc/sdhci.h
> > +++ b/include/linux/mmc/sdhci.h
> > @@ -145,6 +145,8 @@ struct sdhci_host {
> > unsigned int ocr_avail_sd;
> > unsigned int ocr_avail_mmc;
> >
> > + struct mmc_command *saved_abort_cmd; /* Abort command saved
> > for data error recovery */
> > +
> > unsigned long private[0] ____cacheline_aligned;
> > };
> > #endif /* __SDHCI_H */
> > --
> > 1.7.1
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
2011-03-15 11:35 ` Nath, Arindam
@ 2011-03-15 11:52 ` Subhash Jadavani
2011-03-16 6:07 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-15 11:52 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Tuesday, March 15, 2011 5:05 PM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Tuesday, March 15, 2011 4:54 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
> >
> > Arindam,
> >
> > Capability to send CMD23 automatically is something specific to your
> > controller.
>
> Auto CMD23 is different from ACMD23. Auto CMD23 is a feature added into
> Host Controller Spec v3.00, and the code follows the procedure to check
> the 4 preconditions required to enable Host to send CMD23
> automatically. I had similar discussion on the community with Arnd few
> weeks back, and we agreed that this should be part of controller
> specific code.
I am not talking about ACMD23 which is SET_WR_BLK_ERASE_COUNT. I am talking
about newly added CMD23 (SET_BLOCK_COUNT) command in SD3.0. Shouldn't the
logic to send this command be part of generic mmc layers (block/core) rather
than at host controller driver level. It's good that your controller have
the hw capability to send the CMD23 automatically but what about for
controllers which doesn't have this type of capability available? Then it
has to rely on the block/core layer to send the CMD23 before CMD19/CMD25. I
think this should be part of block/core layer functionality in order to be
SD3.01 spec compliant.
>
> Thanks,
> Arindam
>
> > CMD23 (SET_BLOCK_COUNT) is a new command added in SD3.0 spec and
> > mandatory
> > for SDR104 mode. Why are you not adding this command in core/block
> > layer
> > before sending multi byte read command to host controller driver?
> >
> > Regards,
> > Subhash
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
> > >
> > > Host Controller v3.00 and later support Auto CMD23 in the Transfer
> > > Mode register. Since Auto CMD23 can be used with or without DMA,
> > > and if used with DMA, it should _only_ be ADMA, we check against
> > > SDHCI_USE_SDMA not being set. This flag is reset when
> SDHCI_USE_ADMA
> > > is set.
> > >
> > > A new definition for SDHCI_ARGUMENT2 register has been added in
> v3.00
> > > spec, which is the same as SDHCI_DMA_ADDRESS. We program the block
> > > count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to stop
> > > multiple block transfers. But during error recovery procedure, we
> > will
> > > need to send Abort command, so we use a variable saved_abort_cmd to
> > > save the stop command to be used later.
> > >
> > > Two bits are added to SCR register as per the Physical Layer Spec
> > > v3.01,
> > > which specify whether the card supports CMD20 and/or CMD23. We use
> > this
> > > as one of the conditions to decide whether to enable Auto CMD23 or
> > not.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/sd.c | 6 ++++
> > > drivers/mmc/host/sdhci.c | 69
> > > +++++++++++++++++++++++++++++++++++++++++---
> > > drivers/mmc/host/sdhci.h | 2 +
> > > include/linux/mmc/card.h | 4 ++
> > > include/linux/mmc/sd.h | 2 +-
> > > include/linux/mmc/sdhci.h | 2 +
> > > 6 files changed, 79 insertions(+), 6 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index d18c32b..b3f8a3c 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -188,6 +188,12 @@ 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 (scr->sda_spec3)
> > > + scr->cmd_support = UNSTUFF_BITS(resp, 32, 2);
> > > + }
> > >
> > > if (UNSTUFF_BITS(resp, 55, 1))
> > > card->erased_byte = 0xFF;
> > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > index 9e15f41..8914a25 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -25,6 +25,7 @@
> > >
> > > #include <linux/mmc/mmc.h>
> > > #include <linux/mmc/host.h>
> > > +#include <linux/mmc/card.h>
> > >
> > > #include "sdhci.h"
> > >
> > > @@ -812,6 +813,30 @@ static void sdhci_prepare_data(struct
> sdhci_host
> > > *host, struct mmc_data *data)
> > > sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
> > > }
> > >
> > > +/*
> > > + * Does the Host Controller support Auto CMD23?
> > > + *
> > > + * There are four preconditions for Auto CMD23 to be supported:
> > > + * 1. Host Controller v3.00 or later
> > > + * 2. Card supports CMD23
> > > + * 3. If DMA is used, it should be ADMA
> > > + * 4. Only when CMD18 or CMD25 is issued
> > > + */
> > > +static int sdhci_host_auto_cmd23_supported(struct sdhci_host
> *host,
> > > + struct mmc_request *mrq)
> > > +{
> > > + struct mmc_card *card = host->mmc->card;
> > > +
> > > + if ((host->version >= SDHCI_SPEC_300) &&
> > > + card && (card->scr.cmd_support & SD_SCR_CMD23_SUPPORT) &&
> > > + !(host->flags & SDHCI_USE_SDMA) &&
> > > + ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
> > > + (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)))
> > > + return 1;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > static void sdhci_set_transfer_mode(struct sdhci_host *host,
> > > struct mmc_data *data)
> > > {
> > > @@ -825,10 +850,21 @@ static void sdhci_set_transfer_mode(struct
> > > sdhci_host *host,
> > > mode = SDHCI_TRNS_BLK_CNT_EN;
> > > if (data->blocks > 1) {
> > > if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
> > > - mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
> > > - else
> > > - mode |= SDHCI_TRNS_MULTI;
> > > + mode |= SDHCI_TRNS_ACMD12;
> > > + else if (sdhci_host_auto_cmd23_supported(host, host->mrq))
> > > {
> > > + /*
> > > + * Host Controller v3.00 can automatically send
> > CMD23
> > > + * before CMD18 or CMD25. For that, we need to
> > enable
> > > + * Auto CMD23 in the Transfer Mode register and
> > > + * program the block count in Argument 2 register.
> > > + */
> > > + mode |= SDHCI_TRNS_AUTO_CMD23;
> > > + sdhci_writel(host, data->blocks, SDHCI_ARGUMENT2);
> > > + }
> > > +
> > > + mode |= SDHCI_TRNS_MULTI;
> > > }
> > > +
> > > if (data->flags & MMC_DATA_READ)
> > > mode |= SDHCI_TRNS_READ;
> > > if (host->flags & SDHCI_REQ_USE_DMA)
> > > @@ -1131,6 +1167,23 @@ static void sdhci_request(struct mmc_host
> > *mmc,
> > > struct mmc_request *mrq)
> > > #ifndef SDHCI_USE_LEDS_CLASS
> > > sdhci_activate_led(host);
> > > #endif
> > > + /*
> > > + * Since the block count for CMD23 has already been specified in
> > > the
> > > + * Argument 2 register, we don't need CMD12 to stop multiple
> > > block
> > > + * transfers.
> > > + */
> > > + if (sdhci_host_auto_cmd23_supported(host, mrq)) {
> > > + if (mrq->stop) {
> > > + /*
> > > + * Save the stop command here to be used during
> > > + * error recovery
> > > + */
> > > + host->saved_abort_cmd = mrq->data->stop;
> > > + mrq->data->stop = NULL;
> > > + mrq->stop = NULL;
> > > + }
> > > + }
> > > +
> > > if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
> > > if (mrq->stop) {
> > > mrq->data->stop = NULL;
> > > @@ -1396,6 +1449,7 @@ static void sdhci_timeout_timer(unsigned long
> > > data)
> > >
> > > if (host->data) {
> > > host->data->error = -ETIMEDOUT;
> > > + host->data->stop = host->saved_abort_cmd;
> > > sdhci_finish_data(host);
> > > } else {
> > > if (host->cmd)
> > > @@ -1534,9 +1588,10 @@ static void sdhci_data_irq(struct sdhci_host
> > > *host, u32 intmask)
> > > host->data->error = -EIO;
> > > }
> > >
> > > - if (host->data->error)
> > > + if (host->data->error) {
> > > + host->data->stop = host->saved_abort_cmd;
> > > sdhci_finish_data(host);
> > > - else {
> > > + } else {
> > > if (intmask & (SDHCI_INT_DATA_AVAIL |
> > > SDHCI_INT_SPACE_AVAIL))
> > > sdhci_transfer_pio(host);
> > >
> > > @@ -1792,6 +1847,10 @@ int sdhci_add_host(struct sdhci_host *host)
> > > host->flags &= ~SDHCI_USE_ADMA;
> > > }
> > >
> > > + /* If the host can perform ADMA operation, we reset SDMA flag */
> > > + if (host->flags & SDHCI_USE_ADMA)
> > > + host->flags &= ~SDHCI_USE_SDMA;
> > > +
> > > if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
> > > if (host->ops->enable_dma) {
> > > if (host->ops->enable_dma(host)) {
> > > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > > index 6e0969e..223762c 100644
> > > --- a/drivers/mmc/host/sdhci.h
> > > +++ b/drivers/mmc/host/sdhci.h
> > > @@ -25,6 +25,7 @@
> > > */
> > >
> > > #define SDHCI_DMA_ADDRESS 0x00
> > > +#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
> > >
> > > #define SDHCI_BLOCK_SIZE 0x04
> > > #define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) |
> (blksz
> > &
> > > 0xFFF))
> > > @@ -37,6 +38,7 @@
> > > #define SDHCI_TRNS_DMA 0x01
> > > #define SDHCI_TRNS_BLK_CNT_EN 0x02
> > > #define SDHCI_TRNS_ACMD12 0x04
> > > +#define SDHCI_TRNS_AUTO_CMD23 0x08
> > > #define SDHCI_TRNS_READ 0x10
> > > #define SDHCI_TRNS_MULTI 0x20
> > >
> > > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > > index 8ce0827..22b0335 100644
> > > --- a/include/linux/mmc/card.h
> > > +++ b/include/linux/mmc/card.h
> > > @@ -58,9 +58,13 @@ 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)
> > > + unsigned char cmd_support;
> > > +#define SD_SCR_CMD20_SUPPORT (1<<0)
> > > +#define SD_SCR_CMD23_SUPPORT (1<<1)
> > > };
> > >
> > > struct sd_ssr {
> > > diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
> > > index 3fd85e0..178363b 100644
> > > --- a/include/linux/mmc/sd.h
> > > +++ b/include/linux/mmc/sd.h
> > > @@ -59,7 +59,7 @@
> > >
> > > #define SCR_SPEC_VER_0 0 /* Implements system
> > specification
> > > 1.0 - 1.01 */
> > > #define SCR_SPEC_VER_1 1 /* Implements system
> > specification
> > > 1.10 */
> > > -#define SCR_SPEC_VER_2 2 /* Implements system
> > specification
> > > 2.00 */
> > > +#define SCR_SPEC_VER_2 2 /* Implements system
> > specification
> > > 2.00 - 3.0x */
> > >
> > > /*
> > > * SD bus widths
> > > diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> > > index 83bd9f7..282d158 100644
> > > --- a/include/linux/mmc/sdhci.h
> > > +++ b/include/linux/mmc/sdhci.h
> > > @@ -145,6 +145,8 @@ struct sdhci_host {
> > > unsigned int ocr_avail_sd;
> > > unsigned int ocr_avail_mmc;
> > >
> > > + struct mmc_command *saved_abort_cmd; /* Abort command saved
> > > for data error recovery */
> > > +
> > > unsigned long private[0] ____cacheline_aligned;
> > > };
> > > #endif /* __SDHCI_H */
> > > --
> > > 1.7.1
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-15 11:28 ` Nath, Arindam
@ 2011-03-15 11:58 ` Subhash Jadavani
2011-03-16 3:03 ` zhangfei gao
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-15 11:58 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Tuesday, March 15, 2011 4:59 PM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Tuesday, March 15, 2011 4:49 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> > switch procedure
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > Sent: Tuesday, March 15, 2011 4:03 PM
> > > To: Subhash Jadavani; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
> voltage
> > > switch procedure
> > >
> > > Hi Subhash,
> > >
> > >
> > > > -----Original Message-----
> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > Sent: Tuesday, March 15, 2011 3:48 PM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
> > voltage
> > > > switch procedure
> > > >
> > > > Arindam,
> > > >
> > > > During voltage switch, voltage level for CLK, DATA and CMD
> > pads/pins
> > > > should
> > > > be changed from 3.3v to 1.8v. for this SD controller has to
> modify
> > > few
> > > > bits
> > > > in their controller registers and also have to set the voltage
> > level
> > > of
> > > > a
> > > > regulator which is powering those pad/pins.
> > > >
> > > > Now with you current patch, you are calling
> > > start_signal_voltage_switch
> > > > mmc_ops when you want to switch from 3.3v to 1.8v and each
> > controller
> > > > driver
> > > > would then perform above operation in their handler (modifying
> > > relevant
> > > > controller register and setting the voltage level) along with
> SD3.0
> > > > spec
> > > > requirements.
> > > >
> > > > Now let's say after all above initialization and some data
> transfer
> > > > with
> > > > UHS-I card, card is removed from the slot. Now if I insert the
> > > > SDHC/SDSC
> > > > card (with SDv2.0 support) then how will controller undone the
> > above
> > > > voltage
> > > > switch operations (means changing pad/ping voltage level back
> from
> > > 1.8v
> > > > to
> > > > 3.3v)?
> > > >
> > > > To avoid above issue, we can have one new entry in mmc_ios which
> > > > specifies
> > > > the voltage level required for CLK, DATA, CMD pins (0 = 3.3v, 1 =
> > > > 1.8v).
> > > > This entry would be set once start_signal_voltage_switch ops
> > returns
> > > > success. So now when card is removed and new card is inserted, at
> > the
> > > > start
> > > > of initialization we should make sure that set_ios() gets called
> > with
> > > > this
> > > > entry reset (0) which would make sure that CLK, DATA and CMD
> > > pads/pins
> > > > voltage level back to 3.3v.
> > >
> > > Rather than following the procedure mentioned above, can we simply
> > not
> > > reset 1.8V signaling enable in the Host Control2 register when a
> card
> > > is removed? Will that serve the purpose?
> >
> > How will the host controller driver comes to know that card is
> removed
> > (assuming that there is no hardware based card detection available)?
>
> Correct me if wrong, but I think bit 7 of Normal Interrupt Status
> Register can inform regarding the card removal event.
Sorry. But again I have to make this point clear. It's not necessary that
all host controller implementation would be same as your host controller
implementation. In our host controller we don't have any such status bit for
card removal event.
>
> > And I
> > don't think host controller driver itself should take decision to
> > revert
> > back from 1.8v to 3.3v until core layer tells it do so.
> >
> > Regarding clearing the bit in Host Control2 register, it's specific
> to
> > your
> > controller. Not all controllers/platform will have same bits
>
> As part of signal voltage switch procedure, we set 1.8V signaling
> enable in the Host Control2 register. So the same bit can be reset to
> set the signaling voltage back to 3.3V.
Same as my above comment.
>
> and also
> > some
> > controller might have to do something extra than setting few bits in
> > controller registers. In our case for switching from 3.3v to 1.8v,
> > other
> > than setting one of the controller register bit, we also need to set
> > the
> > voltage level of a regulator which is powering these interface pins
> > CMD,
> > DATA, CLK.
>
> In that case I will modify the code to add an entry into mmc_ios as per
> your suggestion.
What about removing start_signal_voltage_switch ops itself and just adding
one entry in ios for voltage_switch? Whenever you want to switch the voltage
level from 1.8v to 2.7v call set_ios() with this new entry set and when you
want to go back from 1.8v to 2.7v call set_ios() with this new entry reset.
>
> Thanks,
> Arindam
>
> >
> >
> >
> > >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > > Regards,
> > > > Subhash
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > To: cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > henry.su@amd.com;
> > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > Subject: [PATCH v2 02/12] mmc: sd: add support for signal
> voltage
> > > > > switch procedure
> > > > >
> > > > > 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 support 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.
> > > > >
> > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > ---
> > > > > drivers/mmc/core/sd.c | 37 ++++++++++-
> > > > > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> > > > > drivers/mmc/core/sd_ops.h | 1 +
> > > > > drivers/mmc/host/sdhci.c | 148
> > > > > +++++++++++++++++++++++++++++++++++++++++----
> > > > > drivers/mmc/host/sdhci.h | 18 +++++-
> > > > > include/linux/mmc/host.h | 10 +++
> > > > > include/linux/mmc/sd.h | 1 +
> > > > > 7 files changed, 229 insertions(+), 18 deletions(-)
> > > > >
> > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > > index b3f8a3c..3e82599 100644
> > > > > --- a/drivers/mmc/core/sd.c
> > > > > +++ b/drivers/mmc/core/sd.c
> > > > > @@ -408,6 +408,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
> > > > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host,
> > u32
> > > > > ocr, u32 *cid)
> > > > > if (!err)
> > > > > ocr |= 1 << 30;
> > > > >
> > > > > - err = mmc_send_app_op_cond(host, ocr, NULL);
> > > > > + /* If the host can supply more than 150mA, XPC should be
> set
> > to
> > > > > 1. */
> > > > > + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300
> |
> > > > > + MMC_CAP_SET_XPC_180))
> > > > > + ocr |= 1 << 28;
> > > > > +
> > > > > + 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)) {
> > > > > + err = mmc_start_voltage_switch(host);
> > > > > + if (err)
> > > > > + return err;
> > > > > + }
> > > > > +
> > > > > if (mmc_host_is_spi(host))
> > > > > err = mmc_send_cid(host, cid);
> > > > > else
> > > > > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> > > > > }
> > > > >
> > > > > /*
> > > > > + * 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))
> > > > > + host->ocr |= (1 << 24);
> > > > > +
> > > > > + /*
> > > > > * Detect and init the card.
> > > > > */
> > > > > err = mmc_sd_init_card(host, host->ocr, NULL);
> > > > > - if (err)
> > > > > - goto err;
> > > > > + if (err == -EAGAIN) {
> > > > > + /*
> > > > > + * Retry initialization with S18R set to 0.
> > > > > + */
> > > > > + host->ocr &= ~(1 << 24);
> > > > > + err = mmc_sd_init_card(host, host->ocr, NULL);
> > > > > + if (err)
> > > > > + goto err;
> > > > > + }
> > > > >
> > > > > mmc_release_host(host);
> > > > > err = mmc_add_card(host->card);
> > > > > diff --git a/drivers/mmc/core/sd_ops.c
> > b/drivers/mmc/core/sd_ops.c
> > > > > index 797cdb5..8a23e2e 100644
> > > > > --- a/drivers/mmc/core/sd_ops.c
> > > > > +++ b/drivers/mmc/core/sd_ops.c
> > > > > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card
> > > *card,
> > > > > int width)
> > > > > return 0;
> > > > > }
> > > > >
> > > > > +int mmc_start_voltage_switch(struct mmc_host *host)
> > > > > +{
> > > > > + struct mmc_command cmd;
> > > > > + int err;
> > > > > +
> > > > > + BUG_ON(!host);
> > > > > +
> > > > > + /*
> > > > > + * If the host does not provide signal voltage switch
> > > procedure,
> > > > > we
> > > > > + * set S18R to 0, and then retry initialization sequence.
> > > > > + */
> > > > > + if (!host->ops->start_signal_voltage_switch)
> > > > > + return -EAGAIN;
> > > > > +
> > > > > + 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;
> > > > > +
> > > > > + err = host->ops->start_signal_voltage_switch(host);
> > > > > +
> > > > > + return err;
> > > > > +}
> > > > > +
> > > > > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32
> > > *rocr)
> > > > > {
> > > > > struct mmc_command cmd;
> > > > > diff --git a/drivers/mmc/core/sd_ops.h
> > b/drivers/mmc/core/sd_ops.h
> > > > > index ffc2305..3cfba59 100644
> > > > > --- a/drivers/mmc/core/sd_ops.h
> > > > > +++ b/drivers/mmc/core/sd_ops.h
> > > > > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card,
> u32
> > > > > *scr);
> > > > > int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> > > > > u8 value, u8 *resp);
> > > > > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > > > > +int mmc_start_voltage_switch(struct mmc_host *host);
> > > > >
> > > > > #endif
> > > > >
> > > > > diff --git a/drivers/mmc/host/sdhci.c
> b/drivers/mmc/host/sdhci.c
> > > > > index 8914a25..5487a0b 100644
> > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > @@ -85,6 +85,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",
> > > > > @@ -1337,11 +1339,70 @@ out:
> > > > > spin_unlock_irqrestore(&host->lock, flags);
> > > > > }
> > > > >
> > > > > +static int sdhci_start_signal_voltage_switch(struct mmc_host
> > *mmc)
> > > > > +{
> > > > > + struct sdhci_host *host;
> > > > > + u8 pwr;
> > > > > + u16 clk, ctrl;
> > > > > + u32 present_state;
> > > > > +
> > > > > + host = mmc_priv(mmc);
> > > > > +
> > > > > + /* Stop SDCLK */
> > > > > + host = mmc_priv(mmc);
> > > > > + 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_readw(host, SDHCI_HOST_CONTROL2);
> > > > > + 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. Stop
> > > > > + * power to 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);
> > > > > +
> > > > > + return -EAGAIN;
> > > > > +}
> > > > > +
> > > > > 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,
> > > > > };
> > > > >
> > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > *******\
> > > > > @@ -1799,7 +1860,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);
> > > > > @@ -1822,12 +1885,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);
> > > > >
> > > > > + if (host->version >= SDHCI_SPEC_300)
> > > > > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> > > > > +
> > > > > 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;
> > > > > @@ -1838,7 +1904,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) &&
> > > > > @@ -1892,10 +1959,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;
> > > > > @@ -1911,7 +1978,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);
> > > > > @@ -1923,7 +1990,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;
> > > > >
> > > > > /*
> > > > > @@ -1950,21 +2017,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)
> > > > > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > @@ -69,6 +69,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
> > > > > @@ -147,7 +149,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
> > > > > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> > > > > --- a/include/linux/mmc/host.h
> > > > > +++ b/include/linux/mmc/host.h
> > > > > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> > > > >
> > > > > /* optional callback for HC quirks */
> > > > > void (*init_card)(struct mmc_host *host, struct mmc_card
> > > *card);
> > > > > +
> > > > > + int (*start_signal_voltage_switch)(struct mmc_host
> > *host);
> > > > > };
> > > > >
> > > > > struct mmc_card;
> > > > > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> > > > > --- a/include/linux/mmc/sd.h
> > > > > +++ b/include/linux/mmc/sd.h
> > > > > @@ -17,6 +17,7 @@
> > > > > /* This is basically the same command as for MMC with some
> > quirks.
> > > > */
> > > > > #define SD_SEND_RELATIVE_ADDR 3 /* bcr
> > > R6
> > > > > */
> > > > > #define SD_SEND_IF_COND 8 /* bcr [11:0] See below
> > > R7
> > > > > */
> > > > > +#define SD_SWITCH_VOLTAGE 11 /* ac
> > > R1
> > > > > */
> > > > >
> > > > > /* class 10 */
> > > > > #define SD_SWITCH 6 /* adtc [31:0] See below
> > > R1
> > > > > */
> > > > > --
> > > > > 1.7.1
> > > > >
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe
> linux-
> > > mmc"
> > > > in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > info.html
> > > >
> > >
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-15 10:43 ` Nath, Arindam
@ 2011-03-16 2:51 ` zhangfei gao
2011-03-16 6:20 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: zhangfei gao @ 2011-03-16 2:51 UTC (permalink / raw)
To: Nath, Arindam
Cc: cjb@laptop.org, prakity@marvell.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
On Tue, Mar 15, 2011 at 6:43 AM, Nath, Arindam <Arindam.Nath@amd.com> wrote:
> Hi Zhangfei,
>
>
>> -----Original Message-----
>> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
>> Sent: Tuesday, March 15, 2011 4:02 PM
>> To: Nath, Arindam
>> Cc: cjb@laptop.org; prakity@marvell.com; subhashj@codeaurora.org;
>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> Subject: Re: [PATCH v2 09/12] mmc: sd: add support for tuning during
>> uhs initialization
>>
>> On Fri, Mar 4, 2011 at 6:32 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. 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 Seletc (bit 7) of Host Control2 register to 0.
>> > Before exiting the tuning procedure, we disable Buffer Read Ready
>> > interrupt.
>> >
>> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>> > ---
>> > drivers/mmc/core/sd.c | 4 +
>> > drivers/mmc/host/sdhci.c | 137
>> ++++++++++++++++++++++++++++++++++++++++++++-
>> > 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, 149 insertions(+), 1 deletions(-)
>> >
>> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>> > index be01397..1e2d157 100644
>> > --- a/drivers/mmc/core/sd.c
>> > +++ b/drivers/mmc/core/sd.c
>> > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card
>> *card)
>> > /* Set current limit for the card */
>> > err = sd_set_current_limit(card, status);
>> >
>> > + /* SPI mode doesn't define CMD19 */
>> > + if (!mmc_host_is_spi(card->host) && card->host->ops-
>> >execute_tuning)
>> > + 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 245cc39..8f4f102 100644
>> > --- a/drivers/mmc/host/sdhci.c
>> > +++ b/drivers/mmc/host/sdhci.c
>> > @@ -39,6 +39,8 @@
>> > #define SDHCI_USE_LEDS_CLASS
>> > #endif
>> >
>> > +#define MAX_TUNING_LOOP 40
>> > +
>> > static unsigned int debug_quirks = 0;
>> >
>> > static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data
>> *);
>> > @@ -985,7 +987,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);
>> > @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct
>> mmc_host *mmc)
>> > return max_current_180;
>> > }
>> >
>> > +static void sdhci_execute_tuning(struct mmc_host *mmc)
>> > +{
>> > + struct sdhci_host *host;
>> > + u16 ctrl;
>> > + int tuning_loop_counter = MAX_TUNING_LOOP;
>> > + unsigned long flags;
>> > + unsigned long timeout;
>> > +
>> > + host = mmc_priv(mmc);
>> > +
>> > + spin_lock_irqsave(&host->lock, flags);
>> > +
>> > + 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_SDR104) ||
>> > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
>> > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
>> > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
>> > + else {
>> > + spin_unlock_irqrestore(&host->lock, flags);
>> > + return;
>> > + }
>> > +
>> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
>> > +
>> > + /*
>> > + * As per the Host Controller spec v3.00, tuning command
>> > + * generates Buffer Read Ready interrupt, so enable that.
>> > + */
>> > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
>> > +
>> > + /*
>> > + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or
>> the number
>> > + * of loops reaches 40 times or a timeout of 150ms occurs.
>> > + */
>> > + timeout = 150;
>> > + do {
>> > + 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;
>>
>> Tune may fail since SDHCI_TRNS_DMA is set in SDHCI_TRANSFER_MODE,
>> SDHCI_TRNS_DMA required to be cleared before send_command, such as
>> sdhci_writew(host, 0x12, SDHCI_TRANSFER_MODE);
>
> Can you please tell me why and under what condition can tuning fail when SDHCI_TRANS_DMA is set? Since cmd->data is set to NULL inside sdhci_execute_tuning(), sdhci_set_transfer_mode() will return at the very beginning of the function before setting SDHCI_TRNS_DMA. Moreover, as per the Host Controller v3.00 spec, during tuning procedure, the host controller does not generate interrupts other than Buffer Read Ready.
>
Hi, Arindam
The Present Status Register describes Buffer Read Enable (bit 11) and
Buffer Write Enable (bit 10) only used for non-DMA transfer.
If set SDHCI_TRANS_DMA during tuning process, controller may get no
irq at all including Buffer Read Enable.
Could you help check, thanks
> Thanks,
> Arindam
>
>>
>> > + sdhci_send_command(host, &cmd);
>> > +
>> > + host->cmd = NULL;
>> > + host->mrq = NULL;
>> > +
>> > + spin_unlock_irqrestore(&host->lock, flags);
>> > +
>> > + /* Wait for Buffer Read Ready interrupt */
>> > + wait_event_interruptible_timeout(host->buf_ready_int,
>> > + (host->tuning_done == 1),
>> > + msecs_to_jiffies(50));
>> > + spin_lock_irqsave(&host->lock, flags);
>> > +
>> > + if (!host->tuning_done) {
>> > + printk(KERN_INFO DRIVER_NAME ": Tuning
>> procedure"
>> > + " failed, falling back to fixed
>> sampling"
>> > + " clock\n");
>> > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
>> > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
>> > + sdhci_writew(host, ctrl,
>> SDHCI_HOST_CONTROL2);
>> > +
>> > + 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");
>> > + }
>> > +
>> > +out:
>> > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
>> > + spin_unlock_irqrestore(&host->lock, flags);
>> > +}
>> > +
>> > static const struct mmc_host_ops sdhci_ops = {
>> > .request = sdhci_request,
>> > .set_ios = sdhci_set_ios,
>> > @@ -1492,6 +1609,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,
>> > .get_max_current_180 = sdhci_get_max_current_180,
>> > + .execute_tuning = sdhci_execute_tuning,
>> > };
>> >
>> >
>> /*********************************************************************
>> ********\
>> > @@ -1703,6 +1821,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
>> > @@ -2126,6 +2254,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;
>> > @@ -2269,6 +2401,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 5bf244d..4746879 100644
>> > --- a/drivers/mmc/host/sdhci.h
>> > +++ b/drivers/mmc/host/sdhci.h
>> > @@ -160,6 +160,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
>> > @@ -187,6 +189,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 e84cd05..651e40b 100644
>> > --- a/include/linux/mmc/host.h
>> > +++ b/include/linux/mmc/host.h
>> > @@ -129,6 +129,7 @@ struct mmc_host_ops {
>> >
>> > int (*start_signal_voltage_switch)(struct mmc_host *host);
>> > int (*get_max_current_180)(struct mmc_host *mmc);
>> > + void (*execute_tuning)(struct mmc_host *host);
>> > };
>> >
>> > struct mmc_card;
>> > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
>> > index 612301f..9194f9e 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 282d158..5203b97 100644
>> > --- a/include/linux/mmc/sdhci.h
>> > +++ b/include/linux/mmc/sdhci.h
>> > @@ -109,6 +109,7 @@ struct sdhci_host {
>> > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
>> > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
>> > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
>> > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning
>> */
>> >
>> > unsigned int version; /* SDHCI spec. version */
>> >
>> > @@ -147,6 +148,9 @@ struct sdhci_host {
>> >
>> > struct mmc_command *saved_abort_cmd; /* Abort command
>> saved for data error recovery */
>> >
>> > + 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] 125+ messages in thread
* Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-15 11:58 ` Subhash Jadavani
@ 2011-03-16 3:03 ` zhangfei gao
2011-03-16 6:30 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: zhangfei gao @ 2011-03-16 3:03 UTC (permalink / raw)
To: Subhash Jadavani, Nath, Arindam
Cc: cjb, prakity, linux-mmc, Su, Henry, Lu, Aaron, anath.amd
On Tue, Mar 15, 2011 at 7:58 AM, Subhash Jadavani
<subhashj@codeaurora.org> wrote:
>
>
>> -----Original Message-----
>> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
>> Sent: Tuesday, March 15, 2011 4:59 PM
>> To: Subhash Jadavani; cjb@laptop.org
>> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
>> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
>> switch procedure
>>
>> Hi Subhash,
>>
>>
>> > -----Original Message-----
>> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
>> > Sent: Tuesday, March 15, 2011 4:49 PM
>> > To: Nath, Arindam; cjb@laptop.org
>> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
>> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage
>> > switch procedure
>> >
>> >
>> >
>> > > -----Original Message-----
>> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
>> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
>> > > Sent: Tuesday, March 15, 2011 4:03 PM
>> > > To: Subhash Jadavani; cjb@laptop.org
>> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
>> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
>> voltage
>> > > switch procedure
>> > >
>> > > Hi Subhash,
>> > >
>> > >
>> > > > -----Original Message-----
>> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
>> > > > Sent: Tuesday, March 15, 2011 3:48 PM
>> > > > To: Nath, Arindam; cjb@laptop.org
>> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
>> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> > > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
>> > voltage
>> > > > switch procedure
>> > > >
>> > > > Arindam,
>> > > >
>> > > > During voltage switch, voltage level for CLK, DATA and CMD
>> > pads/pins
>> > > > should
>> > > > be changed from 3.3v to 1.8v. for this SD controller has to
>> modify
>> > > few
>> > > > bits
>> > > > in their controller registers and also have to set the voltage
>> > level
>> > > of
>> > > > a
>> > > > regulator which is powering those pad/pins.
>> > > >
>> > > > Now with you current patch, you are calling
>> > > start_signal_voltage_switch
>> > > > mmc_ops when you want to switch from 3.3v to 1.8v and each
>> > controller
>> > > > driver
>> > > > would then perform above operation in their handler (modifying
>> > > relevant
>> > > > controller register and setting the voltage level) along with
>> SD3.0
>> > > > spec
>> > > > requirements.
>> > > >
>> > > > Now let's say after all above initialization and some data
>> transfer
>> > > > with
>> > > > UHS-I card, card is removed from the slot. Now if I insert the
>> > > > SDHC/SDSC
>> > > > card (with SDv2.0 support) then how will controller undone the
>> > above
>> > > > voltage
>> > > > switch operations (means changing pad/ping voltage level back
>> from
>> > > 1.8v
>> > > > to
>> > > > 3.3v)?
>> > > >
>> > > > To avoid above issue, we can have one new entry in mmc_ios which
>> > > > specifies
>> > > > the voltage level required for CLK, DATA, CMD pins (0 = 3.3v, 1 =
>> > > > 1.8v).
>> > > > This entry would be set once start_signal_voltage_switch ops
>> > returns
>> > > > success. So now when card is removed and new card is inserted, at
>> > the
>> > > > start
>> > > > of initialization we should make sure that set_ios() gets called
>> > with
>> > > > this
>> > > > entry reset (0) which would make sure that CLK, DATA and CMD
>> > > pads/pins
>> > > > voltage level back to 3.3v.
>> > >
>> > > Rather than following the procedure mentioned above, can we simply
>> > not
>> > > reset 1.8V signaling enable in the Host Control2 register when a
>> card
>> > > is removed? Will that serve the purpose?
>> >
>> > How will the host controller driver comes to know that card is
>> removed
>> > (assuming that there is no hardware based card detection available)?
>>
>> Correct me if wrong, but I think bit 7 of Normal Interrupt Status
>> Register can inform regarding the card removal event.
>
> Sorry. But again I have to make this point clear. It's not necessary that
> all host controller implementation would be same as your host controller
> implementation. In our host controller we don't have any such status bit for
> card removal event.
>
>>
>> > And I
>> > don't think host controller driver itself should take decision to
>> > revert
>> > back from 1.8v to 3.3v until core layer tells it do so.
>> >
>> > Regarding clearing the bit in Host Control2 register, it's specific
>> to
>> > your
>> > controller. Not all controllers/platform will have same bits
>>
>> As part of signal voltage switch procedure, we set 1.8V signaling
>> enable in the Host Control2 register. So the same bit can be reset to
>> set the signaling voltage back to 3.3V.
>
> Same as my above comment.
>
>
>>
>> and also
>> > some
>> > controller might have to do something extra than setting few bits in
>> > controller registers. In our case for switching from 3.3v to 1.8v,
>> > other
>> > than setting one of the controller register bit, we also need to set
>> > the
>> > voltage level of a regulator which is powering these interface pins
>> > CMD,
>> > DATA, CLK.
>>
>> In that case I will modify the code to add an entry into mmc_ios as per
>> your suggestion.
>
> What about removing start_signal_voltage_switch ops itself and just adding
> one entry in ios for voltage_switch? Whenever you want to switch the voltage
> level from 1.8v to 2.7v call set_ios() with this new entry set and when you
> want to go back from 1.8v to 2.7v call set_ios() with this new entry reset.
Not sure set_ios is a good place to change voltage, which need some
time to wait for voltage to be stable, no matter voltage provided by
controller or external pmic, while set_ios is protected by spin_lock.
Besides, find start_signal_voltage_switch is not protected by
spin_lock when accessing register, it would be not safe.
>
>>
>> Thanks,
>> Arindam
>>
>> >
>> >
>> >
>> > >
>> > > Thanks,
>> > > Arindam
>> > >
>> > > >
>> > > > Regards,
>> > > > Subhash
>> > > >
>> > > >
>> > > > > -----Original Message-----
>> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
>> > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
>> > > > > Sent: Friday, March 04, 2011 5:03 PM
>> > > > > To: cjb@laptop.org
>> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
>> > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
>> > > henry.su@amd.com;
>> > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
>> > > > > Subject: [PATCH v2 02/12] mmc: sd: add support for signal
>> voltage
>> > > > > switch procedure
>> > > > >
>> > > > > 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 support 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.
>> > > > >
>> > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>> > > > > ---
>> > > > > drivers/mmc/core/sd.c | 37 ++++++++++-
>> > > > > drivers/mmc/core/sd_ops.c | 32 ++++++++++
>> > > > > drivers/mmc/core/sd_ops.h | 1 +
>> > > > > drivers/mmc/host/sdhci.c | 148
>> > > > > +++++++++++++++++++++++++++++++++++++++++----
>> > > > > drivers/mmc/host/sdhci.h | 18 +++++-
>> > > > > include/linux/mmc/host.h | 10 +++
>> > > > > include/linux/mmc/sd.h | 1 +
>> > > > > 7 files changed, 229 insertions(+), 18 deletions(-)
>> > > > >
>> > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>> > > > > index b3f8a3c..3e82599 100644
>> > > > > --- a/drivers/mmc/core/sd.c
>> > > > > +++ b/drivers/mmc/core/sd.c
>> > > > > @@ -408,6 +408,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
>> > > > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host,
>> > u32
>> > > > > ocr, u32 *cid)
>> > > > > if (!err)
>> > > > > ocr |= 1 << 30;
>> > > > >
>> > > > > - err = mmc_send_app_op_cond(host, ocr, NULL);
>> > > > > + /* If the host can supply more than 150mA, XPC should be
>> set
>> > to
>> > > > > 1. */
>> > > > > + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300
>> |
>> > > > > + MMC_CAP_SET_XPC_180))
>> > > > > + ocr |= 1 << 28;
>> > > > > +
>> > > > > + 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)) {
>> > > > > + err = mmc_start_voltage_switch(host);
>> > > > > + if (err)
>> > > > > + return err;
>> > > > > + }
>> > > > > +
>> > > > > if (mmc_host_is_spi(host))
>> > > > > err = mmc_send_cid(host, cid);
>> > > > > else
>> > > > > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
>> > > > > }
>> > > > >
>> > > > > /*
>> > > > > + * 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))
>> > > > > + host->ocr |= (1 << 24);
>> > > > > +
>> > > > > + /*
>> > > > > * Detect and init the card.
>> > > > > */
>> > > > > err = mmc_sd_init_card(host, host->ocr, NULL);
>> > > > > - if (err)
>> > > > > - goto err;
>> > > > > + if (err == -EAGAIN) {
>> > > > > + /*
>> > > > > + * Retry initialization with S18R set to 0.
>> > > > > + */
>> > > > > + host->ocr &= ~(1 << 24);
>> > > > > + err = mmc_sd_init_card(host, host->ocr, NULL);
>> > > > > + if (err)
>> > > > > + goto err;
>> > > > > + }
>> > > > >
>> > > > > mmc_release_host(host);
>> > > > > err = mmc_add_card(host->card);
>> > > > > diff --git a/drivers/mmc/core/sd_ops.c
>> > b/drivers/mmc/core/sd_ops.c
>> > > > > index 797cdb5..8a23e2e 100644
>> > > > > --- a/drivers/mmc/core/sd_ops.c
>> > > > > +++ b/drivers/mmc/core/sd_ops.c
>> > > > > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card
>> > > *card,
>> > > > > int width)
>> > > > > return 0;
>> > > > > }
>> > > > >
>> > > > > +int mmc_start_voltage_switch(struct mmc_host *host)
>> > > > > +{
>> > > > > + struct mmc_command cmd;
>> > > > > + int err;
>> > > > > +
>> > > > > + BUG_ON(!host);
>> > > > > +
>> > > > > + /*
>> > > > > + * If the host does not provide signal voltage switch
>> > > procedure,
>> > > > > we
>> > > > > + * set S18R to 0, and then retry initialization sequence.
>> > > > > + */
>> > > > > + if (!host->ops->start_signal_voltage_switch)
>> > > > > + return -EAGAIN;
>> > > > > +
>> > > > > + 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;
>> > > > > +
>> > > > > + err = host->ops->start_signal_voltage_switch(host);
>> > > > > +
>> > > > > + return err;
>> > > > > +}
>> > > > > +
>> > > > > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32
>> > > *rocr)
>> > > > > {
>> > > > > struct mmc_command cmd;
>> > > > > diff --git a/drivers/mmc/core/sd_ops.h
>> > b/drivers/mmc/core/sd_ops.h
>> > > > > index ffc2305..3cfba59 100644
>> > > > > --- a/drivers/mmc/core/sd_ops.h
>> > > > > +++ b/drivers/mmc/core/sd_ops.h
>> > > > > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card,
>> u32
>> > > > > *scr);
>> > > > > int mmc_sd_switch(struct mmc_card *card, int mode, int group,
>> > > > > u8 value, u8 *resp);
>> > > > > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
>> > > > > +int mmc_start_voltage_switch(struct mmc_host *host);
>> > > > >
>> > > > > #endif
>> > > > >
>> > > > > diff --git a/drivers/mmc/host/sdhci.c
>> b/drivers/mmc/host/sdhci.c
>> > > > > index 8914a25..5487a0b 100644
>> > > > > --- a/drivers/mmc/host/sdhci.c
>> > > > > +++ b/drivers/mmc/host/sdhci.c
>> > > > > @@ -85,6 +85,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",
>> > > > > @@ -1337,11 +1339,70 @@ out:
>> > > > > spin_unlock_irqrestore(&host->lock, flags);
>> > > > > }
>> > > > >
>> > > > > +static int sdhci_start_signal_voltage_switch(struct mmc_host
>> > *mmc)
>> > > > > +{
>> > > > > + struct sdhci_host *host;
>> > > > > + u8 pwr;
>> > > > > + u16 clk, ctrl;
>> > > > > + u32 present_state;
>> > > > > +
>> > > > > + host = mmc_priv(mmc);
>> > > > > +
>> > > > > + /* Stop SDCLK */
>> > > > > + host = mmc_priv(mmc);
>> > > > > + 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_readw(host, SDHCI_HOST_CONTROL2);
>> > > > > + 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. Stop
>> > > > > + * power to 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);
>> > > > > +
>> > > > > + return -EAGAIN;
>> > > > > +}
>> > > > > +
>> > > > > 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,
>> > > > > };
>> > > > >
>> > > > >
>> > > > >
>> > > >
>> > >
>> >
>> /**********************************************************************
>> > > > > *******\
>> > > > > @@ -1799,7 +1860,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);
>> > > > > @@ -1822,12 +1885,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);
>> > > > >
>> > > > > + if (host->version >= SDHCI_SPEC_300)
>> > > > > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
>> > > > > +
>> > > > > 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;
>> > > > > @@ -1838,7 +1904,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) &&
>> > > > > @@ -1892,10 +1959,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;
>> > > > > @@ -1911,7 +1978,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);
>> > > > > @@ -1923,7 +1990,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;
>> > > > >
>> > > > > /*
>> > > > > @@ -1950,21 +2017,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)
>> > > > > @@ -2024,7 +2146,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 223762c..95d70e6 100644
>> > > > > --- a/drivers/mmc/host/sdhci.h
>> > > > > +++ b/drivers/mmc/host/sdhci.h
>> > > > > @@ -69,6 +69,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
>> > > > > @@ -147,7 +149,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
>> > > > > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
>> > > > > --- a/include/linux/mmc/host.h
>> > > > > +++ b/include/linux/mmc/host.h
>> > > > > @@ -117,6 +117,8 @@ struct mmc_host_ops {
>> > > > >
>> > > > > /* optional callback for HC quirks */
>> > > > > void (*init_card)(struct mmc_host *host, struct mmc_card
>> > > *card);
>> > > > > +
>> > > > > + int (*start_signal_voltage_switch)(struct mmc_host
>> > *host);
>> > > > > };
>> > > > >
>> > > > > struct mmc_card;
>> > > > > @@ -173,6 +175,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 178363b..3ba5aa6 100644
>> > > > > --- a/include/linux/mmc/sd.h
>> > > > > +++ b/include/linux/mmc/sd.h
>> > > > > @@ -17,6 +17,7 @@
>> > > > > /* This is basically the same command as for MMC with some
>> > quirks.
>> > > > */
>> > > > > #define SD_SEND_RELATIVE_ADDR 3 /* bcr
>> > > R6
>> > > > > */
>> > > > > #define SD_SEND_IF_COND 8 /* bcr [11:0] See below
>> > > R7
>> > > > > */
>> > > > > +#define SD_SWITCH_VOLTAGE 11 /* ac
>> > > R1
>> > > > > */
>> > > > >
>> > > > > /* class 10 */
>> > > > > #define SD_SWITCH 6 /* adtc [31:0] See below
>> > > R1
>> > > > > */
>> > > > > --
>> > > > > 1.7.1
>> > > > >
>> > > > > --
>> > > > > To unsubscribe from this list: send the line "unsubscribe
>> linux-
>> > > mmc"
>> > > > in
>> > > > > the body of a message to majordomo@vger.kernel.org
>> > > > > More majordomo info at http://vger.kernel.org/majordomo-
>> > info.html
>> > > >
>> > >
>> > >
>> > > --
>> > > To unsubscribe from this list: send the line "unsubscribe linux-
>> mmc"
>> > in
>> > > the body of a message to majordomo@vger.kernel.org
>> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
>> >
>>
>
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
2011-03-15 11:52 ` Subhash Jadavani
@ 2011-03-16 6:07 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-16 6:07 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Tuesday, March 15, 2011 5:23 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
>
>
>
> > -----Original Message-----
> > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > Sent: Tuesday, March 15, 2011 5:05 PM
> > To: Subhash Jadavani; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > Sent: Tuesday, March 15, 2011 4:54 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 01/12] mmc: sdhci: add support for auto
> CMD23
> > >
> > > Arindam,
> > >
> > > Capability to send CMD23 automatically is something specific to
> your
> > > controller.
> >
> > Auto CMD23 is different from ACMD23. Auto CMD23 is a feature added
> into
> > Host Controller Spec v3.00, and the code follows the procedure to
> check
> > the 4 preconditions required to enable Host to send CMD23
> > automatically. I had similar discussion on the community with Arnd
> few
> > weeks back, and we agreed that this should be part of controller
> > specific code.
>
> I am not talking about ACMD23 which is SET_WR_BLK_ERASE_COUNT. I am
> talking
> about newly added CMD23 (SET_BLOCK_COUNT) command in SD3.0. Shouldn't
> the
> logic to send this command be part of generic mmc layers (block/core)
> rather
> than at host controller driver level. It's good that your controller
> have
> the hw capability to send the CMD23 automatically but what about for
> controllers which doesn't have this type of capability available? Then
> it
> has to rely on the block/core layer to send the CMD23 before
> CMD19/CMD25. I
> think this should be part of block/core layer functionality in order to
> be
> SD3.01 spec compliant.
I don't think I will be able to add the support for CMD23 in the mmc block layer right now. I will probably add the support in the next phase once the base support for SD3.0 is already in the kernel.
Thanks,
Arindam
>
> >
> > Thanks,
> > Arindam
> >
> > > CMD23 (SET_BLOCK_COUNT) is a new command added in SD3.0 spec and
> > > mandatory
> > > for SDR104 mode. Why are you not adding this command in core/block
> > > layer
> > > before sending multi byte read command to host controller driver?
> > >
> > > Regards,
> > > Subhash
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > To: cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > henry.su@amd.com;
> > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > Subject: [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23
> > > >
> > > > Host Controller v3.00 and later support Auto CMD23 in the
> Transfer
> > > > Mode register. Since Auto CMD23 can be used with or without DMA,
> > > > and if used with DMA, it should _only_ be ADMA, we check against
> > > > SDHCI_USE_SDMA not being set. This flag is reset when
> > SDHCI_USE_ADMA
> > > > is set.
> > > >
> > > > A new definition for SDHCI_ARGUMENT2 register has been added in
> > v3.00
> > > > spec, which is the same as SDHCI_DMA_ADDRESS. We program the
> block
> > > > count for CMD23 in SDHCI_ARGUMENT2, so we don't need CMD12 to
> stop
> > > > multiple block transfers. But during error recovery procedure, we
> > > will
> > > > need to send Abort command, so we use a variable saved_abort_cmd
> to
> > > > save the stop command to be used later.
> > > >
> > > > Two bits are added to SCR register as per the Physical Layer Spec
> > > > v3.01,
> > > > which specify whether the card supports CMD20 and/or CMD23. We
> use
> > > this
> > > > as one of the conditions to decide whether to enable Auto CMD23
> or
> > > not.
> > > >
> > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > ---
> > > > drivers/mmc/core/sd.c | 6 ++++
> > > > drivers/mmc/host/sdhci.c | 69
> > > > +++++++++++++++++++++++++++++++++++++++++---
> > > > drivers/mmc/host/sdhci.h | 2 +
> > > > include/linux/mmc/card.h | 4 ++
> > > > include/linux/mmc/sd.h | 2 +-
> > > > include/linux/mmc/sdhci.h | 2 +
> > > > 6 files changed, 79 insertions(+), 6 deletions(-)
> > > >
> > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > index d18c32b..b3f8a3c 100644
> > > > --- a/drivers/mmc/core/sd.c
> > > > +++ b/drivers/mmc/core/sd.c
> > > > @@ -188,6 +188,12 @@ 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 (scr->sda_spec3)
> > > > + scr->cmd_support = UNSTUFF_BITS(resp, 32, 2);
> > > > + }
> > > >
> > > > if (UNSTUFF_BITS(resp, 55, 1))
> > > > card->erased_byte = 0xFF;
> > > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > > index 9e15f41..8914a25 100644
> > > > --- a/drivers/mmc/host/sdhci.c
> > > > +++ b/drivers/mmc/host/sdhci.c
> > > > @@ -25,6 +25,7 @@
> > > >
> > > > #include <linux/mmc/mmc.h>
> > > > #include <linux/mmc/host.h>
> > > > +#include <linux/mmc/card.h>
> > > >
> > > > #include "sdhci.h"
> > > >
> > > > @@ -812,6 +813,30 @@ static void sdhci_prepare_data(struct
> > sdhci_host
> > > > *host, struct mmc_data *data)
> > > > sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
> > > > }
> > > >
> > > > +/*
> > > > + * Does the Host Controller support Auto CMD23?
> > > > + *
> > > > + * There are four preconditions for Auto CMD23 to be supported:
> > > > + * 1. Host Controller v3.00 or later
> > > > + * 2. Card supports CMD23
> > > > + * 3. If DMA is used, it should be ADMA
> > > > + * 4. Only when CMD18 or CMD25 is issued
> > > > + */
> > > > +static int sdhci_host_auto_cmd23_supported(struct sdhci_host
> > *host,
> > > > + struct mmc_request *mrq)
> > > > +{
> > > > + struct mmc_card *card = host->mmc->card;
> > > > +
> > > > + if ((host->version >= SDHCI_SPEC_300) &&
> > > > + card && (card->scr.cmd_support & SD_SCR_CMD23_SUPPORT)
> &&
> > > > + !(host->flags & SDHCI_USE_SDMA) &&
> > > > + ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
> > > > + (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)))
> > > > + return 1;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > static void sdhci_set_transfer_mode(struct sdhci_host *host,
> > > > struct mmc_data *data)
> > > > {
> > > > @@ -825,10 +850,21 @@ static void sdhci_set_transfer_mode(struct
> > > > sdhci_host *host,
> > > > mode = SDHCI_TRNS_BLK_CNT_EN;
> > > > if (data->blocks > 1) {
> > > > if (host->quirks &
> SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
> > > > - mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
> > > > - else
> > > > - mode |= SDHCI_TRNS_MULTI;
> > > > + mode |= SDHCI_TRNS_ACMD12;
> > > > + else if (sdhci_host_auto_cmd23_supported(host, host-
> >mrq))
> > > > {
> > > > + /*
> > > > + * Host Controller v3.00 can automatically send
> > > CMD23
> > > > + * before CMD18 or CMD25. For that, we need to
> > > enable
> > > > + * Auto CMD23 in the Transfer Mode register and
> > > > + * program the block count in Argument 2
> register.
> > > > + */
> > > > + mode |= SDHCI_TRNS_AUTO_CMD23;
> > > > + sdhci_writel(host, data->blocks,
> SDHCI_ARGUMENT2);
> > > > + }
> > > > +
> > > > + mode |= SDHCI_TRNS_MULTI;
> > > > }
> > > > +
> > > > if (data->flags & MMC_DATA_READ)
> > > > mode |= SDHCI_TRNS_READ;
> > > > if (host->flags & SDHCI_REQ_USE_DMA)
> > > > @@ -1131,6 +1167,23 @@ static void sdhci_request(struct mmc_host
> > > *mmc,
> > > > struct mmc_request *mrq)
> > > > #ifndef SDHCI_USE_LEDS_CLASS
> > > > sdhci_activate_led(host);
> > > > #endif
> > > > + /*
> > > > + * Since the block count for CMD23 has already been
> specified in
> > > > the
> > > > + * Argument 2 register, we don't need CMD12 to stop
> multiple
> > > > block
> > > > + * transfers.
> > > > + */
> > > > + if (sdhci_host_auto_cmd23_supported(host, mrq)) {
> > > > + if (mrq->stop) {
> > > > + /*
> > > > + * Save the stop command here to be used during
> > > > + * error recovery
> > > > + */
> > > > + host->saved_abort_cmd = mrq->data->stop;
> > > > + mrq->data->stop = NULL;
> > > > + mrq->stop = NULL;
> > > > + }
> > > > + }
> > > > +
> > > > if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
> > > > if (mrq->stop) {
> > > > mrq->data->stop = NULL;
> > > > @@ -1396,6 +1449,7 @@ static void sdhci_timeout_timer(unsigned
> long
> > > > data)
> > > >
> > > > if (host->data) {
> > > > host->data->error = -ETIMEDOUT;
> > > > + host->data->stop = host->saved_abort_cmd;
> > > > sdhci_finish_data(host);
> > > > } else {
> > > > if (host->cmd)
> > > > @@ -1534,9 +1588,10 @@ static void sdhci_data_irq(struct
> sdhci_host
> > > > *host, u32 intmask)
> > > > host->data->error = -EIO;
> > > > }
> > > >
> > > > - if (host->data->error)
> > > > + if (host->data->error) {
> > > > + host->data->stop = host->saved_abort_cmd;
> > > > sdhci_finish_data(host);
> > > > - else {
> > > > + } else {
> > > > if (intmask & (SDHCI_INT_DATA_AVAIL |
> > > > SDHCI_INT_SPACE_AVAIL))
> > > > sdhci_transfer_pio(host);
> > > >
> > > > @@ -1792,6 +1847,10 @@ int sdhci_add_host(struct sdhci_host
> *host)
> > > > host->flags &= ~SDHCI_USE_ADMA;
> > > > }
> > > >
> > > > + /* If the host can perform ADMA operation, we reset SDMA
> flag */
> > > > + if (host->flags & SDHCI_USE_ADMA)
> > > > + host->flags &= ~SDHCI_USE_SDMA;
> > > > +
> > > > if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
> > > > if (host->ops->enable_dma) {
> > > > if (host->ops->enable_dma(host)) {
> > > > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > > > index 6e0969e..223762c 100644
> > > > --- a/drivers/mmc/host/sdhci.h
> > > > +++ b/drivers/mmc/host/sdhci.h
> > > > @@ -25,6 +25,7 @@
> > > > */
> > > >
> > > > #define SDHCI_DMA_ADDRESS 0x00
> > > > +#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
> > > >
> > > > #define SDHCI_BLOCK_SIZE 0x04
> > > > #define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) |
> > (blksz
> > > &
> > > > 0xFFF))
> > > > @@ -37,6 +38,7 @@
> > > > #define SDHCI_TRNS_DMA 0x01
> > > > #define SDHCI_TRNS_BLK_CNT_EN 0x02
> > > > #define SDHCI_TRNS_ACMD12 0x04
> > > > +#define SDHCI_TRNS_AUTO_CMD23 0x08
> > > > #define SDHCI_TRNS_READ 0x10
> > > > #define SDHCI_TRNS_MULTI 0x20
> > > >
> > > > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > > > index 8ce0827..22b0335 100644
> > > > --- a/include/linux/mmc/card.h
> > > > +++ b/include/linux/mmc/card.h
> > > > @@ -58,9 +58,13 @@ 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)
> > > > + unsigned char cmd_support;
> > > > +#define SD_SCR_CMD20_SUPPORT (1<<0)
> > > > +#define SD_SCR_CMD23_SUPPORT (1<<1)
> > > > };
> > > >
> > > > struct sd_ssr {
> > > > diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h
> > > > index 3fd85e0..178363b 100644
> > > > --- a/include/linux/mmc/sd.h
> > > > +++ b/include/linux/mmc/sd.h
> > > > @@ -59,7 +59,7 @@
> > > >
> > > > #define SCR_SPEC_VER_0 0 /* Implements system
> > > specification
> > > > 1.0 - 1.01 */
> > > > #define SCR_SPEC_VER_1 1 /* Implements system
> > > specification
> > > > 1.10 */
> > > > -#define SCR_SPEC_VER_2 2 /* Implements system
> > > specification
> > > > 2.00 */
> > > > +#define SCR_SPEC_VER_2 2 /* Implements system
> > > specification
> > > > 2.00 - 3.0x */
> > > >
> > > > /*
> > > > * SD bus widths
> > > > diff --git a/include/linux/mmc/sdhci.h
> b/include/linux/mmc/sdhci.h
> > > > index 83bd9f7..282d158 100644
> > > > --- a/include/linux/mmc/sdhci.h
> > > > +++ b/include/linux/mmc/sdhci.h
> > > > @@ -145,6 +145,8 @@ struct sdhci_host {
> > > > unsigned int ocr_avail_sd;
> > > > unsigned int ocr_avail_mmc;
> > > >
> > > > + struct mmc_command *saved_abort_cmd; /* Abort command
> saved
> > > > for data error recovery */
> > > > +
> > > > unsigned long private[0] ____cacheline_aligned;
> > > > };
> > > > #endif /* __SDHCI_H */
> > > > --
> > > > 1.7.1
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-16 2:51 ` zhangfei gao
@ 2011-03-16 6:20 ` Nath, Arindam
2011-03-16 10:18 ` zhangfei gao
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-16 6:20 UTC (permalink / raw)
To: zhangfei gao
Cc: cjb@laptop.org, prakity@marvell.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Zhangfei,
> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Wednesday, March 16, 2011 8:21 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; prakity@marvell.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
> On Tue, Mar 15, 2011 at 6:43 AM, Nath, Arindam <Arindam.Nath@amd.com>
> wrote:
> > Hi Zhangfei,
> >
> >
> >> -----Original Message-----
> >> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> >> Sent: Tuesday, March 15, 2011 4:02 PM
> >> To: Nath, Arindam
> >> Cc: cjb@laptop.org; prakity@marvell.com; subhashj@codeaurora.org;
> >> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> >> Subject: Re: [PATCH v2 09/12] mmc: sd: add support for tuning during
> >> uhs initialization
> >>
> >> On Fri, Mar 4, 2011 at 6:32 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. 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 Seletc (bit 7) of Host Control2 register to 0.
> >> > Before exiting the tuning procedure, we disable Buffer Read Ready
> >> > interrupt.
> >> >
> >> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> >> > ---
> >> > drivers/mmc/core/sd.c | 4 +
> >> > drivers/mmc/host/sdhci.c | 137
> >> ++++++++++++++++++++++++++++++++++++++++++++-
> >> > 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, 149 insertions(+), 1 deletions(-)
> >> >
> >> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> >> > index be01397..1e2d157 100644
> >> > --- a/drivers/mmc/core/sd.c
> >> > +++ b/drivers/mmc/core/sd.c
> >> > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct
> mmc_card
> >> *card)
> >> > /* Set current limit for the card */
> >> > err = sd_set_current_limit(card, status);
> >> >
> >> > + /* SPI mode doesn't define CMD19 */
> >> > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> >> >execute_tuning)
> >> > + 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 245cc39..8f4f102 100644
> >> > --- a/drivers/mmc/host/sdhci.c
> >> > +++ b/drivers/mmc/host/sdhci.c
> >> > @@ -39,6 +39,8 @@
> >> > #define SDHCI_USE_LEDS_CLASS
> >> > #endif
> >> >
> >> > +#define MAX_TUNING_LOOP 40
> >> > +
> >> > static unsigned int debug_quirks = 0;
> >> >
> >> > static void sdhci_prepare_data(struct sdhci_host *, struct
> mmc_data
> >> *);
> >> > @@ -985,7 +987,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);
> >> > @@ -1485,6 +1489,119 @@ static int
> sdhci_get_max_current_180(struct
> >> mmc_host *mmc)
> >> > return max_current_180;
> >> > }
> >> >
> >> > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> >> > +{
> >> > + struct sdhci_host *host;
> >> > + u16 ctrl;
> >> > + int tuning_loop_counter = MAX_TUNING_LOOP;
> >> > + unsigned long flags;
> >> > + unsigned long timeout;
> >> > +
> >> > + host = mmc_priv(mmc);
> >> > +
> >> > + spin_lock_irqsave(&host->lock, flags);
> >> > +
> >> > + 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_SDR104) ||
> >> > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> >> > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> >> > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> >> > + else {
> >> > + spin_unlock_irqrestore(&host->lock, flags);
> >> > + return;
> >> > + }
> >> > +
> >> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> >> > +
> >> > + /*
> >> > + * As per the Host Controller spec v3.00, tuning command
> >> > + * generates Buffer Read Ready interrupt, so enable that.
> >> > + */
> >> > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> >> > +
> >> > + /*
> >> > + * Issue CMD19 repeatedly till Execute Tuning is set to 0
> or
> >> the number
> >> > + * of loops reaches 40 times or a timeout of 150ms occurs.
> >> > + */
> >> > + timeout = 150;
> >> > + do {
> >> > + 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;
> >>
> >> Tune may fail since SDHCI_TRNS_DMA is set in SDHCI_TRANSFER_MODE,
> >> SDHCI_TRNS_DMA required to be cleared before send_command, such as
> >> sdhci_writew(host, 0x12, SDHCI_TRANSFER_MODE);
> >
> > Can you please tell me why and under what condition can tuning fail
> when SDHCI_TRANS_DMA is set? Since cmd->data is set to NULL inside
> sdhci_execute_tuning(), sdhci_set_transfer_mode() will return at the
> very beginning of the function before setting SDHCI_TRNS_DMA. Moreover,
> as per the Host Controller v3.00 spec, during tuning procedure, the
> host controller does not generate interrupts other than Buffer Read
> Ready.
> >
>
> Hi, Arindam
>
> The Present Status Register describes Buffer Read Enable (bit 11) and
> Buffer Write Enable (bit 10) only used for non-DMA transfer.
> If set SDHCI_TRANS_DMA during tuning process, controller may get no
> irq at all including Buffer Read Enable.
> Could you help check, thanks
In the current implementation of tuning procedure, data is set to NULL before calling sdhci_send_command(), which in turn will make sure that SDHCI_TRNS_DMA is not set. So can you give me some specific scenario when the condition you mentioned above can occur?
Thanks,
Arindam
>
> > Thanks,
> > Arindam
> >
> >>
> >> > + sdhci_send_command(host, &cmd);
> >> > +
> >> > + host->cmd = NULL;
> >> > + host->mrq = NULL;
> >> > +
> >> > + spin_unlock_irqrestore(&host->lock, flags);
> >> > +
> >> > + /* Wait for Buffer Read Ready interrupt */
> >> > + wait_event_interruptible_timeout(host-
> >buf_ready_int,
> >> > + (host->tuning_done == 1),
> >> > + msecs_to_jiffies(50));
> >> > + spin_lock_irqsave(&host->lock, flags);
> >> > +
> >> > + if (!host->tuning_done) {
> >> > + printk(KERN_INFO DRIVER_NAME ": Tuning
> >> procedure"
> >> > + " failed, falling back to fixed
> >> sampling"
> >> > + " clock\n");
> >> > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> >> > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> >> > + sdhci_writew(host, ctrl,
> >> SDHCI_HOST_CONTROL2);
> >> > +
> >> > + 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");
> >> > + }
> >> > +
> >> > +out:
> >> > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> >> > + spin_unlock_irqrestore(&host->lock, flags);
> >> > +}
> >> > +
> >> > static const struct mmc_host_ops sdhci_ops = {
> >> > .request = sdhci_request,
> >> > .set_ios = sdhci_set_ios,
> >> > @@ -1492,6 +1609,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,
> >> > .get_max_current_180 =
> sdhci_get_max_current_180,
> >> > + .execute_tuning = sdhci_execute_tuning,
> >> > };
> >> >
> >> >
> >>
> /*********************************************************************
> >> ********\
> >> > @@ -1703,6 +1821,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
> >> > @@ -2126,6 +2254,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;
> >> > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> >> > --- a/drivers/mmc/host/sdhci.h
> >> > +++ b/drivers/mmc/host/sdhci.h
> >> > @@ -160,6 +160,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
> >> > @@ -187,6 +189,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 e84cd05..651e40b 100644
> >> > --- a/include/linux/mmc/host.h
> >> > +++ b/include/linux/mmc/host.h
> >> > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> >> >
> >> > int (*start_signal_voltage_switch)(struct mmc_host
> *host);
> >> > int (*get_max_current_180)(struct mmc_host *mmc);
> >> > + void (*execute_tuning)(struct mmc_host *host);
> >> > };
> >> >
> >> > struct mmc_card;
> >> > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> >> > index 612301f..9194f9e 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 282d158..5203b97 100644
> >> > --- a/include/linux/mmc/sdhci.h
> >> > +++ b/include/linux/mmc/sdhci.h
> >> > @@ -109,6 +109,7 @@ struct sdhci_host {
> >> > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
> >> > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req.
> */
> >> > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> >> > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs
> tuning
> >> */
> >> >
> >> > unsigned int version; /* SDHCI spec. version */
> >> >
> >> > @@ -147,6 +148,9 @@ struct sdhci_host {
> >> >
> >> > struct mmc_command *saved_abort_cmd; /* Abort command
> >> saved for data error recovery */
> >> >
> >> > + 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] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-16 3:03 ` zhangfei gao
@ 2011-03-16 6:30 ` Nath, Arindam
2011-03-16 10:44 ` zhangfei gao
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-16 6:30 UTC (permalink / raw)
To: zhangfei gao, Subhash Jadavani
Cc: cjb@laptop.org, prakity@marvell.com, linux-mmc@vger.kernel.org,
Su, Henry, Lu, Aaron, anath.amd@gmail.com
Hi Zhangfei,
> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Wednesday, March 16, 2011 8:33 AM
> To: Subhash Jadavani; Nath, Arindam
> Cc: cjb@laptop.org; prakity@marvell.com; linux-mmc@vger.kernel.org; Su,
> Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
> On Tue, Mar 15, 2011 at 7:58 AM, Subhash Jadavani
> <subhashj@codeaurora.org> wrote:
> >
> >
> >> -----Original Message-----
> >> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> >> Sent: Tuesday, March 15, 2011 4:59 PM
> >> To: Subhash Jadavani; cjb@laptop.org
> >> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> >> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> >> Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
> voltage
> >> switch procedure
> >>
> >> Hi Subhash,
> >>
> >>
> >> > -----Original Message-----
> >> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> >> > Sent: Tuesday, March 15, 2011 4:49 PM
> >> > To: Nath, Arindam; cjb@laptop.org
> >> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> >> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> >> > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
> voltage
> >> > switch procedure
> >> >
> >> >
> >> >
> >> > > -----Original Message-----
> >> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> >> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> >> > > Sent: Tuesday, March 15, 2011 4:03 PM
> >> > > To: Subhash Jadavani; cjb@laptop.org
> >> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> >> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> >> > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
> >> voltage
> >> > > switch procedure
> >> > >
> >> > > Hi Subhash,
> >> > >
> >> > >
> >> > > > -----Original Message-----
> >> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> >> > > > Sent: Tuesday, March 15, 2011 3:48 PM
> >> > > > To: Nath, Arindam; cjb@laptop.org
> >> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> >> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> >> > > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
> >> > voltage
> >> > > > switch procedure
> >> > > >
> >> > > > Arindam,
> >> > > >
> >> > > > During voltage switch, voltage level for CLK, DATA and CMD
> >> > pads/pins
> >> > > > should
> >> > > > be changed from 3.3v to 1.8v. for this SD controller has to
> >> modify
> >> > > few
> >> > > > bits
> >> > > > in their controller registers and also have to set the voltage
> >> > level
> >> > > of
> >> > > > a
> >> > > > regulator which is powering those pad/pins.
> >> > > >
> >> > > > Now with you current patch, you are calling
> >> > > start_signal_voltage_switch
> >> > > > mmc_ops when you want to switch from 3.3v to 1.8v and each
> >> > controller
> >> > > > driver
> >> > > > would then perform above operation in their handler (modifying
> >> > > relevant
> >> > > > controller register and setting the voltage level) along with
> >> SD3.0
> >> > > > spec
> >> > > > requirements.
> >> > > >
> >> > > > Now let's say after all above initialization and some data
> >> transfer
> >> > > > with
> >> > > > UHS-I card, card is removed from the slot. Now if I insert the
> >> > > > SDHC/SDSC
> >> > > > card (with SDv2.0 support) then how will controller undone the
> >> > above
> >> > > > voltage
> >> > > > switch operations (means changing pad/ping voltage level back
> >> from
> >> > > 1.8v
> >> > > > to
> >> > > > 3.3v)?
> >> > > >
> >> > > > To avoid above issue, we can have one new entry in mmc_ios
> which
> >> > > > specifies
> >> > > > the voltage level required for CLK, DATA, CMD pins (0 = 3.3v,
> 1 =
> >> > > > 1.8v).
> >> > > > This entry would be set once start_signal_voltage_switch ops
> >> > returns
> >> > > > success. So now when card is removed and new card is inserted,
> at
> >> > the
> >> > > > start
> >> > > > of initialization we should make sure that set_ios() gets
> called
> >> > with
> >> > > > this
> >> > > > entry reset (0) which would make sure that CLK, DATA and CMD
> >> > > pads/pins
> >> > > > voltage level back to 3.3v.
> >> > >
> >> > > Rather than following the procedure mentioned above, can we
> simply
> >> > not
> >> > > reset 1.8V signaling enable in the Host Control2 register when a
> >> card
> >> > > is removed? Will that serve the purpose?
> >> >
> >> > How will the host controller driver comes to know that card is
> >> removed
> >> > (assuming that there is no hardware based card detection
> available)?
> >>
> >> Correct me if wrong, but I think bit 7 of Normal Interrupt Status
> >> Register can inform regarding the card removal event.
> >
> > Sorry. But again I have to make this point clear. It's not necessary
> that
> > all host controller implementation would be same as your host
> controller
> > implementation. In our host controller we don't have any such status
> bit for
> > card removal event.
> >
> >>
> >> > And I
> >> > don't think host controller driver itself should take decision to
> >> > revert
> >> > back from 1.8v to 3.3v until core layer tells it do so.
> >> >
> >> > Regarding clearing the bit in Host Control2 register, it's
> specific
> >> to
> >> > your
> >> > controller. Not all controllers/platform will have same bits
> >>
> >> As part of signal voltage switch procedure, we set 1.8V signaling
> >> enable in the Host Control2 register. So the same bit can be reset
> to
> >> set the signaling voltage back to 3.3V.
> >
> > Same as my above comment.
> >
> >
> >>
> >> and also
> >> > some
> >> > controller might have to do something extra than setting few bits
> in
> >> > controller registers. In our case for switching from 3.3v to 1.8v,
> >> > other
> >> > than setting one of the controller register bit, we also need to
> set
> >> > the
> >> > voltage level of a regulator which is powering these interface
> pins
> >> > CMD,
> >> > DATA, CLK.
> >>
> >> In that case I will modify the code to add an entry into mmc_ios as
> per
> >> your suggestion.
> >
> > What about removing start_signal_voltage_switch ops itself and just
> adding
> > one entry in ios for voltage_switch? Whenever you want to switch the
> voltage
> > level from 1.8v to 2.7v call set_ios() with this new entry set and
> when you
> > want to go back from 1.8v to 2.7v call set_ios() with this new entry
> reset.
>
> Not sure set_ios is a good place to change voltage, which need some
> time to wait for voltage to be stable, no matter voltage provided by
> controller or external pmic, while set_ios is protected by spin_lock.
> Besides, find start_signal_voltage_switch is not protected by
> spin_lock when accessing register, it would be not safe.
I agree with you. Moreover, as Nicolas mentioned in one of the discussions before, during signal voltage switching, there should not be any concurrent access, so we don't need locking anyways. That's why the current implementation has done away with locking. I am planning to implement using a variable inside mmc_ios and not part of set_ios(). This should be able to cater to the needs of all host controllers.
Thanks,
Arindam
>
> >
> >>
> >> Thanks,
> >> Arindam
> >>
> >> >
> >> >
> >> >
> >> > >
> >> > > Thanks,
> >> > > Arindam
> >> > >
> >> > > >
> >> > > > Regards,
> >> > > > Subhash
> >> > > >
> >> > > >
> >> > > > > -----Original Message-----
> >> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> >> > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> >> > > > > Sent: Friday, March 04, 2011 5:03 PM
> >> > > > > To: cjb@laptop.org
> >> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> >> > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> >> > > henry.su@amd.com;
> >> > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> >> > > > > Subject: [PATCH v2 02/12] mmc: sd: add support for signal
> >> voltage
> >> > > > > switch procedure
> >> > > > >
> >> > > > > 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 support 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.
> >> > > > >
> >> > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> >> > > > > ---
> >> > > > > drivers/mmc/core/sd.c | 37 ++++++++++-
> >> > > > > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> >> > > > > drivers/mmc/core/sd_ops.h | 1 +
> >> > > > > drivers/mmc/host/sdhci.c | 148
> >> > > > > +++++++++++++++++++++++++++++++++++++++++----
> >> > > > > drivers/mmc/host/sdhci.h | 18 +++++-
> >> > > > > include/linux/mmc/host.h | 10 +++
> >> > > > > include/linux/mmc/sd.h | 1 +
> >> > > > > 7 files changed, 229 insertions(+), 18 deletions(-)
> >> > > > >
> >> > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> >> > > > > index b3f8a3c..3e82599 100644
> >> > > > > --- a/drivers/mmc/core/sd.c
> >> > > > > +++ b/drivers/mmc/core/sd.c
> >> > > > > @@ -408,6 +408,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
> >> > > > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host
> *host,
> >> > u32
> >> > > > > ocr, u32 *cid)
> >> > > > > if (!err)
> >> > > > > ocr |= 1 << 30;
> >> > > > >
> >> > > > > - err = mmc_send_app_op_cond(host, ocr, NULL);
> >> > > > > + /* If the host can supply more than 150mA, XPC should be
> >> set
> >> > to
> >> > > > > 1. */
> >> > > > > + if (host->caps & (MMC_CAP_SET_XPC_330 |
> MMC_CAP_SET_XPC_300
> >> |
> >> > > > > + MMC_CAP_SET_XPC_180))
> >> > > > > + ocr |= 1 << 28;
> >> > > > > +
> >> > > > > + 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)) {
> >> > > > > + err = mmc_start_voltage_switch(host);
> >> > > > > + if (err)
> >> > > > > + return err;
> >> > > > > + }
> >> > > > > +
> >> > > > > if (mmc_host_is_spi(host))
> >> > > > > err = mmc_send_cid(host, cid);
> >> > > > > else
> >> > > > > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host
> *host)
> >> > > > > }
> >> > > > >
> >> > > > > /*
> >> > > > > + * 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))
> >> > > > > + host->ocr |= (1 << 24);
> >> > > > > +
> >> > > > > + /*
> >> > > > > * Detect and init the card.
> >> > > > > */
> >> > > > > err = mmc_sd_init_card(host, host->ocr, NULL);
> >> > > > > - if (err)
> >> > > > > - goto err;
> >> > > > > + if (err == -EAGAIN) {
> >> > > > > + /*
> >> > > > > + * Retry initialization with S18R set to 0.
> >> > > > > + */
> >> > > > > + host->ocr &= ~(1 << 24);
> >> > > > > + err = mmc_sd_init_card(host, host->ocr, NULL);
> >> > > > > + if (err)
> >> > > > > + goto err;
> >> > > > > + }
> >> > > > >
> >> > > > > mmc_release_host(host);
> >> > > > > err = mmc_add_card(host->card);
> >> > > > > diff --git a/drivers/mmc/core/sd_ops.c
> >> > b/drivers/mmc/core/sd_ops.c
> >> > > > > index 797cdb5..8a23e2e 100644
> >> > > > > --- a/drivers/mmc/core/sd_ops.c
> >> > > > > +++ b/drivers/mmc/core/sd_ops.c
> >> > > > > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct
> mmc_card
> >> > > *card,
> >> > > > > int width)
> >> > > > > return 0;
> >> > > > > }
> >> > > > >
> >> > > > > +int mmc_start_voltage_switch(struct mmc_host *host)
> >> > > > > +{
> >> > > > > + struct mmc_command cmd;
> >> > > > > + int err;
> >> > > > > +
> >> > > > > + BUG_ON(!host);
> >> > > > > +
> >> > > > > + /*
> >> > > > > + * If the host does not provide signal voltage switch
> >> > > procedure,
> >> > > > > we
> >> > > > > + * set S18R to 0, and then retry initialization
> sequence.
> >> > > > > + */
> >> > > > > + if (!host->ops->start_signal_voltage_switch)
> >> > > > > + return -EAGAIN;
> >> > > > > +
> >> > > > > + 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;
> >> > > > > +
> >> > > > > + err = host->ops->start_signal_voltage_switch(host);
> >> > > > > +
> >> > > > > + return err;
> >> > > > > +}
> >> > > > > +
> >> > > > > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr,
> u32
> >> > > *rocr)
> >> > > > > {
> >> > > > > struct mmc_command cmd;
> >> > > > > diff --git a/drivers/mmc/core/sd_ops.h
> >> > b/drivers/mmc/core/sd_ops.h
> >> > > > > index ffc2305..3cfba59 100644
> >> > > > > --- a/drivers/mmc/core/sd_ops.h
> >> > > > > +++ b/drivers/mmc/core/sd_ops.h
> >> > > > > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card
> *card,
> >> u32
> >> > > > > *scr);
> >> > > > > int mmc_sd_switch(struct mmc_card *card, int mode, int
> group,
> >> > > > > u8 value, u8 *resp);
> >> > > > > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> >> > > > > +int mmc_start_voltage_switch(struct mmc_host *host);
> >> > > > >
> >> > > > > #endif
> >> > > > >
> >> > > > > diff --git a/drivers/mmc/host/sdhci.c
> >> b/drivers/mmc/host/sdhci.c
> >> > > > > index 8914a25..5487a0b 100644
> >> > > > > --- a/drivers/mmc/host/sdhci.c
> >> > > > > +++ b/drivers/mmc/host/sdhci.c
> >> > > > > @@ -85,6 +85,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",
> >> > > > > @@ -1337,11 +1339,70 @@ out:
> >> > > > > spin_unlock_irqrestore(&host->lock, flags);
> >> > > > > }
> >> > > > >
> >> > > > > +static int sdhci_start_signal_voltage_switch(struct
> mmc_host
> >> > *mmc)
> >> > > > > +{
> >> > > > > + struct sdhci_host *host;
> >> > > > > + u8 pwr;
> >> > > > > + u16 clk, ctrl;
> >> > > > > + u32 present_state;
> >> > > > > +
> >> > > > > + host = mmc_priv(mmc);
> >> > > > > +
> >> > > > > + /* Stop SDCLK */
> >> > > > > + host = mmc_priv(mmc);
> >> > > > > + 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_readw(host, SDHCI_HOST_CONTROL2);
> >> > > > > + 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. Stop
> >> > > > > + * power to 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);
> >> > > > > +
> >> > > > > + return -EAGAIN;
> >> > > > > +}
> >> > > > > +
> >> > > > > 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,
> >> > > > > };
> >> > > > >
> >> > > > >
> >> > > > >
> >> > > >
> >> > >
> >> >
> >>
> /**********************************************************************
> >> > > > > *******\
> >> > > > > @@ -1799,7 +1860,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);
> >> > > > > @@ -1822,12 +1885,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);
> >> > > > >
> >> > > > > + if (host->version >= SDHCI_SPEC_300)
> >> > > > > + caps[1] = sdhci_readl(host,
> SDHCI_CAPABILITIES_1);
> >> > > > > +
> >> > > > > 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;
> >> > > > > @@ -1838,7 +1904,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) &&
> >> > > > > @@ -1892,10 +1959,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;
> >> > > > > @@ -1911,7 +1978,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);
> >> > > > > @@ -1923,7 +1990,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;
> >> > > > >
> >> > > > > /*
> >> > > > > @@ -1950,21 +2017,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)
> >> > > > > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> >> > > > > --- a/drivers/mmc/host/sdhci.h
> >> > > > > +++ b/drivers/mmc/host/sdhci.h
> >> > > > > @@ -69,6 +69,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
> >> > > > > @@ -147,7 +149,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
> >> > > > > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> >> > > > > --- a/include/linux/mmc/host.h
> >> > > > > +++ b/include/linux/mmc/host.h
> >> > > > > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> >> > > > >
> >> > > > > /* optional callback for HC quirks */
> >> > > > > void (*init_card)(struct mmc_host *host, struct
> mmc_card
> >> > > *card);
> >> > > > > +
> >> > > > > + int (*start_signal_voltage_switch)(struct mmc_host
> >> > *host);
> >> > > > > };
> >> > > > >
> >> > > > > struct mmc_card;
> >> > > > > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> >> > > > > --- a/include/linux/mmc/sd.h
> >> > > > > +++ b/include/linux/mmc/sd.h
> >> > > > > @@ -17,6 +17,7 @@
> >> > > > > /* This is basically the same command as for MMC with some
> >> > quirks.
> >> > > > */
> >> > > > > #define SD_SEND_RELATIVE_ADDR 3 /* bcr
> >> > > R6
> >> > > > > */
> >> > > > > #define SD_SEND_IF_COND 8 /* bcr [11:0] See
> below
> >> > > R7
> >> > > > > */
> >> > > > > +#define SD_SWITCH_VOLTAGE 11 /* ac
> >> > > R1
> >> > > > > */
> >> > > > >
> >> > > > > /* class 10 */
> >> > > > > #define SD_SWITCH 6 /* adtc [31:0] See
> below
> >> > > R1
> >> > > > > */
> >> > > > > --
> >> > > > > 1.7.1
> >> > > > >
> >> > > > > --
> >> > > > > To unsubscribe from this list: send the line "unsubscribe
> >> linux-
> >> > > mmc"
> >> > > > in
> >> > > > > the body of a message to majordomo@vger.kernel.org
> >> > > > > More majordomo info at http://vger.kernel.org/majordomo-
> >> > info.html
> >> > > >
> >> > >
> >> > >
> >> > > --
> >> > > To unsubscribe from this list: send the line "unsubscribe linux-
> >> mmc"
> >> > in
> >> > > the body of a message to majordomo@vger.kernel.org
> >> > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> >> >
> >>
> >
> >
> >
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-16 6:20 ` Nath, Arindam
@ 2011-03-16 10:18 ` zhangfei gao
0 siblings, 0 replies; 125+ messages in thread
From: zhangfei gao @ 2011-03-16 10:18 UTC (permalink / raw)
To: Nath, Arindam
Cc: cjb@laptop.org, prakity@marvell.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
On Wed, Mar 16, 2011 at 2:20 AM, Nath, Arindam <Arindam.Nath@amd.com> wrote:
> Hi Zhangfei,
>
>
>> -----Original Message-----
>> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
>> Sent: Wednesday, March 16, 2011 8:21 AM
>> To: Nath, Arindam
>> Cc: cjb@laptop.org; prakity@marvell.com; subhashj@codeaurora.org;
>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> Subject: Re: [PATCH v2 09/12] mmc: sd: add support for tuning during
>> uhs initialization
>>
>> On Tue, Mar 15, 2011 at 6:43 AM, Nath, Arindam <Arindam.Nath@amd.com>
>> wrote:
>> > Hi Zhangfei,
>> >
>> >
>> >> -----Original Message-----
>> >> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
>> >> Sent: Tuesday, March 15, 2011 4:02 PM
>> >> To: Nath, Arindam
>> >> Cc: cjb@laptop.org; prakity@marvell.com; subhashj@codeaurora.org;
>> >> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> >> Subject: Re: [PATCH v2 09/12] mmc: sd: add support for tuning during
>> >> uhs initialization
>> >>
>> >> On Fri, Mar 4, 2011 at 6:32 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. 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 Seletc (bit 7) of Host Control2 register to 0.
>> >> > Before exiting the tuning procedure, we disable Buffer Read Ready
>> >> > interrupt.
>> >> >
>> >> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>> >> > ---
>> >> > drivers/mmc/core/sd.c | 4 +
>> >> > drivers/mmc/host/sdhci.c | 137
>> >> ++++++++++++++++++++++++++++++++++++++++++++-
>> >> > 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, 149 insertions(+), 1 deletions(-)
>> >> >
>> >> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>> >> > index be01397..1e2d157 100644
>> >> > --- a/drivers/mmc/core/sd.c
>> >> > +++ b/drivers/mmc/core/sd.c
>> >> > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct
>> mmc_card
>> >> *card)
>> >> > /* Set current limit for the card */
>> >> > err = sd_set_current_limit(card, status);
>> >> >
>> >> > + /* SPI mode doesn't define CMD19 */
>> >> > + if (!mmc_host_is_spi(card->host) && card->host->ops-
>> >> >execute_tuning)
>> >> > + 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 245cc39..8f4f102 100644
>> >> > --- a/drivers/mmc/host/sdhci.c
>> >> > +++ b/drivers/mmc/host/sdhci.c
>> >> > @@ -39,6 +39,8 @@
>> >> > #define SDHCI_USE_LEDS_CLASS
>> >> > #endif
>> >> >
>> >> > +#define MAX_TUNING_LOOP 40
>> >> > +
>> >> > static unsigned int debug_quirks = 0;
>> >> >
>> >> > static void sdhci_prepare_data(struct sdhci_host *, struct
>> mmc_data
>> >> *);
>> >> > @@ -985,7 +987,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);
>> >> > @@ -1485,6 +1489,119 @@ static int
>> sdhci_get_max_current_180(struct
>> >> mmc_host *mmc)
>> >> > return max_current_180;
>> >> > }
>> >> >
>> >> > +static void sdhci_execute_tuning(struct mmc_host *mmc)
>> >> > +{
>> >> > + struct sdhci_host *host;
>> >> > + u16 ctrl;
>> >> > + int tuning_loop_counter = MAX_TUNING_LOOP;
>> >> > + unsigned long flags;
>> >> > + unsigned long timeout;
>> >> > +
>> >> > + host = mmc_priv(mmc);
>> >> > +
>> >> > + spin_lock_irqsave(&host->lock, flags);
>> >> > +
>> >> > + 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_SDR104) ||
>> >> > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
>> >> > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
>> >> > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
>> >> > + else {
>> >> > + spin_unlock_irqrestore(&host->lock, flags);
>> >> > + return;
>> >> > + }
>> >> > +
>> >> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
>> >> > +
>> >> > + /*
>> >> > + * As per the Host Controller spec v3.00, tuning command
>> >> > + * generates Buffer Read Ready interrupt, so enable that.
>> >> > + */
>> >> > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
>> >> > +
>> >> > + /*
>> >> > + * Issue CMD19 repeatedly till Execute Tuning is set to 0
>> or
>> >> the number
>> >> > + * of loops reaches 40 times or a timeout of 150ms occurs.
>> >> > + */
>> >> > + timeout = 150;
>> >> > + do {
>> >> > + 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;
>> >>
>> >> Tune may fail since SDHCI_TRNS_DMA is set in SDHCI_TRANSFER_MODE,
>> >> SDHCI_TRNS_DMA required to be cleared before send_command, such as
>> >> sdhci_writew(host, 0x12, SDHCI_TRANSFER_MODE);
>> >
>> > Can you please tell me why and under what condition can tuning fail
>> when SDHCI_TRANS_DMA is set? Since cmd->data is set to NULL inside
>> sdhci_execute_tuning(), sdhci_set_transfer_mode() will return at the
>> very beginning of the function before setting SDHCI_TRNS_DMA. Moreover,
>> as per the Host Controller v3.00 spec, during tuning procedure, the
>> host controller does not generate interrupts other than Buffer Read
>> Ready.
>> >
>>
>> Hi, Arindam
>>
>> The Present Status Register describes Buffer Read Enable (bit 11) and
>> Buffer Write Enable (bit 10) only used for non-DMA transfer.
>> If set SDHCI_TRANS_DMA during tuning process, controller may get no
>> irq at all including Buffer Read Enable.
>> Could you help check, thanks
>
> In the current implementation of tuning procedure, data is set to NULL before calling sdhci_send_command(), which in turn will make sure that SDHCI_TRNS_DMA is not set. So can you give me some specific scenario when the condition you mentioned above can occur?
Hi, Arindam
Since sdhci_send_command as well as sdhci_send_command is called many
times before, the SDHCI_TRNS_DMA have already been set, if driver does
not clear SDHCI_TRNS_DMA, I am afraid the tune can not get any irq and
fail. Thanks
>
> Thanks,
> Arindam
>
>>
>> > Thanks,
>> > Arindam
>> >
>> >>
>> >> > + sdhci_send_command(host, &cmd);
>> >> > +
>> >> > + host->cmd = NULL;
>> >> > + host->mrq = NULL;
>> >> > +
>> >> > + spin_unlock_irqrestore(&host->lock, flags);
>> >> > +
>> >> > + /* Wait for Buffer Read Ready interrupt */
>> >> > + wait_event_interruptible_timeout(host-
>> >buf_ready_int,
>> >> > + (host->tuning_done == 1),
>> >> > + msecs_to_jiffies(50));
>> >> > + spin_lock_irqsave(&host->lock, flags);
>> >> > +
>> >> > + if (!host->tuning_done) {
>> >> > + printk(KERN_INFO DRIVER_NAME ": Tuning
>> >> procedure"
>> >> > + " failed, falling back to fixed
>> >> sampling"
>> >> > + " clock\n");
>> >> > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
>> >> > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
>> >> > + sdhci_writew(host, ctrl,
>> >> SDHCI_HOST_CONTROL2);
>> >> > +
>> >> > + 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");
>> >> > + }
>> >> > +
>> >> > +out:
>> >> > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
>> >> > + spin_unlock_irqrestore(&host->lock, flags);
>> >> > +}
>> >> > +
>> >> > static const struct mmc_host_ops sdhci_ops = {
>> >> > .request = sdhci_request,
>> >> > .set_ios = sdhci_set_ios,
>> >> > @@ -1492,6 +1609,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,
>> >> > .get_max_current_180 =
>> sdhci_get_max_current_180,
>> >> > + .execute_tuning = sdhci_execute_tuning,
>> >> > };
>> >> >
>> >> >
>> >>
>> /*********************************************************************
>> >> ********\
>> >> > @@ -1703,6 +1821,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
>> >> > @@ -2126,6 +2254,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;
>> >> > @@ -2269,6 +2401,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 5bf244d..4746879 100644
>> >> > --- a/drivers/mmc/host/sdhci.h
>> >> > +++ b/drivers/mmc/host/sdhci.h
>> >> > @@ -160,6 +160,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
>> >> > @@ -187,6 +189,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 e84cd05..651e40b 100644
>> >> > --- a/include/linux/mmc/host.h
>> >> > +++ b/include/linux/mmc/host.h
>> >> > @@ -129,6 +129,7 @@ struct mmc_host_ops {
>> >> >
>> >> > int (*start_signal_voltage_switch)(struct mmc_host
>> *host);
>> >> > int (*get_max_current_180)(struct mmc_host *mmc);
>> >> > + void (*execute_tuning)(struct mmc_host *host);
>> >> > };
>> >> >
>> >> > struct mmc_card;
>> >> > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
>> >> > index 612301f..9194f9e 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 282d158..5203b97 100644
>> >> > --- a/include/linux/mmc/sdhci.h
>> >> > +++ b/include/linux/mmc/sdhci.h
>> >> > @@ -109,6 +109,7 @@ struct sdhci_host {
>> >> > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
>> >> > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req.
>> */
>> >> > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
>> >> > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs
>> tuning
>> >> */
>> >> >
>> >> > unsigned int version; /* SDHCI spec. version */
>> >> >
>> >> > @@ -147,6 +148,9 @@ struct sdhci_host {
>> >> >
>> >> > struct mmc_command *saved_abort_cmd; /* Abort command
>> >> saved for data error recovery */
>> >> >
>> >> > + 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] 125+ messages in thread
* Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-16 6:30 ` Nath, Arindam
@ 2011-03-16 10:44 ` zhangfei gao
2011-03-16 10:48 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: zhangfei gao @ 2011-03-16 10:44 UTC (permalink / raw)
To: Nath, Arindam
Cc: Subhash Jadavani, cjb@laptop.org, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
On Wed, Mar 16, 2011 at 2:30 AM, Nath, Arindam <Arindam.Nath@amd.com> wrote:
> Hi Zhangfei,
>
>
>> -----Original Message-----
>> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
>> Sent: Wednesday, March 16, 2011 8:33 AM
>> To: Subhash Jadavani; Nath, Arindam
>> Cc: cjb@laptop.org; prakity@marvell.com; linux-mmc@vger.kernel.org; Su,
>> Henry; Lu, Aaron; anath.amd@gmail.com
>> Subject: Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage
>> switch procedure
>>
>> On Tue, Mar 15, 2011 at 7:58 AM, Subhash Jadavani
>> <subhashj@codeaurora.org> wrote:
>> >
>> >
>> >> -----Original Message-----
>> >> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
>> >> Sent: Tuesday, March 15, 2011 4:59 PM
>> >> To: Subhash Jadavani; cjb@laptop.org
>> >> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
>> >> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> >> Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
>> voltage
>> >> switch procedure
>> >>
>> >> Hi Subhash,
>> >>
>> >>
>> >> > -----Original Message-----
>> >> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
>> >> > Sent: Tuesday, March 15, 2011 4:49 PM
>> >> > To: Nath, Arindam; cjb@laptop.org
>> >> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
>> >> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> >> > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
>> voltage
>> >> > switch procedure
>> >> >
>> >> >
>> >> >
>> >> > > -----Original Message-----
>> >> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
>> >> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
>> >> > > Sent: Tuesday, March 15, 2011 4:03 PM
>> >> > > To: Subhash Jadavani; cjb@laptop.org
>> >> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
>> >> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> >> > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
>> >> voltage
>> >> > > switch procedure
>> >> > >
>> >> > > Hi Subhash,
>> >> > >
>> >> > >
>> >> > > > -----Original Message-----
>> >> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
>> >> > > > Sent: Tuesday, March 15, 2011 3:48 PM
>> >> > > > To: Nath, Arindam; cjb@laptop.org
>> >> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
>> >> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> >> > > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
>> >> > voltage
>> >> > > > switch procedure
>> >> > > >
>> >> > > > Arindam,
>> >> > > >
>> >> > > > During voltage switch, voltage level for CLK, DATA and CMD
>> >> > pads/pins
>> >> > > > should
>> >> > > > be changed from 3.3v to 1.8v. for this SD controller has to
>> >> modify
>> >> > > few
>> >> > > > bits
>> >> > > > in their controller registers and also have to set the voltage
>> >> > level
>> >> > > of
>> >> > > > a
>> >> > > > regulator which is powering those pad/pins.
>> >> > > >
>> >> > > > Now with you current patch, you are calling
>> >> > > start_signal_voltage_switch
>> >> > > > mmc_ops when you want to switch from 3.3v to 1.8v and each
>> >> > controller
>> >> > > > driver
>> >> > > > would then perform above operation in their handler (modifying
>> >> > > relevant
>> >> > > > controller register and setting the voltage level) along with
>> >> SD3.0
>> >> > > > spec
>> >> > > > requirements.
>> >> > > >
>> >> > > > Now let's say after all above initialization and some data
>> >> transfer
>> >> > > > with
>> >> > > > UHS-I card, card is removed from the slot. Now if I insert the
>> >> > > > SDHC/SDSC
>> >> > > > card (with SDv2.0 support) then how will controller undone the
>> >> > above
>> >> > > > voltage
>> >> > > > switch operations (means changing pad/ping voltage level back
>> >> from
>> >> > > 1.8v
>> >> > > > to
>> >> > > > 3.3v)?
>> >> > > >
>> >> > > > To avoid above issue, we can have one new entry in mmc_ios
>> which
>> >> > > > specifies
>> >> > > > the voltage level required for CLK, DATA, CMD pins (0 = 3.3v,
>> 1 =
>> >> > > > 1.8v).
>> >> > > > This entry would be set once start_signal_voltage_switch ops
>> >> > returns
>> >> > > > success. So now when card is removed and new card is inserted,
>> at
>> >> > the
>> >> > > > start
>> >> > > > of initialization we should make sure that set_ios() gets
>> called
>> >> > with
>> >> > > > this
>> >> > > > entry reset (0) which would make sure that CLK, DATA and CMD
>> >> > > pads/pins
>> >> > > > voltage level back to 3.3v.
>> >> > >
>> >> > > Rather than following the procedure mentioned above, can we
>> simply
>> >> > not
>> >> > > reset 1.8V signaling enable in the Host Control2 register when a
>> >> card
>> >> > > is removed? Will that serve the purpose?
>> >> >
>> >> > How will the host controller driver comes to know that card is
>> >> removed
>> >> > (assuming that there is no hardware based card detection
>> available)?
>> >>
>> >> Correct me if wrong, but I think bit 7 of Normal Interrupt Status
>> >> Register can inform regarding the card removal event.
>> >
>> > Sorry. But again I have to make this point clear. It's not necessary
>> that
>> > all host controller implementation would be same as your host
>> controller
>> > implementation. In our host controller we don't have any such status
>> bit for
>> > card removal event.
>> >
>> >>
>> >> > And I
>> >> > don't think host controller driver itself should take decision to
>> >> > revert
>> >> > back from 1.8v to 3.3v until core layer tells it do so.
>> >> >
>> >> > Regarding clearing the bit in Host Control2 register, it's
>> specific
>> >> to
>> >> > your
>> >> > controller. Not all controllers/platform will have same bits
>> >>
>> >> As part of signal voltage switch procedure, we set 1.8V signaling
>> >> enable in the Host Control2 register. So the same bit can be reset
>> to
>> >> set the signaling voltage back to 3.3V.
>> >
>> > Same as my above comment.
>> >
>> >
>> >>
>> >> and also
>> >> > some
>> >> > controller might have to do something extra than setting few bits
>> in
>> >> > controller registers. In our case for switching from 3.3v to 1.8v,
>> >> > other
>> >> > than setting one of the controller register bit, we also need to
>> set
>> >> > the
>> >> > voltage level of a regulator which is powering these interface
>> pins
>> >> > CMD,
>> >> > DATA, CLK.
>> >>
>> >> In that case I will modify the code to add an entry into mmc_ios as
>> per
>> >> your suggestion.
>> >
>> > What about removing start_signal_voltage_switch ops itself and just
>> adding
>> > one entry in ios for voltage_switch? Whenever you want to switch the
>> voltage
>> > level from 1.8v to 2.7v call set_ios() with this new entry set and
>> when you
>> > want to go back from 1.8v to 2.7v call set_ios() with this new entry
>> reset.
>>
>> Not sure set_ios is a good place to change voltage, which need some
>> time to wait for voltage to be stable, no matter voltage provided by
>> controller or external pmic, while set_ios is protected by spin_lock.
>> Besides, find start_signal_voltage_switch is not protected by
>> spin_lock when accessing register, it would be not safe.
>
> I agree with you. Moreover, as Nicolas mentioned in one of the discussions before, during signal voltage switching, there should not be any concurrent access, so we don't need locking anyways. That's why the current implementation has done away with locking. I am planning to implement using a variable inside mmc_ios and not part of set_ios(). This should be able to cater to the needs of all host controllers.
How about add spin_lock/unlock outside of sleep and protect the
register accessing?
>
> Thanks,
> Arindam
>
>>
>> >
>> >>
>> >> Thanks,
>> >> Arindam
>> >>
>> >> >
>> >> >
>> >> >
>> >> > >
>> >> > > Thanks,
>> >> > > Arindam
>> >> > >
>> >> > > >
>> >> > > > Regards,
>> >> > > > Subhash
>> >> > > >
>> >> > > >
>> >> > > > > -----Original Message-----
>> >> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
>> >> > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
>> >> > > > > Sent: Friday, March 04, 2011 5:03 PM
>> >> > > > > To: cjb@laptop.org
>> >> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
>> >> > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
>> >> > > henry.su@amd.com;
>> >> > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
>> >> > > > > Subject: [PATCH v2 02/12] mmc: sd: add support for signal
>> >> voltage
>> >> > > > > switch procedure
>> >> > > > >
>> >> > > > > 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 support 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.
>> >> > > > >
>> >> > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>> >> > > > > ---
>> >> > > > > drivers/mmc/core/sd.c | 37 ++++++++++-
>> >> > > > > drivers/mmc/core/sd_ops.c | 32 ++++++++++
>> >> > > > > drivers/mmc/core/sd_ops.h | 1 +
>> >> > > > > drivers/mmc/host/sdhci.c | 148
>> >> > > > > +++++++++++++++++++++++++++++++++++++++++----
>> >> > > > > drivers/mmc/host/sdhci.h | 18 +++++-
>> >> > > > > include/linux/mmc/host.h | 10 +++
>> >> > > > > include/linux/mmc/sd.h | 1 +
>> >> > > > > 7 files changed, 229 insertions(+), 18 deletions(-)
>> >> > > > >
>> >> > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>> >> > > > > index b3f8a3c..3e82599 100644
>> >> > > > > --- a/drivers/mmc/core/sd.c
>> >> > > > > +++ b/drivers/mmc/core/sd.c
>> >> > > > > @@ -408,6 +408,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
>> >> > > > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host
>> *host,
>> >> > u32
>> >> > > > > ocr, u32 *cid)
>> >> > > > > if (!err)
>> >> > > > > ocr |= 1 << 30;
>> >> > > > >
>> >> > > > > - err = mmc_send_app_op_cond(host, ocr, NULL);
>> >> > > > > + /* If the host can supply more than 150mA, XPC should be
>> >> set
>> >> > to
>> >> > > > > 1. */
>> >> > > > > + if (host->caps & (MMC_CAP_SET_XPC_330 |
>> MMC_CAP_SET_XPC_300
>> >> |
>> >> > > > > + MMC_CAP_SET_XPC_180))
>> >> > > > > + ocr |= 1 << 28;
>> >> > > > > +
>> >> > > > > + 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)) {
>> >> > > > > + err = mmc_start_voltage_switch(host);
>> >> > > > > + if (err)
>> >> > > > > + return err;
>> >> > > > > + }
>> >> > > > > +
>> >> > > > > if (mmc_host_is_spi(host))
>> >> > > > > err = mmc_send_cid(host, cid);
>> >> > > > > else
>> >> > > > > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host
>> *host)
>> >> > > > > }
>> >> > > > >
>> >> > > > > /*
>> >> > > > > + * 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))
>> >> > > > > + host->ocr |= (1 << 24);
>> >> > > > > +
>> >> > > > > + /*
>> >> > > > > * Detect and init the card.
>> >> > > > > */
>> >> > > > > err = mmc_sd_init_card(host, host->ocr, NULL);
>> >> > > > > - if (err)
>> >> > > > > - goto err;
>> >> > > > > + if (err == -EAGAIN) {
>> >> > > > > + /*
>> >> > > > > + * Retry initialization with S18R set to 0.
>> >> > > > > + */
>> >> > > > > + host->ocr &= ~(1 << 24);
>> >> > > > > + err = mmc_sd_init_card(host, host->ocr, NULL);
>> >> > > > > + if (err)
>> >> > > > > + goto err;
>> >> > > > > + }
>> >> > > > >
>> >> > > > > mmc_release_host(host);
>> >> > > > > err = mmc_add_card(host->card);
>> >> > > > > diff --git a/drivers/mmc/core/sd_ops.c
>> >> > b/drivers/mmc/core/sd_ops.c
>> >> > > > > index 797cdb5..8a23e2e 100644
>> >> > > > > --- a/drivers/mmc/core/sd_ops.c
>> >> > > > > +++ b/drivers/mmc/core/sd_ops.c
>> >> > > > > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct
>> mmc_card
>> >> > > *card,
>> >> > > > > int width)
>> >> > > > > return 0;
>> >> > > > > }
>> >> > > > >
>> >> > > > > +int mmc_start_voltage_switch(struct mmc_host *host)
>> >> > > > > +{
>> >> > > > > + struct mmc_command cmd;
>> >> > > > > + int err;
>> >> > > > > +
>> >> > > > > + BUG_ON(!host);
>> >> > > > > +
>> >> > > > > + /*
>> >> > > > > + * If the host does not provide signal voltage switch
>> >> > > procedure,
>> >> > > > > we
>> >> > > > > + * set S18R to 0, and then retry initialization
>> sequence.
>> >> > > > > + */
>> >> > > > > + if (!host->ops->start_signal_voltage_switch)
>> >> > > > > + return -EAGAIN;
>> >> > > > > +
>> >> > > > > + 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;
>> >> > > > > +
>> >> > > > > + err = host->ops->start_signal_voltage_switch(host);
>> >> > > > > +
>> >> > > > > + return err;
>> >> > > > > +}
>> >> > > > > +
>> >> > > > > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr,
>> u32
>> >> > > *rocr)
>> >> > > > > {
>> >> > > > > struct mmc_command cmd;
>> >> > > > > diff --git a/drivers/mmc/core/sd_ops.h
>> >> > b/drivers/mmc/core/sd_ops.h
>> >> > > > > index ffc2305..3cfba59 100644
>> >> > > > > --- a/drivers/mmc/core/sd_ops.h
>> >> > > > > +++ b/drivers/mmc/core/sd_ops.h
>> >> > > > > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card
>> *card,
>> >> u32
>> >> > > > > *scr);
>> >> > > > > int mmc_sd_switch(struct mmc_card *card, int mode, int
>> group,
>> >> > > > > u8 value, u8 *resp);
>> >> > > > > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
>> >> > > > > +int mmc_start_voltage_switch(struct mmc_host *host);
>> >> > > > >
>> >> > > > > #endif
>> >> > > > >
>> >> > > > > diff --git a/drivers/mmc/host/sdhci.c
>> >> b/drivers/mmc/host/sdhci.c
>> >> > > > > index 8914a25..5487a0b 100644
>> >> > > > > --- a/drivers/mmc/host/sdhci.c
>> >> > > > > +++ b/drivers/mmc/host/sdhci.c
>> >> > > > > @@ -85,6 +85,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",
>> >> > > > > @@ -1337,11 +1339,70 @@ out:
>> >> > > > > spin_unlock_irqrestore(&host->lock, flags);
>> >> > > > > }
>> >> > > > >
>> >> > > > > +static int sdhci_start_signal_voltage_switch(struct
>> mmc_host
>> >> > *mmc)
>> >> > > > > +{
>> >> > > > > + struct sdhci_host *host;
>> >> > > > > + u8 pwr;
>> >> > > > > + u16 clk, ctrl;
>> >> > > > > + u32 present_state;
>> >> > > > > +
>> >> > > > > + host = mmc_priv(mmc);
>> >> > > > > +
>> >> > > > > + /* Stop SDCLK */
>> >> > > > > + host = mmc_priv(mmc);
>> >> > > > > + 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_readw(host, SDHCI_HOST_CONTROL2);
>> >> > > > > + 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. Stop
>> >> > > > > + * power to 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);
>> >> > > > > +
>> >> > > > > + return -EAGAIN;
>> >> > > > > +}
>> >> > > > > +
>> >> > > > > 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,
>> >> > > > > };
>> >> > > > >
>> >> > > > >
>> >> > > > >
>> >> > > >
>> >> > >
>> >> >
>> >>
>> /**********************************************************************
>> >> > > > > *******\
>> >> > > > > @@ -1799,7 +1860,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);
>> >> > > > > @@ -1822,12 +1885,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);
>> >> > > > >
>> >> > > > > + if (host->version >= SDHCI_SPEC_300)
>> >> > > > > + caps[1] = sdhci_readl(host,
>> SDHCI_CAPABILITIES_1);
>> >> > > > > +
>> >> > > > > 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;
>> >> > > > > @@ -1838,7 +1904,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) &&
>> >> > > > > @@ -1892,10 +1959,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;
>> >> > > > > @@ -1911,7 +1978,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);
>> >> > > > > @@ -1923,7 +1990,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;
>> >> > > > >
>> >> > > > > /*
>> >> > > > > @@ -1950,21 +2017,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)
>> >> > > > > @@ -2024,7 +2146,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 223762c..95d70e6 100644
>> >> > > > > --- a/drivers/mmc/host/sdhci.h
>> >> > > > > +++ b/drivers/mmc/host/sdhci.h
>> >> > > > > @@ -69,6 +69,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
>> >> > > > > @@ -147,7 +149,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
>> >> > > > > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
>> >> > > > > --- a/include/linux/mmc/host.h
>> >> > > > > +++ b/include/linux/mmc/host.h
>> >> > > > > @@ -117,6 +117,8 @@ struct mmc_host_ops {
>> >> > > > >
>> >> > > > > /* optional callback for HC quirks */
>> >> > > > > void (*init_card)(struct mmc_host *host, struct
>> mmc_card
>> >> > > *card);
>> >> > > > > +
>> >> > > > > + int (*start_signal_voltage_switch)(struct mmc_host
>> >> > *host);
>> >> > > > > };
>> >> > > > >
>> >> > > > > struct mmc_card;
>> >> > > > > @@ -173,6 +175,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 178363b..3ba5aa6 100644
>> >> > > > > --- a/include/linux/mmc/sd.h
>> >> > > > > +++ b/include/linux/mmc/sd.h
>> >> > > > > @@ -17,6 +17,7 @@
>> >> > > > > /* This is basically the same command as for MMC with some
>> >> > quirks.
>> >> > > > */
>> >> > > > > #define SD_SEND_RELATIVE_ADDR 3 /* bcr
>> >> > > R6
>> >> > > > > */
>> >> > > > > #define SD_SEND_IF_COND 8 /* bcr [11:0] See
>> below
>> >> > > R7
>> >> > > > > */
>> >> > > > > +#define SD_SWITCH_VOLTAGE 11 /* ac
>> >> > > R1
>> >> > > > > */
>> >> > > > >
>> >> > > > > /* class 10 */
>> >> > > > > #define SD_SWITCH 6 /* adtc [31:0] See
>> below
>> >> > > R1
>> >> > > > > */
>> >> > > > > --
>> >> > > > > 1.7.1
>> >> > > > >
>> >> > > > > --
>> >> > > > > To unsubscribe from this list: send the line "unsubscribe
>> >> linux-
>> >> > > mmc"
>> >> > > > in
>> >> > > > > the body of a message to majordomo@vger.kernel.org
>> >> > > > > More majordomo info at http://vger.kernel.org/majordomo-
>> >> > info.html
>> >> > > >
>> >> > >
>> >> > >
>> >> > > --
>> >> > > To unsubscribe from this list: send the line "unsubscribe linux-
>> >> mmc"
>> >> > in
>> >> > > the body of a message to majordomo@vger.kernel.org
>> >> > > More majordomo info at http://vger.kernel.org/majordomo-
>> info.html
>> >> >
>> >>
>> >
>> >
>> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-16 10:44 ` zhangfei gao
@ 2011-03-16 10:48 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-16 10:48 UTC (permalink / raw)
To: zhangfei gao
Cc: Subhash Jadavani, cjb@laptop.org, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Zhangfei,
> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Wednesday, March 16, 2011 4:14 PM
> To: Nath, Arindam
> Cc: Subhash Jadavani; cjb@laptop.org; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
> On Wed, Mar 16, 2011 at 2:30 AM, Nath, Arindam <Arindam.Nath@amd.com>
> wrote:
> > Hi Zhangfei,
> >
> >
> >> -----Original Message-----
> >> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> >> Sent: Wednesday, March 16, 2011 8:33 AM
> >> To: Subhash Jadavani; Nath, Arindam
> >> Cc: cjb@laptop.org; prakity@marvell.com; linux-mmc@vger.kernel.org;
> Su,
> >> Henry; Lu, Aaron; anath.amd@gmail.com
> >> Subject: Re: [PATCH v2 02/12] mmc: sd: add support for signal
> voltage
> >> switch procedure
> >>
> >> On Tue, Mar 15, 2011 at 7:58 AM, Subhash Jadavani
> >> <subhashj@codeaurora.org> wrote:
> >> >
> >> >
> >> >> -----Original Message-----
> >> >> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> >> >> Sent: Tuesday, March 15, 2011 4:59 PM
> >> >> To: Subhash Jadavani; cjb@laptop.org
> >> >> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> >> >> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> >> >> Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
> >> voltage
> >> >> switch procedure
> >> >>
> >> >> Hi Subhash,
> >> >>
> >> >>
> >> >> > -----Original Message-----
> >> >> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> >> >> > Sent: Tuesday, March 15, 2011 4:49 PM
> >> >> > To: Nath, Arindam; cjb@laptop.org
> >> >> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> >> >> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> >> >> > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
> >> voltage
> >> >> > switch procedure
> >> >> >
> >> >> >
> >> >> >
> >> >> > > -----Original Message-----
> >> >> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> >> >> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> >> >> > > Sent: Tuesday, March 15, 2011 4:03 PM
> >> >> > > To: Subhash Jadavani; cjb@laptop.org
> >> >> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> >> >> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> >> >> > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for signal
> >> >> voltage
> >> >> > > switch procedure
> >> >> > >
> >> >> > > Hi Subhash,
> >> >> > >
> >> >> > >
> >> >> > > > -----Original Message-----
> >> >> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> >> >> > > > Sent: Tuesday, March 15, 2011 3:48 PM
> >> >> > > > To: Nath, Arindam; cjb@laptop.org
> >> >> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> >> >> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> >> >> > > > Subject: RE: [PATCH v2 02/12] mmc: sd: add support for
> signal
> >> >> > voltage
> >> >> > > > switch procedure
> >> >> > > >
> >> >> > > > Arindam,
> >> >> > > >
> >> >> > > > During voltage switch, voltage level for CLK, DATA and CMD
> >> >> > pads/pins
> >> >> > > > should
> >> >> > > > be changed from 3.3v to 1.8v. for this SD controller has to
> >> >> modify
> >> >> > > few
> >> >> > > > bits
> >> >> > > > in their controller registers and also have to set the
> voltage
> >> >> > level
> >> >> > > of
> >> >> > > > a
> >> >> > > > regulator which is powering those pad/pins.
> >> >> > > >
> >> >> > > > Now with you current patch, you are calling
> >> >> > > start_signal_voltage_switch
> >> >> > > > mmc_ops when you want to switch from 3.3v to 1.8v and each
> >> >> > controller
> >> >> > > > driver
> >> >> > > > would then perform above operation in their handler
> (modifying
> >> >> > > relevant
> >> >> > > > controller register and setting the voltage level) along
> with
> >> >> SD3.0
> >> >> > > > spec
> >> >> > > > requirements.
> >> >> > > >
> >> >> > > > Now let's say after all above initialization and some data
> >> >> transfer
> >> >> > > > with
> >> >> > > > UHS-I card, card is removed from the slot. Now if I insert
> the
> >> >> > > > SDHC/SDSC
> >> >> > > > card (with SDv2.0 support) then how will controller undone
> the
> >> >> > above
> >> >> > > > voltage
> >> >> > > > switch operations (means changing pad/ping voltage level
> back
> >> >> from
> >> >> > > 1.8v
> >> >> > > > to
> >> >> > > > 3.3v)?
> >> >> > > >
> >> >> > > > To avoid above issue, we can have one new entry in mmc_ios
> >> which
> >> >> > > > specifies
> >> >> > > > the voltage level required for CLK, DATA, CMD pins (0 =
> 3.3v,
> >> 1 =
> >> >> > > > 1.8v).
> >> >> > > > This entry would be set once start_signal_voltage_switch
> ops
> >> >> > returns
> >> >> > > > success. So now when card is removed and new card is
> inserted,
> >> at
> >> >> > the
> >> >> > > > start
> >> >> > > > of initialization we should make sure that set_ios() gets
> >> called
> >> >> > with
> >> >> > > > this
> >> >> > > > entry reset (0) which would make sure that CLK, DATA and
> CMD
> >> >> > > pads/pins
> >> >> > > > voltage level back to 3.3v.
> >> >> > >
> >> >> > > Rather than following the procedure mentioned above, can we
> >> simply
> >> >> > not
> >> >> > > reset 1.8V signaling enable in the Host Control2 register
> when a
> >> >> card
> >> >> > > is removed? Will that serve the purpose?
> >> >> >
> >> >> > How will the host controller driver comes to know that card is
> >> >> removed
> >> >> > (assuming that there is no hardware based card detection
> >> available)?
> >> >>
> >> >> Correct me if wrong, but I think bit 7 of Normal Interrupt Status
> >> >> Register can inform regarding the card removal event.
> >> >
> >> > Sorry. But again I have to make this point clear. It's not
> necessary
> >> that
> >> > all host controller implementation would be same as your host
> >> controller
> >> > implementation. In our host controller we don't have any such
> status
> >> bit for
> >> > card removal event.
> >> >
> >> >>
> >> >> > And I
> >> >> > don't think host controller driver itself should take decision
> to
> >> >> > revert
> >> >> > back from 1.8v to 3.3v until core layer tells it do so.
> >> >> >
> >> >> > Regarding clearing the bit in Host Control2 register, it's
> >> specific
> >> >> to
> >> >> > your
> >> >> > controller. Not all controllers/platform will have same bits
> >> >>
> >> >> As part of signal voltage switch procedure, we set 1.8V signaling
> >> >> enable in the Host Control2 register. So the same bit can be
> reset
> >> to
> >> >> set the signaling voltage back to 3.3V.
> >> >
> >> > Same as my above comment.
> >> >
> >> >
> >> >>
> >> >> and also
> >> >> > some
> >> >> > controller might have to do something extra than setting few
> bits
> >> in
> >> >> > controller registers. In our case for switching from 3.3v to
> 1.8v,
> >> >> > other
> >> >> > than setting one of the controller register bit, we also need
> to
> >> set
> >> >> > the
> >> >> > voltage level of a regulator which is powering these interface
> >> pins
> >> >> > CMD,
> >> >> > DATA, CLK.
> >> >>
> >> >> In that case I will modify the code to add an entry into mmc_ios
> as
> >> per
> >> >> your suggestion.
> >> >
> >> > What about removing start_signal_voltage_switch ops itself and
> just
> >> adding
> >> > one entry in ios for voltage_switch? Whenever you want to switch
> the
> >> voltage
> >> > level from 1.8v to 2.7v call set_ios() with this new entry set and
> >> when you
> >> > want to go back from 1.8v to 2.7v call set_ios() with this new
> entry
> >> reset.
> >>
> >> Not sure set_ios is a good place to change voltage, which need some
> >> time to wait for voltage to be stable, no matter voltage provided by
> >> controller or external pmic, while set_ios is protected by
> spin_lock.
> >> Besides, find start_signal_voltage_switch is not protected by
> >> spin_lock when accessing register, it would be not safe.
> >
> > I agree with you. Moreover, as Nicolas mentioned in one of the
> discussions before, during signal voltage switching, there should not
> be any concurrent access, so we don't need locking anyways. That's why
> the current implementation has done away with locking. I am planning to
> implement using a variable inside mmc_ios and not part of set_ios().
> This should be able to cater to the needs of all host controllers.
>
> How about add spin_lock/unlock outside of sleep and protect the
> register accessing?
But if we cannot have concurrent access to start_signal_voltage_switch(), why do we need locking?
Thanks,
Arindam
> >
> > Thanks,
> > Arindam
> >
> >>
> >> >
> >> >>
> >> >> Thanks,
> >> >> Arindam
> >> >>
> >> >> >
> >> >> >
> >> >> >
> >> >> > >
> >> >> > > Thanks,
> >> >> > > Arindam
> >> >> > >
> >> >> > > >
> >> >> > > > Regards,
> >> >> > > > Subhash
> >> >> > > >
> >> >> > > >
> >> >> > > > > -----Original Message-----
> >> >> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> >> >> > > > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> >> >> > > > > Sent: Friday, March 04, 2011 5:03 PM
> >> >> > > > > To: cjb@laptop.org
> >> >> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> >> >> > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> >> >> > > henry.su@amd.com;
> >> >> > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> >> >> > > > > Subject: [PATCH v2 02/12] mmc: sd: add support for signal
> >> >> voltage
> >> >> > > > > switch procedure
> >> >> > > > >
> >> >> > > > > 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 support 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.
> >> >> > > > >
> >> >> > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> >> >> > > > > ---
> >> >> > > > > drivers/mmc/core/sd.c | 37 ++++++++++-
> >> >> > > > > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> >> >> > > > > drivers/mmc/core/sd_ops.h | 1 +
> >> >> > > > > drivers/mmc/host/sdhci.c | 148
> >> >> > > > > +++++++++++++++++++++++++++++++++++++++++----
> >> >> > > > > drivers/mmc/host/sdhci.h | 18 +++++-
> >> >> > > > > include/linux/mmc/host.h | 10 +++
> >> >> > > > > include/linux/mmc/sd.h | 1 +
> >> >> > > > > 7 files changed, 229 insertions(+), 18 deletions(-)
> >> >> > > > >
> >> >> > > > > diff --git a/drivers/mmc/core/sd.c
> b/drivers/mmc/core/sd.c
> >> >> > > > > index b3f8a3c..3e82599 100644
> >> >> > > > > --- a/drivers/mmc/core/sd.c
> >> >> > > > > +++ b/drivers/mmc/core/sd.c
> >> >> > > > > @@ -408,6 +408,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
> >> >> > > > > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host
> >> *host,
> >> >> > u32
> >> >> > > > > ocr, u32 *cid)
> >> >> > > > > if (!err)
> >> >> > > > > ocr |= 1 << 30;
> >> >> > > > >
> >> >> > > > > - err = mmc_send_app_op_cond(host, ocr, NULL);
> >> >> > > > > + /* If the host can supply more than 150mA, XPC should
> be
> >> >> set
> >> >> > to
> >> >> > > > > 1. */
> >> >> > > > > + if (host->caps & (MMC_CAP_SET_XPC_330 |
> >> MMC_CAP_SET_XPC_300
> >> >> |
> >> >> > > > > + MMC_CAP_SET_XPC_180))
> >> >> > > > > + ocr |= 1 << 28;
> >> >> > > > > +
> >> >> > > > > + 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)) {
> >> >> > > > > + err = mmc_start_voltage_switch(host);
> >> >> > > > > + if (err)
> >> >> > > > > + return err;
> >> >> > > > > + }
> >> >> > > > > +
> >> >> > > > > if (mmc_host_is_spi(host))
> >> >> > > > > err = mmc_send_cid(host, cid);
> >> >> > > > > else
> >> >> > > > > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host
> >> *host)
> >> >> > > > > }
> >> >> > > > >
> >> >> > > > > /*
> >> >> > > > > + * 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))
> >> >> > > > > + host->ocr |= (1 << 24);
> >> >> > > > > +
> >> >> > > > > + /*
> >> >> > > > > * Detect and init the card.
> >> >> > > > > */
> >> >> > > > > err = mmc_sd_init_card(host, host->ocr, NULL);
> >> >> > > > > - if (err)
> >> >> > > > > - goto err;
> >> >> > > > > + if (err == -EAGAIN) {
> >> >> > > > > + /*
> >> >> > > > > + * Retry initialization with S18R set to 0.
> >> >> > > > > + */
> >> >> > > > > + host->ocr &= ~(1 << 24);
> >> >> > > > > + err = mmc_sd_init_card(host, host->ocr,
> NULL);
> >> >> > > > > + if (err)
> >> >> > > > > + goto err;
> >> >> > > > > + }
> >> >> > > > >
> >> >> > > > > mmc_release_host(host);
> >> >> > > > > err = mmc_add_card(host->card);
> >> >> > > > > diff --git a/drivers/mmc/core/sd_ops.c
> >> >> > b/drivers/mmc/core/sd_ops.c
> >> >> > > > > index 797cdb5..8a23e2e 100644
> >> >> > > > > --- a/drivers/mmc/core/sd_ops.c
> >> >> > > > > +++ b/drivers/mmc/core/sd_ops.c
> >> >> > > > > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct
> >> mmc_card
> >> >> > > *card,
> >> >> > > > > int width)
> >> >> > > > > return 0;
> >> >> > > > > }
> >> >> > > > >
> >> >> > > > > +int mmc_start_voltage_switch(struct mmc_host *host)
> >> >> > > > > +{
> >> >> > > > > + struct mmc_command cmd;
> >> >> > > > > + int err;
> >> >> > > > > +
> >> >> > > > > + BUG_ON(!host);
> >> >> > > > > +
> >> >> > > > > + /*
> >> >> > > > > + * If the host does not provide signal voltage switch
> >> >> > > procedure,
> >> >> > > > > we
> >> >> > > > > + * set S18R to 0, and then retry initialization
> >> sequence.
> >> >> > > > > + */
> >> >> > > > > + if (!host->ops->start_signal_voltage_switch)
> >> >> > > > > + return -EAGAIN;
> >> >> > > > > +
> >> >> > > > > + 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;
> >> >> > > > > +
> >> >> > > > > + err = host->ops->start_signal_voltage_switch(host);
> >> >> > > > > +
> >> >> > > > > + return err;
> >> >> > > > > +}
> >> >> > > > > +
> >> >> > > > > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr,
> >> u32
> >> >> > > *rocr)
> >> >> > > > > {
> >> >> > > > > struct mmc_command cmd;
> >> >> > > > > diff --git a/drivers/mmc/core/sd_ops.h
> >> >> > b/drivers/mmc/core/sd_ops.h
> >> >> > > > > index ffc2305..3cfba59 100644
> >> >> > > > > --- a/drivers/mmc/core/sd_ops.h
> >> >> > > > > +++ b/drivers/mmc/core/sd_ops.h
> >> >> > > > > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card
> >> *card,
> >> >> u32
> >> >> > > > > *scr);
> >> >> > > > > int mmc_sd_switch(struct mmc_card *card, int mode, int
> >> group,
> >> >> > > > > u8 value, u8 *resp);
> >> >> > > > > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> >> >> > > > > +int mmc_start_voltage_switch(struct mmc_host *host);
> >> >> > > > >
> >> >> > > > > #endif
> >> >> > > > >
> >> >> > > > > diff --git a/drivers/mmc/host/sdhci.c
> >> >> b/drivers/mmc/host/sdhci.c
> >> >> > > > > index 8914a25..5487a0b 100644
> >> >> > > > > --- a/drivers/mmc/host/sdhci.c
> >> >> > > > > +++ b/drivers/mmc/host/sdhci.c
> >> >> > > > > @@ -85,6 +85,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",
> >> >> > > > > @@ -1337,11 +1339,70 @@ out:
> >> >> > > > > spin_unlock_irqrestore(&host->lock, flags);
> >> >> > > > > }
> >> >> > > > >
> >> >> > > > > +static int sdhci_start_signal_voltage_switch(struct
> >> mmc_host
> >> >> > *mmc)
> >> >> > > > > +{
> >> >> > > > > + struct sdhci_host *host;
> >> >> > > > > + u8 pwr;
> >> >> > > > > + u16 clk, ctrl;
> >> >> > > > > + u32 present_state;
> >> >> > > > > +
> >> >> > > > > + host = mmc_priv(mmc);
> >> >> > > > > +
> >> >> > > > > + /* Stop SDCLK */
> >> >> > > > > + host = mmc_priv(mmc);
> >> >> > > > > + 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_readw(host,
> SDHCI_HOST_CONTROL2);
> >> >> > > > > + 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. Stop
> >> >> > > > > + * power to 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);
> >> >> > > > > +
> >> >> > > > > + return -EAGAIN;
> >> >> > > > > +}
> >> >> > > > > +
> >> >> > > > > 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,
> >> >> > > > > };
> >> >> > > > >
> >> >> > > > >
> >> >> > > > >
> >> >> > > >
> >> >> > >
> >> >> >
> >> >>
> >>
> /**********************************************************************
> >> >> > > > > *******\
> >> >> > > > > @@ -1799,7 +1860,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);
> >> >> > > > > @@ -1822,12 +1885,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);
> >> >> > > > >
> >> >> > > > > + if (host->version >= SDHCI_SPEC_300)
> >> >> > > > > + caps[1] = sdhci_readl(host,
> >> SDHCI_CAPABILITIES_1);
> >> >> > > > > +
> >> >> > > > > 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;
> >> >> > > > > @@ -1838,7 +1904,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) &&
> >> >> > > > > @@ -1892,10 +1959,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;
> >> >> > > > > @@ -1911,7 +1978,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);
> >> >> > > > > @@ -1923,7 +1990,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;
> >> >> > > > >
> >> >> > > > > /*
> >> >> > > > > @@ -1950,21 +2017,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)
> >> >> > > > > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> >> >> > > > > --- a/drivers/mmc/host/sdhci.h
> >> >> > > > > +++ b/drivers/mmc/host/sdhci.h
> >> >> > > > > @@ -69,6 +69,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
> >> >> > > > > @@ -147,7 +149,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
> >> >> > > > > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> >> >> > > > > --- a/include/linux/mmc/host.h
> >> >> > > > > +++ b/include/linux/mmc/host.h
> >> >> > > > > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> >> >> > > > >
> >> >> > > > > /* optional callback for HC quirks */
> >> >> > > > > void (*init_card)(struct mmc_host *host, struct
> >> mmc_card
> >> >> > > *card);
> >> >> > > > > +
> >> >> > > > > + int (*start_signal_voltage_switch)(struct
> mmc_host
> >> >> > *host);
> >> >> > > > > };
> >> >> > > > >
> >> >> > > > > struct mmc_card;
> >> >> > > > > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> >> >> > > > > --- a/include/linux/mmc/sd.h
> >> >> > > > > +++ b/include/linux/mmc/sd.h
> >> >> > > > > @@ -17,6 +17,7 @@
> >> >> > > > > /* This is basically the same command as for MMC with
> some
> >> >> > quirks.
> >> >> > > > */
> >> >> > > > > #define SD_SEND_RELATIVE_ADDR 3 /* bcr
> >> >> > > R6
> >> >> > > > > */
> >> >> > > > > #define SD_SEND_IF_COND 8 /* bcr [11:0] See
> >> below
> >> >> > > R7
> >> >> > > > > */
> >> >> > > > > +#define SD_SWITCH_VOLTAGE 11 /* ac
> >> >> > > R1
> >> >> > > > > */
> >> >> > > > >
> >> >> > > > > /* class 10 */
> >> >> > > > > #define SD_SWITCH 6 /* adtc [31:0] See
> >> below
> >> >> > > R1
> >> >> > > > > */
> >> >> > > > > --
> >> >> > > > > 1.7.1
> >> >> > > > >
> >> >> > > > > --
> >> >> > > > > To unsubscribe from this list: send the line "unsubscribe
> >> >> linux-
> >> >> > > mmc"
> >> >> > > > in
> >> >> > > > > the body of a message to majordomo@vger.kernel.org
> >> >> > > > > More majordomo info at http://vger.kernel.org/majordomo-
> >> >> > info.html
> >> >> > > >
> >> >> > >
> >> >> > >
> >> >> > > --
> >> >> > > To unsubscribe from this list: send the line "unsubscribe
> linux-
> >> >> mmc"
> >> >> > in
> >> >> > > the body of a message to majordomo@vger.kernel.org
> >> >> > > More majordomo info at http://vger.kernel.org/majordomo-
> >> info.html
> >> >> >
> >> >>
> >> >
> >> >
> >> >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-04 11:32 ` [PATCH v2 07/12] mmc: sd: set current limit for uhs cards Arindam Nath
2011-03-09 21:41 ` Philip Rakity
2011-03-10 8:16 ` subhashj
@ 2011-03-16 14:26 ` Philip Rakity
2011-03-16 14:32 ` Nath, Arindam
2011-03-21 7:43 ` Subhash Jadavani
3 siblings, 1 reply; 125+ messages in thread
From: Philip Rakity @ 2011-03-16 14:26 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
On Mar 4, 2011, at 3:32 AM, 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.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> include/linux/mmc/card.h | 9 +++++++++
> include/linux/mmc/host.h | 1 +
> 4 files changed, 79 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index ec0d8e6..df98a2c 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -550,6 +550,46 @@ 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)
> +{
> + struct mmc_host *host = card->host;
> + int mmc_host_max_current_180, current_limit;
> + int err;
> +
> + /* sanity check */
> + if (!host->ops->get_max_current_180)
> + return 0;
a better name would be get_max_current rather than get_max_current_180
do you want a test for get_max_current_180 < 400 ? and return 0 have it do the switch
by setting the value ?
> +
> + /* Maximum current supported by host at 1.8V */
> + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
> +
> + if (mmc_host_max_current_180 >= 800) {
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
> + current_limit = SD_SET_CURRENT_LIMIT_800;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> + current_limit = SD_SET_CURRENT_LIMIT_600;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> + } else if (mmc_host_max_current_180 >= 600) {
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> + current_limit = SD_SET_CURRENT_LIMIT_600;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> + } else if (mmc_host_max_current_180 >= 400)
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
or
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
> */
> @@ -590,6 +630,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 f127fa2..245cc39 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1462,12 +1462,36 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> return -EAGAIN;
> }
>
better name is sdhci_get_max_current
> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u32 max_current_caps;
> + unsigned long flags;
> + int max_current_180;
> +
> + host = mmc_priv(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + /* Maximum current is 4 times the register value for 1.8V */
> + max_current_180 = ((max_current_caps & SDHCI_MAX_CURRENT_180_MASK) >>
> + SDHCI_MAX_CURRENT_180_SHIFT) *
> + SDHCI_MAX_CURRENT_MULTIPLIER;
SDHCI_MAX_CURRENT_SHIFT is better name.
> +
> + return max_current_180;
> +}
> +
> 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,
> + .get_max_current_180 = sdhci_get_max_current_180,
.get_max_current = sdhci_get_max_current;
> };
>
> /*****************************************************************************\
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 0b24c41..a6811ae 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -98,6 +98,15 @@ struct sd_switch_caps {
> #define SD_DRIVER_TYPE_C 0x04
> #define SD_DRIVER_TYPE_D 0x08
> unsigned int uhs_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 4dfff6d..e84cd05 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -128,6 +128,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);
> + int (*get_max_current_180)(struct mmc_host *mmc);
> };
>
> struct mmc_card;
> --
> 1.7.1
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-16 14:26 ` Philip Rakity
@ 2011-03-16 14:32 ` Nath, Arindam
2011-03-16 14:51 ` Philip Rakity
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-16 14:32 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Wednesday, March 16, 2011 7:56 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>
>
> On Mar 4, 2011, at 3:32 AM, 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.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 45
> +++++++++++++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> > include/linux/mmc/card.h | 9 +++++++++
> > include/linux/mmc/host.h | 1 +
> > 4 files changed, 79 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index ec0d8e6..df98a2c 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -550,6 +550,46 @@ 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)
> > +{
> > + struct mmc_host *host = card->host;
> > + int mmc_host_max_current_180, current_limit;
> > + int err;
> > +
> > + /* sanity check */
> > + if (!host->ops->get_max_current_180)
> > + return 0;
>
> a better name would be get_max_current rather than get_max_current_180
The Max Current Capabilities register reports maximum currents for 1.8V, 3.0V and 3.3V. Since we are only interested in the maximum current at 1.8V, so I have added *_180 to the variable names to make it explicit.
>
> do you want a test for get_max_current_180 < 400 ? and return 0 have
> it do the switch
> by setting the value ?
As mentioned in the Physical Layer spec v3.01, <400mA falls under the default current limit, so we don't need to set it in case any or all of the above conditions fail.
Thanks,
Arindam
> > +
> > + /* Maximum current supported by host at 1.8V */
> > + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
> > +
> > + if (mmc_host_max_current_180 >= 800) {
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
> > + current_limit = SD_SET_CURRENT_LIMIT_800;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> > + current_limit = SD_SET_CURRENT_LIMIT_600;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > + } else if (mmc_host_max_current_180 >= 600) {
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> > + current_limit = SD_SET_CURRENT_LIMIT_600;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > + } else if (mmc_host_max_current_180 >= 400)
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
>
> or
> 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
> > */
> > @@ -590,6 +630,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 f127fa2..245cc39 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -1462,12 +1462,36 @@ static int
> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > return -EAGAIN;
> > }
> >
>
> better name is sdhci_get_max_current
>
> > +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u32 max_current_caps;
> > + unsigned long flags;
> > + int max_current_180;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + /* Maximum current is 4 times the register value for 1.8V */
> > + max_current_180 = ((max_current_caps &
> SDHCI_MAX_CURRENT_180_MASK) >>
> > + SDHCI_MAX_CURRENT_180_SHIFT) *
> > + SDHCI_MAX_CURRENT_MULTIPLIER;
>
> SDHCI_MAX_CURRENT_SHIFT is better name.
>
> > +
> > + return max_current_180;
> > +}
> > +
> > 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,
> > + .get_max_current_180 = sdhci_get_max_current_180,
>
> .get_max_current = sdhci_get_max_current;
> > };
> >
> >
> /**********************************************************************
> *******\
> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > index 0b24c41..a6811ae 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -98,6 +98,15 @@ struct sd_switch_caps {
> > #define SD_DRIVER_TYPE_C 0x04
> > #define SD_DRIVER_TYPE_D 0x08
> > unsigned int uhs_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 4dfff6d..e84cd05 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -128,6 +128,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);
> > + int (*get_max_current_180)(struct mmc_host *mmc);
> > };
> >
> > struct mmc_card;
> > --
> > 1.7.1
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-16 14:32 ` Nath, Arindam
@ 2011-03-16 14:51 ` Philip Rakity
2011-03-16 15:00 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Philip Rakity @ 2011-03-16 14:51 UTC (permalink / raw)
To: Nath, Arindam
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
On Mar 16, 2011, at 7:32 AM, Nath, Arindam wrote:
> Hi Philip,
>
>
>> -----Original Message-----
>> From: Philip Rakity [mailto:prakity@marvell.com]
>> Sent: Wednesday, March 16, 2011 7:56 PM
>> To: Nath, Arindam
>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>>
>>
>> On Mar 4, 2011, at 3:32 AM, 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.
>>>
>>> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>>> ---
>>> drivers/mmc/core/sd.c | 45
>> +++++++++++++++++++++++++++++++++++++++++++++
>>> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
>>> include/linux/mmc/card.h | 9 +++++++++
>>> include/linux/mmc/host.h | 1 +
>>> 4 files changed, 79 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>>> index ec0d8e6..df98a2c 100644
>>> --- a/drivers/mmc/core/sd.c
>>> +++ b/drivers/mmc/core/sd.c
>>> @@ -550,6 +550,46 @@ 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)
>>> +{
>>> + struct mmc_host *host = card->host;
>>> + int mmc_host_max_current_180, current_limit;
>>> + int err;
>>> +
>>> + /* sanity check */
>>> + if (!host->ops->get_max_current_180)
>>> + return 0;
>>
>> a better name would be get_max_current rather than get_max_current_180
>
> The Max Current Capabilities register reports maximum currents for 1.8V, 3.0V and 3.3V. Since we are only interested in the maximum current at 1.8V, so I have added *_180 to the variable names to make it explicit.
understand but that is the usage now and if we need to extend the code the name becomes misleading.
>
>>
>> do you want a test for get_max_current_180 < 400 ? and return 0 have
>> it do the switch
>> by setting the value ?
>
> As mentioned in the Physical Layer spec v3.01, <400mA falls under the default current limit, so we don't need to set it in case any or all of the above conditions fail.
if future cards have memory or power is not removed they will they retain the old setting ? if so better to explicitly initialize.
>
> Thanks,
> Arindam
>
>>> +
>>> + /* Maximum current supported by host at 1.8V */
>>> + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
>>> +
>>> + if (mmc_host_max_current_180 >= 800) {
>>> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
>>> + current_limit = SD_SET_CURRENT_LIMIT_800;
>>> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
>>> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>> + } else if (mmc_host_max_current_180 >= 600) {
>>> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
>>> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>> + } else if (mmc_host_max_current_180 >= 400)
>>> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>
>> or
>> 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
>>> */
>>> @@ -590,6 +630,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 f127fa2..245cc39 100644
>>> --- a/drivers/mmc/host/sdhci.c
>>> +++ b/drivers/mmc/host/sdhci.c
>>> @@ -1462,12 +1462,36 @@ static int
>> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
>>> return -EAGAIN;
>>> }
>>>
>>
>> better name is sdhci_get_max_current
>>
>>> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
>>> +{
>>> + struct sdhci_host *host;
>>> + u32 max_current_caps;
>>> + unsigned long flags;
>>> + int max_current_180;
>>> +
>>> + host = mmc_priv(mmc);
>>> +
>>> + spin_lock_irqsave(&host->lock, flags);
>>> +
>>> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
>>> +
>>> + spin_unlock_irqrestore(&host->lock, flags);
>>> +
>>> + /* Maximum current is 4 times the register value for 1.8V */
>>> + max_current_180 = ((max_current_caps &
>> SDHCI_MAX_CURRENT_180_MASK) >>
>>> + SDHCI_MAX_CURRENT_180_SHIFT) *
>>> + SDHCI_MAX_CURRENT_MULTIPLIER;
>>
>> SDHCI_MAX_CURRENT_SHIFT is better name.
>>
>>> +
>>> + return max_current_180;
>>> +}
>>> +
>>> 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,
>>> + .get_max_current_180 = sdhci_get_max_current_180,
>>
>> .get_max_current = sdhci_get_max_current;
>>> };
>>>
>>>
>> /**********************************************************************
>> *******\
>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>> index 0b24c41..a6811ae 100644
>>> --- a/include/linux/mmc/card.h
>>> +++ b/include/linux/mmc/card.h
>>> @@ -98,6 +98,15 @@ struct sd_switch_caps {
>>> #define SD_DRIVER_TYPE_C 0x04
>>> #define SD_DRIVER_TYPE_D 0x08
>>> unsigned int uhs_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 4dfff6d..e84cd05 100644
>>> --- a/include/linux/mmc/host.h
>>> +++ b/include/linux/mmc/host.h
>>> @@ -128,6 +128,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);
>>> + int (*get_max_current_180)(struct mmc_host *mmc);
>>> };
>>>
>>> struct mmc_card;
>>> --
>>> 1.7.1
>>>
>>
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-16 14:51 ` Philip Rakity
@ 2011-03-16 15:00 ` Nath, Arindam
2011-03-16 15:18 ` Philip Rakity
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-16 15:00 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Wednesday, March 16, 2011 8:22 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>
>
> On Mar 16, 2011, at 7:32 AM, Nath, Arindam wrote:
>
> > Hi Philip,
> >
> >
> >> -----Original Message-----
> >> From: Philip Rakity [mailto:prakity@marvell.com]
> >> Sent: Wednesday, March 16, 2011 7:56 PM
> >> To: Nath, Arindam
> >> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> >> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> >> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
> cards
> >>
> >>
> >> On Mar 4, 2011, at 3:32 AM, 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.
> >>>
> >>> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> >>> ---
> >>> drivers/mmc/core/sd.c | 45
> >> +++++++++++++++++++++++++++++++++++++++++++++
> >>> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> >>> include/linux/mmc/card.h | 9 +++++++++
> >>> include/linux/mmc/host.h | 1 +
> >>> 4 files changed, 79 insertions(+), 0 deletions(-)
> >>>
> >>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> >>> index ec0d8e6..df98a2c 100644
> >>> --- a/drivers/mmc/core/sd.c
> >>> +++ b/drivers/mmc/core/sd.c
> >>> @@ -550,6 +550,46 @@ 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)
> >>> +{
> >>> + struct mmc_host *host = card->host;
> >>> + int mmc_host_max_current_180, current_limit;
> >>> + int err;
> >>> +
> >>> + /* sanity check */
> >>> + if (!host->ops->get_max_current_180)
> >>> + return 0;
> >>
> >> a better name would be get_max_current rather than
> get_max_current_180
> >
> > The Max Current Capabilities register reports maximum currents for
> 1.8V, 3.0V and 3.3V. Since we are only interested in the maximum
> current at 1.8V, so I have added *_180 to the variable names to make it
> explicit.
>
> understand but that is the usage now and if we need to extend the code
> the name becomes misleading.
Okay. I will remove *_180 in next version.
> >
> >>
> >> do you want a test for get_max_current_180 < 400 ? and return 0
> have
> >> it do the switch
> >> by setting the value ?
> >
> > As mentioned in the Physical Layer spec v3.01, <400mA falls under the
> default current limit, so we don't need to set it in case any or all of
> the above conditions fail.
>
> if future cards have memory or power is not removed they will they
> retain the old setting ? if so better to explicitly initialize.
Can you please elaborate this a little further?
Thanks,
Arindam
>
> >
> > Thanks,
> > Arindam
> >
> >>> +
> >>> + /* Maximum current supported by host at 1.8V */
> >>> + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
> >>> +
> >>> + if (mmc_host_max_current_180 >= 800) {
> >>> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
> >>> + current_limit = SD_SET_CURRENT_LIMIT_800;
> >>> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> >>> + current_limit = SD_SET_CURRENT_LIMIT_600;
> >>> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> >>> + current_limit = SD_SET_CURRENT_LIMIT_400;
> >>> + } else if (mmc_host_max_current_180 >= 600) {
> >>> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> >>> + current_limit = SD_SET_CURRENT_LIMIT_600;
> >>> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> >>> + current_limit = SD_SET_CURRENT_LIMIT_400;
> >>> + } else if (mmc_host_max_current_180 >= 400)
> >>> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> >>> + current_limit = SD_SET_CURRENT_LIMIT_400;
> >>
> >> or
> >> 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
> >>> */
> >>> @@ -590,6 +630,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 f127fa2..245cc39 100644
> >>> --- a/drivers/mmc/host/sdhci.c
> >>> +++ b/drivers/mmc/host/sdhci.c
> >>> @@ -1462,12 +1462,36 @@ static int
> >> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> >>> return -EAGAIN;
> >>> }
> >>>
> >>
> >> better name is sdhci_get_max_current
> >>
> >>> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> >>> +{
> >>> + struct sdhci_host *host;
> >>> + u32 max_current_caps;
> >>> + unsigned long flags;
> >>> + int max_current_180;
> >>> +
> >>> + host = mmc_priv(mmc);
> >>> +
> >>> + spin_lock_irqsave(&host->lock, flags);
> >>> +
> >>> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> >>> +
> >>> + spin_unlock_irqrestore(&host->lock, flags);
> >>> +
> >>> + /* Maximum current is 4 times the register value for 1.8V */
> >>> + max_current_180 = ((max_current_caps &
> >> SDHCI_MAX_CURRENT_180_MASK) >>
> >>> + SDHCI_MAX_CURRENT_180_SHIFT) *
> >>> + SDHCI_MAX_CURRENT_MULTIPLIER;
> >>
> >> SDHCI_MAX_CURRENT_SHIFT is better name.
> >>
> >>> +
> >>> + return max_current_180;
> >>> +}
> >>> +
> >>> 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,
> >>> + .get_max_current_180 = sdhci_get_max_current_180,
> >>
> >> .get_max_current = sdhci_get_max_current;
> >>> };
> >>>
> >>>
> >>
> /**********************************************************************
> >> *******\
> >>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> >>> index 0b24c41..a6811ae 100644
> >>> --- a/include/linux/mmc/card.h
> >>> +++ b/include/linux/mmc/card.h
> >>> @@ -98,6 +98,15 @@ struct sd_switch_caps {
> >>> #define SD_DRIVER_TYPE_C 0x04
> >>> #define SD_DRIVER_TYPE_D 0x08
> >>> unsigned int uhs_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 4dfff6d..e84cd05 100644
> >>> --- a/include/linux/mmc/host.h
> >>> +++ b/include/linux/mmc/host.h
> >>> @@ -128,6 +128,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);
> >>> + int (*get_max_current_180)(struct mmc_host *mmc);
> >>> };
> >>>
> >>> struct mmc_card;
> >>> --
> >>> 1.7.1
> >>>
> >>
> >
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-16 15:00 ` Nath, Arindam
@ 2011-03-16 15:18 ` Philip Rakity
2011-03-16 15:24 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Philip Rakity @ 2011-03-16 15:18 UTC (permalink / raw)
To: Nath, Arindam
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
On Mar 16, 2011, at 8:00 AM, Nath, Arindam wrote:
> Hi Philip,
>
>
>> -----Original Message-----
>> From: Philip Rakity [mailto:prakity@marvell.com]
>> Sent: Wednesday, March 16, 2011 8:22 PM
>> To: Nath, Arindam
>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>>
>>
>> On Mar 16, 2011, at 7:32 AM, Nath, Arindam wrote:
>>
>>> Hi Philip,
>>>
>>>
>>>> -----Original Message-----
>>>> From: Philip Rakity [mailto:prakity@marvell.com]
>>>> Sent: Wednesday, March 16, 2011 7:56 PM
>>>> To: Nath, Arindam
>>>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
>>>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>>>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
>> cards
>>>>
>>>>
>>>> On Mar 4, 2011, at 3:32 AM, 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.
>>>>>
>>>>> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>>>>> ---
>>>>> drivers/mmc/core/sd.c | 45
>>>> +++++++++++++++++++++++++++++++++++++++++++++
>>>>> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
>>>>> include/linux/mmc/card.h | 9 +++++++++
>>>>> include/linux/mmc/host.h | 1 +
>>>>> 4 files changed, 79 insertions(+), 0 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>>>>> index ec0d8e6..df98a2c 100644
>>>>> --- a/drivers/mmc/core/sd.c
>>>>> +++ b/drivers/mmc/core/sd.c
>>>>> @@ -550,6 +550,46 @@ 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)
>>>>> +{
>>>>> + struct mmc_host *host = card->host;
>>>>> + int mmc_host_max_current_180, current_limit;
>>>>> + int err;
>>>>> +
>>>>> + /* sanity check */
>>>>> + if (!host->ops->get_max_current_180)
>>>>> + return 0;
>>>>
>>>> a better name would be get_max_current rather than
>> get_max_current_180
>>>
>>> The Max Current Capabilities register reports maximum currents for
>> 1.8V, 3.0V and 3.3V. Since we are only interested in the maximum
>> current at 1.8V, so I have added *_180 to the variable names to make it
>> explicit.
>>
>> understand but that is the usage now and if we need to extend the code
>> the name becomes misleading.
>
> Okay. I will remove *_180 in next version.
>
>>>
>>>>
>>>> do you want a test for get_max_current_180 < 400 ? and return 0
>> have
>>>> it do the switch
>>>> by setting the value ?
>>>
>>> As mentioned in the Physical Layer spec v3.01, <400mA falls under the
>> default current limit, so we don't need to set it in case any or all of
>> the above conditions fail.
>>
>> if future cards have memory or power is not removed they will they
>> retain the old setting ? if so better to explicitly initialize.
>
> Can you please elaborate this a little further?
Power to the sd slot is supplied via a regulator. This is turned on at power on.
sometime in the future a reboot is done. power is NOT removed. The device does not reset
into the default state. Have seen this on some boards.
>
> Thanks,
> Arindam
>
>>
>>>
>>> Thanks,
>>> Arindam
>>>
>>>>> +
>>>>> + /* Maximum current supported by host at 1.8V */
>>>>> + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
>>>>> +
>>>>> + if (mmc_host_max_current_180 >= 800) {
>>>>> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
>>>>> + current_limit = SD_SET_CURRENT_LIMIT_800;
>>>>> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
>>>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
>>>>> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>>>> + } else if (mmc_host_max_current_180 >= 600) {
>>>>> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
>>>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
>>>>> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>>>> + } else if (mmc_host_max_current_180 >= 400)
>>>>> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>>>
>>>> or
>>>> 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
>>>>> */
>>>>> @@ -590,6 +630,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 f127fa2..245cc39 100644
>>>>> --- a/drivers/mmc/host/sdhci.c
>>>>> +++ b/drivers/mmc/host/sdhci.c
>>>>> @@ -1462,12 +1462,36 @@ static int
>>>> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
>>>>> return -EAGAIN;
>>>>> }
>>>>>
>>>>
>>>> better name is sdhci_get_max_current
>>>>
>>>>> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
>>>>> +{
>>>>> + struct sdhci_host *host;
>>>>> + u32 max_current_caps;
>>>>> + unsigned long flags;
>>>>> + int max_current_180;
>>>>> +
>>>>> + host = mmc_priv(mmc);
>>>>> +
>>>>> + spin_lock_irqsave(&host->lock, flags);
>>>>> +
>>>>> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
>>>>> +
>>>>> + spin_unlock_irqrestore(&host->lock, flags);
>>>>> +
>>>>> + /* Maximum current is 4 times the register value for 1.8V */
>>>>> + max_current_180 = ((max_current_caps &
>>>> SDHCI_MAX_CURRENT_180_MASK) >>
>>>>> + SDHCI_MAX_CURRENT_180_SHIFT) *
>>>>> + SDHCI_MAX_CURRENT_MULTIPLIER;
>>>>
>>>> SDHCI_MAX_CURRENT_SHIFT is better name.
>>>>
>>>>> +
>>>>> + return max_current_180;
>>>>> +}
>>>>> +
>>>>> 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,
>>>>> + .get_max_current_180 = sdhci_get_max_current_180,
>>>>
>>>> .get_max_current = sdhci_get_max_current;
>>>>> };
>>>>>
>>>>>
>>>>
>> /**********************************************************************
>>>> *******\
>>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>>> index 0b24c41..a6811ae 100644
>>>>> --- a/include/linux/mmc/card.h
>>>>> +++ b/include/linux/mmc/card.h
>>>>> @@ -98,6 +98,15 @@ struct sd_switch_caps {
>>>>> #define SD_DRIVER_TYPE_C 0x04
>>>>> #define SD_DRIVER_TYPE_D 0x08
>>>>> unsigned int uhs_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 4dfff6d..e84cd05 100644
>>>>> --- a/include/linux/mmc/host.h
>>>>> +++ b/include/linux/mmc/host.h
>>>>> @@ -128,6 +128,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);
>>>>> + int (*get_max_current_180)(struct mmc_host *mmc);
>>>>> };
>>>>>
>>>>> struct mmc_card;
>>>>> --
>>>>> 1.7.1
>>>>>
>>>>
>>>
>>>
>>
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-16 15:18 ` Philip Rakity
@ 2011-03-16 15:24 ` Nath, Arindam
2011-03-16 15:31 ` Philip Rakity
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-16 15:24 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Wednesday, March 16, 2011 8:48 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>
>
> On Mar 16, 2011, at 8:00 AM, Nath, Arindam wrote:
>
> > Hi Philip,
> >
> >
> >> -----Original Message-----
> >> From: Philip Rakity [mailto:prakity@marvell.com]
> >> Sent: Wednesday, March 16, 2011 8:22 PM
> >> To: Nath, Arindam
> >> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> >> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> >> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
> cards
> >>
> >>
> >> On Mar 16, 2011, at 7:32 AM, Nath, Arindam wrote:
> >>
> >>> Hi Philip,
> >>>
> >>>
> >>>> -----Original Message-----
> >>>> From: Philip Rakity [mailto:prakity@marvell.com]
> >>>> Sent: Wednesday, March 16, 2011 7:56 PM
> >>>> To: Nath, Arindam
> >>>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com;
> subhashj@codeaurora.org;
> >>>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> >>>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
> >> cards
> >>>>
> >>>>
> >>>> On Mar 4, 2011, at 3:32 AM, 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.
> >>>>>
> >>>>> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> >>>>> ---
> >>>>> drivers/mmc/core/sd.c | 45
> >>>> +++++++++++++++++++++++++++++++++++++++++++++
> >>>>> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> >>>>> include/linux/mmc/card.h | 9 +++++++++
> >>>>> include/linux/mmc/host.h | 1 +
> >>>>> 4 files changed, 79 insertions(+), 0 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> >>>>> index ec0d8e6..df98a2c 100644
> >>>>> --- a/drivers/mmc/core/sd.c
> >>>>> +++ b/drivers/mmc/core/sd.c
> >>>>> @@ -550,6 +550,46 @@ 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)
> >>>>> +{
> >>>>> + struct mmc_host *host = card->host;
> >>>>> + int mmc_host_max_current_180, current_limit;
> >>>>> + int err;
> >>>>> +
> >>>>> + /* sanity check */
> >>>>> + if (!host->ops->get_max_current_180)
> >>>>> + return 0;
> >>>>
> >>>> a better name would be get_max_current rather than
> >> get_max_current_180
> >>>
> >>> The Max Current Capabilities register reports maximum currents for
> >> 1.8V, 3.0V and 3.3V. Since we are only interested in the maximum
> >> current at 1.8V, so I have added *_180 to the variable names to make
> it
> >> explicit.
> >>
> >> understand but that is the usage now and if we need to extend the
> code
> >> the name becomes misleading.
> >
> > Okay. I will remove *_180 in next version.
> >
> >>>
> >>>>
> >>>> do you want a test for get_max_current_180 < 400 ? and return 0
> >> have
> >>>> it do the switch
> >>>> by setting the value ?
> >>>
> >>> As mentioned in the Physical Layer spec v3.01, <400mA falls under
> the
> >> default current limit, so we don't need to set it in case any or all
> of
> >> the above conditions fail.
> >>
> >> if future cards have memory or power is not removed they will they
> >> retain the old setting ? if so better to explicitly initialize.
> >
> > Can you please elaborate this a little further?
>
> Power to the sd slot is supplied via a regulator. This is turned on at
> power on.
> sometime in the future a reboot is done. power is NOT removed. The
> device does not reset
> into the default state. Have seen this on some boards.
Thanks for the explanation. I will modify code to set the default current limit for the card.
Regards,
Arindam
>
> >
> > Thanks,
> > Arindam
> >
> >>
> >>>
> >>> Thanks,
> >>> Arindam
> >>>
> >>>>> +
> >>>>> + /* Maximum current supported by host at 1.8V */
> >>>>> + mmc_host_max_current_180 = host->ops-
> >get_max_current_180(host);
> >>>>> +
> >>>>> + if (mmc_host_max_current_180 >= 800) {
> >>>>> + if (card->sw_caps.uhs_curr_limit &
> SD_MAX_CURRENT_800)
> >>>>> + current_limit = SD_SET_CURRENT_LIMIT_800;
> >>>>> + else if (card->sw_caps.uhs_curr_limit &
> SD_MAX_CURRENT_600)
> >>>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
> >>>>> + else if (card->sw_caps.uhs_curr_limit &
> SD_MAX_CURRENT_400)
> >>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
> >>>>> + } else if (mmc_host_max_current_180 >= 600) {
> >>>>> + if (card->sw_caps.uhs_curr_limit &
> SD_MAX_CURRENT_600)
> >>>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
> >>>>> + else if (card->sw_caps.uhs_curr_limit &
> SD_MAX_CURRENT_400)
> >>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
> >>>>> + } else if (mmc_host_max_current_180 >= 400)
> >>>>> + if (card->sw_caps.uhs_curr_limit &
> SD_MAX_CURRENT_400)
> >>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
> >>>>
> >>>> or
> >>>> 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
> >>>>> */
> >>>>> @@ -590,6 +630,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 f127fa2..245cc39 100644
> >>>>> --- a/drivers/mmc/host/sdhci.c
> >>>>> +++ b/drivers/mmc/host/sdhci.c
> >>>>> @@ -1462,12 +1462,36 @@ static int
> >>>> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> >>>>> return -EAGAIN;
> >>>>> }
> >>>>>
> >>>>
> >>>> better name is sdhci_get_max_current
> >>>>
> >>>>> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> >>>>> +{
> >>>>> + struct sdhci_host *host;
> >>>>> + u32 max_current_caps;
> >>>>> + unsigned long flags;
> >>>>> + int max_current_180;
> >>>>> +
> >>>>> + host = mmc_priv(mmc);
> >>>>> +
> >>>>> + spin_lock_irqsave(&host->lock, flags);
> >>>>> +
> >>>>> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> >>>>> +
> >>>>> + spin_unlock_irqrestore(&host->lock, flags);
> >>>>> +
> >>>>> + /* Maximum current is 4 times the register value for 1.8V
> */
> >>>>> + max_current_180 = ((max_current_caps &
> >>>> SDHCI_MAX_CURRENT_180_MASK) >>
> >>>>> + SDHCI_MAX_CURRENT_180_SHIFT) *
> >>>>> + SDHCI_MAX_CURRENT_MULTIPLIER;
> >>>>
> >>>> SDHCI_MAX_CURRENT_SHIFT is better name.
> >>>>
> >>>>> +
> >>>>> + return max_current_180;
> >>>>> +}
> >>>>> +
> >>>>> 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,
> >>>>> + .get_max_current_180 = sdhci_get_max_current_180,
> >>>>
> >>>> .get_max_current = sdhci_get_max_current;
> >>>>> };
> >>>>>
> >>>>>
> >>>>
> >>
> /**********************************************************************
> >>>> *******\
> >>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> >>>>> index 0b24c41..a6811ae 100644
> >>>>> --- a/include/linux/mmc/card.h
> >>>>> +++ b/include/linux/mmc/card.h
> >>>>> @@ -98,6 +98,15 @@ struct sd_switch_caps {
> >>>>> #define SD_DRIVER_TYPE_C 0x04
> >>>>> #define SD_DRIVER_TYPE_D 0x08
> >>>>> unsigned int uhs_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 4dfff6d..e84cd05 100644
> >>>>> --- a/include/linux/mmc/host.h
> >>>>> +++ b/include/linux/mmc/host.h
> >>>>> @@ -128,6 +128,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);
> >>>>> + int (*get_max_current_180)(struct mmc_host *mmc);
> >>>>> };
> >>>>>
> >>>>> struct mmc_card;
> >>>>> --
> >>>>> 1.7.1
> >>>>>
> >>>>
> >>>
> >>>
> >>
> >
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-16 15:24 ` Nath, Arindam
@ 2011-03-16 15:31 ` Philip Rakity
2011-03-16 15:33 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Philip Rakity @ 2011-03-16 15:31 UTC (permalink / raw)
To: Nath, Arindam
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Arindam,
Thanks,
There are a few other functions in the same file that should also be changed for the same reason.
Philip
On Mar 16, 2011, at 8:24 AM, Nath, Arindam wrote:
> Hi Philip,
>
>
>> -----Original Message-----
>> From: Philip Rakity [mailto:prakity@marvell.com]
>> Sent: Wednesday, March 16, 2011 8:48 PM
>> To: Nath, Arindam
>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>>
>>
>> On Mar 16, 2011, at 8:00 AM, Nath, Arindam wrote:
>>
>>> Hi Philip,
>>>
>>>
>>>> -----Original Message-----
>>>> From: Philip Rakity [mailto:prakity@marvell.com]
>>>> Sent: Wednesday, March 16, 2011 8:22 PM
>>>> To: Nath, Arindam
>>>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
>>>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>>>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
>> cards
>>>>
>>>>
>>>> On Mar 16, 2011, at 7:32 AM, Nath, Arindam wrote:
>>>>
>>>>> Hi Philip,
>>>>>
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Philip Rakity [mailto:prakity@marvell.com]
>>>>>> Sent: Wednesday, March 16, 2011 7:56 PM
>>>>>> To: Nath, Arindam
>>>>>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com;
>> subhashj@codeaurora.org;
>>>>>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
>> anath.amd@gmail.com
>>>>>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
>>>> cards
>>>>>>
>>>>>>
>>>>>> On Mar 4, 2011, at 3:32 AM, 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.
>>>>>>>
>>>>>>> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>>>>>>> ---
>>>>>>> drivers/mmc/core/sd.c | 45
>>>>>> +++++++++++++++++++++++++++++++++++++++++++++
>>>>>>> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
>>>>>>> include/linux/mmc/card.h | 9 +++++++++
>>>>>>> include/linux/mmc/host.h | 1 +
>>>>>>> 4 files changed, 79 insertions(+), 0 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>>>>>>> index ec0d8e6..df98a2c 100644
>>>>>>> --- a/drivers/mmc/core/sd.c
>>>>>>> +++ b/drivers/mmc/core/sd.c
>>>>>>> @@ -550,6 +550,46 @@ 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)
>>>>>>> +{
>>>>>>> + struct mmc_host *host = card->host;
>>>>>>> + int mmc_host_max_current_180, current_limit;
>>>>>>> + int err;
>>>>>>> +
>>>>>>> + /* sanity check */
>>>>>>> + if (!host->ops->get_max_current_180)
>>>>>>> + return 0;
>>>>>>
>>>>>> a better name would be get_max_current rather than
>>>> get_max_current_180
>>>>>
>>>>> The Max Current Capabilities register reports maximum currents for
>>>> 1.8V, 3.0V and 3.3V. Since we are only interested in the maximum
>>>> current at 1.8V, so I have added *_180 to the variable names to make
>> it
>>>> explicit.
>>>>
>>>> understand but that is the usage now and if we need to extend the
>> code
>>>> the name becomes misleading.
>>>
>>> Okay. I will remove *_180 in next version.
>>>
>>>>>
>>>>>>
>>>>>> do you want a test for get_max_current_180 < 400 ? and return 0
>>>> have
>>>>>> it do the switch
>>>>>> by setting the value ?
>>>>>
>>>>> As mentioned in the Physical Layer spec v3.01, <400mA falls under
>> the
>>>> default current limit, so we don't need to set it in case any or all
>> of
>>>> the above conditions fail.
>>>>
>>>> if future cards have memory or power is not removed they will they
>>>> retain the old setting ? if so better to explicitly initialize.
>>>
>>> Can you please elaborate this a little further?
>>
>> Power to the sd slot is supplied via a regulator. This is turned on at
>> power on.
>> sometime in the future a reboot is done. power is NOT removed. The
>> device does not reset
>> into the default state. Have seen this on some boards.
>
> Thanks for the explanation. I will modify code to set the default current limit for the card.
>
> Regards,
> Arindam
>
>>
>>>
>>> Thanks,
>>> Arindam
>>>
>>>>
>>>>>
>>>>> Thanks,
>>>>> Arindam
>>>>>
>>>>>>> +
>>>>>>> + /* Maximum current supported by host at 1.8V */
>>>>>>> + mmc_host_max_current_180 = host->ops-
>>> get_max_current_180(host);
>>>>>>> +
>>>>>>> + if (mmc_host_max_current_180 >= 800) {
>>>>>>> + if (card->sw_caps.uhs_curr_limit &
>> SD_MAX_CURRENT_800)
>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_800;
>>>>>>> + else if (card->sw_caps.uhs_curr_limit &
>> SD_MAX_CURRENT_600)
>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
>>>>>>> + else if (card->sw_caps.uhs_curr_limit &
>> SD_MAX_CURRENT_400)
>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>>>>>> + } else if (mmc_host_max_current_180 >= 600) {
>>>>>>> + if (card->sw_caps.uhs_curr_limit &
>> SD_MAX_CURRENT_600)
>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
>>>>>>> + else if (card->sw_caps.uhs_curr_limit &
>> SD_MAX_CURRENT_400)
>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>>>>>> + } else if (mmc_host_max_current_180 >= 400)
>>>>>>> + if (card->sw_caps.uhs_curr_limit &
>> SD_MAX_CURRENT_400)
>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>>>>>
>>>>>> or
>>>>>> 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
>>>>>>> */
>>>>>>> @@ -590,6 +630,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 f127fa2..245cc39 100644
>>>>>>> --- a/drivers/mmc/host/sdhci.c
>>>>>>> +++ b/drivers/mmc/host/sdhci.c
>>>>>>> @@ -1462,12 +1462,36 @@ static int
>>>>>> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
>>>>>>> return -EAGAIN;
>>>>>>> }
>>>>>>>
>>>>>>
>>>>>> better name is sdhci_get_max_current
>>>>>>
>>>>>>> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
>>>>>>> +{
>>>>>>> + struct sdhci_host *host;
>>>>>>> + u32 max_current_caps;
>>>>>>> + unsigned long flags;
>>>>>>> + int max_current_180;
>>>>>>> +
>>>>>>> + host = mmc_priv(mmc);
>>>>>>> +
>>>>>>> + spin_lock_irqsave(&host->lock, flags);
>>>>>>> +
>>>>>>> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
>>>>>>> +
>>>>>>> + spin_unlock_irqrestore(&host->lock, flags);
>>>>>>> +
>>>>>>> + /* Maximum current is 4 times the register value for 1.8V
>> */
>>>>>>> + max_current_180 = ((max_current_caps &
>>>>>> SDHCI_MAX_CURRENT_180_MASK) >>
>>>>>>> + SDHCI_MAX_CURRENT_180_SHIFT) *
>>>>>>> + SDHCI_MAX_CURRENT_MULTIPLIER;
>>>>>>
>>>>>> SDHCI_MAX_CURRENT_SHIFT is better name.
>>>>>>
>>>>>>> +
>>>>>>> + return max_current_180;
>>>>>>> +}
>>>>>>> +
>>>>>>> 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,
>>>>>>> + .get_max_current_180 = sdhci_get_max_current_180,
>>>>>>
>>>>>> .get_max_current = sdhci_get_max_current;
>>>>>>> };
>>>>>>>
>>>>>>>
>>>>>>
>>>>
>> /**********************************************************************
>>>>>> *******\
>>>>>>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>>>>>>> index 0b24c41..a6811ae 100644
>>>>>>> --- a/include/linux/mmc/card.h
>>>>>>> +++ b/include/linux/mmc/card.h
>>>>>>> @@ -98,6 +98,15 @@ struct sd_switch_caps {
>>>>>>> #define SD_DRIVER_TYPE_C 0x04
>>>>>>> #define SD_DRIVER_TYPE_D 0x08
>>>>>>> unsigned int uhs_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 4dfff6d..e84cd05 100644
>>>>>>> --- a/include/linux/mmc/host.h
>>>>>>> +++ b/include/linux/mmc/host.h
>>>>>>> @@ -128,6 +128,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);
>>>>>>> + int (*get_max_current_180)(struct mmc_host *mmc);
>>>>>>> };
>>>>>>>
>>>>>>> struct mmc_card;
>>>>>>> --
>>>>>>> 1.7.1
>>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>
>>>
>>>
>>
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-16 15:31 ` Philip Rakity
@ 2011-03-16 15:33 ` Nath, Arindam
2011-03-16 15:34 ` Philip Rakity
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-16 15:33 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Wednesday, March 16, 2011 9:01 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>
>
> Arindam,
>
> Thanks,
>
> There are a few other functions in the same file that should also be
> changed for the same reason.
I hope you mean by setting default bus speed mode and driver type for the card. If so, I will take care of them.
Thanks,
Arindam
>
> Philip
>
> On Mar 16, 2011, at 8:24 AM, Nath, Arindam wrote:
>
> > Hi Philip,
> >
> >
> >> -----Original Message-----
> >> From: Philip Rakity [mailto:prakity@marvell.com]
> >> Sent: Wednesday, March 16, 2011 8:48 PM
> >> To: Nath, Arindam
> >> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> >> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> >> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
> cards
> >>
> >>
> >> On Mar 16, 2011, at 8:00 AM, Nath, Arindam wrote:
> >>
> >>> Hi Philip,
> >>>
> >>>
> >>>> -----Original Message-----
> >>>> From: Philip Rakity [mailto:prakity@marvell.com]
> >>>> Sent: Wednesday, March 16, 2011 8:22 PM
> >>>> To: Nath, Arindam
> >>>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com;
> subhashj@codeaurora.org;
> >>>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> >>>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
> >> cards
> >>>>
> >>>>
> >>>> On Mar 16, 2011, at 7:32 AM, Nath, Arindam wrote:
> >>>>
> >>>>> Hi Philip,
> >>>>>
> >>>>>
> >>>>>> -----Original Message-----
> >>>>>> From: Philip Rakity [mailto:prakity@marvell.com]
> >>>>>> Sent: Wednesday, March 16, 2011 7:56 PM
> >>>>>> To: Nath, Arindam
> >>>>>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com;
> >> subhashj@codeaurora.org;
> >>>>>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> >> anath.amd@gmail.com
> >>>>>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
> >>>> cards
> >>>>>>
> >>>>>>
> >>>>>> On Mar 4, 2011, at 3:32 AM, 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.
> >>>>>>>
> >>>>>>> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> >>>>>>> ---
> >>>>>>> drivers/mmc/core/sd.c | 45
> >>>>>> +++++++++++++++++++++++++++++++++++++++++++++
> >>>>>>> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> >>>>>>> include/linux/mmc/card.h | 9 +++++++++
> >>>>>>> include/linux/mmc/host.h | 1 +
> >>>>>>> 4 files changed, 79 insertions(+), 0 deletions(-)
> >>>>>>>
> >>>>>>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> >>>>>>> index ec0d8e6..df98a2c 100644
> >>>>>>> --- a/drivers/mmc/core/sd.c
> >>>>>>> +++ b/drivers/mmc/core/sd.c
> >>>>>>> @@ -550,6 +550,46 @@ 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)
> >>>>>>> +{
> >>>>>>> + struct mmc_host *host = card->host;
> >>>>>>> + int mmc_host_max_current_180, current_limit;
> >>>>>>> + int err;
> >>>>>>> +
> >>>>>>> + /* sanity check */
> >>>>>>> + if (!host->ops->get_max_current_180)
> >>>>>>> + return 0;
> >>>>>>
> >>>>>> a better name would be get_max_current rather than
> >>>> get_max_current_180
> >>>>>
> >>>>> The Max Current Capabilities register reports maximum currents
> for
> >>>> 1.8V, 3.0V and 3.3V. Since we are only interested in the maximum
> >>>> current at 1.8V, so I have added *_180 to the variable names to
> make
> >> it
> >>>> explicit.
> >>>>
> >>>> understand but that is the usage now and if we need to extend the
> >> code
> >>>> the name becomes misleading.
> >>>
> >>> Okay. I will remove *_180 in next version.
> >>>
> >>>>>
> >>>>>>
> >>>>>> do you want a test for get_max_current_180 < 400 ? and return 0
> >>>> have
> >>>>>> it do the switch
> >>>>>> by setting the value ?
> >>>>>
> >>>>> As mentioned in the Physical Layer spec v3.01, <400mA falls under
> >> the
> >>>> default current limit, so we don't need to set it in case any or
> all
> >> of
> >>>> the above conditions fail.
> >>>>
> >>>> if future cards have memory or power is not removed they will they
> >>>> retain the old setting ? if so better to explicitly initialize.
> >>>
> >>> Can you please elaborate this a little further?
> >>
> >> Power to the sd slot is supplied via a regulator. This is turned on
> at
> >> power on.
> >> sometime in the future a reboot is done. power is NOT removed. The
> >> device does not reset
> >> into the default state. Have seen this on some boards.
> >
> > Thanks for the explanation. I will modify code to set the default
> current limit for the card.
> >
> > Regards,
> > Arindam
> >
> >>
> >>>
> >>> Thanks,
> >>> Arindam
> >>>
> >>>>
> >>>>>
> >>>>> Thanks,
> >>>>> Arindam
> >>>>>
> >>>>>>> +
> >>>>>>> + /* Maximum current supported by host at 1.8V */
> >>>>>>> + mmc_host_max_current_180 = host->ops-
> >>> get_max_current_180(host);
> >>>>>>> +
> >>>>>>> + if (mmc_host_max_current_180 >= 800) {
> >>>>>>> + if (card->sw_caps.uhs_curr_limit &
> >> SD_MAX_CURRENT_800)
> >>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_800;
> >>>>>>> + else if (card->sw_caps.uhs_curr_limit &
> >> SD_MAX_CURRENT_600)
> >>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
> >>>>>>> + else if (card->sw_caps.uhs_curr_limit &
> >> SD_MAX_CURRENT_400)
> >>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
> >>>>>>> + } else if (mmc_host_max_current_180 >= 600) {
> >>>>>>> + if (card->sw_caps.uhs_curr_limit &
> >> SD_MAX_CURRENT_600)
> >>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
> >>>>>>> + else if (card->sw_caps.uhs_curr_limit &
> >> SD_MAX_CURRENT_400)
> >>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
> >>>>>>> + } else if (mmc_host_max_current_180 >= 400)
> >>>>>>> + if (card->sw_caps.uhs_curr_limit &
> >> SD_MAX_CURRENT_400)
> >>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
> >>>>>>
> >>>>>> or
> >>>>>> 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
> >>>>>>> */
> >>>>>>> @@ -590,6 +630,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 f127fa2..245cc39 100644
> >>>>>>> --- a/drivers/mmc/host/sdhci.c
> >>>>>>> +++ b/drivers/mmc/host/sdhci.c
> >>>>>>> @@ -1462,12 +1462,36 @@ static int
> >>>>>> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> >>>>>>> return -EAGAIN;
> >>>>>>> }
> >>>>>>>
> >>>>>>
> >>>>>> better name is sdhci_get_max_current
> >>>>>>
> >>>>>>> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> >>>>>>> +{
> >>>>>>> + struct sdhci_host *host;
> >>>>>>> + u32 max_current_caps;
> >>>>>>> + unsigned long flags;
> >>>>>>> + int max_current_180;
> >>>>>>> +
> >>>>>>> + host = mmc_priv(mmc);
> >>>>>>> +
> >>>>>>> + spin_lock_irqsave(&host->lock, flags);
> >>>>>>> +
> >>>>>>> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> >>>>>>> +
> >>>>>>> + spin_unlock_irqrestore(&host->lock, flags);
> >>>>>>> +
> >>>>>>> + /* Maximum current is 4 times the register value for 1.8V
> >> */
> >>>>>>> + max_current_180 = ((max_current_caps &
> >>>>>> SDHCI_MAX_CURRENT_180_MASK) >>
> >>>>>>> + SDHCI_MAX_CURRENT_180_SHIFT) *
> >>>>>>> + SDHCI_MAX_CURRENT_MULTIPLIER;
> >>>>>>
> >>>>>> SDHCI_MAX_CURRENT_SHIFT is better name.
> >>>>>>
> >>>>>>> +
> >>>>>>> + return max_current_180;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> 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,
> >>>>>>> + .get_max_current_180 = sdhci_get_max_current_180,
> >>>>>>
> >>>>>> .get_max_current = sdhci_get_max_current;
> >>>>>>> };
> >>>>>>>
> >>>>>>>
> >>>>>>
> >>>>
> >>
> /**********************************************************************
> >>>>>> *******\
> >>>>>>> diff --git a/include/linux/mmc/card.h
> b/include/linux/mmc/card.h
> >>>>>>> index 0b24c41..a6811ae 100644
> >>>>>>> --- a/include/linux/mmc/card.h
> >>>>>>> +++ b/include/linux/mmc/card.h
> >>>>>>> @@ -98,6 +98,15 @@ struct sd_switch_caps {
> >>>>>>> #define SD_DRIVER_TYPE_C 0x04
> >>>>>>> #define SD_DRIVER_TYPE_D 0x08
> >>>>>>> unsigned int uhs_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 4dfff6d..e84cd05 100644
> >>>>>>> --- a/include/linux/mmc/host.h
> >>>>>>> +++ b/include/linux/mmc/host.h
> >>>>>>> @@ -128,6 +128,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);
> >>>>>>> + int (*get_max_current_180)(struct mmc_host *mmc);
> >>>>>>> };
> >>>>>>>
> >>>>>>> struct mmc_card;
> >>>>>>> --
> >>>>>>> 1.7.1
> >>>>>>>
> >>>>>>
> >>>>>
> >>>>>
> >>>>
> >>>
> >>>
> >>
> >
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-16 15:33 ` Nath, Arindam
@ 2011-03-16 15:34 ` Philip Rakity
0 siblings, 0 replies; 125+ messages in thread
From: Philip Rakity @ 2011-03-16 15:34 UTC (permalink / raw)
To: Nath, Arindam
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
On Mar 16, 2011, at 8:33 AM, Nath, Arindam wrote:
> Hi Philip,
>
>
>> -----Original Message-----
>> From: Philip Rakity [mailto:prakity@marvell.com]
>> Sent: Wednesday, March 16, 2011 9:01 PM
>> To: Nath, Arindam
>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>>
>>
>> Arindam,
>>
>> Thanks,
>>
>> There are a few other functions in the same file that should also be
>> changed for the same reason.
>
> I hope you mean by setting default bus speed mode and driver type for the card. If so, I will take care of them.
yes ! -- thank you.
>
> Thanks,
> Arindam
>
>>
>> Philip
>>
>> On Mar 16, 2011, at 8:24 AM, Nath, Arindam wrote:
>>
>>> Hi Philip,
>>>
>>>
>>>> -----Original Message-----
>>>> From: Philip Rakity [mailto:prakity@marvell.com]
>>>> Sent: Wednesday, March 16, 2011 8:48 PM
>>>> To: Nath, Arindam
>>>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
>>>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
>>>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
>> cards
>>>>
>>>>
>>>> On Mar 16, 2011, at 8:00 AM, Nath, Arindam wrote:
>>>>
>>>>> Hi Philip,
>>>>>
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Philip Rakity [mailto:prakity@marvell.com]
>>>>>> Sent: Wednesday, March 16, 2011 8:22 PM
>>>>>> To: Nath, Arindam
>>>>>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com;
>> subhashj@codeaurora.org;
>>>>>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
>> anath.amd@gmail.com
>>>>>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
>>>> cards
>>>>>>
>>>>>>
>>>>>> On Mar 16, 2011, at 7:32 AM, Nath, Arindam wrote:
>>>>>>
>>>>>>> Hi Philip,
>>>>>>>
>>>>>>>
>>>>>>>> -----Original Message-----
>>>>>>>> From: Philip Rakity [mailto:prakity@marvell.com]
>>>>>>>> Sent: Wednesday, March 16, 2011 7:56 PM
>>>>>>>> To: Nath, Arindam
>>>>>>>> Cc: cjb@laptop.org; zhangfei.gao@gmail.com;
>>>> subhashj@codeaurora.org;
>>>>>>>> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
>>>> anath.amd@gmail.com
>>>>>>>> Subject: Re: [PATCH v2 07/12] mmc: sd: set current limit for uhs
>>>>>> cards
>>>>>>>>
>>>>>>>>
>>>>>>>> On Mar 4, 2011, at 3:32 AM, 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.
>>>>>>>>>
>>>>>>>>> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
>>>>>>>>> ---
>>>>>>>>> drivers/mmc/core/sd.c | 45
>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
>>>>>>>>> include/linux/mmc/card.h | 9 +++++++++
>>>>>>>>> include/linux/mmc/host.h | 1 +
>>>>>>>>> 4 files changed, 79 insertions(+), 0 deletions(-)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>>>>>>>>> index ec0d8e6..df98a2c 100644
>>>>>>>>> --- a/drivers/mmc/core/sd.c
>>>>>>>>> +++ b/drivers/mmc/core/sd.c
>>>>>>>>> @@ -550,6 +550,46 @@ 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)
>>>>>>>>> +{
>>>>>>>>> + struct mmc_host *host = card->host;
>>>>>>>>> + int mmc_host_max_current_180, current_limit;
>>>>>>>>> + int err;
>>>>>>>>> +
>>>>>>>>> + /* sanity check */
>>>>>>>>> + if (!host->ops->get_max_current_180)
>>>>>>>>> + return 0;
>>>>>>>>
>>>>>>>> a better name would be get_max_current rather than
>>>>>> get_max_current_180
>>>>>>>
>>>>>>> The Max Current Capabilities register reports maximum currents
>> for
>>>>>> 1.8V, 3.0V and 3.3V. Since we are only interested in the maximum
>>>>>> current at 1.8V, so I have added *_180 to the variable names to
>> make
>>>> it
>>>>>> explicit.
>>>>>>
>>>>>> understand but that is the usage now and if we need to extend the
>>>> code
>>>>>> the name becomes misleading.
>>>>>
>>>>> Okay. I will remove *_180 in next version.
>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> do you want a test for get_max_current_180 < 400 ? and return 0
>>>>>> have
>>>>>>>> it do the switch
>>>>>>>> by setting the value ?
>>>>>>>
>>>>>>> As mentioned in the Physical Layer spec v3.01, <400mA falls under
>>>> the
>>>>>> default current limit, so we don't need to set it in case any or
>> all
>>>> of
>>>>>> the above conditions fail.
>>>>>>
>>>>>> if future cards have memory or power is not removed they will they
>>>>>> retain the old setting ? if so better to explicitly initialize.
>>>>>
>>>>> Can you please elaborate this a little further?
>>>>
>>>> Power to the sd slot is supplied via a regulator. This is turned on
>> at
>>>> power on.
>>>> sometime in the future a reboot is done. power is NOT removed. The
>>>> device does not reset
>>>> into the default state. Have seen this on some boards.
>>>
>>> Thanks for the explanation. I will modify code to set the default
>> current limit for the card.
>>>
>>> Regards,
>>> Arindam
>>>
>>>>
>>>>>
>>>>> Thanks,
>>>>> Arindam
>>>>>
>>>>>>
>>>>>>>
>>>>>>> Thanks,
>>>>>>> Arindam
>>>>>>>
>>>>>>>>> +
>>>>>>>>> + /* Maximum current supported by host at 1.8V */
>>>>>>>>> + mmc_host_max_current_180 = host->ops-
>>>>> get_max_current_180(host);
>>>>>>>>> +
>>>>>>>>> + if (mmc_host_max_current_180 >= 800) {
>>>>>>>>> + if (card->sw_caps.uhs_curr_limit &
>>>> SD_MAX_CURRENT_800)
>>>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_800;
>>>>>>>>> + else if (card->sw_caps.uhs_curr_limit &
>>>> SD_MAX_CURRENT_600)
>>>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
>>>>>>>>> + else if (card->sw_caps.uhs_curr_limit &
>>>> SD_MAX_CURRENT_400)
>>>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>>>>>>>> + } else if (mmc_host_max_current_180 >= 600) {
>>>>>>>>> + if (card->sw_caps.uhs_curr_limit &
>>>> SD_MAX_CURRENT_600)
>>>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_600;
>>>>>>>>> + else if (card->sw_caps.uhs_curr_limit &
>>>> SD_MAX_CURRENT_400)
>>>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>>>>>>>> + } else if (mmc_host_max_current_180 >= 400)
>>>>>>>>> + if (card->sw_caps.uhs_curr_limit &
>>>> SD_MAX_CURRENT_400)
>>>>>>>>> + current_limit = SD_SET_CURRENT_LIMIT_400;
>>>>>>>>
>>>>>>>> or
>>>>>>>> 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
>>>>>>>>> */
>>>>>>>>> @@ -590,6 +630,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 f127fa2..245cc39 100644
>>>>>>>>> --- a/drivers/mmc/host/sdhci.c
>>>>>>>>> +++ b/drivers/mmc/host/sdhci.c
>>>>>>>>> @@ -1462,12 +1462,36 @@ static int
>>>>>>>> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
>>>>>>>>> return -EAGAIN;
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>
>>>>>>>> better name is sdhci_get_max_current
>>>>>>>>
>>>>>>>>> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
>>>>>>>>> +{
>>>>>>>>> + struct sdhci_host *host;
>>>>>>>>> + u32 max_current_caps;
>>>>>>>>> + unsigned long flags;
>>>>>>>>> + int max_current_180;
>>>>>>>>> +
>>>>>>>>> + host = mmc_priv(mmc);
>>>>>>>>> +
>>>>>>>>> + spin_lock_irqsave(&host->lock, flags);
>>>>>>>>> +
>>>>>>>>> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
>>>>>>>>> +
>>>>>>>>> + spin_unlock_irqrestore(&host->lock, flags);
>>>>>>>>> +
>>>>>>>>> + /* Maximum current is 4 times the register value for 1.8V
>>>> */
>>>>>>>>> + max_current_180 = ((max_current_caps &
>>>>>>>> SDHCI_MAX_CURRENT_180_MASK) >>
>>>>>>>>> + SDHCI_MAX_CURRENT_180_SHIFT) *
>>>>>>>>> + SDHCI_MAX_CURRENT_MULTIPLIER;
>>>>>>>>
>>>>>>>> SDHCI_MAX_CURRENT_SHIFT is better name.
>>>>>>>>
>>>>>>>>> +
>>>>>>>>> + return max_current_180;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> 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,
>>>>>>>>> + .get_max_current_180 = sdhci_get_max_current_180,
>>>>>>>>
>>>>>>>> .get_max_current = sdhci_get_max_current;
>>>>>>>>> };
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>
>>>>
>> /**********************************************************************
>>>>>>>> *******\
>>>>>>>>> diff --git a/include/linux/mmc/card.h
>> b/include/linux/mmc/card.h
>>>>>>>>> index 0b24c41..a6811ae 100644
>>>>>>>>> --- a/include/linux/mmc/card.h
>>>>>>>>> +++ b/include/linux/mmc/card.h
>>>>>>>>> @@ -98,6 +98,15 @@ struct sd_switch_caps {
>>>>>>>>> #define SD_DRIVER_TYPE_C 0x04
>>>>>>>>> #define SD_DRIVER_TYPE_D 0x08
>>>>>>>>> unsigned int uhs_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 4dfff6d..e84cd05 100644
>>>>>>>>> --- a/include/linux/mmc/host.h
>>>>>>>>> +++ b/include/linux/mmc/host.h
>>>>>>>>> @@ -128,6 +128,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);
>>>>>>>>> + int (*get_max_current_180)(struct mmc_host *mmc);
>>>>>>>>> };
>>>>>>>>>
>>>>>>>>> struct mmc_card;
>>>>>>>>> --
>>>>>>>>> 1.7.1
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>
>>>
>>>
>>
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-04 11:32 ` [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
` (3 preceding siblings ...)
2011-03-15 10:18 ` Subhash Jadavani
@ 2011-03-16 21:39 ` Philip Rakity
2011-03-17 4:18 ` Nath, Arindam
2011-03-24 10:52 ` zhangfei gao
5 siblings, 1 reply; 125+ messages in thread
From: Philip Rakity @ 2011-03-16 21:39 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, henry.su@amd.com, aaron.lu@amd.com,
anath.amd@gmail.com
Hi Arindam,
was getting kernel crash on SD card -- fix is below
Philip
On Mar 4, 2011, at 3:32 AM, 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 support 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.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 37 ++++++++++-
> drivers/mmc/core/sd_ops.c | 32 ++++++++++
> drivers/mmc/core/sd_ops.h | 1 +
> drivers/mmc/host/sdhci.c | 148 +++++++++++++++++++++++++++++++++++++++++----
> drivers/mmc/host/sdhci.h | 18 +++++-
> include/linux/mmc/host.h | 10 +++
> include/linux/mmc/sd.h | 1 +
> 7 files changed, 229 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index b3f8a3c..3e82599 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -408,6 +408,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
> @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> if (!err)
> ocr |= 1 << 30;
>
> - err = mmc_send_app_op_cond(host, ocr, NULL);
> + /* If the host can supply more than 150mA, XPC should be set to 1. */
> + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> + MMC_CAP_SET_XPC_180))
> + ocr |= 1 << 28;
> +
> + 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)) {
getting kernel crash -- change code to
> + if (!mmc_host_is_spi(host) && (rocr & 0x41000000) == 0x41000000) {
need both bits set -- if one bit is set code is true.
> + err = mmc_start_voltage_switch(host);
> + if (err)
> + return err;
> + }
> +
> if (mmc_host_is_spi(host))
> err = mmc_send_cid(host, cid);
> else
> @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> }
>
> /*
> + * 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))
> + host->ocr |= (1 << 24);
> +
> + /*
> * Detect and init the card.
> */
> err = mmc_sd_init_card(host, host->ocr, NULL);
> - if (err)
> - goto err;
change
> + if (err == -EAGAIN) {
> + /*
> + * Retry initialization with S18R set to 0.
> + */
> + host->ocr &= ~(1 << 24);
> + err = mmc_sd_init_card(host, host->ocr, NULL);
> + if (err)
> + goto err;
> + }
to
if (err) {
if (err == -EAGAIN) {
/*
* Retry initialization with S18R set to 0.
*/
host->ocr &= ~(1 << 24);
err = mmc_sd_init_card(host, host->ocr, NULL);
}
if (err)
goto err;
}
was getting kernel crash when init failed and host->card was NULL
>
> mmc_release_host(host);
> err = mmc_add_card(host->card);
> diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> index 797cdb5..8a23e2e 100644
> --- a/drivers/mmc/core/sd_ops.c
> +++ b/drivers/mmc/core/sd_ops.c
> @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
> return 0;
> }
>
> +int mmc_start_voltage_switch(struct mmc_host *host)
> +{
> + struct mmc_command cmd;
> + int err;
> +
> + BUG_ON(!host);
> +
> + /*
> + * If the host does not provide signal voltage switch procedure, we
> + * set S18R to 0, and then retry initialization sequence.
> + */
> + if (!host->ops->start_signal_voltage_switch)
> + return -EAGAIN;
> +
> + 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;
> +
> + err = host->ops->start_signal_voltage_switch(host);
> +
> + return err;
> +}
> +
> int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> {
> struct mmc_command cmd;
> diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> index ffc2305..3cfba59 100644
> --- a/drivers/mmc/core/sd_ops.h
> +++ b/drivers/mmc/core/sd_ops.h
> @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
> int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> u8 value, u8 *resp);
> int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> +int mmc_start_voltage_switch(struct mmc_host *host);
>
> #endif
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 8914a25..5487a0b 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -85,6 +85,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",
> @@ -1337,11 +1339,70 @@ out:
> spin_unlock_irqrestore(&host->lock, flags);
> }
>
> +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u8 pwr;
> + u16 clk, ctrl;
> + u32 present_state;
> +
> + host = mmc_priv(mmc);
> +
> + /* Stop SDCLK */
> + host = mmc_priv(mmc);
> + 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_readw(host, SDHCI_HOST_CONTROL2);
> + 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. Stop
> + * power to 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);
> +
> + return -EAGAIN;
> +}
> +
> 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,
> };
>
> /*****************************************************************************\
> @@ -1799,7 +1860,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);
> @@ -1822,12 +1885,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);
>
> + if (host->version >= SDHCI_SPEC_300)
> + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> +
> 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;
> @@ -1838,7 +1904,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) &&
> @@ -1892,10 +1959,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;
> @@ -1911,7 +1978,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);
> @@ -1923,7 +1990,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;
>
> /*
> @@ -1950,21 +2017,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)
> @@ -2024,7 +2146,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 223762c..95d70e6 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -69,6 +69,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
> @@ -147,7 +149,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
> @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -117,6 +117,8 @@ struct mmc_host_ops {
>
> /* optional callback for HC quirks */
> void (*init_card)(struct mmc_host *host, struct mmc_card *card);
> +
> + int (*start_signal_voltage_switch)(struct mmc_host *host);
> };
>
> struct mmc_card;
> @@ -173,6 +175,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 178363b..3ba5aa6 100644
> --- a/include/linux/mmc/sd.h
> +++ b/include/linux/mmc/sd.h
> @@ -17,6 +17,7 @@
> /* This is basically the same command as for MMC with some quirks. */
> #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
> #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
> +#define SD_SWITCH_VOLTAGE 11 /* ac R1 */
>
> /* class 10 */
> #define SD_SWITCH 6 /* adtc [31:0] See below R1 */
> --
> 1.7.1
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-16 21:39 ` Philip Rakity
@ 2011-03-17 4:18 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-17 4:18 UTC (permalink / raw)
To: Philip Rakity
Cc: cjb@laptop.org, zhangfei.gao@gmail.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Philip,
> -----Original Message-----
> From: Philip Rakity [mailto:prakity@marvell.com]
> Sent: Thursday, March 17, 2011 3:09 AM
> To: Nath, Arindam
> Cc: cjb@laptop.org; zhangfei.gao@gmail.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
>
> Hi Arindam,
>
> was getting kernel crash on SD card -- fix is below
>
> Philip
>
> On Mar 4, 2011, at 3:32 AM, 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 support 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.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 37 ++++++++++-
> > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> > drivers/mmc/core/sd_ops.h | 1 +
> > drivers/mmc/host/sdhci.c | 148
> +++++++++++++++++++++++++++++++++++++++++----
> > drivers/mmc/host/sdhci.h | 18 +++++-
> > include/linux/mmc/host.h | 10 +++
> > include/linux/mmc/sd.h | 1 +
> > 7 files changed, 229 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index b3f8a3c..3e82599 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -408,6 +408,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
> > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> ocr, u32 *cid)
> > if (!err)
> > ocr |= 1 << 30;
> >
> > - err = mmc_send_app_op_cond(host, ocr, NULL);
> > + /* If the host can supply more than 150mA, XPC should be set
> to 1. */
> > + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > + MMC_CAP_SET_XPC_180))
> > + ocr |= 1 << 28;
> > +
> > + 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)) {
>
> getting kernel crash -- change code to
> > + if (!mmc_host_is_spi(host) && (rocr & 0x41000000) ==
> 0x41000000) {
>
> need both bits set -- if one bit is set code is true.
This was already pointed out by Zhangfei, and it will be fixed in V3.
>
>
> > + err = mmc_start_voltage_switch(host);
> > + if (err)
> > + return err;
> > + }
> > +
> > if (mmc_host_is_spi(host))
> > err = mmc_send_cid(host, cid);
> > else
> > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> > }
> >
> > /*
> > + * 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))
> > + host->ocr |= (1 << 24);
> > +
> > + /*
> > * Detect and init the card.
> > */
> > err = mmc_sd_init_card(host, host->ocr, NULL);
> > - if (err)
> > - goto err;
>
> change
> > + if (err == -EAGAIN) {
> > + /*
> > + * Retry initialization with S18R set to 0.
> > + */
> > + host->ocr &= ~(1 << 24);
> > + err = mmc_sd_init_card(host, host->ocr, NULL);
> > + if (err)
> > + goto err;
> > + }
>
> to
> if (err) {
> if (err == -EAGAIN) {
> /*
> * Retry initialization with S18R set to 0.
> */
> host->ocr &= ~(1 << 24);
> err = mmc_sd_init_card(host, host->ocr, NULL);
> }
> if (err)
> goto err;
> }
>
>
> was getting kernel crash when init failed and host->card was NULL
Zhangfei mentioned this one too. Will be fixed.
Thanks,
Arindam
>
> >
> > mmc_release_host(host);
> > err = mmc_add_card(host->card);
> > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > index 797cdb5..8a23e2e 100644
> > --- a/drivers/mmc/core/sd_ops.c
> > +++ b/drivers/mmc/core/sd_ops.c
> > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card,
> int width)
> > return 0;
> > }
> >
> > +int mmc_start_voltage_switch(struct mmc_host *host)
> > +{
> > + struct mmc_command cmd;
> > + int err;
> > +
> > + BUG_ON(!host);
> > +
> > + /*
> > + * If the host does not provide signal voltage switch
> procedure, we
> > + * set S18R to 0, and then retry initialization sequence.
> > + */
> > + if (!host->ops->start_signal_voltage_switch)
> > + return -EAGAIN;
> > +
> > + 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;
> > +
> > + err = host->ops->start_signal_voltage_switch(host);
> > +
> > + return err;
> > +}
> > +
> > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> > {
> > struct mmc_command cmd;
> > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > index ffc2305..3cfba59 100644
> > --- a/drivers/mmc/core/sd_ops.h
> > +++ b/drivers/mmc/core/sd_ops.h
> > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> *scr);
> > int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> > u8 value, u8 *resp);
> > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > +int mmc_start_voltage_switch(struct mmc_host *host);
> >
> > #endif
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 8914a25..5487a0b 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -85,6 +85,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",
> > @@ -1337,11 +1339,70 @@ out:
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> >
> > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u8 pwr;
> > + u16 clk, ctrl;
> > + u32 present_state;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + /* Stop SDCLK */
> > + host = mmc_priv(mmc);
> > + 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_readw(host, SDHCI_HOST_CONTROL2);
> > + 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. Stop
> > + * power to 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);
> > +
> > + return -EAGAIN;
> > +}
> > +
> > 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,
> > };
> >
> >
> /**********************************************************************
> *******\
> > @@ -1799,7 +1860,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);
> > @@ -1822,12 +1885,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);
> >
> > + if (host->version >= SDHCI_SPEC_300)
> > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> > +
> > 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;
> > @@ -1838,7 +1904,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) &&
> > @@ -1892,10 +1959,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;
> > @@ -1911,7 +1978,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);
> > @@ -1923,7 +1990,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;
> >
> > /*
> > @@ -1950,21 +2017,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)
> > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -69,6 +69,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
> > @@ -147,7 +149,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
> > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> >
> > /* optional callback for HC quirks */
> > void (*init_card)(struct mmc_host *host, struct mmc_card
> *card);
> > +
> > + int (*start_signal_voltage_switch)(struct mmc_host
> *host);
> > };
> >
> > struct mmc_card;
> > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> > --- a/include/linux/mmc/sd.h
> > +++ b/include/linux/mmc/sd.h
> > @@ -17,6 +17,7 @@
> > /* This is basically the same command as for MMC with some quirks. */
> > #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6
> */
> > #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7
> */
> > +#define SD_SWITCH_VOLTAGE 11 /* ac R1
> */
> >
> > /* class 10 */
> > #define SD_SWITCH 6 /* adtc [31:0] See below R1
> */
> > --
> > 1.7.1
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-04 11:32 ` [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
2011-03-10 8:00 ` subhashj
@ 2011-03-21 6:42 ` Subhash Jadavani
2011-03-23 6:04 ` Nath, Arindam
1 sibling, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-21 6:42 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode
> selection
>
> 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. Since MMC_TIMING_UHS_SDR25 is same as
> MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag for
> UHS SDR25 again. We also 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 set the clock frequency for the
> UHS-I mode selected.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 91
> ++++++++++++++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> drivers/mmc/host/sdhci.h | 5 +++
> include/linux/mmc/card.h | 16 ++++++++
> include/linux/mmc/host.h | 4 ++
> 5 files changed, 146 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index f6a4fab..ec0d8e6 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -464,6 +464,92 @@ static int sd_select_driver_type(struct mmc_card
> *card, u8 *status)
> return 0;
> }
>
> +static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
> +{
> + unsigned int bus_speed, timing;
> + int err;
> +
> + /*
> + * If the host doesn't support any of the UHS-I modes, fallback
> on
> + * default speed.
> + */
> + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
> + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
> + return 0;
> +
> + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> + if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) {
> + bus_speed = UHS_SDR104_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR104;
> + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)
> {
> + bus_speed = UHS_SDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR50;
> + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> {
> + bus_speed = UHS_DDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_DDR50;
> + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> {
> + bus_speed = UHS_SDR25_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR25;
> + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> + }
I would not agree that if Host cap is set to MMC_CAP_UHS_SDR104 implies that
DDR50 mode is supported by host. There may be a case where host controller
only support SDR timing modes.
Basically if host should advertise all the supported modes (SDR104, SDR50,
SDR25, SDR12 & DDR) by caps and then we should choose the highest
performance mode depending on both the card and host's highest performance
mode.
So this should be the check:
if ((card->host->caps & MMC_CAP_UHS_SDR104 &&
(card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
bus_speed = UHS_SDR104_BUS_SPEED;
timing = MMC_TIMING_UHS_SDR104;
card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
} else if ((card->host->caps & MMC_CAP_UHS_DDR50 &&
(card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
bus_speed = UHS_DDR50_BUS_SPEED;
timing = MMC_TIMING_UHS_DDR50;
card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
} else if ((card->host->caps & MMC_CAP_UHS_SDR50 &&
(card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
bus_speed = UHS_SDR50_BUS_SPEED;
timing = MMC_TIMING_UHS_SDR50;
card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
} else if ((card->host->caps & MMC_CAP_UHS_SDR25 &&
(card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
bus_speed = UHS_SDR50_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_SDR12 &&
(card->sw_caps.uhs_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;
}
Regards,
Subhash
> + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> + bus_speed = UHS_SDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR50;
> + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> {
> + bus_speed = UHS_DDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_DDR50;
> + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> {
> + bus_speed = UHS_SDR25_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR25;
> + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> + }
> + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> + bus_speed = UHS_DDR50_BUS_SPEED;
> + timing = MMC_TIMING_UHS_DDR50;
> + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> {
> + bus_speed = UHS_SDR25_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR25;
> + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> + }
> + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) ||
> + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> + bus_speed = UHS_SDR25_BUS_SPEED;
> + timing = MMC_TIMING_UHS_SDR25;
> + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> + }
> + }
> +
> + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> + if (err)
> + return err;
> +
> + if ((status[16] & 0xF) != bus_speed)
> + printk(KERN_WARNING "%s: Problem setting bus speed
> mode!\n",
> + mmc_hostname(card->host));
> + else {
> + if (bus_speed) {
> + mmc_set_timing(card->host, timing);
> + mmc_set_clock(card->host,
card->sw_caps.uhs_max_dtr);
> + }
> + }
> +
> + return 0;
> +}
> +
> /*
> * UHS-I specific initialization procedure
> */
> @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
>
> /* Set the driver strength for the card */
> err = sd_select_driver_type(card, status);
> + if (err)
> + goto out;
> +
> + /* Set bus speed mode of the card */
> + err = sd_set_bus_speed_mode(card, status);
>
> out:
> kfree(status);
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 5d3bb11..f127fa2 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct mmc_host *mmc,
> struct mmc_ios *ios)
> ctrl &= ~SDHCI_CTRL_HISPD;
>
> if (host->version >= SDHCI_SPEC_300) {
> - u16 ctrl_2;
> + u16 clk, ctrl_2;
> +
> + /* In case of UHS-I modes, set High Speed Enable */
> + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> + (ios->timing == MMC_TIMING_UHS_DDR50))
> + ctrl |= SDHCI_CTRL_HISPD;
>
> ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct mmc_host *mmc,
> struct mmc_ios *ios)
> * need to reset SD Clock Enable before changing
High
> * Speed Enable to avoid generating clock gliches.
> */
> - u16 clk;
>
> /* Reset SD Clock Enable */
> clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct mmc_host *mmc,
> struct mmc_ios *ios)
> clk |= SDHCI_CLOCK_CARD_EN;
> sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> }
> +
> + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +
> + /* Select Bus Speed Mode for host */
> + if (ios->timing == MMC_TIMING_UHS_SDR25)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> + else if (ios->timing == MMC_TIMING_UHS_SDR104)
> + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> +
> + /* Reset SD Clock Enable */
> + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> + clk &= ~SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> +
> + /* Re-enable SD Clock */
> + clk |= SDHCI_CLOCK_CARD_EN;
> + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> } else
> sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
>
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index a407b5b..5bf244d 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -150,6 +150,11 @@
> #define SDHCI_ACMD12_ERR 0x3C
>
> #define SDHCI_HOST_CONTROL2 0x3E
> +#define SDHCI_CTRL_UHS_SDR12 0x0000
> +#define SDHCI_CTRL_UHS_SDR25 0x0001
> +#define SDHCI_CTRL_UHS_SDR50 0x0002
> +#define SDHCI_CTRL_UHS_SDR104 0x0003
> +#define SDHCI_CTRL_UHS_DDR50 0x0004
> #define SDHCI_CTRL_VDD_180 0x0008
> #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 2d7f7a3..0b24c41 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -75,7 +75,23 @@ struct sd_ssr {
>
> struct sd_switch_caps {
> unsigned int hs_max_dtr;
> + unsigned int uhs_max_dtr;
> +#define UHS_SDR104_MAX_DTR 208000000
> +#define UHS_SDR50_MAX_DTR 100000000
> +#define UHS_DDR50_MAX_DTR 50000000
> +#define UHS_SDR25_MAX_DTR 50000000
> unsigned int uhs_bus_mode;
> +#define UHS_SDR12_BUS_SPEED 0
> +#define UHS_SDR25_BUS_SPEED 1
> +#define UHS_SDR50_BUS_SPEED 2
> +#define UHS_SDR104_BUS_SPEED 3
> +#define UHS_DDR50_BUS_SPEED 4
> +
> +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> unsigned int uhs_drv_type;
> #define SD_DRIVER_TYPE_B 0x01
> #define SD_DRIVER_TYPE_A 0x02
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index bc2121e..4dfff6d 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -50,6 +50,10 @@ struct mmc_ios {
> #define MMC_TIMING_LEGACY 0
> #define MMC_TIMING_MMC_HS 1
> #define MMC_TIMING_SD_HS 2
> +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> +#define MMC_TIMING_UHS_SDR50 3
> +#define MMC_TIMING_UHS_SDR104 4
> +#define MMC_TIMING_UHS_DDR50 5
>
> unsigned char ddr; /* dual data rate used */
>
> --
> 1.7.1
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-04 11:32 ` [PATCH v2 07/12] mmc: sd: set current limit for uhs cards Arindam Nath
` (2 preceding siblings ...)
2011-03-16 14:26 ` Philip Rakity
@ 2011-03-21 7:43 ` Subhash Jadavani
2011-03-21 7:54 ` Nath, Arindam
3 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-21 7:43 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>
> 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.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 45
> +++++++++++++++++++++++++++++++++++++++++++++
> drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> include/linux/mmc/card.h | 9 +++++++++
> include/linux/mmc/host.h | 1 +
> 4 files changed, 79 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index ec0d8e6..df98a2c 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -550,6 +550,46 @@ 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)
> +{
> + struct mmc_host *host = card->host;
> + int mmc_host_max_current_180, current_limit;
> + int err;
> +
> + /* sanity check */
> + if (!host->ops->get_max_current_180)
> + return 0;
> +
> + /* Maximum current supported by host at 1.8V */
> + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
> +
> + if (mmc_host_max_current_180 >= 800) {
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
> + current_limit = SD_SET_CURRENT_LIMIT_800;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> + current_limit = SD_SET_CURRENT_LIMIT_600;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> + } else if (mmc_host_max_current_180 >= 600) {
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> + current_limit = SD_SET_CURRENT_LIMIT_600;
> + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> + } else if (mmc_host_max_current_180 >= 400)
> + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> + current_limit = SD_SET_CURRENT_LIMIT_400;
> +
> + 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));
This is what SD3.01 spec says:
"Current limit switch is only for SDR50, SDR104 and DDR50. Current limit
does not act on the card in SDR12 and SDR25 modes."
But in this function you are not checking for the currently selected bus
speed mode. If let's say you are in SDR12/SDR25 mode and if host supports
MAX_CURRENT_800 then this function will try to the current limit to 800 but
card won't switch to it as SDR12/SDR25 mode doen't allow the current switch.
So basically your following check will always print the warning for
SDR12/SDR25 mode.
if (((status[15] >> 4) & 0x0F) != current_limit)
printk(KERN_WARNING "%s: Problem setting current limit!\n",
mmc_hostname(card->host));
So in this function you should also check the current bus speed mode. If
current bus_speed_mode is SDR12/SDR25/legacy then you should select the
current limit to 200ma and for SDR104/SDR50/DDR50, you should select it
depending on the host's Max. current limit.
> +
> + return 0;
> +}
> +
> /*
> * UHS-I specific initialization procedure
> */
> @@ -590,6 +630,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 f127fa2..245cc39 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1462,12 +1462,36 @@ static int
> sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> return -EAGAIN;
> }
>
> +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u32 max_current_caps;
> + unsigned long flags;
> + int max_current_180;
> +
> + host = mmc_priv(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + /* Maximum current is 4 times the register value for 1.8V */
> + max_current_180 = ((max_current_caps &
> SDHCI_MAX_CURRENT_180_MASK) >>
> + SDHCI_MAX_CURRENT_180_SHIFT) *
> + SDHCI_MAX_CURRENT_MULTIPLIER;
> +
> + return max_current_180;
> +}
> +
> 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,
> + .get_max_current_180 = sdhci_get_max_current_180,
> };
>
>
> /**********************************************************************
> *******\
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 0b24c41..a6811ae 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -98,6 +98,15 @@ struct sd_switch_caps {
> #define SD_DRIVER_TYPE_C 0x04
> #define SD_DRIVER_TYPE_D 0x08
> unsigned int uhs_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 4dfff6d..e84cd05 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -128,6 +128,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);
> + int (*get_max_current_180)(struct mmc_host *mmc);
> };
>
> struct mmc_card;
> --
> 1.7.1
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
2011-03-21 7:43 ` Subhash Jadavani
@ 2011-03-21 7:54 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-21 7:54 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Monday, March 21, 2011 1:14 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
>
>
>
> > -----Original Message-----
> > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> > Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 07/12] mmc: sd: set current limit for uhs cards
> >
> > 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.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 45
> > +++++++++++++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++++
> > include/linux/mmc/card.h | 9 +++++++++
> > include/linux/mmc/host.h | 1 +
> > 4 files changed, 79 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index ec0d8e6..df98a2c 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -550,6 +550,46 @@ 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)
> > +{
> > + struct mmc_host *host = card->host;
> > + int mmc_host_max_current_180, current_limit;
> > + int err;
> > +
> > + /* sanity check */
> > + if (!host->ops->get_max_current_180)
> > + return 0;
> > +
> > + /* Maximum current supported by host at 1.8V */
> > + mmc_host_max_current_180 = host->ops->get_max_current_180(host);
> > +
> > + if (mmc_host_max_current_180 >= 800) {
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
> > + current_limit = SD_SET_CURRENT_LIMIT_800;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> > + current_limit = SD_SET_CURRENT_LIMIT_600;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > + } else if (mmc_host_max_current_180 >= 600) {
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
> > + current_limit = SD_SET_CURRENT_LIMIT_600;
> > + else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > + } else if (mmc_host_max_current_180 >= 400)
> > + if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
> > + current_limit = SD_SET_CURRENT_LIMIT_400;
> > +
> > + 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));
>
> This is what SD3.01 spec says:
> "Current limit switch is only for SDR50, SDR104 and DDR50. Current
> limit
> does not act on the card in SDR12 and SDR25 modes."
>
> But in this function you are not checking for the currently selected
> bus
> speed mode. If let's say you are in SDR12/SDR25 mode and if host
> supports
> MAX_CURRENT_800 then this function will try to the current limit to 800
> but
> card won't switch to it as SDR12/SDR25 mode doen't allow the current
> switch.
>
> So basically your following check will always print the warning for
> SDR12/SDR25 mode.
>
> if (((status[15] >> 4) & 0x0F) != current_limit)
> printk(KERN_WARNING "%s: Problem setting current limit!\n",
> mmc_hostname(card->host));
>
> So in this function you should also check the current bus speed mode.
> If
> current bus_speed_mode is SDR12/SDR25/legacy then you should select the
> current limit to 200ma and for SDR104/SDR50/DDR50, you should select it
> depending on the host's Max. current limit.
Thanks for the correction. Yes, I verified from the spec what you said. I will modify the code accordingly.
Thanks,
Arindam
>
>
> > +
> > + return 0;
> > +}
> > +
> > /*
> > * UHS-I specific initialization procedure
> > */
> > @@ -590,6 +630,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 f127fa2..245cc39 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -1462,12 +1462,36 @@ static int
> > sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > return -EAGAIN;
> > }
> >
> > +static int sdhci_get_max_current_180(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u32 max_current_caps;
> > + unsigned long flags;
> > + int max_current_180;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + /* Maximum current is 4 times the register value for 1.8V */
> > + max_current_180 = ((max_current_caps &
> > SDHCI_MAX_CURRENT_180_MASK) >>
> > + SDHCI_MAX_CURRENT_180_SHIFT) *
> > + SDHCI_MAX_CURRENT_MULTIPLIER;
> > +
> > + return max_current_180;
> > +}
> > +
> > 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,
> > + .get_max_current_180 = sdhci_get_max_current_180,
> > };
> >
> >
> >
> /**********************************************************************
> > *******\
> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > index 0b24c41..a6811ae 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -98,6 +98,15 @@ struct sd_switch_caps {
> > #define SD_DRIVER_TYPE_C 0x04
> > #define SD_DRIVER_TYPE_D 0x08
> > unsigned int uhs_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 4dfff6d..e84cd05 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -128,6 +128,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);
> > + int (*get_max_current_180)(struct mmc_host *mmc);
> > };
> >
> > struct mmc_card;
> > --
> > 1.7.1
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-04 11:32 ` [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
` (2 preceding siblings ...)
2011-03-15 10:32 ` zhangfei gao
@ 2011-03-21 9:43 ` Subhash Jadavani
2011-03-21 9:50 ` Nath, Arindam
2011-03-21 10:58 ` Subhash Jadavani
4 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-21 9:43 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Arindam Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs
> initialization
>
> 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. 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 Seletc (bit 7) of Host Control2 register to 0.
> Before exiting the tuning procedure, we disable Buffer Read Ready
> interrupt.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 4 +
> drivers/mmc/host/sdhci.c | 137
> ++++++++++++++++++++++++++++++++++++++++++++-
> 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, 149 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index be01397..1e2d157 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
> /* Set current limit for the card */
> err = sd_set_current_limit(card, status);
>
> + /* SPI mode doesn't define CMD19 */
> + if (!mmc_host_is_spi(card->host) && card->host->ops-
> >execute_tuning)
> + card->host->ops->execute_tuning(card->host);
As you have mentioned, Host Controller needs tuning only in case of SDR104
mode and for SDR50 mode. This means we should not call execute_tuning ops if
bus speed mode is other than SD104/SDR50. Host driver should not be
responsible for putting the check if the current bus speed mode is
SDR104/SDR50 before start tuning.
> +
> out:
> kfree(status);
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 245cc39..8f4f102 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -39,6 +39,8 @@
> #define SDHCI_USE_LEDS_CLASS
> #endif
>
> +#define MAX_TUNING_LOOP 40
> +
> static unsigned int debug_quirks = 0;
>
> static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data
> *);
> @@ -985,7 +987,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);
> @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct
> mmc_host *mmc)
> return max_current_180;
> }
>
> +static void sdhci_execute_tuning(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u16 ctrl;
> + int tuning_loop_counter = MAX_TUNING_LOOP;
> + unsigned long flags;
> + unsigned long timeout;
> +
> + host = mmc_priv(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + 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_SDR104) ||
> + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> + else {
> + spin_unlock_irqrestore(&host->lock, flags);
> + return;
> + }
> +
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + /*
> + * As per the Host Controller spec v3.00, tuning command
> + * generates Buffer Read Ready interrupt, so enable that.
> + */
> + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> +
> + /*
> + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the
> number
> + * of loops reaches 40 times or a timeout of 150ms occurs.
> + */
> + timeout = 150;
> + do {
> + 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;
> + sdhci_send_command(host, &cmd);
> +
> + host->cmd = NULL;
> + host->mrq = NULL;
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + /* Wait for Buffer Read Ready interrupt */
> + wait_event_interruptible_timeout(host->buf_ready_int,
> + (host->tuning_done == 1),
> + msecs_to_jiffies(50));
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (!host->tuning_done) {
> + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> + " failed, falling back to fixed sampling"
> + " clock\n");
> + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + 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");
> + }
> +
> +out:
> + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> static const struct mmc_host_ops sdhci_ops = {
> .request = sdhci_request,
> .set_ios = sdhci_set_ios,
> @@ -1492,6 +1609,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,
> .get_max_current_180 = sdhci_get_max_current_180,
> + .execute_tuning = sdhci_execute_tuning,
> };
>
>
> /**********************************************************************
> *******\
> @@ -1703,6 +1821,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
> @@ -2126,6 +2254,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;
> @@ -2269,6 +2401,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 5bf244d..4746879 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -160,6 +160,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
> @@ -187,6 +189,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 e84cd05..651e40b 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -129,6 +129,7 @@ struct mmc_host_ops {
>
> int (*start_signal_voltage_switch)(struct mmc_host *host);
> int (*get_max_current_180)(struct mmc_host *mmc);
> + void (*execute_tuning)(struct mmc_host *host);
> };
>
> struct mmc_card;
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 612301f..9194f9e 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 282d158..5203b97 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -109,6 +109,7 @@ struct sdhci_host {
> #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
> #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
>
> unsigned int version; /* SDHCI spec. version */
>
> @@ -147,6 +148,9 @@ struct sdhci_host {
>
> struct mmc_command *saved_abort_cmd; /* Abort command saved
> for data error recovery */
>
> + 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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-21 9:43 ` Subhash Jadavani
@ 2011-03-21 9:50 ` Nath, Arindam
2011-03-23 6:58 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-21 9:50 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Monday, March 21, 2011 3:14 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs
> > initialization
> >
> > 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. 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 Seletc (bit 7) of Host Control2 register to 0.
> > Before exiting the tuning procedure, we disable Buffer Read Ready
> > interrupt.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 4 +
> > drivers/mmc/host/sdhci.c | 137
> > ++++++++++++++++++++++++++++++++++++++++++++-
> > 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, 149 insertions(+), 1 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index be01397..1e2d157 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> > *card)
> > /* Set current limit for the card */
> > err = sd_set_current_limit(card, status);
> >
> > + /* SPI mode doesn't define CMD19 */
> > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> > >execute_tuning)
> > + card->host->ops->execute_tuning(card->host);
>
> As you have mentioned, Host Controller needs tuning only in case of
> SDR104
> mode and for SDR50 mode. This means we should not call execute_tuning
> ops if
> bus speed mode is other than SD104/SDR50. Host driver should not be
> responsible for putting the check if the current bus speed mode is
> SDR104/SDR50 before start tuning.
But since tuning procedure needs to be performed by the Host Controller itself, isn't it better for the controller itself to check which bus speed mode it is operating on currently before deciding on whether to carry on tuning or not?
Thanks,
Arindam
>
> > +
> > out:
> > kfree(status);
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 245cc39..8f4f102 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -39,6 +39,8 @@
> > #define SDHCI_USE_LEDS_CLASS
> > #endif
> >
> > +#define MAX_TUNING_LOOP 40
> > +
> > static unsigned int debug_quirks = 0;
> >
> > static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data
> > *);
> > @@ -985,7 +987,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);
> > @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct
> > mmc_host *mmc)
> > return max_current_180;
> > }
> >
> > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u16 ctrl;
> > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > + unsigned long flags;
> > + unsigned long timeout;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + 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_SDR104) ||
> > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > + else {
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return;
> > + }
> > +
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + /*
> > + * As per the Host Controller spec v3.00, tuning command
> > + * generates Buffer Read Ready interrupt, so enable that.
> > + */
> > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > +
> > + /*
> > + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the
> > number
> > + * of loops reaches 40 times or a timeout of 150ms occurs.
> > + */
> > + timeout = 150;
> > + do {
> > + 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;
> > + sdhci_send_command(host, &cmd);
> > +
> > + host->cmd = NULL;
> > + host->mrq = NULL;
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + /* Wait for Buffer Read Ready interrupt */
> > + wait_event_interruptible_timeout(host->buf_ready_int,
> > + (host->tuning_done == 1),
> > + msecs_to_jiffies(50));
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + if (!host->tuning_done) {
> > + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> > + " failed, falling back to fixed sampling"
> > + " clock\n");
> > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + 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");
> > + }
> > +
> > +out:
> > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +}
> > +
> > static const struct mmc_host_ops sdhci_ops = {
> > .request = sdhci_request,
> > .set_ios = sdhci_set_ios,
> > @@ -1492,6 +1609,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,
> > .get_max_current_180 = sdhci_get_max_current_180,
> > + .execute_tuning = sdhci_execute_tuning,
> > };
> >
> >
> >
> /**********************************************************************
> > *******\
> > @@ -1703,6 +1821,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
> > @@ -2126,6 +2254,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;
> > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -160,6 +160,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
> > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> >
> > int (*start_signal_voltage_switch)(struct mmc_host *host);
> > int (*get_max_current_180)(struct mmc_host *mmc);
> > + void (*execute_tuning)(struct mmc_host *host);
> > };
> >
> > struct mmc_card;
> > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> > index 612301f..9194f9e 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 282d158..5203b97 100644
> > --- a/include/linux/mmc/sdhci.h
> > +++ b/include/linux/mmc/sdhci.h
> > @@ -109,6 +109,7 @@ struct sdhci_host {
> > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable
> */
> > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning
> */
> >
> > unsigned int version; /* SDHCI spec. version */
> >
> > @@ -147,6 +148,9 @@ struct sdhci_host {
> >
> > struct mmc_command *saved_abort_cmd; /* Abort command saved
> > for data error recovery */
> >
> > + 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
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-04 11:32 ` [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
` (3 preceding siblings ...)
2011-03-21 9:43 ` Subhash Jadavani
@ 2011-03-21 10:58 ` Subhash Jadavani
2011-03-21 11:07 ` Nath, Arindam
4 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-21 10:58 UTC (permalink / raw)
To: 'Arindam Nath', cjb
Cc: zhangfei.gao, prakity, linux-mmc, henry.su, aaron.lu, anath.amd
> -----Original Message-----
> From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> Nath
> Sent: Friday, March 04, 2011 5:03 PM
> To: cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> Subject: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs
> initialization
>
> 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. 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 Seletc (bit 7) of Host Control2 register to 0.
> Before exiting the tuning procedure, we disable Buffer Read Ready
> interrupt.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 4 +
> drivers/mmc/host/sdhci.c | 137
> ++++++++++++++++++++++++++++++++++++++++++++-
> 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, 149 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index be01397..1e2d157 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
> /* Set current limit for the card */
> err = sd_set_current_limit(card, status);
>
> + /* SPI mode doesn't define CMD19 */
> + if (!mmc_host_is_spi(card->host) && card->host->ops-
> >execute_tuning)
> + 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 245cc39..8f4f102 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -39,6 +39,8 @@
> #define SDHCI_USE_LEDS_CLASS
> #endif
>
> +#define MAX_TUNING_LOOP 40
> +
> static unsigned int debug_quirks = 0;
>
> static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data
> *);
> @@ -985,7 +987,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);
> @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct
> mmc_host *mmc)
> return max_current_180;
> }
>
> +static void sdhci_execute_tuning(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u16 ctrl;
> + int tuning_loop_counter = MAX_TUNING_LOOP;
> + unsigned long flags;
> + unsigned long timeout;
> +
> + host = mmc_priv(mmc);
> +
> + spin_lock_irqsave(&host->lock, flags);
> +
> + 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_SDR104) ||
> + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> + else {
> + spin_unlock_irqrestore(&host->lock, flags);
> + return;
> + }
> +
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + /*
> + * As per the Host Controller spec v3.00, tuning command
> + * generates Buffer Read Ready interrupt, so enable that.
> + */
> + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> +
> + /*
> + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the
> number
> + * of loops reaches 40 times or a timeout of 150ms occurs.
> + */
> + timeout = 150;
> + do {
> + 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;
> + sdhci_send_command(host, &cmd);
> +
> + host->cmd = NULL;
> + host->mrq = NULL;
> +
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + /* Wait for Buffer Read Ready interrupt */
> + wait_event_interruptible_timeout(host->buf_ready_int,
> + (host->tuning_done == 1),
> + msecs_to_jiffies(50));
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (!host->tuning_done) {
> + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> + " failed, falling back to fixed sampling"
> + " clock\n");
> + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> + 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");
> + }
> +
> +out:
> + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> static const struct mmc_host_ops sdhci_ops = {
> .request = sdhci_request,
> .set_ios = sdhci_set_ios,
> @@ -1492,6 +1609,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,
> .get_max_current_180 = sdhci_get_max_current_180,
> + .execute_tuning = sdhci_execute_tuning,
> };
>
>
> /**********************************************************************
> *******\
> @@ -1703,6 +1821,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
> @@ -2126,6 +2254,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;
> @@ -2269,6 +2401,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 5bf244d..4746879 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -160,6 +160,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
> @@ -187,6 +189,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 e84cd05..651e40b 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -129,6 +129,7 @@ struct mmc_host_ops {
>
> int (*start_signal_voltage_switch)(struct mmc_host *host);
> int (*get_max_current_180)(struct mmc_host *mmc);
> + void (*execute_tuning)(struct mmc_host *host);
Why execute_tuning ops return type is void? Will there not be any error
scenarios? What if host fails to find any tuning point? Shouldn't it return
error to the core layer and then core layer should not begin with the any
read/write operations?
> };
>
> struct mmc_card;
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 612301f..9194f9e 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 282d158..5203b97 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -109,6 +109,7 @@ struct sdhci_host {
> #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
> #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
>
> unsigned int version; /* SDHCI spec. version */
>
> @@ -147,6 +148,9 @@ struct sdhci_host {
>
> struct mmc_command *saved_abort_cmd; /* Abort command saved
> for data error recovery */
>
> + 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] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-21 10:58 ` Subhash Jadavani
@ 2011-03-21 11:07 ` Nath, Arindam
2011-03-23 6:08 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-21 11:07 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Monday, March 21, 2011 4:29 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
>
>
> > -----Original Message-----
> > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> > Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs
> > initialization
> >
> > 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. 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 Seletc (bit 7) of Host Control2 register to 0.
> > Before exiting the tuning procedure, we disable Buffer Read Ready
> > interrupt.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 4 +
> > drivers/mmc/host/sdhci.c | 137
> > ++++++++++++++++++++++++++++++++++++++++++++-
> > 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, 149 insertions(+), 1 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index be01397..1e2d157 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> > *card)
> > /* Set current limit for the card */
> > err = sd_set_current_limit(card, status);
> >
> > + /* SPI mode doesn't define CMD19 */
> > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> > >execute_tuning)
> > + 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 245cc39..8f4f102 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -39,6 +39,8 @@
> > #define SDHCI_USE_LEDS_CLASS
> > #endif
> >
> > +#define MAX_TUNING_LOOP 40
> > +
> > static unsigned int debug_quirks = 0;
> >
> > static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data
> > *);
> > @@ -985,7 +987,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);
> > @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct
> > mmc_host *mmc)
> > return max_current_180;
> > }
> >
> > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u16 ctrl;
> > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > + unsigned long flags;
> > + unsigned long timeout;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + 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_SDR104) ||
> > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > + else {
> > + spin_unlock_irqrestore(&host->lock, flags);
> > + return;
> > + }
> > +
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + /*
> > + * As per the Host Controller spec v3.00, tuning command
> > + * generates Buffer Read Ready interrupt, so enable that.
> > + */
> > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > +
> > + /*
> > + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the
> > number
> > + * of loops reaches 40 times or a timeout of 150ms occurs.
> > + */
> > + timeout = 150;
> > + do {
> > + 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;
> > + sdhci_send_command(host, &cmd);
> > +
> > + host->cmd = NULL;
> > + host->mrq = NULL;
> > +
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +
> > + /* Wait for Buffer Read Ready interrupt */
> > + wait_event_interruptible_timeout(host->buf_ready_int,
> > + (host->tuning_done == 1),
> > + msecs_to_jiffies(50));
> > + spin_lock_irqsave(&host->lock, flags);
> > +
> > + if (!host->tuning_done) {
> > + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> > + " failed, falling back to fixed sampling"
> > + " clock\n");
> > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > +
> > + 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");
> > + }
> > +
> > +out:
> > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > + spin_unlock_irqrestore(&host->lock, flags);
> > +}
> > +
> > static const struct mmc_host_ops sdhci_ops = {
> > .request = sdhci_request,
> > .set_ios = sdhci_set_ios,
> > @@ -1492,6 +1609,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,
> > .get_max_current_180 = sdhci_get_max_current_180,
> > + .execute_tuning = sdhci_execute_tuning,
> > };
> >
> >
> >
> /**********************************************************************
> > *******\
> > @@ -1703,6 +1821,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
> > @@ -2126,6 +2254,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;
> > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -160,6 +160,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
> > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> >
> > int (*start_signal_voltage_switch)(struct mmc_host *host);
> > int (*get_max_current_180)(struct mmc_host *mmc);
> > + void (*execute_tuning)(struct mmc_host *host);
>
>
> Why execute_tuning ops return type is void? Will there not be any error
> scenarios? What if host fails to find any tuning point? Shouldn't it
> return
> error to the core layer and then core layer should not begin with the
> any
> read/write operations?
In case the tuning procedure fails, Host Controller falls back on using Fixed Sampling clock, so data transfer can still continue.
Thanks,
Arindam
>
>
> > };
> >
> > struct mmc_card;
> > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> > index 612301f..9194f9e 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 282d158..5203b97 100644
> > --- a/include/linux/mmc/sdhci.h
> > +++ b/include/linux/mmc/sdhci.h
> > @@ -109,6 +109,7 @@ struct sdhci_host {
> > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable
> */
> > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
> > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
> > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning
> */
> >
> > unsigned int version; /* SDHCI spec. version */
> >
> > @@ -147,6 +148,9 @@ struct sdhci_host {
> >
> > struct mmc_command *saved_abort_cmd; /* Abort command saved
> > for data error recovery */
> >
> > + 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] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-21 6:42 ` Subhash Jadavani
@ 2011-03-23 6:04 ` Nath, Arindam
2011-03-23 6:14 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-23 6:04 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Monday, March 21, 2011 12:13 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
>
>
> > -----Original Message-----
> > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of Arindam
> > Nath
> > Sent: Friday, March 04, 2011 5:03 PM
> > To: cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > subhashj@codeaurora.org; linux-mmc@vger.kernel.org; henry.su@amd.com;
> > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode
> > selection
> >
> > 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. Since MMC_TIMING_UHS_SDR25 is same as
> > MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag for
> > UHS SDR25 again. We also 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 set the clock frequency for the
> > UHS-I mode selected.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 91
> > ++++++++++++++++++++++++++++++++++++++++++++++
> > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > drivers/mmc/host/sdhci.h | 5 +++
> > include/linux/mmc/card.h | 16 ++++++++
> > include/linux/mmc/host.h | 4 ++
> > 5 files changed, 146 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index f6a4fab..ec0d8e6 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -464,6 +464,92 @@ static int sd_select_driver_type(struct mmc_card
> > *card, u8 *status)
> > return 0;
> > }
> >
> > +static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
> > +{
> > + unsigned int bus_speed, timing;
> > + int err;
> > +
> > + /*
> > + * If the host doesn't support any of the UHS-I modes, fallback
> > on
> > + * default speed.
> > + */
> > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
> > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
> > + return 0;
> > +
> > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > + if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) {
> > + bus_speed = UHS_SDR104_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR104;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)
> > {
> > + bus_speed = UHS_SDR50_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR50;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> > {
> > + bus_speed = UHS_DDR50_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_DDR50;
> > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > {
> > + bus_speed = UHS_SDR25_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR25;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > + }
>
> I would not agree that if Host cap is set to MMC_CAP_UHS_SDR104 implies
> that
> DDR50 mode is supported by host. There may be a case where host
> controller
> only support SDR timing modes.
> Basically if host should advertise all the supported modes (SDR104,
> SDR50,
> SDR25, SDR12 & DDR) by caps and then we should choose the highest
> performance mode depending on both the card and host's highest
> performance
> mode.
>
> So this should be the check:
>
> if ((card->host->caps & MMC_CAP_UHS_SDR104 &&
> (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
> bus_speed = UHS_SDR104_BUS_SPEED;
> timing = MMC_TIMING_UHS_SDR104;
> card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> } else if ((card->host->caps & MMC_CAP_UHS_DDR50 &&
> (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> bus_speed = UHS_DDR50_BUS_SPEED;
> timing = MMC_TIMING_UHS_DDR50;
> card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> } else if ((card->host->caps & MMC_CAP_UHS_SDR50 &&
> (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> bus_speed = UHS_SDR50_BUS_SPEED;
> timing = MMC_TIMING_UHS_SDR50;
> card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> } else if ((card->host->caps & MMC_CAP_UHS_SDR25 &&
> (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> bus_speed = UHS_SDR50_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_SDR12 &&
> (card->sw_caps.uhs_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;
> }
A host capable of operating at a higher bus speed mode should be able to support cards with a lower bus speed mode. In case of your conditions checks above, you are checking for the exact match for the bus speed modes, otherwise you will fall back on using default bus speed mode, thus performance loss.
Thanks,
Arindam
>
> Regards,
> Subhash
>
>
> > + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> > + bus_speed = UHS_SDR50_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR50;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> > {
> > + bus_speed = UHS_DDR50_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_DDR50;
> > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > {
> > + bus_speed = UHS_SDR25_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR25;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > + }
> > + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > + bus_speed = UHS_DDR50_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_DDR50;
> > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > {
> > + bus_speed = UHS_SDR25_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR25;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > + }
> > + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) ||
> > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > + bus_speed = UHS_SDR25_BUS_SPEED;
> > + timing = MMC_TIMING_UHS_SDR25;
> > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > + }
> > + }
> > +
> > + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> > + if (err)
> > + return err;
> > +
> > + if ((status[16] & 0xF) != bus_speed)
> > + printk(KERN_WARNING "%s: Problem setting bus speed
> > mode!\n",
> > + mmc_hostname(card->host));
> > + else {
> > + if (bus_speed) {
> > + mmc_set_timing(card->host, timing);
> > + mmc_set_clock(card->host,
> card->sw_caps.uhs_max_dtr);
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > /*
> > * UHS-I specific initialization procedure
> > */
> > @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> > *card)
> >
> > /* Set the driver strength for the card */
> > err = sd_select_driver_type(card, status);
> > + if (err)
> > + goto out;
> > +
> > + /* Set bus speed mode of the card */
> > + err = sd_set_bus_speed_mode(card, status);
> >
> > out:
> > kfree(status);
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 5d3bb11..f127fa2 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct mmc_host
> *mmc,
> > struct mmc_ios *ios)
> > ctrl &= ~SDHCI_CTRL_HISPD;
> >
> > if (host->version >= SDHCI_SPEC_300) {
> > - u16 ctrl_2;
> > + u16 clk, ctrl_2;
> > +
> > + /* In case of UHS-I modes, set High Speed Enable */
> > + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> > + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> > + (ios->timing == MMC_TIMING_UHS_DDR50))
> > + ctrl |= SDHCI_CTRL_HISPD;
> >
> > ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct mmc_host *mmc,
> > struct mmc_ios *ios)
> > * need to reset SD Clock Enable before changing
> High
> > * Speed Enable to avoid generating clock gliches.
> > */
> > - u16 clk;
> >
> > /* Reset SD Clock Enable */
> > clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct mmc_host
> *mmc,
> > struct mmc_ios *ios)
> > clk |= SDHCI_CLOCK_CARD_EN;
> > sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > }
> > +
> > + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > +
> > + /* Select Bus Speed Mode for host */
> > + if (ios->timing == MMC_TIMING_UHS_SDR25)
> > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > + else if (ios->timing == MMC_TIMING_UHS_SDR104)
> > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> > + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > +
> > + /* Reset SD Clock Enable */
> > + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > +
> > + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> > +
> > + /* Re-enable SD Clock */
> > + clk |= SDHCI_CLOCK_CARD_EN;
> > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > } else
> > sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
> >
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index a407b5b..5bf244d 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -150,6 +150,11 @@
> > #define SDHCI_ACMD12_ERR 0x3C
> >
> > #define SDHCI_HOST_CONTROL2 0x3E
> > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > #define SDHCI_CTRL_VDD_180 0x0008
> > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > index 2d7f7a3..0b24c41 100644
> > --- a/include/linux/mmc/card.h
> > +++ b/include/linux/mmc/card.h
> > @@ -75,7 +75,23 @@ struct sd_ssr {
> >
> > struct sd_switch_caps {
> > unsigned int hs_max_dtr;
> > + unsigned int uhs_max_dtr;
> > +#define UHS_SDR104_MAX_DTR 208000000
> > +#define UHS_SDR50_MAX_DTR 100000000
> > +#define UHS_DDR50_MAX_DTR 50000000
> > +#define UHS_SDR25_MAX_DTR 50000000
> > unsigned int uhs_bus_mode;
> > +#define UHS_SDR12_BUS_SPEED 0
> > +#define UHS_SDR25_BUS_SPEED 1
> > +#define UHS_SDR50_BUS_SPEED 2
> > +#define UHS_SDR104_BUS_SPEED 3
> > +#define UHS_DDR50_BUS_SPEED 4
> > +
> > +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> > +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> > +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> > +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> > +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> > unsigned int uhs_drv_type;
> > #define SD_DRIVER_TYPE_B 0x01
> > #define SD_DRIVER_TYPE_A 0x02
> > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > index bc2121e..4dfff6d 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -50,6 +50,10 @@ struct mmc_ios {
> > #define MMC_TIMING_LEGACY 0
> > #define MMC_TIMING_MMC_HS 1
> > #define MMC_TIMING_SD_HS 2
> > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > +#define MMC_TIMING_UHS_SDR50 3
> > +#define MMC_TIMING_UHS_SDR104 4
> > +#define MMC_TIMING_UHS_DDR50 5
> >
> > unsigned char ddr; /* dual data rate used */
> >
> > --
> > 1.7.1
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-21 11:07 ` Nath, Arindam
@ 2011-03-23 6:08 ` Subhash Jadavani
2011-03-23 6:14 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-23 6:08 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Monday, March 21, 2011 4:37 PM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Monday, March 21, 2011 4:29 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> > uhs initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> Arindam
> > > Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs
> > > initialization
> > >
> > > 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. 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 Seletc (bit 7) of Host Control2 register to 0.
> > > Before exiting the tuning procedure, we disable Buffer Read Ready
> > > interrupt.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/sd.c | 4 +
> > > drivers/mmc/host/sdhci.c | 137
> > > ++++++++++++++++++++++++++++++++++++++++++++-
> > > 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, 149 insertions(+), 1 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index be01397..1e2d157 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct
> mmc_card
> > > *card)
> > > /* Set current limit for the card */
> > > err = sd_set_current_limit(card, status);
> > >
> > > + /* SPI mode doesn't define CMD19 */
> > > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> > > >execute_tuning)
> > > + 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 245cc39..8f4f102 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -39,6 +39,8 @@
> > > #define SDHCI_USE_LEDS_CLASS
> > > #endif
> > >
> > > +#define MAX_TUNING_LOOP 40
> > > +
> > > static unsigned int debug_quirks = 0;
> > >
> > > static void sdhci_prepare_data(struct sdhci_host *, struct
> mmc_data
> > > *);
> > > @@ -985,7 +987,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);
> > > @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct
> > > mmc_host *mmc)
> > > return max_current_180;
> > > }
> > >
> > > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > > +{
> > > + struct sdhci_host *host;
> > > + u16 ctrl;
> > > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > > + unsigned long flags;
> > > + unsigned long timeout;
> > > +
> > > + host = mmc_priv(mmc);
> > > +
> > > + spin_lock_irqsave(&host->lock, flags);
> > > +
> > > + 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_SDR104) ||
> > > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > > + else {
> > > + spin_unlock_irqrestore(&host->lock, flags);
> > > + return;
> > > + }
> > > +
> > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > +
> > > + /*
> > > + * As per the Host Controller spec v3.00, tuning command
> > > + * generates Buffer Read Ready interrupt, so enable that.
> > > + */
> > > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > +
> > > + /*
> > > + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the
> > > number
> > > + * of loops reaches 40 times or a timeout of 150ms occurs.
> > > + */
> > > + timeout = 150;
> > > + do {
> > > + 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;
> > > + sdhci_send_command(host, &cmd);
> > > +
> > > + host->cmd = NULL;
> > > + host->mrq = NULL;
> > > +
> > > + spin_unlock_irqrestore(&host->lock, flags);
> > > +
> > > + /* Wait for Buffer Read Ready interrupt */
> > > + wait_event_interruptible_timeout(host->buf_ready_int,
> > > + (host->tuning_done == 1),
> > > + msecs_to_jiffies(50));
> > > + spin_lock_irqsave(&host->lock, flags);
> > > +
> > > + if (!host->tuning_done) {
> > > + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> > > + " failed, falling back to fixed sampling"
> > > + " clock\n");
> > > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > +
> > > + 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");
> > > + }
> > > +
> > > +out:
> > > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > + spin_unlock_irqrestore(&host->lock, flags);
> > > +}
> > > +
> > > static const struct mmc_host_ops sdhci_ops = {
> > > .request = sdhci_request,
> > > .set_ios = sdhci_set_ios,
> > > @@ -1492,6 +1609,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,
> > > .get_max_current_180 = sdhci_get_max_current_180,
> > > + .execute_tuning = sdhci_execute_tuning,
> > > };
> > >
> > >
> > >
> >
> /**********************************************************************
> > > *******\
> > > @@ -1703,6 +1821,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
> > > @@ -2126,6 +2254,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;
> > > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > > --- a/drivers/mmc/host/sdhci.h
> > > +++ b/drivers/mmc/host/sdhci.h
> > > @@ -160,6 +160,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
> > > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > > --- a/include/linux/mmc/host.h
> > > +++ b/include/linux/mmc/host.h
> > > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> > >
> > > int (*start_signal_voltage_switch)(struct mmc_host *host);
> > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > + void (*execute_tuning)(struct mmc_host *host);
> >
> >
> > Why execute_tuning ops return type is void? Will there not be any
> error
> > scenarios? What if host fails to find any tuning point? Shouldn't it
> > return
> > error to the core layer and then core layer should not begin with the
> > any
> > read/write operations?
>
> In case the tuning procedure fails, Host Controller falls back on using
> Fixed Sampling clock, so data transfer can still continue.
>
I think that's not a correct assumption. Let's say tuning fails and host
controller falls back to fixed sampling clock and card still operates in
SDR104 with CLK=208MHz. This means that all further read/write transactions
will mainly result in CRC Fail errors and none of the transactions may be
successful. So we can't just leave the host controller in state where it
cannot perform high speed transfers (because it's tuning had failed) and
then start issuing commands with high speed transfer conditions
(CLK=208MHz). Better way is for host to inform core layer that tuning has
failed and then there should not be any further transactions with the card
or fallback to low speed modes (SDR12, SDR25 or SDR50) or power cycle the
card.
> Thanks,
> Arindam
>
> >
> >
> > > };
> > >
> > > struct mmc_card;
> > > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> > > index 612301f..9194f9e 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 282d158..5203b97 100644
> > > --- a/include/linux/mmc/sdhci.h
> > > +++ b/include/linux/mmc/sdhci.h
> > > @@ -109,6 +109,7 @@ struct sdhci_host {
> > > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable
> > */
> > > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this
> req. */
> > > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive
> */
> > > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning
> > */
> > >
> > > unsigned int version; /* SDHCI spec. version */
> > >
> > > @@ -147,6 +148,9 @@ struct sdhci_host {
> > >
> > > struct mmc_command *saved_abort_cmd; /* Abort command saved
> > > for data error recovery */
> > >
> > > + 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
> >
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-23 6:04 ` Nath, Arindam
@ 2011-03-23 6:14 ` Subhash Jadavani
2011-03-23 6:17 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-23 6:14 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Wednesday, March 23, 2011 11:35 AM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Monday, March 21, 2011 12:13 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode selection
> >
> >
> >
> > > -----Original Message-----
> > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> Arindam
> > > Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode
> > > selection
> > >
> > > 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. Since MMC_TIMING_UHS_SDR25 is same as
> > > MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag
> for
> > > UHS SDR25 again. We also 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 set the clock frequency for the
> > > UHS-I mode selected.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/sd.c | 91
> > > ++++++++++++++++++++++++++++++++++++++++++++++
> > > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > > drivers/mmc/host/sdhci.h | 5 +++
> > > include/linux/mmc/card.h | 16 ++++++++
> > > include/linux/mmc/host.h | 4 ++
> > > 5 files changed, 146 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index f6a4fab..ec0d8e6 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -464,6 +464,92 @@ static int sd_select_driver_type(struct
> mmc_card
> > > *card, u8 *status)
> > > return 0;
> > > }
> > >
> > > +static int sd_set_bus_speed_mode(struct mmc_card *card, u8
> *status)
> > > +{
> > > + unsigned int bus_speed, timing;
> > > + int err;
> > > +
> > > + /*
> > > + * If the host doesn't support any of the UHS-I modes, fallback
> > > on
> > > + * default speed.
> > > + */
> > > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
> > > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
> > > + return 0;
> > > +
> > > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > > + if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) {
> > > + bus_speed = UHS_SDR104_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR104;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)
> > > {
> > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR50;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> > > {
> > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_DDR50;
> > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > > {
> > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR25;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > + }
> >
> > I would not agree that if Host cap is set to MMC_CAP_UHS_SDR104
> implies
> > that
> > DDR50 mode is supported by host. There may be a case where host
> > controller
> > only support SDR timing modes.
> > Basically if host should advertise all the supported modes (SDR104,
> > SDR50,
> > SDR25, SDR12 & DDR) by caps and then we should choose the highest
> > performance mode depending on both the card and host's highest
> > performance
> > mode.
> >
> > So this should be the check:
> >
> > if ((card->host->caps & MMC_CAP_UHS_SDR104 &&
> > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
> > bus_speed = UHS_SDR104_BUS_SPEED;
> > timing = MMC_TIMING_UHS_SDR104;
> > card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> > } else if ((card->host->caps & MMC_CAP_UHS_DDR50 &&
> > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > bus_speed = UHS_DDR50_BUS_SPEED;
> > timing = MMC_TIMING_UHS_DDR50;
> > card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > } else if ((card->host->caps & MMC_CAP_UHS_SDR50 &&
> > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> > bus_speed = UHS_SDR50_BUS_SPEED;
> > timing = MMC_TIMING_UHS_SDR50;
> > card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > } else if ((card->host->caps & MMC_CAP_UHS_SDR25 &&
> > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > bus_speed = UHS_SDR50_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_SDR12 &&
> > (card->sw_caps.uhs_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;
> > }
>
> A host capable of operating at a higher bus speed mode should be able
> to support cards with a lower bus speed mode. In case of your
> conditions checks above, you are checking for the exact match for the
> bus speed modes, otherwise you will fall back on using default bus
> speed mode, thus performance loss.
Why should we implicitly assume that host supporting SDR104 mode would also
support DDR50 mode? Is there any spec. which says so? If not then we should
rely on host capability to take the decision. DDR50 would require different
timing implementation on the host side and host may choose not to implement
DDR50 timing support then?
I would agree that host supporting SDR104 mode should also be supporting
SDR50/SDR25/SDR12 modes but it may not be true for DDR50 mode.
>
> Thanks,
> Arindam
>
> >
> > Regards,
> > Subhash
> >
> >
> > > + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR50;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> > > {
> > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_DDR50;
> > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > > {
> > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR25;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > + }
> > > + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_DDR50;
> > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > + } else if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)
> > > {
> > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR25;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > + }
> > > + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50) ||
> > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > + timing = MMC_TIMING_UHS_SDR25;
> > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > + }
> > > + }
> > > +
> > > + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> > > + if (err)
> > > + return err;
> > > +
> > > + if ((status[16] & 0xF) != bus_speed)
> > > + printk(KERN_WARNING "%s: Problem setting bus speed
> > > mode!\n",
> > > + mmc_hostname(card->host));
> > > + else {
> > > + if (bus_speed) {
> > > + mmc_set_timing(card->host, timing);
> > > + mmc_set_clock(card->host,
> > card->sw_caps.uhs_max_dtr);
> > > + }
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > /*
> > > * UHS-I specific initialization procedure
> > > */
> > > @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct
> mmc_card
> > > *card)
> > >
> > > /* Set the driver strength for the card */
> > > err = sd_select_driver_type(card, status);
> > > + if (err)
> > > + goto out;
> > > +
> > > + /* Set bus speed mode of the card */
> > > + err = sd_set_bus_speed_mode(card, status);
> > >
> > > out:
> > > kfree(status);
> > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > index 5d3bb11..f127fa2 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct mmc_host
> > *mmc,
> > > struct mmc_ios *ios)
> > > ctrl &= ~SDHCI_CTRL_HISPD;
> > >
> > > if (host->version >= SDHCI_SPEC_300) {
> > > - u16 ctrl_2;
> > > + u16 clk, ctrl_2;
> > > +
> > > + /* In case of UHS-I modes, set High Speed Enable */
> > > + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> > > + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> > > + (ios->timing == MMC_TIMING_UHS_DDR50))
> > > + ctrl |= SDHCI_CTRL_HISPD;
> > >
> > > ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> > > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct mmc_host
> *mmc,
> > > struct mmc_ios *ios)
> > > * need to reset SD Clock Enable before changing
> > High
> > > * Speed Enable to avoid generating clock gliches.
> > > */
> > > - u16 clk;
> > >
> > > /* Reset SD Clock Enable */
> > > clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct mmc_host
> > *mmc,
> > > struct mmc_ios *ios)
> > > clk |= SDHCI_CLOCK_CARD_EN;
> > > sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > }
> > > +
> > > + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > +
> > > + /* Select Bus Speed Mode for host */
> > > + if (ios->timing == MMC_TIMING_UHS_SDR25)
> > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > > + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > > + else if (ios->timing == MMC_TIMING_UHS_SDR104)
> > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> > > + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> > > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > > +
> > > + /* Reset SD Clock Enable */
> > > + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > +
> > > + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> > > +
> > > + /* Re-enable SD Clock */
> > > + clk |= SDHCI_CLOCK_CARD_EN;
> > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > } else
> > > sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
> > >
> > > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > > index a407b5b..5bf244d 100644
> > > --- a/drivers/mmc/host/sdhci.h
> > > +++ b/drivers/mmc/host/sdhci.h
> > > @@ -150,6 +150,11 @@
> > > #define SDHCI_ACMD12_ERR 0x3C
> > >
> > > #define SDHCI_HOST_CONTROL2 0x3E
> > > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > > #define SDHCI_CTRL_VDD_180 0x0008
> > > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > > index 2d7f7a3..0b24c41 100644
> > > --- a/include/linux/mmc/card.h
> > > +++ b/include/linux/mmc/card.h
> > > @@ -75,7 +75,23 @@ struct sd_ssr {
> > >
> > > struct sd_switch_caps {
> > > unsigned int hs_max_dtr;
> > > + unsigned int uhs_max_dtr;
> > > +#define UHS_SDR104_MAX_DTR 208000000
> > > +#define UHS_SDR50_MAX_DTR 100000000
> > > +#define UHS_DDR50_MAX_DTR 50000000
> > > +#define UHS_SDR25_MAX_DTR 50000000
> > > unsigned int uhs_bus_mode;
> > > +#define UHS_SDR12_BUS_SPEED 0
> > > +#define UHS_SDR25_BUS_SPEED 1
> > > +#define UHS_SDR50_BUS_SPEED 2
> > > +#define UHS_SDR104_BUS_SPEED 3
> > > +#define UHS_DDR50_BUS_SPEED 4
> > > +
> > > +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> > > +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> > > +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> > > +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> > > +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> > > unsigned int uhs_drv_type;
> > > #define SD_DRIVER_TYPE_B 0x01
> > > #define SD_DRIVER_TYPE_A 0x02
> > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > > index bc2121e..4dfff6d 100644
> > > --- a/include/linux/mmc/host.h
> > > +++ b/include/linux/mmc/host.h
> > > @@ -50,6 +50,10 @@ struct mmc_ios {
> > > #define MMC_TIMING_LEGACY 0
> > > #define MMC_TIMING_MMC_HS 1
> > > #define MMC_TIMING_SD_HS 2
> > > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > > +#define MMC_TIMING_UHS_SDR50 3
> > > +#define MMC_TIMING_UHS_SDR104 4
> > > +#define MMC_TIMING_UHS_DDR50 5
> > >
> > > unsigned char ddr; /* dual data rate used */
> > >
> > > --
> > > 1.7.1
> >
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-23 6:08 ` Subhash Jadavani
@ 2011-03-23 6:14 ` Nath, Arindam
2011-03-23 6:19 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-23 6:14 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Wednesday, March 23, 2011 11:38 AM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Monday, March 21, 2011 4:37 PM
> > To: Subhash Jadavani; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> > uhs initialization
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > Sent: Monday, March 21, 2011 4:29 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> during
> > > uhs initialization
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> > Arindam
> > > > Nath
> > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > To: cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > henry.su@amd.com;
> > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > Subject: [PATCH v2 09/12] mmc: sd: add support for tuning during
> > uhs
> > > > initialization
> > > >
> > > > 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. 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 Seletc (bit 7) of Host Control2 register to 0.
> > > > Before exiting the tuning procedure, we disable Buffer Read Ready
> > > > interrupt.
> > > >
> > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > ---
> > > > drivers/mmc/core/sd.c | 4 +
> > > > drivers/mmc/host/sdhci.c | 137
> > > > ++++++++++++++++++++++++++++++++++++++++++++-
> > > > 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, 149 insertions(+), 1 deletions(-)
> > > >
> > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > index be01397..1e2d157 100644
> > > > --- a/drivers/mmc/core/sd.c
> > > > +++ b/drivers/mmc/core/sd.c
> > > > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct
> > mmc_card
> > > > *card)
> > > > /* Set current limit for the card */
> > > > err = sd_set_current_limit(card, status);
> > > >
> > > > + /* SPI mode doesn't define CMD19 */
> > > > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> > > > >execute_tuning)
> > > > + 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 245cc39..8f4f102 100644
> > > > --- a/drivers/mmc/host/sdhci.c
> > > > +++ b/drivers/mmc/host/sdhci.c
> > > > @@ -39,6 +39,8 @@
> > > > #define SDHCI_USE_LEDS_CLASS
> > > > #endif
> > > >
> > > > +#define MAX_TUNING_LOOP 40
> > > > +
> > > > static unsigned int debug_quirks = 0;
> > > >
> > > > static void sdhci_prepare_data(struct sdhci_host *, struct
> > mmc_data
> > > > *);
> > > > @@ -985,7 +987,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);
> > > > @@ -1485,6 +1489,119 @@ static int
> sdhci_get_max_current_180(struct
> > > > mmc_host *mmc)
> > > > return max_current_180;
> > > > }
> > > >
> > > > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > > > +{
> > > > + struct sdhci_host *host;
> > > > + u16 ctrl;
> > > > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > > > + unsigned long flags;
> > > > + unsigned long timeout;
> > > > +
> > > > + host = mmc_priv(mmc);
> > > > +
> > > > + spin_lock_irqsave(&host->lock, flags);
> > > > +
> > > > + 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_SDR104) ||
> > > > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > > > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > > > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > > > + else {
> > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > + return;
> > > > + }
> > > > +
> > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > +
> > > > + /*
> > > > + * As per the Host Controller spec v3.00, tuning command
> > > > + * generates Buffer Read Ready interrupt, so enable that.
> > > > + */
> > > > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > +
> > > > + /*
> > > > + * Issue CMD19 repeatedly till Execute Tuning is set to 0
> or the
> > > > number
> > > > + * of loops reaches 40 times or a timeout of 150ms occurs.
> > > > + */
> > > > + timeout = 150;
> > > > + do {
> > > > + 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;
> > > > + sdhci_send_command(host, &cmd);
> > > > +
> > > > + host->cmd = NULL;
> > > > + host->mrq = NULL;
> > > > +
> > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > +
> > > > + /* Wait for Buffer Read Ready interrupt */
> > > > + wait_event_interruptible_timeout(host->buf_ready_int,
> > > > + (host->tuning_done == 1),
> > > > + msecs_to_jiffies(50));
> > > > + spin_lock_irqsave(&host->lock, flags);
> > > > +
> > > > + if (!host->tuning_done) {
> > > > + printk(KERN_INFO DRIVER_NAME ": Tuning
> procedure"
> > > > + " failed, falling back to fixed sampling"
> > > > + " clock\n");
> > > > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > > > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > +
> > > > + 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");
> > > > + }
> > > > +
> > > > +out:
> > > > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > +}
> > > > +
> > > > static const struct mmc_host_ops sdhci_ops = {
> > > > .request = sdhci_request,
> > > > .set_ios = sdhci_set_ios,
> > > > @@ -1492,6 +1609,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,
> > > > .get_max_current_180 = sdhci_get_max_current_180,
> > > > + .execute_tuning = sdhci_execute_tuning,
> > > > };
> > > >
> > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > *******\
> > > > @@ -1703,6 +1821,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
> > > > @@ -2126,6 +2254,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;
> > > > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > > > --- a/drivers/mmc/host/sdhci.h
> > > > +++ b/drivers/mmc/host/sdhci.h
> > > > @@ -160,6 +160,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
> > > > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > > > --- a/include/linux/mmc/host.h
> > > > +++ b/include/linux/mmc/host.h
> > > > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> > > >
> > > > int (*start_signal_voltage_switch)(struct mmc_host
> *host);
> > > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > > + void (*execute_tuning)(struct mmc_host *host);
> > >
> > >
> > > Why execute_tuning ops return type is void? Will there not be any
> > error
> > > scenarios? What if host fails to find any tuning point? Shouldn't
> it
> > > return
> > > error to the core layer and then core layer should not begin with
> the
> > > any
> > > read/write operations?
> >
> > In case the tuning procedure fails, Host Controller falls back on
> using
> > Fixed Sampling clock, so data transfer can still continue.
> >
>
> I think that's not a correct assumption. Let's say tuning fails and
> host
> controller falls back to fixed sampling clock and card still operates
> in
> SDR104 with CLK=208MHz. This means that all further read/write
> transactions
> will mainly result in CRC Fail errors and none of the transactions may
> be
> successful. So we can't just leave the host controller in state where
> it
> cannot perform high speed transfers (because it's tuning had failed)
> and
> then start issuing commands with high speed transfer conditions
> (CLK=208MHz). Better way is for host to inform core layer that tuning
> has
> failed and then there should not be any further transactions with the
> card
> or fallback to low speed modes (SDR12, SDR25 or SDR50) or power cycle
> the
> card.
The host controller spec v3.00 clearly says that the occurrence of an error during tuning procedure is indicated by Sampling Select, which will be set to 0 in case tuning fails. But that won't stop the controller to retry re-tuning procedure based on the re-tuning mode. So I don't see any reason for transaction to be stopped in case tuning fails.
Thanks,
Arindam
>
>
> > Thanks,
> > Arindam
> >
> > >
> > >
> > > > };
> > > >
> > > > struct mmc_card;
> > > > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> > > > index 612301f..9194f9e 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 282d158..5203b97 100644
> > > > --- a/include/linux/mmc/sdhci.h
> > > > +++ b/include/linux/mmc/sdhci.h
> > > > @@ -109,6 +109,7 @@ struct sdhci_host {
> > > > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable
> > > */
> > > > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this
> > req. */
> > > > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive
> > */
> > > > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning
> > > */
> > > >
> > > > unsigned int version; /* SDHCI spec. version */
> > > >
> > > > @@ -147,6 +148,9 @@ struct sdhci_host {
> > > >
> > > > struct mmc_command *saved_abort_cmd; /* Abort command
> saved
> > > > for data error recovery */
> > > >
> > > > + 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
> > >
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-23 6:14 ` Subhash Jadavani
@ 2011-03-23 6:17 ` Nath, Arindam
2011-03-23 6:26 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-23 6:17 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Wednesday, March 23, 2011 11:44 AM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Wednesday, March 23, 2011 11:35 AM
> > To: Subhash Jadavani; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode selection
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > Sent: Monday, March 21, 2011 12:13 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> speed
> > > mode selection
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> > Arindam
> > > > Nath
> > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > To: cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > henry.su@amd.com;
> > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode
> > > > selection
> > > >
> > > > 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. Since MMC_TIMING_UHS_SDR25 is same as
> > > > MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD flag
> > for
> > > > UHS SDR25 again. We also 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 set the clock frequency for
> the
> > > > UHS-I mode selected.
> > > >
> > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > ---
> > > > drivers/mmc/core/sd.c | 91
> > > > ++++++++++++++++++++++++++++++++++++++++++++++
> > > > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > > > drivers/mmc/host/sdhci.h | 5 +++
> > > > include/linux/mmc/card.h | 16 ++++++++
> > > > include/linux/mmc/host.h | 4 ++
> > > > 5 files changed, 146 insertions(+), 2 deletions(-)
> > > >
> > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > index f6a4fab..ec0d8e6 100644
> > > > --- a/drivers/mmc/core/sd.c
> > > > +++ b/drivers/mmc/core/sd.c
> > > > @@ -464,6 +464,92 @@ static int sd_select_driver_type(struct
> > mmc_card
> > > > *card, u8 *status)
> > > > return 0;
> > > > }
> > > >
> > > > +static int sd_set_bus_speed_mode(struct mmc_card *card, u8
> > *status)
> > > > +{
> > > > + unsigned int bus_speed, timing;
> > > > + int err;
> > > > +
> > > > + /*
> > > > + * If the host doesn't support any of the UHS-I modes,
> fallback
> > > > on
> > > > + * default speed.
> > > > + */
> > > > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 |
> MMC_CAP_UHS_SDR25 |
> > > > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> MMC_CAP_UHS_DDR50)))
> > > > + return 0;
> > > > +
> > > > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > > > + if (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)
> {
> > > > + bus_speed = UHS_SDR104_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR104;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR50)
> > > > {
> > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_DDR50)
> > > > {
> > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR25)
> > > > {
> > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > > + }
> > >
> > > I would not agree that if Host cap is set to MMC_CAP_UHS_SDR104
> > implies
> > > that
> > > DDR50 mode is supported by host. There may be a case where host
> > > controller
> > > only support SDR timing modes.
> > > Basically if host should advertise all the supported modes (SDR104,
> > > SDR50,
> > > SDR25, SDR12 & DDR) by caps and then we should choose the highest
> > > performance mode depending on both the card and host's highest
> > > performance
> > > mode.
> > >
> > > So this should be the check:
> > >
> > > if ((card->host->caps & MMC_CAP_UHS_SDR104 &&
> > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
> > > bus_speed = UHS_SDR104_BUS_SPEED;
> > > timing = MMC_TIMING_UHS_SDR104;
> > > card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> > > } else if ((card->host->caps & MMC_CAP_UHS_DDR50 &&
> > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > > bus_speed = UHS_DDR50_BUS_SPEED;
> > > timing = MMC_TIMING_UHS_DDR50;
> > > card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > } else if ((card->host->caps & MMC_CAP_UHS_SDR50 &&
> > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> > > bus_speed = UHS_SDR50_BUS_SPEED;
> > > timing = MMC_TIMING_UHS_SDR50;
> > > card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > > } else if ((card->host->caps & MMC_CAP_UHS_SDR25 &&
> > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > > bus_speed = UHS_SDR50_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_SDR12 &&
> > > (card->sw_caps.uhs_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;
> > > }
> >
> > A host capable of operating at a higher bus speed mode should be able
> > to support cards with a lower bus speed mode. In case of your
> > conditions checks above, you are checking for the exact match for the
> > bus speed modes, otherwise you will fall back on using default bus
> > speed mode, thus performance loss.
>
> Why should we implicitly assume that host supporting SDR104 mode would
> also
> support DDR50 mode? Is there any spec. which says so? If not then we
> should
> rely on host capability to take the decision. DDR50 would require
> different
> timing implementation on the host side and host may choose not to
> implement
> DDR50 timing support then?
>
> I would agree that host supporting SDR104 mode should also be
> supporting
> SDR50/SDR25/SDR12 modes but it may not be true for DDR50 mode.
Please refer to footnote of table 3-5 of the Physical Layer spec v3.01. It says:
"The card supports a UHS-I mode shall support all lower UHS-I modes."
So if this is true for card, doesn't the same apply to host?
Thanks,
Arindam
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > > Regards,
> > > Subhash
> > >
> > >
> > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> > > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50))
> {
> > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_DDR50)
> > > > {
> > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR25)
> > > > {
> > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > > + }
> > > > + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> > > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50))
> {
> > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > > + } else if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR25)
> > > > {
> > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > > + }
> > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> > > > + if ((card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)
> ||
> > > > + (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25))
> {
> > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
> > > > + }
> > > > + }
> > > > +
> > > > + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> > > > + if (err)
> > > > + return err;
> > > > +
> > > > + if ((status[16] & 0xF) != bus_speed)
> > > > + printk(KERN_WARNING "%s: Problem setting bus speed
> > > > mode!\n",
> > > > + mmc_hostname(card->host));
> > > > + else {
> > > > + if (bus_speed) {
> > > > + mmc_set_timing(card->host, timing);
> > > > + mmc_set_clock(card->host,
> > > card->sw_caps.uhs_max_dtr);
> > > > + }
> > > > + }
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > /*
> > > > * UHS-I specific initialization procedure
> > > > */
> > > > @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct
> > mmc_card
> > > > *card)
> > > >
> > > > /* Set the driver strength for the card */
> > > > err = sd_select_driver_type(card, status);
> > > > + if (err)
> > > > + goto out;
> > > > +
> > > > + /* Set bus speed mode of the card */
> > > > + err = sd_set_bus_speed_mode(card, status);
> > > >
> > > > out:
> > > > kfree(status);
> > > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > > index 5d3bb11..f127fa2 100644
> > > > --- a/drivers/mmc/host/sdhci.c
> > > > +++ b/drivers/mmc/host/sdhci.c
> > > > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct mmc_host
> > > *mmc,
> > > > struct mmc_ios *ios)
> > > > ctrl &= ~SDHCI_CTRL_HISPD;
> > > >
> > > > if (host->version >= SDHCI_SPEC_300) {
> > > > - u16 ctrl_2;
> > > > + u16 clk, ctrl_2;
> > > > +
> > > > + /* In case of UHS-I modes, set High Speed Enable */
> > > > + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> > > > + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> > > > + (ios->timing == MMC_TIMING_UHS_DDR50))
> > > > + ctrl |= SDHCI_CTRL_HISPD;
> > > >
> > > > ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> > > > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct mmc_host
> > *mmc,
> > > > struct mmc_ios *ios)
> > > > * need to reset SD Clock Enable before
> changing
> > > High
> > > > * Speed Enable to avoid generating clock
> gliches.
> > > > */
> > > > - u16 clk;
> > > >
> > > > /* Reset SD Clock Enable */
> > > > clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct mmc_host
> > > *mmc,
> > > > struct mmc_ios *ios)
> > > > clk |= SDHCI_CLOCK_CARD_EN;
> > > > sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > > }
> > > > +
> > > > + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> > > > +
> > > > + /* Select Bus Speed Mode for host */
> > > > + if (ios->timing == MMC_TIMING_UHS_SDR25)
> > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > > > + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > > > + else if (ios->timing == MMC_TIMING_UHS_SDR104)
> > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> > > > + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> > > > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > > > +
> > > > + /* Reset SD Clock Enable */
> > > > + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > > +
> > > > + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> > > > +
> > > > + /* Re-enable SD Clock */
> > > > + clk |= SDHCI_CLOCK_CARD_EN;
> > > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > > } else
> > > > sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
> > > >
> > > > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > > > index a407b5b..5bf244d 100644
> > > > --- a/drivers/mmc/host/sdhci.h
> > > > +++ b/drivers/mmc/host/sdhci.h
> > > > @@ -150,6 +150,11 @@
> > > > #define SDHCI_ACMD12_ERR 0x3C
> > > >
> > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > > > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > > > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > > > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > > > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > > > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > > > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> > > > index 2d7f7a3..0b24c41 100644
> > > > --- a/include/linux/mmc/card.h
> > > > +++ b/include/linux/mmc/card.h
> > > > @@ -75,7 +75,23 @@ struct sd_ssr {
> > > >
> > > > struct sd_switch_caps {
> > > > unsigned int hs_max_dtr;
> > > > + unsigned int uhs_max_dtr;
> > > > +#define UHS_SDR104_MAX_DTR 208000000
> > > > +#define UHS_SDR50_MAX_DTR 100000000
> > > > +#define UHS_DDR50_MAX_DTR 50000000
> > > > +#define UHS_SDR25_MAX_DTR 50000000
> > > > unsigned int uhs_bus_mode;
> > > > +#define UHS_SDR12_BUS_SPEED 0
> > > > +#define UHS_SDR25_BUS_SPEED 1
> > > > +#define UHS_SDR50_BUS_SPEED 2
> > > > +#define UHS_SDR104_BUS_SPEED 3
> > > > +#define UHS_DDR50_BUS_SPEED 4
> > > > +
> > > > +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> > > > +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> > > > +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> > > > +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> > > > +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> > > > unsigned int uhs_drv_type;
> > > > #define SD_DRIVER_TYPE_B 0x01
> > > > #define SD_DRIVER_TYPE_A 0x02
> > > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> > > > index bc2121e..4dfff6d 100644
> > > > --- a/include/linux/mmc/host.h
> > > > +++ b/include/linux/mmc/host.h
> > > > @@ -50,6 +50,10 @@ struct mmc_ios {
> > > > #define MMC_TIMING_LEGACY 0
> > > > #define MMC_TIMING_MMC_HS 1
> > > > #define MMC_TIMING_SD_HS 2
> > > > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > > > +#define MMC_TIMING_UHS_SDR50 3
> > > > +#define MMC_TIMING_UHS_SDR104 4
> > > > +#define MMC_TIMING_UHS_DDR50 5
> > > >
> > > > unsigned char ddr; /* dual data rate used
> */
> > > >
> > > > --
> > > > 1.7.1
> > >
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-23 6:14 ` Nath, Arindam
@ 2011-03-23 6:19 ` Subhash Jadavani
2011-03-23 6:22 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-23 6:19 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Wednesday, March 23, 2011 11:45 AM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Wednesday, March 23, 2011 11:38 AM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> > uhs initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > Sent: Monday, March 21, 2011 4:37 PM
> > > To: Subhash Jadavani; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> during
> > > uhs initialization
> > >
> > > Hi Subhash,
> > >
> > >
> > > > -----Original Message-----
> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > Sent: Monday, March 21, 2011 4:29 PM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > during
> > > > uhs initialization
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> > > Arindam
> > > > > Nath
> > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > To: cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > henry.su@amd.com;
> > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > Subject: [PATCH v2 09/12] mmc: sd: add support for tuning
> during
> > > uhs
> > > > > initialization
> > > > >
> > > > > 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. 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 Seletc (bit 7) of Host Control2 register to
> 0.
> > > > > Before exiting the tuning procedure, we disable Buffer Read
> Ready
> > > > > interrupt.
> > > > >
> > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > ---
> > > > > drivers/mmc/core/sd.c | 4 +
> > > > > drivers/mmc/host/sdhci.c | 137
> > > > > ++++++++++++++++++++++++++++++++++++++++++++-
> > > > > 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, 149 insertions(+), 1 deletions(-)
> > > > >
> > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > > index be01397..1e2d157 100644
> > > > > --- a/drivers/mmc/core/sd.c
> > > > > +++ b/drivers/mmc/core/sd.c
> > > > > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct
> > > mmc_card
> > > > > *card)
> > > > > /* Set current limit for the card */
> > > > > err = sd_set_current_limit(card, status);
> > > > >
> > > > > + /* SPI mode doesn't define CMD19 */
> > > > > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> > > > > >execute_tuning)
> > > > > + 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 245cc39..8f4f102 100644
> > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > @@ -39,6 +39,8 @@
> > > > > #define SDHCI_USE_LEDS_CLASS
> > > > > #endif
> > > > >
> > > > > +#define MAX_TUNING_LOOP 40
> > > > > +
> > > > > static unsigned int debug_quirks = 0;
> > > > >
> > > > > static void sdhci_prepare_data(struct sdhci_host *, struct
> > > mmc_data
> > > > > *);
> > > > > @@ -985,7 +987,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);
> > > > > @@ -1485,6 +1489,119 @@ static int
> > sdhci_get_max_current_180(struct
> > > > > mmc_host *mmc)
> > > > > return max_current_180;
> > > > > }
> > > > >
> > > > > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > > > > +{
> > > > > + struct sdhci_host *host;
> > > > > + u16 ctrl;
> > > > > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > > > > + unsigned long flags;
> > > > > + unsigned long timeout;
> > > > > +
> > > > > + host = mmc_priv(mmc);
> > > > > +
> > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > +
> > > > > + 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_SDR104) ||
> > > > > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > > > > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > > > > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > > > > + else {
> > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > + return;
> > > > > + }
> > > > > +
> > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > +
> > > > > + /*
> > > > > + * As per the Host Controller spec v3.00, tuning
> command
> > > > > + * generates Buffer Read Ready interrupt, so enable
> that.
> > > > > + */
> > > > > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > +
> > > > > + /*
> > > > > + * Issue CMD19 repeatedly till Execute Tuning is set to
> 0
> > or the
> > > > > number
> > > > > + * of loops reaches 40 times or a timeout of 150ms
> occurs.
> > > > > + */
> > > > > + timeout = 150;
> > > > > + do {
> > > > > + 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;
> > > > > + sdhci_send_command(host, &cmd);
> > > > > +
> > > > > + host->cmd = NULL;
> > > > > + host->mrq = NULL;
> > > > > +
> > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > +
> > > > > + /* Wait for Buffer Read Ready interrupt */
> > > > > + wait_event_interruptible_timeout(host-
> >buf_ready_int,
> > > > > + (host->tuning_done ==
> 1),
> > > > > + msecs_to_jiffies(50));
> > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > +
> > > > > + if (!host->tuning_done) {
> > > > > + printk(KERN_INFO DRIVER_NAME ": Tuning
> > procedure"
> > > > > + " failed, falling back to fixed
> sampling"
> > > > > + " clock\n");
> > > > > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > > > > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > > > > + sdhci_writew(host, ctrl,
> SDHCI_HOST_CONTROL2);
> > > > > +
> > > > > + 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");
> > > > > + }
> > > > > +
> > > > > +out:
> > > > > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > +}
> > > > > +
> > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > .request = sdhci_request,
> > > > > .set_ios = sdhci_set_ios,
> > > > > @@ -1492,6 +1609,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,
> > > > > .get_max_current_180 =
> sdhci_get_max_current_180,
> > > > > + .execute_tuning = sdhci_execute_tuning,
> > > > > };
> > > > >
> > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > *******\
> > > > > @@ -1703,6 +1821,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
> > > > > @@ -2126,6 +2254,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;
> > > > > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > @@ -160,6 +160,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
> > > > > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > > > > --- a/include/linux/mmc/host.h
> > > > > +++ b/include/linux/mmc/host.h
> > > > > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> > > > >
> > > > > int (*start_signal_voltage_switch)(struct mmc_host
> > *host);
> > > > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > > > + void (*execute_tuning)(struct mmc_host *host);
> > > >
> > > >
> > > > Why execute_tuning ops return type is void? Will there not be any
> > > error
> > > > scenarios? What if host fails to find any tuning point? Shouldn't
> > it
> > > > return
> > > > error to the core layer and then core layer should not begin with
> > the
> > > > any
> > > > read/write operations?
> > >
> > > In case the tuning procedure fails, Host Controller falls back on
> > using
> > > Fixed Sampling clock, so data transfer can still continue.
> > >
> >
> > I think that's not a correct assumption. Let's say tuning fails and
> > host
> > controller falls back to fixed sampling clock and card still operates
> > in
> > SDR104 with CLK=208MHz. This means that all further read/write
> > transactions
> > will mainly result in CRC Fail errors and none of the transactions
> may
> > be
> > successful. So we can't just leave the host controller in state where
> > it
> > cannot perform high speed transfers (because it's tuning had failed)
> > and
> > then start issuing commands with high speed transfer conditions
> > (CLK=208MHz). Better way is for host to inform core layer that tuning
> > has
> > failed and then there should not be any further transactions with the
> > card
> > or fallback to low speed modes (SDR12, SDR25 or SDR50) or power cycle
> > the
> > card.
>
> The host controller spec v3.00 clearly says that the occurrence of an
> error during tuning procedure is indicated by Sampling Select, which
> will be set to 0 in case tuning fails. But that won't stop the
> controller to retry re-tuning procedure based on the re-tuning mode. So
> I don't see any reason for transaction to be stopped in case tuning
> fails.
This capability is not there with host controller on our chipsets. So then
what to do? I think referring Host controller spec v3.00 should not be
proper here. As this spec is not a must condition for all host controller
implementations.
>
> Thanks,
> Arindam
>
> >
> >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > >
> > > > > };
> > > > >
> > > > > struct mmc_card;
> > > > > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> > > > > index 612301f..9194f9e 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 282d158..5203b97 100644
> > > > > --- a/include/linux/mmc/sdhci.h
> > > > > +++ b/include/linux/mmc/sdhci.h
> > > > > @@ -109,6 +109,7 @@ struct sdhci_host {
> > > > > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable
> > > > */
> > > > > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this
> > > req. */
> > > > > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive
> > > */
> > > > > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs
> tuning
> > > > */
> > > > >
> > > > > unsigned int version; /* SDHCI spec. version */
> > > > >
> > > > > @@ -147,6 +148,9 @@ struct sdhci_host {
> > > > >
> > > > > struct mmc_command *saved_abort_cmd; /* Abort
> command
> > saved
> > > > > for data error recovery */
> > > > >
> > > > > + 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
> > > >
> > > >
> > >
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-23 6:19 ` Subhash Jadavani
@ 2011-03-23 6:22 ` Nath, Arindam
2011-03-23 6:34 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-23 6:22 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Wednesday, March 23, 2011 11:50 AM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
>
>
> > -----Original Message-----
> > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > Sent: Wednesday, March 23, 2011 11:45 AM
> > To: Subhash Jadavani; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> > uhs initialization
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > Sent: Wednesday, March 23, 2011 11:38 AM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> during
> > > uhs initialization
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > Sent: Monday, March 21, 2011 4:37 PM
> > > > To: Subhash Jadavani; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > during
> > > > uhs initialization
> > > >
> > > > Hi Subhash,
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > Sent: Monday, March 21, 2011 4:29 PM
> > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > during
> > > > > uhs initialization
> > > > >
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> > > > Arindam
> > > > > > Nath
> > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > To: cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > henry.su@amd.com;
> > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > Subject: [PATCH v2 09/12] mmc: sd: add support for tuning
> > during
> > > > uhs
> > > > > > initialization
> > > > > >
> > > > > > 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. 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 Seletc (bit 7) of Host Control2 register
> to
> > 0.
> > > > > > Before exiting the tuning procedure, we disable Buffer Read
> > Ready
> > > > > > interrupt.
> > > > > >
> > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > ---
> > > > > > drivers/mmc/core/sd.c | 4 +
> > > > > > drivers/mmc/host/sdhci.c | 137
> > > > > > ++++++++++++++++++++++++++++++++++++++++++++-
> > > > > > 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, 149 insertions(+), 1 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > > > index be01397..1e2d157 100644
> > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct
> > > > mmc_card
> > > > > > *card)
> > > > > > /* Set current limit for the card */
> > > > > > err = sd_set_current_limit(card, status);
> > > > > >
> > > > > > + /* SPI mode doesn't define CMD19 */
> > > > > > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> > > > > > >execute_tuning)
> > > > > > + 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 245cc39..8f4f102 100644
> > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > @@ -39,6 +39,8 @@
> > > > > > #define SDHCI_USE_LEDS_CLASS
> > > > > > #endif
> > > > > >
> > > > > > +#define MAX_TUNING_LOOP 40
> > > > > > +
> > > > > > static unsigned int debug_quirks = 0;
> > > > > >
> > > > > > static void sdhci_prepare_data(struct sdhci_host *, struct
> > > > mmc_data
> > > > > > *);
> > > > > > @@ -985,7 +987,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);
> > > > > > @@ -1485,6 +1489,119 @@ static int
> > > sdhci_get_max_current_180(struct
> > > > > > mmc_host *mmc)
> > > > > > return max_current_180;
> > > > > > }
> > > > > >
> > > > > > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > > > > > +{
> > > > > > + struct sdhci_host *host;
> > > > > > + u16 ctrl;
> > > > > > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > > > > > + unsigned long flags;
> > > > > > + unsigned long timeout;
> > > > > > +
> > > > > > + host = mmc_priv(mmc);
> > > > > > +
> > > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > > +
> > > > > > + 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_SDR104) ||
> > > > > > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > > > > > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > > > > > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > > > > > + else {
> > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > + return;
> > > > > > + }
> > > > > > +
> > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > +
> > > > > > + /*
> > > > > > + * As per the Host Controller spec v3.00, tuning
> > command
> > > > > > + * generates Buffer Read Ready interrupt, so enable
> > that.
> > > > > > + */
> > > > > > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > > +
> > > > > > + /*
> > > > > > + * Issue CMD19 repeatedly till Execute Tuning is set
> to
> > 0
> > > or the
> > > > > > number
> > > > > > + * of loops reaches 40 times or a timeout of 150ms
> > occurs.
> > > > > > + */
> > > > > > + timeout = 150;
> > > > > > + do {
> > > > > > + 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;
> > > > > > + sdhci_send_command(host, &cmd);
> > > > > > +
> > > > > > + host->cmd = NULL;
> > > > > > + host->mrq = NULL;
> > > > > > +
> > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > +
> > > > > > + /* Wait for Buffer Read Ready interrupt */
> > > > > > + wait_event_interruptible_timeout(host-
> > >buf_ready_int,
> > > > > > + (host->tuning_done ==
> > 1),
> > > > > > +
> msecs_to_jiffies(50));
> > > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > > +
> > > > > > + if (!host->tuning_done) {
> > > > > > + printk(KERN_INFO DRIVER_NAME ":
> Tuning
> > > procedure"
> > > > > > + " failed, falling back to
> fixed
> > sampling"
> > > > > > + " clock\n");
> > > > > > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > > > > > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > > > > > + sdhci_writew(host, ctrl,
> > SDHCI_HOST_CONTROL2);
> > > > > > +
> > > > > > + 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");
> > > > > > + }
> > > > > > +
> > > > > > +out:
> > > > > > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > +}
> > > > > > +
> > > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > > .request = sdhci_request,
> > > > > > .set_ios = sdhci_set_ios,
> > > > > > @@ -1492,6 +1609,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,
> > > > > > .get_max_current_180 =
> > sdhci_get_max_current_180,
> > > > > > + .execute_tuning =
> sdhci_execute_tuning,
> > > > > > };
> > > > > >
> > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > > *******\
> > > > > > @@ -1703,6 +1821,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
> > > > > > @@ -2126,6 +2254,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;
> > > > > > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > @@ -160,6 +160,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
> > > > > > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > > > > > --- a/include/linux/mmc/host.h
> > > > > > +++ b/include/linux/mmc/host.h
> > > > > > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> > > > > >
> > > > > > int (*start_signal_voltage_switch)(struct
> mmc_host
> > > *host);
> > > > > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > > > > + void (*execute_tuning)(struct mmc_host *host);
> > > > >
> > > > >
> > > > > Why execute_tuning ops return type is void? Will there not be
> any
> > > > error
> > > > > scenarios? What if host fails to find any tuning point?
> Shouldn't
> > > it
> > > > > return
> > > > > error to the core layer and then core layer should not begin
> with
> > > the
> > > > > any
> > > > > read/write operations?
> > > >
> > > > In case the tuning procedure fails, Host Controller falls back on
> > > using
> > > > Fixed Sampling clock, so data transfer can still continue.
> > > >
> > >
> > > I think that's not a correct assumption. Let's say tuning fails and
> > > host
> > > controller falls back to fixed sampling clock and card still
> operates
> > > in
> > > SDR104 with CLK=208MHz. This means that all further read/write
> > > transactions
> > > will mainly result in CRC Fail errors and none of the transactions
> > may
> > > be
> > > successful. So we can't just leave the host controller in state
> where
> > > it
> > > cannot perform high speed transfers (because it's tuning had
> failed)
> > > and
> > > then start issuing commands with high speed transfer conditions
> > > (CLK=208MHz). Better way is for host to inform core layer that
> tuning
> > > has
> > > failed and then there should not be any further transactions with
> the
> > > card
> > > or fallback to low speed modes (SDR12, SDR25 or SDR50) or power
> cycle
> > > the
> > > card.
> >
> > The host controller spec v3.00 clearly says that the occurrence of an
> > error during tuning procedure is indicated by Sampling Select, which
> > will be set to 0 in case tuning fails. But that won't stop the
> > controller to retry re-tuning procedure based on the re-tuning mode.
> So
> > I don't see any reason for transaction to be stopped in case tuning
> > fails.
>
> This capability is not there with host controller on our chipsets. So
> then
> what to do? I think referring Host controller spec v3.00 should not be
> proper here. As this spec is not a must condition for all host
> controller
> implementations.
In that case, I would really appreciate if you could implement the procedure which will work for your controller, and then we can merge the code with mine.
Thanks,
Arindam
>
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > >
> > > > Thanks,
> > > > Arindam
> > > >
> > > > >
> > > > >
> > > > > > };
> > > > > >
> > > > > > struct mmc_card;
> > > > > > diff --git a/include/linux/mmc/mmc.h
> b/include/linux/mmc/mmc.h
> > > > > > index 612301f..9194f9e 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 282d158..5203b97 100644
> > > > > > --- a/include/linux/mmc/sdhci.h
> > > > > > +++ b/include/linux/mmc/sdhci.h
> > > > > > @@ -109,6 +109,7 @@ struct sdhci_host {
> > > > > > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA
> capable
> > > > > */
> > > > > > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this
> > > > req. */
> > > > > > #define SDHCI_DEVICE_DEAD (1<<3) /* Device
> unresponsive
> > > > */
> > > > > > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50
> needs
> > tuning
> > > > > */
> > > > > >
> > > > > > unsigned int version; /* SDHCI spec. version */
> > > > > >
> > > > > > @@ -147,6 +148,9 @@ struct sdhci_host {
> > > > > >
> > > > > > struct mmc_command *saved_abort_cmd; /* Abort
> > command
> > > saved
> > > > > > for data error recovery */
> > > > > >
> > > > > > + 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
> > > > >
> > > > >
> > > >
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-23 6:17 ` Nath, Arindam
@ 2011-03-23 6:26 ` Subhash Jadavani
2011-03-23 6:35 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-23 6:26 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Wednesday, March 23, 2011 11:48 AM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Wednesday, March 23, 2011 11:44 AM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode selection
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > Sent: Wednesday, March 23, 2011 11:35 AM
> > > To: Subhash Jadavani; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> speed
> > > mode selection
> > >
> > > Hi Subhash,
> > >
> > >
> > > > -----Original Message-----
> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > Sent: Monday, March 21, 2011 12:13 PM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > speed
> > > > mode selection
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> > > Arindam
> > > > > Nath
> > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > To: cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > henry.su@amd.com;
> > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> speed
> > > mode
> > > > > selection
> > > > >
> > > > > 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. Since MMC_TIMING_UHS_SDR25 is same as
> > > > > MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD
> flag
> > > for
> > > > > UHS SDR25 again. We also 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 set the clock frequency for
> > the
> > > > > UHS-I mode selected.
> > > > >
> > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > ---
> > > > > drivers/mmc/core/sd.c | 91
> > > > > ++++++++++++++++++++++++++++++++++++++++++++++
> > > > > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > > > > drivers/mmc/host/sdhci.h | 5 +++
> > > > > include/linux/mmc/card.h | 16 ++++++++
> > > > > include/linux/mmc/host.h | 4 ++
> > > > > 5 files changed, 146 insertions(+), 2 deletions(-)
> > > > >
> > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > > index f6a4fab..ec0d8e6 100644
> > > > > --- a/drivers/mmc/core/sd.c
> > > > > +++ b/drivers/mmc/core/sd.c
> > > > > @@ -464,6 +464,92 @@ static int sd_select_driver_type(struct
> > > mmc_card
> > > > > *card, u8 *status)
> > > > > return 0;
> > > > > }
> > > > >
> > > > > +static int sd_set_bus_speed_mode(struct mmc_card *card, u8
> > > *status)
> > > > > +{
> > > > > + unsigned int bus_speed, timing;
> > > > > + int err;
> > > > > +
> > > > > + /*
> > > > > + * If the host doesn't support any of the UHS-I modes,
> > fallback
> > > > > on
> > > > > + * default speed.
> > > > > + */
> > > > > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 |
> > MMC_CAP_UHS_SDR25 |
> > > > > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> > MMC_CAP_UHS_DDR50)))
> > > > > + return 0;
> > > > > +
> > > > > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > > > > + if (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR104)
> > {
> > > > > + bus_speed = UHS_SDR104_BUS_SPEED;
> > > > > + timing = MMC_TIMING_UHS_SDR104;
> > > > > + card->sw_caps.uhs_max_dtr =
> UHS_SDR104_MAX_DTR;
> > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR50)
> > > > > {
> > > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > > + card->sw_caps.uhs_max_dtr =
> UHS_SDR50_MAX_DTR;
> > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_DDR50)
> > > > > {
> > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > + card->sw_caps.uhs_max_dtr =
> UHS_DDR50_MAX_DTR;
> > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR25)
> > > > > {
> > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > + card->sw_caps.uhs_max_dtr =
> UHS_SDR25_MAX_DTR;
> > > > > + }
> > > >
> > > > I would not agree that if Host cap is set to MMC_CAP_UHS_SDR104
> > > implies
> > > > that
> > > > DDR50 mode is supported by host. There may be a case where host
> > > > controller
> > > > only support SDR timing modes.
> > > > Basically if host should advertise all the supported modes
> (SDR104,
> > > > SDR50,
> > > > SDR25, SDR12 & DDR) by caps and then we should choose the highest
> > > > performance mode depending on both the card and host's highest
> > > > performance
> > > > mode.
> > > >
> > > > So this should be the check:
> > > >
> > > > if ((card->host->caps & MMC_CAP_UHS_SDR104 &&
> > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
> > > > bus_speed = UHS_SDR104_BUS_SPEED;
> > > > timing = MMC_TIMING_UHS_SDR104;
> > > > card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> > > > } else if ((card->host->caps & MMC_CAP_UHS_DDR50 &&
> > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > > > bus_speed = UHS_DDR50_BUS_SPEED;
> > > > timing = MMC_TIMING_UHS_DDR50;
> > > > card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR50 &&
> > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> > > > bus_speed = UHS_SDR50_BUS_SPEED;
> > > > timing = MMC_TIMING_UHS_SDR50;
> > > > card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR25 &&
> > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > > > bus_speed = UHS_SDR50_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_SDR12 &&
> > > > (card->sw_caps.uhs_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;
> > > > }
> > >
> > > A host capable of operating at a higher bus speed mode should be
> able
> > > to support cards with a lower bus speed mode. In case of your
> > > conditions checks above, you are checking for the exact match for
> the
> > > bus speed modes, otherwise you will fall back on using default bus
> > > speed mode, thus performance loss.
> >
> > Why should we implicitly assume that host supporting SDR104 mode
> would
> > also
> > support DDR50 mode? Is there any spec. which says so? If not then we
> > should
> > rely on host capability to take the decision. DDR50 would require
> > different
> > timing implementation on the host side and host may choose not to
> > implement
> > DDR50 timing support then?
> >
> > I would agree that host supporting SDR104 mode should also be
> > supporting
> > SDR50/SDR25/SDR12 modes but it may not be true for DDR50 mode.
>
> Please refer to footnote of table 3-5 of the Physical Layer spec v3.01.
> It says:
>
> "The card supports a UHS-I mode shall support all lower UHS-I modes."
>
> So if this is true for card, doesn't the same apply to host?
Yes, this is the exact point I am stretching. Spec. never says the same
thing for host so we should not assume it. we already have luxury to use
host caps then why should we assume something which may not be correct?
>
> Thanks,
> Arindam
>
> >
> > >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > > Regards,
> > > > Subhash
> > > >
> > > >
> > > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> > > > > + if ((card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR104)
> > ||
> > > > > + (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR50))
> > {
> > > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > > + card->sw_caps.uhs_max_dtr =
> UHS_SDR50_MAX_DTR;
> > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_DDR50)
> > > > > {
> > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > + card->sw_caps.uhs_max_dtr =
> UHS_DDR50_MAX_DTR;
> > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR25)
> > > > > {
> > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > + card->sw_caps.uhs_max_dtr =
> UHS_SDR25_MAX_DTR;
> > > > > + }
> > > > > + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> > > > > + if ((card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR104)
> > ||
> > > > > + (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR50)
> > ||
> > > > > + (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_DDR50))
> > {
> > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > + card->sw_caps.uhs_max_dtr =
> UHS_DDR50_MAX_DTR;
> > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR25)
> > > > > {
> > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > + card->sw_caps.uhs_max_dtr =
> UHS_SDR25_MAX_DTR;
> > > > > + }
> > > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> > > > > + if ((card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR104)
> > ||
> > > > > + (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR50)
> > ||
> > > > > + (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_DDR50)
> > ||
> > > > > + (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR25))
> > {
> > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > + card->sw_caps.uhs_max_dtr =
> UHS_SDR25_MAX_DTR;
> > > > > + }
> > > > > + }
> > > > > +
> > > > > + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> > > > > + if (err)
> > > > > + return err;
> > > > > +
> > > > > + if ((status[16] & 0xF) != bus_speed)
> > > > > + printk(KERN_WARNING "%s: Problem setting bus
> speed
> > > > > mode!\n",
> > > > > + mmc_hostname(card->host));
> > > > > + else {
> > > > > + if (bus_speed) {
> > > > > + mmc_set_timing(card->host, timing);
> > > > > + mmc_set_clock(card->host,
> > > > card->sw_caps.uhs_max_dtr);
> > > > > + }
> > > > > + }
> > > > > +
> > > > > + return 0;
> > > > > +}
> > > > > +
> > > > > /*
> > > > > * UHS-I specific initialization procedure
> > > > > */
> > > > > @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct
> > > mmc_card
> > > > > *card)
> > > > >
> > > > > /* Set the driver strength for the card */
> > > > > err = sd_select_driver_type(card, status);
> > > > > + if (err)
> > > > > + goto out;
> > > > > +
> > > > > + /* Set bus speed mode of the card */
> > > > > + err = sd_set_bus_speed_mode(card, status);
> > > > >
> > > > > out:
> > > > > kfree(status);
> > > > > diff --git a/drivers/mmc/host/sdhci.c
> b/drivers/mmc/host/sdhci.c
> > > > > index 5d3bb11..f127fa2 100644
> > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct
> mmc_host
> > > > *mmc,
> > > > > struct mmc_ios *ios)
> > > > > ctrl &= ~SDHCI_CTRL_HISPD;
> > > > >
> > > > > if (host->version >= SDHCI_SPEC_300) {
> > > > > - u16 ctrl_2;
> > > > > + u16 clk, ctrl_2;
> > > > > +
> > > > > + /* In case of UHS-I modes, set High Speed
> Enable */
> > > > > + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> > > > > + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> > > > > + (ios->timing == MMC_TIMING_UHS_DDR50))
> > > > > + ctrl |= SDHCI_CTRL_HISPD;
> > > > >
> > > > > ctrl_2 = sdhci_readw(host,
> SDHCI_HOST_CONTROL2);
> > > > > if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
> > > > > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct mmc_host
> > > *mmc,
> > > > > struct mmc_ios *ios)
> > > > > * need to reset SD Clock Enable before
> > changing
> > > > High
> > > > > * Speed Enable to avoid generating
> clock
> > gliches.
> > > > > */
> > > > > - u16 clk;
> > > > >
> > > > > /* Reset SD Clock Enable */
> > > > > clk = sdhci_readw(host,
> SDHCI_CLOCK_CONTROL);
> > > > > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct
> mmc_host
> > > > *mmc,
> > > > > struct mmc_ios *ios)
> > > > > clk |= SDHCI_CLOCK_CARD_EN;
> > > > > sdhci_writew(host, clk,
> SDHCI_CLOCK_CONTROL);
> > > > > }
> > > > > +
> > > > > + ctrl_2 = sdhci_readw(host,
> SDHCI_HOST_CONTROL2);
> > > > > +
> > > > > + /* Select Bus Speed Mode for host */
> > > > > + if (ios->timing == MMC_TIMING_UHS_SDR25)
> > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > > > > + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > > > > + else if (ios->timing == MMC_TIMING_UHS_SDR104)
> > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> > > > > + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> > > > > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > > > > +
> > > > > + /* Reset SD Clock Enable */
> > > > > + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > > > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > > > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > > > +
> > > > > + sdhci_writew(host, ctrl_2,
> SDHCI_HOST_CONTROL2);
> > > > > +
> > > > > + /* Re-enable SD Clock */
> > > > > + clk |= SDHCI_CLOCK_CARD_EN;
> > > > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > > > } else
> > > > > sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL1);
> > > > >
> > > > > diff --git a/drivers/mmc/host/sdhci.h
> b/drivers/mmc/host/sdhci.h
> > > > > index a407b5b..5bf244d 100644
> > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > @@ -150,6 +150,11 @@
> > > > > #define SDHCI_ACMD12_ERR 0x3C
> > > > >
> > > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > > > > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > > > > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > > > > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > > > > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > > > > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > > > > diff --git a/include/linux/mmc/card.h
> b/include/linux/mmc/card.h
> > > > > index 2d7f7a3..0b24c41 100644
> > > > > --- a/include/linux/mmc/card.h
> > > > > +++ b/include/linux/mmc/card.h
> > > > > @@ -75,7 +75,23 @@ struct sd_ssr {
> > > > >
> > > > > struct sd_switch_caps {
> > > > > unsigned int hs_max_dtr;
> > > > > + unsigned int uhs_max_dtr;
> > > > > +#define UHS_SDR104_MAX_DTR 208000000
> > > > > +#define UHS_SDR50_MAX_DTR 100000000
> > > > > +#define UHS_DDR50_MAX_DTR 50000000
> > > > > +#define UHS_SDR25_MAX_DTR 50000000
> > > > > unsigned int uhs_bus_mode;
> > > > > +#define UHS_SDR12_BUS_SPEED 0
> > > > > +#define UHS_SDR25_BUS_SPEED 1
> > > > > +#define UHS_SDR50_BUS_SPEED 2
> > > > > +#define UHS_SDR104_BUS_SPEED 3
> > > > > +#define UHS_DDR50_BUS_SPEED 4
> > > > > +
> > > > > +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> > > > > +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> > > > > +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> > > > > +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> > > > > +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> > > > > unsigned int uhs_drv_type;
> > > > > #define SD_DRIVER_TYPE_B 0x01
> > > > > #define SD_DRIVER_TYPE_A 0x02
> > > > > diff --git a/include/linux/mmc/host.h
> b/include/linux/mmc/host.h
> > > > > index bc2121e..4dfff6d 100644
> > > > > --- a/include/linux/mmc/host.h
> > > > > +++ b/include/linux/mmc/host.h
> > > > > @@ -50,6 +50,10 @@ struct mmc_ios {
> > > > > #define MMC_TIMING_LEGACY 0
> > > > > #define MMC_TIMING_MMC_HS 1
> > > > > #define MMC_TIMING_SD_HS 2
> > > > > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > > > > +#define MMC_TIMING_UHS_SDR50 3
> > > > > +#define MMC_TIMING_UHS_SDR104 4
> > > > > +#define MMC_TIMING_UHS_DDR50 5
> > > > >
> > > > > unsigned char ddr; /* dual data
> rate used
> > */
> > > > >
> > > > > --
> > > > > 1.7.1
> > > >
> > > >
> > >
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-23 6:22 ` Nath, Arindam
@ 2011-03-23 6:34 ` Subhash Jadavani
2011-03-23 6:41 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-23 6:34 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Wednesday, March 23, 2011 11:53 AM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Wednesday, March 23, 2011 11:50 AM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> > uhs initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > > Sent: Wednesday, March 23, 2011 11:45 AM
> > > To: Subhash Jadavani; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> during
> > > uhs initialization
> > >
> > > Hi Subhash,
> > >
> > >
> > > > -----Original Message-----
> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > Sent: Wednesday, March 23, 2011 11:38 AM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > during
> > > > uhs initialization
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > Sent: Monday, March 21, 2011 4:37 PM
> > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > during
> > > > > uhs initialization
> > > > >
> > > > > Hi Subhash,
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > Sent: Monday, March 21, 2011 4:29 PM
> > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > > during
> > > > > > uhs initialization
> > > > > >
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf
> Of
> > > > > Arindam
> > > > > > > Nath
> > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > To: cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > henry.su@amd.com;
> > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > Subject: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > during
> > > > > uhs
> > > > > > > initialization
> > > > > > >
> > > > > > > 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. 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 Seletc (bit 7) of Host Control2 register
> > to
> > > 0.
> > > > > > > Before exiting the tuning procedure, we disable Buffer Read
> > > Ready
> > > > > > > interrupt.
> > > > > > >
> > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > ---
> > > > > > > drivers/mmc/core/sd.c | 4 +
> > > > > > > drivers/mmc/host/sdhci.c | 137
> > > > > > > ++++++++++++++++++++++++++++++++++++++++++++-
> > > > > > > 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, 149 insertions(+), 1 deletions(-)
> > > > > > >
> > > > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > > > > index be01397..1e2d157 100644
> > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct
> > > > > mmc_card
> > > > > > > *card)
> > > > > > > /* Set current limit for the card */
> > > > > > > err = sd_set_current_limit(card, status);
> > > > > > >
> > > > > > > + /* SPI mode doesn't define CMD19 */
> > > > > > > + if (!mmc_host_is_spi(card->host) && card->host-
> >ops-
> > > > > > > >execute_tuning)
> > > > > > > + 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 245cc39..8f4f102 100644
> > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > @@ -39,6 +39,8 @@
> > > > > > > #define SDHCI_USE_LEDS_CLASS
> > > > > > > #endif
> > > > > > >
> > > > > > > +#define MAX_TUNING_LOOP 40
> > > > > > > +
> > > > > > > static unsigned int debug_quirks = 0;
> > > > > > >
> > > > > > > static void sdhci_prepare_data(struct sdhci_host *, struct
> > > > > mmc_data
> > > > > > > *);
> > > > > > > @@ -985,7 +987,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);
> > > > > > > @@ -1485,6 +1489,119 @@ static int
> > > > sdhci_get_max_current_180(struct
> > > > > > > mmc_host *mmc)
> > > > > > > return max_current_180;
> > > > > > > }
> > > > > > >
> > > > > > > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > > > > > > +{
> > > > > > > + struct sdhci_host *host;
> > > > > > > + u16 ctrl;
> > > > > > > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > > > > > > + unsigned long flags;
> > > > > > > + unsigned long timeout;
> > > > > > > +
> > > > > > > + host = mmc_priv(mmc);
> > > > > > > +
> > > > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > > > +
> > > > > > > + 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_SDR104) ||
> > > > > > > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > > > > > > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > > > > > > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > > > > > > + else {
> > > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > + return;
> > > > > > > + }
> > > > > > > +
> > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > +
> > > > > > > + /*
> > > > > > > + * As per the Host Controller spec v3.00, tuning
> > > command
> > > > > > > + * generates Buffer Read Ready interrupt, so enable
> > > that.
> > > > > > > + */
> > > > > > > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > > > +
> > > > > > > + /*
> > > > > > > + * Issue CMD19 repeatedly till Execute Tuning is
> set
> > to
> > > 0
> > > > or the
> > > > > > > number
> > > > > > > + * of loops reaches 40 times or a timeout of 150ms
> > > occurs.
> > > > > > > + */
> > > > > > > + timeout = 150;
> > > > > > > + do {
> > > > > > > + 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;
> > > > > > > + sdhci_send_command(host, &cmd);
> > > > > > > +
> > > > > > > + host->cmd = NULL;
> > > > > > > + host->mrq = NULL;
> > > > > > > +
> > > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > +
> > > > > > > + /* Wait for Buffer Read Ready interrupt */
> > > > > > > + wait_event_interruptible_timeout(host-
> > > >buf_ready_int,
> > > > > > > + (host->tuning_done
> ==
> > > 1),
> > > > > > > +
> > msecs_to_jiffies(50));
> > > > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > > > +
> > > > > > > + if (!host->tuning_done) {
> > > > > > > + printk(KERN_INFO DRIVER_NAME ":
> > Tuning
> > > > procedure"
> > > > > > > + " failed, falling back to
> > fixed
> > > sampling"
> > > > > > > + " clock\n");
> > > > > > > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > > > > > > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > > > > > > + sdhci_writew(host, ctrl,
> > > SDHCI_HOST_CONTROL2);
> > > > > > > +
> > > > > > > + 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");
> > > > > > > + }
> > > > > > > +
> > > > > > > +out:
> > > > > > > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > +}
> > > > > > > +
> > > > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > > > .request = sdhci_request,
> > > > > > > .set_ios = sdhci_set_ios,
> > > > > > > @@ -1492,6 +1609,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,
> > > > > > > .get_max_current_180 =
> > > sdhci_get_max_current_180,
> > > > > > > + .execute_tuning =
> > sdhci_execute_tuning,
> > > > > > > };
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > > > *******\
> > > > > > > @@ -1703,6 +1821,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
> > > > > > > @@ -2126,6 +2254,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;
> > > > > > > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > > @@ -160,6 +160,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
> > > > > > > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> > > > > > >
> > > > > > > int (*start_signal_voltage_switch)(struct
> > mmc_host
> > > > *host);
> > > > > > > int (*get_max_current_180)(struct mmc_host
> *mmc);
> > > > > > > + void (*execute_tuning)(struct mmc_host *host);
> > > > > >
> > > > > >
> > > > > > Why execute_tuning ops return type is void? Will there not be
> > any
> > > > > error
> > > > > > scenarios? What if host fails to find any tuning point?
> > Shouldn't
> > > > it
> > > > > > return
> > > > > > error to the core layer and then core layer should not begin
> > with
> > > > the
> > > > > > any
> > > > > > read/write operations?
> > > > >
> > > > > In case the tuning procedure fails, Host Controller falls back
> on
> > > > using
> > > > > Fixed Sampling clock, so data transfer can still continue.
> > > > >
> > > >
> > > > I think that's not a correct assumption. Let's say tuning fails
> and
> > > > host
> > > > controller falls back to fixed sampling clock and card still
> > operates
> > > > in
> > > > SDR104 with CLK=208MHz. This means that all further read/write
> > > > transactions
> > > > will mainly result in CRC Fail errors and none of the
> transactions
> > > may
> > > > be
> > > > successful. So we can't just leave the host controller in state
> > where
> > > > it
> > > > cannot perform high speed transfers (because it's tuning had
> > failed)
> > > > and
> > > > then start issuing commands with high speed transfer conditions
> > > > (CLK=208MHz). Better way is for host to inform core layer that
> > tuning
> > > > has
> > > > failed and then there should not be any further transactions with
> > the
> > > > card
> > > > or fallback to low speed modes (SDR12, SDR25 or SDR50) or power
> > cycle
> > > > the
> > > > card.
> > >
> > > The host controller spec v3.00 clearly says that the occurrence of
> an
> > > error during tuning procedure is indicated by Sampling Select,
> which
> > > will be set to 0 in case tuning fails. But that won't stop the
> > > controller to retry re-tuning procedure based on the re-tuning
> mode.
> > So
> > > I don't see any reason for transaction to be stopped in case tuning
> > > fails.
> >
> > This capability is not there with host controller on our chipsets. So
> > then
> > what to do? I think referring Host controller spec v3.00 should not
> be
> > proper here. As this spec is not a must condition for all host
> > controller
> > implementations.
>
> In that case, I would really appreciate if you could implement the
> procedure which will work for your controller, and then we can merge
> the code with mine.
Ok that's fine. but that doesn't mean "execute_tuning" ops should not have
any return type in your implementation. We should be able to return the
error from this ops. From your driver you can always return the success from
"execute_tuning" ops which should solve your purpose. In case
"execute_tuning" returns error then "mmc_sd_init_uhs_card" would return
error which would result in removing the card.
So this way it should satisfy all the different implementations of
"execute_tuning" ops by different host controller drivers.
Please let me know if this is fine with you or not?
>
> Thanks,
> Arindam
>
> >
> >
> > >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > >
> > > > > Thanks,
> > > > > Arindam
> > > > >
> > > > > >
> > > > > >
> > > > > > > };
> > > > > > >
> > > > > > > struct mmc_card;
> > > > > > > diff --git a/include/linux/mmc/mmc.h
> > b/include/linux/mmc/mmc.h
> > > > > > > index 612301f..9194f9e 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 282d158..5203b97 100644
> > > > > > > --- a/include/linux/mmc/sdhci.h
> > > > > > > +++ b/include/linux/mmc/sdhci.h
> > > > > > > @@ -109,6 +109,7 @@ struct sdhci_host {
> > > > > > > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA
> > capable
> > > > > > */
> > > > > > > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this
> > > > > req. */
> > > > > > > #define SDHCI_DEVICE_DEAD (1<<3) /* Device
> > unresponsive
> > > > > */
> > > > > > > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50
> > needs
> > > tuning
> > > > > > */
> > > > > > >
> > > > > > > unsigned int version; /* SDHCI spec. version */
> > > > > > >
> > > > > > > @@ -147,6 +148,9 @@ struct sdhci_host {
> > > > > > >
> > > > > > > struct mmc_command *saved_abort_cmd; /* Abort
> > > command
> > > > saved
> > > > > > > for data error recovery */
> > > > > > >
> > > > > > > + 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
> > > > > >
> > > > > >
> > > > >
> > > > >
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe
> linux-
> > > mmc"
> > > > in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > info.html
> > > >
> > >
> >
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-23 6:26 ` Subhash Jadavani
@ 2011-03-23 6:35 ` Nath, Arindam
2011-03-23 7:23 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-23 6:35 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Wednesday, March 23, 2011 11:56 AM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Wednesday, March 23, 2011 11:48 AM
> > To: Subhash Jadavani; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode selection
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > Sent: Wednesday, March 23, 2011 11:44 AM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> speed
> > > mode selection
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > Sent: Wednesday, March 23, 2011 11:35 AM
> > > > To: Subhash Jadavani; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > speed
> > > > mode selection
> > > >
> > > > Hi Subhash,
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > Sent: Monday, March 21, 2011 12:13 PM
> > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > > speed
> > > > > mode selection
> > > > >
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf Of
> > > > Arindam
> > > > > > Nath
> > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > To: cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > henry.su@amd.com;
> > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > speed
> > > > mode
> > > > > > selection
> > > > > >
> > > > > > 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. Since MMC_TIMING_UHS_SDR25 is same
> as
> > > > > > MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD
> > flag
> > > > for
> > > > > > UHS SDR25 again. We also 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 set the clock frequency
> for
> > > the
> > > > > > UHS-I mode selected.
> > > > > >
> > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > ---
> > > > > > drivers/mmc/core/sd.c | 91
> > > > > > ++++++++++++++++++++++++++++++++++++++++++++++
> > > > > > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > > > > > drivers/mmc/host/sdhci.h | 5 +++
> > > > > > include/linux/mmc/card.h | 16 ++++++++
> > > > > > include/linux/mmc/host.h | 4 ++
> > > > > > 5 files changed, 146 insertions(+), 2 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > > > index f6a4fab..ec0d8e6 100644
> > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > @@ -464,6 +464,92 @@ static int sd_select_driver_type(struct
> > > > mmc_card
> > > > > > *card, u8 *status)
> > > > > > return 0;
> > > > > > }
> > > > > >
> > > > > > +static int sd_set_bus_speed_mode(struct mmc_card *card, u8
> > > > *status)
> > > > > > +{
> > > > > > + unsigned int bus_speed, timing;
> > > > > > + int err;
> > > > > > +
> > > > > > + /*
> > > > > > + * If the host doesn't support any of the UHS-I
> modes,
> > > fallback
> > > > > > on
> > > > > > + * default speed.
> > > > > > + */
> > > > > > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 |
> > > MMC_CAP_UHS_SDR25 |
> > > > > > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> > > MMC_CAP_UHS_DDR50)))
> > > > > > + return 0;
> > > > > > +
> > > > > > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > > > > > + if (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR104)
> > > {
> > > > > > + bus_speed = UHS_SDR104_BUS_SPEED;
> > > > > > + timing = MMC_TIMING_UHS_SDR104;
> > > > > > + card->sw_caps.uhs_max_dtr =
> > UHS_SDR104_MAX_DTR;
> > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR50)
> > > > > > {
> > > > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > > > + card->sw_caps.uhs_max_dtr =
> > UHS_SDR50_MAX_DTR;
> > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_DDR50)
> > > > > > {
> > > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > + card->sw_caps.uhs_max_dtr =
> > UHS_DDR50_MAX_DTR;
> > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR25)
> > > > > > {
> > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > + card->sw_caps.uhs_max_dtr =
> > UHS_SDR25_MAX_DTR;
> > > > > > + }
> > > > >
> > > > > I would not agree that if Host cap is set to MMC_CAP_UHS_SDR104
> > > > implies
> > > > > that
> > > > > DDR50 mode is supported by host. There may be a case where host
> > > > > controller
> > > > > only support SDR timing modes.
> > > > > Basically if host should advertise all the supported modes
> > (SDR104,
> > > > > SDR50,
> > > > > SDR25, SDR12 & DDR) by caps and then we should choose the
> highest
> > > > > performance mode depending on both the card and host's highest
> > > > > performance
> > > > > mode.
> > > > >
> > > > > So this should be the check:
> > > > >
> > > > > if ((card->host->caps & MMC_CAP_UHS_SDR104 &&
> > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
> > > > > bus_speed = UHS_SDR104_BUS_SPEED;
> > > > > timing = MMC_TIMING_UHS_SDR104;
> > > > > card->sw_caps.uhs_max_dtr =
> UHS_SDR104_MAX_DTR;
> > > > > } else if ((card->host->caps & MMC_CAP_UHS_DDR50 &&
> > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > > > > bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > timing = MMC_TIMING_UHS_DDR50;
> > > > > card->sw_caps.uhs_max_dtr =
> UHS_DDR50_MAX_DTR;
> > > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR50 &&
> > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> > > > > bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > timing = MMC_TIMING_UHS_SDR50;
> > > > > card->sw_caps.uhs_max_dtr =
> UHS_SDR50_MAX_DTR;
> > > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR25 &&
> > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > > > > bus_speed = UHS_SDR50_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_SDR12 &&
> > > > > (card->sw_caps.uhs_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;
> > > > > }
> > > >
> > > > A host capable of operating at a higher bus speed mode should be
> > able
> > > > to support cards with a lower bus speed mode. In case of your
> > > > conditions checks above, you are checking for the exact match for
> > the
> > > > bus speed modes, otherwise you will fall back on using default
> bus
> > > > speed mode, thus performance loss.
> > >
> > > Why should we implicitly assume that host supporting SDR104 mode
> > would
> > > also
> > > support DDR50 mode? Is there any spec. which says so? If not then
> we
> > > should
> > > rely on host capability to take the decision. DDR50 would require
> > > different
> > > timing implementation on the host side and host may choose not to
> > > implement
> > > DDR50 timing support then?
> > >
> > > I would agree that host supporting SDR104 mode should also be
> > > supporting
> > > SDR50/SDR25/SDR12 modes but it may not be true for DDR50 mode.
> >
> > Please refer to footnote of table 3-5 of the Physical Layer spec
> v3.01.
> > It says:
> >
> > "The card supports a UHS-I mode shall support all lower UHS-I modes."
> >
> > So if this is true for card, doesn't the same apply to host?
>
> Yes, this is the exact point I am stretching. Spec. never says the same
> thing for host so we should not assume it. we already have luxury to
> use
> host caps then why should we assume something which may not be correct?
I still think we should have a combination which works best based on the capabilities of the host and card, rather than falling back to default bus speed right away. I can try to separate out the case for DDR50, if you want that. Otherwise, I will wait for few more comments on the same.
Thanks,
Arindam
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > > >
> > > > Thanks,
> > > > Arindam
> > > >
> > > > >
> > > > > Regards,
> > > > > Subhash
> > > > >
> > > > >
> > > > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR104)
> > > ||
> > > > > > + (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR50))
> > > {
> > > > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > > > + card->sw_caps.uhs_max_dtr =
> > UHS_SDR50_MAX_DTR;
> > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_DDR50)
> > > > > > {
> > > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > + card->sw_caps.uhs_max_dtr =
> > UHS_DDR50_MAX_DTR;
> > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR25)
> > > > > > {
> > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > + card->sw_caps.uhs_max_dtr =
> > UHS_SDR25_MAX_DTR;
> > > > > > + }
> > > > > > + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR104)
> > > ||
> > > > > > + (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR50)
> > > ||
> > > > > > + (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_DDR50))
> > > {
> > > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > + card->sw_caps.uhs_max_dtr =
> > UHS_DDR50_MAX_DTR;
> > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR25)
> > > > > > {
> > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > + card->sw_caps.uhs_max_dtr =
> > UHS_SDR25_MAX_DTR;
> > > > > > + }
> > > > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR104)
> > > ||
> > > > > > + (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR50)
> > > ||
> > > > > > + (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_DDR50)
> > > ||
> > > > > > + (card->sw_caps.uhs_bus_mode &
> > SD_MODE_UHS_SDR25))
> > > {
> > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > + card->sw_caps.uhs_max_dtr =
> > UHS_SDR25_MAX_DTR;
> > > > > > + }
> > > > > > + }
> > > > > > +
> > > > > > + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> > > > > > + if (err)
> > > > > > + return err;
> > > > > > +
> > > > > > + if ((status[16] & 0xF) != bus_speed)
> > > > > > + printk(KERN_WARNING "%s: Problem setting bus
> > speed
> > > > > > mode!\n",
> > > > > > + mmc_hostname(card->host));
> > > > > > + else {
> > > > > > + if (bus_speed) {
> > > > > > + mmc_set_timing(card->host, timing);
> > > > > > + mmc_set_clock(card->host,
> > > > > card->sw_caps.uhs_max_dtr);
> > > > > > + }
> > > > > > + }
> > > > > > +
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > /*
> > > > > > * UHS-I specific initialization procedure
> > > > > > */
> > > > > > @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct
> > > > mmc_card
> > > > > > *card)
> > > > > >
> > > > > > /* Set the driver strength for the card */
> > > > > > err = sd_select_driver_type(card, status);
> > > > > > + if (err)
> > > > > > + goto out;
> > > > > > +
> > > > > > + /* Set bus speed mode of the card */
> > > > > > + err = sd_set_bus_speed_mode(card, status);
> > > > > >
> > > > > > out:
> > > > > > kfree(status);
> > > > > > diff --git a/drivers/mmc/host/sdhci.c
> > b/drivers/mmc/host/sdhci.c
> > > > > > index 5d3bb11..f127fa2 100644
> > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct
> > mmc_host
> > > > > *mmc,
> > > > > > struct mmc_ios *ios)
> > > > > > ctrl &= ~SDHCI_CTRL_HISPD;
> > > > > >
> > > > > > if (host->version >= SDHCI_SPEC_300) {
> > > > > > - u16 ctrl_2;
> > > > > > + u16 clk, ctrl_2;
> > > > > > +
> > > > > > + /* In case of UHS-I modes, set High Speed
> > Enable */
> > > > > > + if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
> > > > > > + (ios->timing == MMC_TIMING_UHS_SDR104) ||
> > > > > > + (ios->timing == MMC_TIMING_UHS_DDR50))
> > > > > > + ctrl |= SDHCI_CTRL_HISPD;
> > > > > >
> > > > > > ctrl_2 = sdhci_readw(host,
> > SDHCI_HOST_CONTROL2);
> > > > > > if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE))
> {
> > > > > > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct
> mmc_host
> > > > *mmc,
> > > > > > struct mmc_ios *ios)
> > > > > > * need to reset SD Clock Enable
> before
> > > changing
> > > > > High
> > > > > > * Speed Enable to avoid generating
> > clock
> > > gliches.
> > > > > > */
> > > > > > - u16 clk;
> > > > > >
> > > > > > /* Reset SD Clock Enable */
> > > > > > clk = sdhci_readw(host,
> > SDHCI_CLOCK_CONTROL);
> > > > > > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct
> > mmc_host
> > > > > *mmc,
> > > > > > struct mmc_ios *ios)
> > > > > > clk |= SDHCI_CLOCK_CARD_EN;
> > > > > > sdhci_writew(host, clk,
> > SDHCI_CLOCK_CONTROL);
> > > > > > }
> > > > > > +
> > > > > > + ctrl_2 = sdhci_readw(host,
> > SDHCI_HOST_CONTROL2);
> > > > > > +
> > > > > > + /* Select Bus Speed Mode for host */
> > > > > > + if (ios->timing == MMC_TIMING_UHS_SDR25)
> > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > > > > > + else if (ios->timing == MMC_TIMING_UHS_SDR50)
> > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > > > > > + else if (ios->timing ==
> MMC_TIMING_UHS_SDR104)
> > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> > > > > > + else if (ios->timing == MMC_TIMING_UHS_DDR50)
> > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > > > > > +
> > > > > > + /* Reset SD Clock Enable */
> > > > > > + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> > > > > > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > > > > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > > > > +
> > > > > > + sdhci_writew(host, ctrl_2,
> > SDHCI_HOST_CONTROL2);
> > > > > > +
> > > > > > + /* Re-enable SD Clock */
> > > > > > + clk |= SDHCI_CLOCK_CARD_EN;
> > > > > > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > > > > > } else
> > > > > > sdhci_writeb(host, ctrl,
> SDHCI_HOST_CONTROL1);
> > > > > >
> > > > > > diff --git a/drivers/mmc/host/sdhci.h
> > b/drivers/mmc/host/sdhci.h
> > > > > > index a407b5b..5bf244d 100644
> > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > @@ -150,6 +150,11 @@
> > > > > > #define SDHCI_ACMD12_ERR 0x3C
> > > > > >
> > > > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > > > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > > > > > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > > > > > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > > > > > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > > > > > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > > > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > > > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > > > > > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > > > > > diff --git a/include/linux/mmc/card.h
> > b/include/linux/mmc/card.h
> > > > > > index 2d7f7a3..0b24c41 100644
> > > > > > --- a/include/linux/mmc/card.h
> > > > > > +++ b/include/linux/mmc/card.h
> > > > > > @@ -75,7 +75,23 @@ struct sd_ssr {
> > > > > >
> > > > > > struct sd_switch_caps {
> > > > > > unsigned int hs_max_dtr;
> > > > > > + unsigned int uhs_max_dtr;
> > > > > > +#define UHS_SDR104_MAX_DTR 208000000
> > > > > > +#define UHS_SDR50_MAX_DTR 100000000
> > > > > > +#define UHS_DDR50_MAX_DTR 50000000
> > > > > > +#define UHS_SDR25_MAX_DTR 50000000
> > > > > > unsigned int uhs_bus_mode;
> > > > > > +#define UHS_SDR12_BUS_SPEED 0
> > > > > > +#define UHS_SDR25_BUS_SPEED 1
> > > > > > +#define UHS_SDR50_BUS_SPEED 2
> > > > > > +#define UHS_SDR104_BUS_SPEED 3
> > > > > > +#define UHS_DDR50_BUS_SPEED 4
> > > > > > +
> > > > > > +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> > > > > > +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> > > > > > +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> > > > > > +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> > > > > > +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> > > > > > unsigned int uhs_drv_type;
> > > > > > #define SD_DRIVER_TYPE_B 0x01
> > > > > > #define SD_DRIVER_TYPE_A 0x02
> > > > > > diff --git a/include/linux/mmc/host.h
> > b/include/linux/mmc/host.h
> > > > > > index bc2121e..4dfff6d 100644
> > > > > > --- a/include/linux/mmc/host.h
> > > > > > +++ b/include/linux/mmc/host.h
> > > > > > @@ -50,6 +50,10 @@ struct mmc_ios {
> > > > > > #define MMC_TIMING_LEGACY 0
> > > > > > #define MMC_TIMING_MMC_HS 1
> > > > > > #define MMC_TIMING_SD_HS 2
> > > > > > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > > > > > +#define MMC_TIMING_UHS_SDR50 3
> > > > > > +#define MMC_TIMING_UHS_SDR104 4
> > > > > > +#define MMC_TIMING_UHS_DDR50 5
> > > > > >
> > > > > > unsigned char ddr; /* dual data
> > rate used
> > > */
> > > > > >
> > > > > > --
> > > > > > 1.7.1
> > > > >
> > > > >
> > > >
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-23 6:34 ` Subhash Jadavani
@ 2011-03-23 6:41 ` Nath, Arindam
2011-03-23 6:45 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-23 6:41 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Wednesday, March 23, 2011 12:04 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Wednesday, March 23, 2011 11:53 AM
> > To: Subhash Jadavani; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> > uhs initialization
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > Sent: Wednesday, March 23, 2011 11:50 AM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> during
> > > uhs initialization
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > > > Sent: Wednesday, March 23, 2011 11:45 AM
> > > > To: Subhash Jadavani; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > during
> > > > uhs initialization
> > > >
> > > > Hi Subhash,
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > Sent: Wednesday, March 23, 2011 11:38 AM
> > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > during
> > > > > uhs initialization
> > > > >
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > Sent: Monday, March 21, 2011 4:37 PM
> > > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > > during
> > > > > > uhs initialization
> > > > > >
> > > > > > Hi Subhash,
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > > Sent: Monday, March 21, 2011 4:29 PM
> > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > > > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for
> tuning
> > > > > during
> > > > > > > uhs initialization
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > > -----Original Message-----
> > > > > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf
> > Of
> > > > > > Arindam
> > > > > > > > Nath
> > > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > > To: cjb@laptop.org
> > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > > henry.su@amd.com;
> > > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > > Subject: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > > during
> > > > > > uhs
> > > > > > > > initialization
> > > > > > > >
> > > > > > > > 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. 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 Seletc (bit 7) of Host Control2
> register
> > > to
> > > > 0.
> > > > > > > > Before exiting the tuning procedure, we disable Buffer
> Read
> > > > Ready
> > > > > > > > interrupt.
> > > > > > > >
> > > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > > ---
> > > > > > > > drivers/mmc/core/sd.c | 4 +
> > > > > > > > drivers/mmc/host/sdhci.c | 137
> > > > > > > > ++++++++++++++++++++++++++++++++++++++++++++-
> > > > > > > > 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, 149 insertions(+), 1 deletions(-)
> > > > > > > >
> > > > > > > > diff --git a/drivers/mmc/core/sd.c
> b/drivers/mmc/core/sd.c
> > > > > > > > index be01397..1e2d157 100644
> > > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > > @@ -637,6 +637,10 @@ static int
> mmc_sd_init_uhs_card(struct
> > > > > > mmc_card
> > > > > > > > *card)
> > > > > > > > /* Set current limit for the card */
> > > > > > > > err = sd_set_current_limit(card, status);
> > > > > > > >
> > > > > > > > + /* SPI mode doesn't define CMD19 */
> > > > > > > > + if (!mmc_host_is_spi(card->host) && card->host-
> > >ops-
> > > > > > > > >execute_tuning)
> > > > > > > > + 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 245cc39..8f4f102 100644
> > > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > > @@ -39,6 +39,8 @@
> > > > > > > > #define SDHCI_USE_LEDS_CLASS
> > > > > > > > #endif
> > > > > > > >
> > > > > > > > +#define MAX_TUNING_LOOP 40
> > > > > > > > +
> > > > > > > > static unsigned int debug_quirks = 0;
> > > > > > > >
> > > > > > > > static void sdhci_prepare_data(struct sdhci_host *,
> struct
> > > > > > mmc_data
> > > > > > > > *);
> > > > > > > > @@ -985,7 +987,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);
> > > > > > > > @@ -1485,6 +1489,119 @@ static int
> > > > > sdhci_get_max_current_180(struct
> > > > > > > > mmc_host *mmc)
> > > > > > > > return max_current_180;
> > > > > > > > }
> > > > > > > >
> > > > > > > > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > > > > > > > +{
> > > > > > > > + struct sdhci_host *host;
> > > > > > > > + u16 ctrl;
> > > > > > > > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > > > > > > > + unsigned long flags;
> > > > > > > > + unsigned long timeout;
> > > > > > > > +
> > > > > > > > + host = mmc_priv(mmc);
> > > > > > > > +
> > > > > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > > > > +
> > > > > > > > + 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_SDR104) ||
> > > > > > > > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > > > > > > > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > > > > > > > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > > > > > > > + else {
> > > > > > > > + spin_unlock_irqrestore(&host->lock,
> flags);
> > > > > > > > + return;
> > > > > > > > + }
> > > > > > > > +
> > > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > > +
> > > > > > > > + /*
> > > > > > > > + * As per the Host Controller spec v3.00, tuning
> > > > command
> > > > > > > > + * generates Buffer Read Ready interrupt, so
> enable
> > > > that.
> > > > > > > > + */
> > > > > > > > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > > > > +
> > > > > > > > + /*
> > > > > > > > + * Issue CMD19 repeatedly till Execute Tuning is
> > set
> > > to
> > > > 0
> > > > > or the
> > > > > > > > number
> > > > > > > > + * of loops reaches 40 times or a timeout of
> 150ms
> > > > occurs.
> > > > > > > > + */
> > > > > > > > + timeout = 150;
> > > > > > > > + do {
> > > > > > > > + 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;
> > > > > > > > + sdhci_send_command(host, &cmd);
> > > > > > > > +
> > > > > > > > + host->cmd = NULL;
> > > > > > > > + host->mrq = NULL;
> > > > > > > > +
> > > > > > > > + spin_unlock_irqrestore(&host->lock,
> flags);
> > > > > > > > +
> > > > > > > > + /* Wait for Buffer Read Ready interrupt
> */
> > > > > > > > + wait_event_interruptible_timeout(host-
> > > > >buf_ready_int,
> > > > > > > > + (host-
> >tuning_done
> > ==
> > > > 1),
> > > > > > > > +
> > > msecs_to_jiffies(50));
> > > > > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > > > > +
> > > > > > > > + if (!host->tuning_done) {
> > > > > > > > + printk(KERN_INFO DRIVER_NAME ":
> > > Tuning
> > > > > procedure"
> > > > > > > > + " failed, falling back to
> > > fixed
> > > > sampling"
> > > > > > > > + " clock\n");
> > > > > > > > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > > > > > > > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > > > > > > > + sdhci_writew(host, ctrl,
> > > > SDHCI_HOST_CONTROL2);
> > > > > > > > +
> > > > > > > > + 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");
> > > > > > > > + }
> > > > > > > > +
> > > > > > > > +out:
> > > > > > > > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > > > > .request = sdhci_request,
> > > > > > > > .set_ios = sdhci_set_ios,
> > > > > > > > @@ -1492,6 +1609,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,
> > > > > > > > .get_max_current_180 =
> > > > sdhci_get_max_current_180,
> > > > > > > > + .execute_tuning =
> > > sdhci_execute_tuning,
> > > > > > > > };
> > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > > > > *******\
> > > > > > > > @@ -1703,6 +1821,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
> > > > > > > > @@ -2126,6 +2254,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;
> > > > > > > > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > > > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > > > @@ -160,6 +160,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
> > > > > > > > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> > > > > > > >
> > > > > > > > int (*start_signal_voltage_switch)(struct
> > > mmc_host
> > > > > *host);
> > > > > > > > int (*get_max_current_180)(struct mmc_host
> > *mmc);
> > > > > > > > + void (*execute_tuning)(struct mmc_host *host);
> > > > > > >
> > > > > > >
> > > > > > > Why execute_tuning ops return type is void? Will there not
> be
> > > any
> > > > > > error
> > > > > > > scenarios? What if host fails to find any tuning point?
> > > Shouldn't
> > > > > it
> > > > > > > return
> > > > > > > error to the core layer and then core layer should not
> begin
> > > with
> > > > > the
> > > > > > > any
> > > > > > > read/write operations?
> > > > > >
> > > > > > In case the tuning procedure fails, Host Controller falls
> back
> > on
> > > > > using
> > > > > > Fixed Sampling clock, so data transfer can still continue.
> > > > > >
> > > > >
> > > > > I think that's not a correct assumption. Let's say tuning fails
> > and
> > > > > host
> > > > > controller falls back to fixed sampling clock and card still
> > > operates
> > > > > in
> > > > > SDR104 with CLK=208MHz. This means that all further read/write
> > > > > transactions
> > > > > will mainly result in CRC Fail errors and none of the
> > transactions
> > > > may
> > > > > be
> > > > > successful. So we can't just leave the host controller in state
> > > where
> > > > > it
> > > > > cannot perform high speed transfers (because it's tuning had
> > > failed)
> > > > > and
> > > > > then start issuing commands with high speed transfer conditions
> > > > > (CLK=208MHz). Better way is for host to inform core layer that
> > > tuning
> > > > > has
> > > > > failed and then there should not be any further transactions
> with
> > > the
> > > > > card
> > > > > or fallback to low speed modes (SDR12, SDR25 or SDR50) or power
> > > cycle
> > > > > the
> > > > > card.
> > > >
> > > > The host controller spec v3.00 clearly says that the occurrence
> of
> > an
> > > > error during tuning procedure is indicated by Sampling Select,
> > which
> > > > will be set to 0 in case tuning fails. But that won't stop the
> > > > controller to retry re-tuning procedure based on the re-tuning
> > mode.
> > > So
> > > > I don't see any reason for transaction to be stopped in case
> tuning
> > > > fails.
> > >
> > > This capability is not there with host controller on our chipsets.
> So
> > > then
> > > what to do? I think referring Host controller spec v3.00 should not
> > be
> > > proper here. As this spec is not a must condition for all host
> > > controller
> > > implementations.
> >
> > In that case, I would really appreciate if you could implement the
> > procedure which will work for your controller, and then we can merge
> > the code with mine.
>
> Ok that's fine. but that doesn't mean "execute_tuning" ops should not
> have
> any return type in your implementation. We should be able to return the
> error from this ops. From your driver you can always return the success
> from
> "execute_tuning" ops which should solve your purpose. In case
> "execute_tuning" returns error then "mmc_sd_init_uhs_card" would return
> error which would result in removing the card.
>
> So this way it should satisfy all the different implementations of
> "execute_tuning" ops by different host controller drivers.
>
> Please let me know if this is fine with you or not?
If you want me to return error the very first time tuning fails, and the card being removed, I am fine with that. But I am not sure if all the host controller would be happy with the implementation. Since some controller like ours would still want re-tuning to be tried the next time round. Anyways, I will implement your suggestion.
Thanks,
Arindam
>
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > >
> > > >
> > > > Thanks,
> > > > Arindam
> > > >
> > > > >
> > > > >
> > > > > > Thanks,
> > > > > > Arindam
> > > > > >
> > > > > > >
> > > > > > >
> > > > > > > > };
> > > > > > > >
> > > > > > > > struct mmc_card;
> > > > > > > > diff --git a/include/linux/mmc/mmc.h
> > > b/include/linux/mmc/mmc.h
> > > > > > > > index 612301f..9194f9e 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 282d158..5203b97 100644
> > > > > > > > --- a/include/linux/mmc/sdhci.h
> > > > > > > > +++ b/include/linux/mmc/sdhci.h
> > > > > > > > @@ -109,6 +109,7 @@ struct sdhci_host {
> > > > > > > > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA
> > > capable
> > > > > > > */
> > > > > > > > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for
> this
> > > > > > req. */
> > > > > > > > #define SDHCI_DEVICE_DEAD (1<<3) /* Device
> > > unresponsive
> > > > > > */
> > > > > > > > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50
> > > needs
> > > > tuning
> > > > > > > */
> > > > > > > >
> > > > > > > > unsigned int version; /* SDHCI spec. version */
> > > > > > > >
> > > > > > > > @@ -147,6 +148,9 @@ struct sdhci_host {
> > > > > > > >
> > > > > > > > struct mmc_command *saved_abort_cmd; /*
> Abort
> > > > command
> > > > > saved
> > > > > > > > for data error recovery */
> > > > > > > >
> > > > > > > > + 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
> > > > > > >
> > > > > > >
> > > > > >
> > > > > >
> > > > > > --
> > > > > > To unsubscribe from this list: send the line "unsubscribe
> > linux-
> > > > mmc"
> > > > > in
> > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > info.html
> > > > >
> > > >
> > >
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-23 6:41 ` Nath, Arindam
@ 2011-03-23 6:45 ` Subhash Jadavani
2011-03-23 6:48 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-23 6:45 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Nath, Arindam
> Sent: Wednesday, March 23, 2011 12:12 PM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Wednesday, March 23, 2011 12:04 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> > uhs initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > Sent: Wednesday, March 23, 2011 11:53 AM
> > > To: Subhash Jadavani; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> during
> > > uhs initialization
> > >
> > > Hi Subhash,
> > >
> > >
> > > > -----Original Message-----
> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > Sent: Wednesday, March 23, 2011 11:50 AM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > during
> > > > uhs initialization
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > > > > Sent: Wednesday, March 23, 2011 11:45 AM
> > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > during
> > > > > uhs initialization
> > > > >
> > > > > Hi Subhash,
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > Sent: Wednesday, March 23, 2011 11:38 AM
> > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > > during
> > > > > > uhs initialization
> > > > > >
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > > Sent: Monday, March 21, 2011 4:37 PM
> > > > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > > > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for
> tuning
> > > > > during
> > > > > > > uhs initialization
> > > > > > >
> > > > > > > Hi Subhash,
> > > > > > >
> > > > > > >
> > > > > > > > -----Original Message-----
> > > > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > > > Sent: Monday, March 21, 2011 4:29 PM
> > > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > anath.amd@gmail.com
> > > > > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for
> > tuning
> > > > > > during
> > > > > > > > uhs initialization
> > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > > -----Original Message-----
> > > > > > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On
> Behalf
> > > Of
> > > > > > > Arindam
> > > > > > > > > Nath
> > > > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > > > To: cjb@laptop.org
> > > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > > > henry.su@amd.com;
> > > > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > > > Subject: [PATCH v2 09/12] mmc: sd: add support for
> tuning
> > > > > during
> > > > > > > uhs
> > > > > > > > > initialization
> > > > > > > > >
> > > > > > > > > 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. 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 Seletc (bit 7) of Host Control2
> > register
> > > > to
> > > > > 0.
> > > > > > > > > Before exiting the tuning procedure, we disable Buffer
> > Read
> > > > > Ready
> > > > > > > > > interrupt.
> > > > > > > > >
> > > > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > > > ---
> > > > > > > > > drivers/mmc/core/sd.c | 4 +
> > > > > > > > > drivers/mmc/host/sdhci.c | 137
> > > > > > > > > ++++++++++++++++++++++++++++++++++++++++++++-
> > > > > > > > > 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, 149 insertions(+), 1 deletions(-)
> > > > > > > > >
> > > > > > > > > diff --git a/drivers/mmc/core/sd.c
> > b/drivers/mmc/core/sd.c
> > > > > > > > > index be01397..1e2d157 100644
> > > > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > > > @@ -637,6 +637,10 @@ static int
> > mmc_sd_init_uhs_card(struct
> > > > > > > mmc_card
> > > > > > > > > *card)
> > > > > > > > > /* Set current limit for the card */
> > > > > > > > > err = sd_set_current_limit(card, status);
> > > > > > > > >
> > > > > > > > > + /* SPI mode doesn't define CMD19 */
> > > > > > > > > + if (!mmc_host_is_spi(card->host) && card->host-
> > > >ops-
> > > > > > > > > >execute_tuning)
> > > > > > > > > + 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 245cc39..8f4f102 100644
> > > > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > > > @@ -39,6 +39,8 @@
> > > > > > > > > #define SDHCI_USE_LEDS_CLASS
> > > > > > > > > #endif
> > > > > > > > >
> > > > > > > > > +#define MAX_TUNING_LOOP 40
> > > > > > > > > +
> > > > > > > > > static unsigned int debug_quirks = 0;
> > > > > > > > >
> > > > > > > > > static void sdhci_prepare_data(struct sdhci_host *,
> > struct
> > > > > > > mmc_data
> > > > > > > > > *);
> > > > > > > > > @@ -985,7 +987,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);
> > > > > > > > > @@ -1485,6 +1489,119 @@ static int
> > > > > > sdhci_get_max_current_180(struct
> > > > > > > > > mmc_host *mmc)
> > > > > > > > > return max_current_180;
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > > > > > > > > +{
> > > > > > > > > + struct sdhci_host *host;
> > > > > > > > > + u16 ctrl;
> > > > > > > > > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > > > > > > > > + unsigned long flags;
> > > > > > > > > + unsigned long timeout;
> > > > > > > > > +
> > > > > > > > > + host = mmc_priv(mmc);
> > > > > > > > > +
> > > > > > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > > > > > +
> > > > > > > > > + 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_SDR104) ||
> > > > > > > > > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > > > > > > > > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > > > > > > > > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > > > > > > > > + else {
> > > > > > > > > + spin_unlock_irqrestore(&host->lock,
> > flags);
> > > > > > > > > + return;
> > > > > > > > > + }
> > > > > > > > > +
> > > > > > > > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > > > > > > > +
> > > > > > > > > + /*
> > > > > > > > > + * As per the Host Controller spec v3.00,
> tuning
> > > > > command
> > > > > > > > > + * generates Buffer Read Ready interrupt, so
> > enable
> > > > > that.
> > > > > > > > > + */
> > > > > > > > > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > > > > > +
> > > > > > > > > + /*
> > > > > > > > > + * Issue CMD19 repeatedly till Execute Tuning
> is
> > > set
> > > > to
> > > > > 0
> > > > > > or the
> > > > > > > > > number
> > > > > > > > > + * of loops reaches 40 times or a timeout of
> > 150ms
> > > > > occurs.
> > > > > > > > > + */
> > > > > > > > > + timeout = 150;
> > > > > > > > > + do {
> > > > > > > > > + 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;
> > > > > > > > > + sdhci_send_command(host, &cmd);
> > > > > > > > > +
> > > > > > > > > + host->cmd = NULL;
> > > > > > > > > + host->mrq = NULL;
> > > > > > > > > +
> > > > > > > > > + spin_unlock_irqrestore(&host->lock,
> > flags);
> > > > > > > > > +
> > > > > > > > > + /* Wait for Buffer Read Ready interrupt
> > */
> > > > > > > > > + wait_event_interruptible_timeout(host-
> > > > > >buf_ready_int,
> > > > > > > > > + (host-
> > >tuning_done
> > > ==
> > > > > 1),
> > > > > > > > > +
> > > > msecs_to_jiffies(50));
> > > > > > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > > > > > +
> > > > > > > > > + if (!host->tuning_done) {
> > > > > > > > > + printk(KERN_INFO DRIVER_NAME ":
> > > > Tuning
> > > > > > procedure"
> > > > > > > > > + " failed, falling back
> to
> > > > fixed
> > > > > sampling"
> > > > > > > > > + " clock\n");
> > > > > > > > > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > > > > > > > > + ctrl &=
> ~SDHCI_CTRL_EXEC_TUNING;
> > > > > > > > > + sdhci_writew(host, ctrl,
> > > > > SDHCI_HOST_CONTROL2);
> > > > > > > > > +
> > > > > > > > > + 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");
> > > > > > > > > + }
> > > > > > > > > +
> > > > > > > > > +out:
> > > > > > > > > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > > > > > .request = sdhci_request,
> > > > > > > > > .set_ios = sdhci_set_ios,
> > > > > > > > > @@ -1492,6 +1609,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,
> > > > > > > > > .get_max_current_180 =
> > > > > sdhci_get_max_current_180,
> > > > > > > > > + .execute_tuning =
> > > > sdhci_execute_tuning,
> > > > > > > > > };
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > > > > > *******\
> > > > > > > > > @@ -1703,6 +1821,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
> > > > > > > > > @@ -2126,6 +2254,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;
> > > > > > > > > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > > > > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > > > > @@ -160,6 +160,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
> > > > > > > > > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > > > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > > > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> > > > > > > > >
> > > > > > > > > int (*start_signal_voltage_switch)(struct
> > > > mmc_host
> > > > > > *host);
> > > > > > > > > int (*get_max_current_180)(struct mmc_host
> > > *mmc);
> > > > > > > > > + void (*execute_tuning)(struct mmc_host
> *host);
> > > > > > > >
> > > > > > > >
> > > > > > > > Why execute_tuning ops return type is void? Will there
> not
> > be
> > > > any
> > > > > > > error
> > > > > > > > scenarios? What if host fails to find any tuning point?
> > > > Shouldn't
> > > > > > it
> > > > > > > > return
> > > > > > > > error to the core layer and then core layer should not
> > begin
> > > > with
> > > > > > the
> > > > > > > > any
> > > > > > > > read/write operations?
> > > > > > >
> > > > > > > In case the tuning procedure fails, Host Controller falls
> > back
> > > on
> > > > > > using
> > > > > > > Fixed Sampling clock, so data transfer can still continue.
> > > > > > >
> > > > > >
> > > > > > I think that's not a correct assumption. Let's say tuning
> fails
> > > and
> > > > > > host
> > > > > > controller falls back to fixed sampling clock and card still
> > > > operates
> > > > > > in
> > > > > > SDR104 with CLK=208MHz. This means that all further
> read/write
> > > > > > transactions
> > > > > > will mainly result in CRC Fail errors and none of the
> > > transactions
> > > > > may
> > > > > > be
> > > > > > successful. So we can't just leave the host controller in
> state
> > > > where
> > > > > > it
> > > > > > cannot perform high speed transfers (because it's tuning had
> > > > failed)
> > > > > > and
> > > > > > then start issuing commands with high speed transfer
> conditions
> > > > > > (CLK=208MHz). Better way is for host to inform core layer
> that
> > > > tuning
> > > > > > has
> > > > > > failed and then there should not be any further transactions
> > with
> > > > the
> > > > > > card
> > > > > > or fallback to low speed modes (SDR12, SDR25 or SDR50) or
> power
> > > > cycle
> > > > > > the
> > > > > > card.
> > > > >
> > > > > The host controller spec v3.00 clearly says that the occurrence
> > of
> > > an
> > > > > error during tuning procedure is indicated by Sampling Select,
> > > which
> > > > > will be set to 0 in case tuning fails. But that won't stop the
> > > > > controller to retry re-tuning procedure based on the re-tuning
> > > mode.
> > > > So
> > > > > I don't see any reason for transaction to be stopped in case
> > tuning
> > > > > fails.
> > > >
> > > > This capability is not there with host controller on our
> chipsets.
> > So
> > > > then
> > > > what to do? I think referring Host controller spec v3.00 should
> not
> > > be
> > > > proper here. As this spec is not a must condition for all host
> > > > controller
> > > > implementations.
> > >
> > > In that case, I would really appreciate if you could implement the
> > > procedure which will work for your controller, and then we can
> merge
> > > the code with mine.
> >
> > Ok that's fine. but that doesn't mean "execute_tuning" ops should not
> > have
> > any return type in your implementation. We should be able to return
> the
> > error from this ops. From your driver you can always return the
> success
> > from
> > "execute_tuning" ops which should solve your purpose. In case
> > "execute_tuning" returns error then "mmc_sd_init_uhs_card" would
> return
> > error which would result in removing the card.
> >
> > So this way it should satisfy all the different implementations of
> > "execute_tuning" ops by different host controller drivers.
> >
> > Please let me know if this is fine with you or not?
>
> If you want me to return error the very first time tuning fails, and
> the card being removed, I am fine with that. But I am not sure if all
> the host controller would be happy with the implementation. Since some
> controller like ours would still want re-tuning to be tried the next
> time round. Anyways, I will implement your suggestion.
Those who want the retuning be tried again at latter point of time, can
return success from "execute_tuning".
>
> Thanks,
> Arindam
>
> >
> >
> > >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > >
> > > > >
> > > > > Thanks,
> > > > > Arindam
> > > > >
> > > > > >
> > > > > >
> > > > > > > Thanks,
> > > > > > > Arindam
> > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > > };
> > > > > > > > >
> > > > > > > > > struct mmc_card;
> > > > > > > > > diff --git a/include/linux/mmc/mmc.h
> > > > b/include/linux/mmc/mmc.h
> > > > > > > > > index 612301f..9194f9e 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 282d158..5203b97 100644
> > > > > > > > > --- a/include/linux/mmc/sdhci.h
> > > > > > > > > +++ b/include/linux/mmc/sdhci.h
> > > > > > > > > @@ -109,6 +109,7 @@ struct sdhci_host {
> > > > > > > > > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA
> > > > capable
> > > > > > > > */
> > > > > > > > > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for
> > this
> > > > > > > req. */
> > > > > > > > > #define SDHCI_DEVICE_DEAD (1<<3) /* Device
> > > > unresponsive
> > > > > > > */
> > > > > > > > > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /*
> SDR50
> > > > needs
> > > > > tuning
> > > > > > > > */
> > > > > > > > >
> > > > > > > > > unsigned int version; /* SDHCI spec. version
> */
> > > > > > > > >
> > > > > > > > > @@ -147,6 +148,9 @@ struct sdhci_host {
> > > > > > > > >
> > > > > > > > > struct mmc_command *saved_abort_cmd; /*
> > Abort
> > > > > command
> > > > > > saved
> > > > > > > > > for data error recovery */
> > > > > > > > >
> > > > > > > > > + 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
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > --
> > > > > > > To unsubscribe from this list: send the line "unsubscribe
> > > linux-
> > > > > mmc"
> > > > > > in
> > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > > info.html
> > > > > >
> > > > >
> > > >
> > > >
> > >
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-23 6:45 ` Subhash Jadavani
@ 2011-03-23 6:48 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-23 6:48 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Wednesday, March 23, 2011 12:15 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
>
>
> > -----Original Message-----
> > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > Sent: Wednesday, March 23, 2011 12:12 PM
> > To: Subhash Jadavani; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> > uhs initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > Sent: Wednesday, March 23, 2011 12:04 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> during
> > > uhs initialization
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > Sent: Wednesday, March 23, 2011 11:53 AM
> > > > To: Subhash Jadavani; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > during
> > > > uhs initialization
> > > >
> > > > Hi Subhash,
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > Sent: Wednesday, March 23, 2011 11:50 AM
> > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > during
> > > > > uhs initialization
> > > > >
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > > > > > Sent: Wednesday, March 23, 2011 11:45 AM
> > > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning
> > > > during
> > > > > > uhs initialization
> > > > > >
> > > > > > Hi Subhash,
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > > Sent: Wednesday, March 23, 2011 11:38 AM
> > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > > > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for
> tuning
> > > > > during
> > > > > > > uhs initialization
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > > -----Original Message-----
> > > > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > > > Sent: Monday, March 21, 2011 4:37 PM
> > > > > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > anath.amd@gmail.com
> > > > > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for
> > tuning
> > > > > > during
> > > > > > > > uhs initialization
> > > > > > > >
> > > > > > > > Hi Subhash,
> > > > > > > >
> > > > > > > >
> > > > > > > > > -----Original Message-----
> > > > > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > > > > Sent: Monday, March 21, 2011 4:29 PM
> > > > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > > anath.amd@gmail.com
> > > > > > > > > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for
> > > tuning
> > > > > > > during
> > > > > > > > > uhs initialization
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > > -----Original Message-----
> > > > > > > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On
> > Behalf
> > > > Of
> > > > > > > > Arindam
> > > > > > > > > > Nath
> > > > > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > > > > To: cjb@laptop.org
> > > > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > > > > henry.su@amd.com;
> > > > > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > > > > Subject: [PATCH v2 09/12] mmc: sd: add support for
> > tuning
> > > > > > during
> > > > > > > > uhs
> > > > > > > > > > initialization
> > > > > > > > > >
> > > > > > > > > > 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. 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 Seletc (bit 7) of Host Control2
> > > register
> > > > > to
> > > > > > 0.
> > > > > > > > > > Before exiting the tuning procedure, we disable
> Buffer
> > > Read
> > > > > > Ready
> > > > > > > > > > interrupt.
> > > > > > > > > >
> > > > > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > > > > ---
> > > > > > > > > > drivers/mmc/core/sd.c | 4 +
> > > > > > > > > > drivers/mmc/host/sdhci.c | 137
> > > > > > > > > > ++++++++++++++++++++++++++++++++++++++++++++-
> > > > > > > > > > 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, 149 insertions(+), 1 deletions(-)
> > > > > > > > > >
> > > > > > > > > > diff --git a/drivers/mmc/core/sd.c
> > > b/drivers/mmc/core/sd.c
> > > > > > > > > > index be01397..1e2d157 100644
> > > > > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > > > > @@ -637,6 +637,10 @@ static int
> > > mmc_sd_init_uhs_card(struct
> > > > > > > > mmc_card
> > > > > > > > > > *card)
> > > > > > > > > > /* Set current limit for the card */
> > > > > > > > > > err = sd_set_current_limit(card, status);
> > > > > > > > > >
> > > > > > > > > > + /* SPI mode doesn't define CMD19 */
> > > > > > > > > > + if (!mmc_host_is_spi(card->host) && card-
> >host-
> > > > >ops-
> > > > > > > > > > >execute_tuning)
> > > > > > > > > > + 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 245cc39..8f4f102 100644
> > > > > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > > > > @@ -39,6 +39,8 @@
> > > > > > > > > > #define SDHCI_USE_LEDS_CLASS
> > > > > > > > > > #endif
> > > > > > > > > >
> > > > > > > > > > +#define MAX_TUNING_LOOP 40
> > > > > > > > > > +
> > > > > > > > > > static unsigned int debug_quirks = 0;
> > > > > > > > > >
> > > > > > > > > > static void sdhci_prepare_data(struct sdhci_host *,
> > > struct
> > > > > > > > mmc_data
> > > > > > > > > > *);
> > > > > > > > > > @@ -985,7 +987,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);
> > > > > > > > > > @@ -1485,6 +1489,119 @@ static int
> > > > > > > sdhci_get_max_current_180(struct
> > > > > > > > > > mmc_host *mmc)
> > > > > > > > > > return max_current_180;
> > > > > > > > > > }
> > > > > > > > > >
> > > > > > > > > > +static void sdhci_execute_tuning(struct mmc_host
> *mmc)
> > > > > > > > > > +{
> > > > > > > > > > + struct sdhci_host *host;
> > > > > > > > > > + u16 ctrl;
> > > > > > > > > > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > > > > > > > > > + unsigned long flags;
> > > > > > > > > > + unsigned long timeout;
> > > > > > > > > > +
> > > > > > > > > > + host = mmc_priv(mmc);
> > > > > > > > > > +
> > > > > > > > > > + spin_lock_irqsave(&host->lock, flags);
> > > > > > > > > > +
> > > > > > > > > > + 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_SDR104) ||
> > > > > > > > > > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > > > > > > > > > + (host->flags &
> SDHCI_SDR50_NEEDS_TUNING)))
> > > > > > > > > > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > > > > > > > > > + else {
> > > > > > > > > > + spin_unlock_irqrestore(&host->lock,
> > > flags);
> > > > > > > > > > + return;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + sdhci_writew(host, ctrl,
> SDHCI_HOST_CONTROL2);
> > > > > > > > > > +
> > > > > > > > > > + /*
> > > > > > > > > > + * As per the Host Controller spec v3.00,
> > tuning
> > > > > > command
> > > > > > > > > > + * generates Buffer Read Ready interrupt, so
> > > enable
> > > > > > that.
> > > > > > > > > > + */
> > > > > > > > > > + sdhci_unmask_irqs(host,
> SDHCI_INT_DATA_AVAIL);
> > > > > > > > > > +
> > > > > > > > > > + /*
> > > > > > > > > > + * Issue CMD19 repeatedly till Execute Tuning
> > is
> > > > set
> > > > > to
> > > > > > 0
> > > > > > > or the
> > > > > > > > > > number
> > > > > > > > > > + * of loops reaches 40 times or a timeout of
> > > 150ms
> > > > > > occurs.
> > > > > > > > > > + */
> > > > > > > > > > + timeout = 150;
> > > > > > > > > > + do {
> > > > > > > > > > + 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;
> > > > > > > > > > + sdhci_send_command(host, &cmd);
> > > > > > > > > > +
> > > > > > > > > > + host->cmd = NULL;
> > > > > > > > > > + host->mrq = NULL;
> > > > > > > > > > +
> > > > > > > > > > + spin_unlock_irqrestore(&host->lock,
> > > flags);
> > > > > > > > > > +
> > > > > > > > > > + /* Wait for Buffer Read Ready
> interrupt
> > > */
> > > > > > > > > > +
> wait_event_interruptible_timeout(host-
> > > > > > >buf_ready_int,
> > > > > > > > > > + (host-
> > > >tuning_done
> > > > ==
> > > > > > 1),
> > > > > > > > > > +
> > > > > msecs_to_jiffies(50));
> > > > > > > > > > + spin_lock_irqsave(&host->lock,
> flags);
> > > > > > > > > > +
> > > > > > > > > > + if (!host->tuning_done) {
> > > > > > > > > > + printk(KERN_INFO DRIVER_NAME
> ":
> > > > > Tuning
> > > > > > > procedure"
> > > > > > > > > > + " failed, falling
> back
> > to
> > > > > fixed
> > > > > > sampling"
> > > > > > > > > > + " clock\n");
> > > > > > > > > > + ctrl &=
> ~SDHCI_CTRL_TUNED_CLK;
> > > > > > > > > > + ctrl &=
> > ~SDHCI_CTRL_EXEC_TUNING;
> > > > > > > > > > + sdhci_writew(host, ctrl,
> > > > > > SDHCI_HOST_CONTROL2);
> > > > > > > > > > +
> > > > > > > > > > + 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");
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > +out:
> > > > > > > > > > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > > > > > > > > + spin_unlock_irqrestore(&host->lock, flags);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > static const struct mmc_host_ops sdhci_ops = {
> > > > > > > > > > .request = sdhci_request,
> > > > > > > > > > .set_ios = sdhci_set_ios,
> > > > > > > > > > @@ -1492,6 +1609,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,
> > > > > > > > > > .get_max_current_180 =
> > > > > > sdhci_get_max_current_180,
> > > > > > > > > > + .execute_tuning =
> > > > > sdhci_execute_tuning,
> > > > > > > > > > };
> > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
> /**********************************************************************
> > > > > > > > > > *******\
> > > > > > > > > > @@ -1703,6 +1821,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
> > > > > > > > > > @@ -2126,6 +2254,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;
> > > > > > > > > > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > > > > > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > > > > > @@ -160,6 +160,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
> > > > > > > > > > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > > > > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > > > > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> > > > > > > > > >
> > > > > > > > > > int (*start_signal_voltage_switch)(struct
> > > > > mmc_host
> > > > > > > *host);
> > > > > > > > > > int (*get_max_current_180)(struct
> mmc_host
> > > > *mmc);
> > > > > > > > > > + void (*execute_tuning)(struct mmc_host
> > *host);
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > Why execute_tuning ops return type is void? Will there
> > not
> > > be
> > > > > any
> > > > > > > > error
> > > > > > > > > scenarios? What if host fails to find any tuning point?
> > > > > Shouldn't
> > > > > > > it
> > > > > > > > > return
> > > > > > > > > error to the core layer and then core layer should not
> > > begin
> > > > > with
> > > > > > > the
> > > > > > > > > any
> > > > > > > > > read/write operations?
> > > > > > > >
> > > > > > > > In case the tuning procedure fails, Host Controller falls
> > > back
> > > > on
> > > > > > > using
> > > > > > > > Fixed Sampling clock, so data transfer can still
> continue.
> > > > > > > >
> > > > > > >
> > > > > > > I think that's not a correct assumption. Let's say tuning
> > fails
> > > > and
> > > > > > > host
> > > > > > > controller falls back to fixed sampling clock and card
> still
> > > > > operates
> > > > > > > in
> > > > > > > SDR104 with CLK=208MHz. This means that all further
> > read/write
> > > > > > > transactions
> > > > > > > will mainly result in CRC Fail errors and none of the
> > > > transactions
> > > > > > may
> > > > > > > be
> > > > > > > successful. So we can't just leave the host controller in
> > state
> > > > > where
> > > > > > > it
> > > > > > > cannot perform high speed transfers (because it's tuning
> had
> > > > > failed)
> > > > > > > and
> > > > > > > then start issuing commands with high speed transfer
> > conditions
> > > > > > > (CLK=208MHz). Better way is for host to inform core layer
> > that
> > > > > tuning
> > > > > > > has
> > > > > > > failed and then there should not be any further
> transactions
> > > with
> > > > > the
> > > > > > > card
> > > > > > > or fallback to low speed modes (SDR12, SDR25 or SDR50) or
> > power
> > > > > cycle
> > > > > > > the
> > > > > > > card.
> > > > > >
> > > > > > The host controller spec v3.00 clearly says that the
> occurrence
> > > of
> > > > an
> > > > > > error during tuning procedure is indicated by Sampling
> Select,
> > > > which
> > > > > > will be set to 0 in case tuning fails. But that won't stop
> the
> > > > > > controller to retry re-tuning procedure based on the re-
> tuning
> > > > mode.
> > > > > So
> > > > > > I don't see any reason for transaction to be stopped in case
> > > tuning
> > > > > > fails.
> > > > >
> > > > > This capability is not there with host controller on our
> > chipsets.
> > > So
> > > > > then
> > > > > what to do? I think referring Host controller spec v3.00 should
> > not
> > > > be
> > > > > proper here. As this spec is not a must condition for all host
> > > > > controller
> > > > > implementations.
> > > >
> > > > In that case, I would really appreciate if you could implement
> the
> > > > procedure which will work for your controller, and then we can
> > merge
> > > > the code with mine.
> > >
> > > Ok that's fine. but that doesn't mean "execute_tuning" ops should
> not
> > > have
> > > any return type in your implementation. We should be able to return
> > the
> > > error from this ops. From your driver you can always return the
> > success
> > > from
> > > "execute_tuning" ops which should solve your purpose. In case
> > > "execute_tuning" returns error then "mmc_sd_init_uhs_card" would
> > return
> > > error which would result in removing the card.
> > >
> > > So this way it should satisfy all the different implementations of
> > > "execute_tuning" ops by different host controller drivers.
> > >
> > > Please let me know if this is fine with you or not?
> >
> > If you want me to return error the very first time tuning fails, and
> > the card being removed, I am fine with that. But I am not sure if all
> > the host controller would be happy with the implementation. Since
> some
> > controller like ours would still want re-tuning to be tried the next
> > time round. Anyways, I will implement your suggestion.
>
> Those who want the retuning be tried again at latter point of time, can
> return success from "execute_tuning".
I think this can be done. I will modify the code accordingly.
Thanks,
Arindam
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > >
> > > >
> > > > Thanks,
> > > > Arindam
> > > >
> > > > >
> > > > >
> > > > > >
> > > > > > Thanks,
> > > > > > Arindam
> > > > > >
> > > > > > >
> > > > > > >
> > > > > > > > Thanks,
> > > > > > > > Arindam
> > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > > };
> > > > > > > > > >
> > > > > > > > > > struct mmc_card;
> > > > > > > > > > diff --git a/include/linux/mmc/mmc.h
> > > > > b/include/linux/mmc/mmc.h
> > > > > > > > > > index 612301f..9194f9e 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 282d158..5203b97 100644
> > > > > > > > > > --- a/include/linux/mmc/sdhci.h
> > > > > > > > > > +++ b/include/linux/mmc/sdhci.h
> > > > > > > > > > @@ -109,6 +109,7 @@ struct sdhci_host {
> > > > > > > > > > #define SDHCI_USE_ADMA (1<<1) /* Host is
> ADMA
> > > > > capable
> > > > > > > > > */
> > > > > > > > > > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA
> for
> > > this
> > > > > > > > req. */
> > > > > > > > > > #define SDHCI_DEVICE_DEAD (1<<3) /* Device
> > > > > unresponsive
> > > > > > > > */
> > > > > > > > > > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /*
> > SDR50
> > > > > needs
> > > > > > tuning
> > > > > > > > > */
> > > > > > > > > >
> > > > > > > > > > unsigned int version; /* SDHCI spec.
> version
> > */
> > > > > > > > > >
> > > > > > > > > > @@ -147,6 +148,9 @@ struct sdhci_host {
> > > > > > > > > >
> > > > > > > > > > struct mmc_command *saved_abort_cmd; /*
> > > Abort
> > > > > > command
> > > > > > > saved
> > > > > > > > > > for data error recovery */
> > > > > > > > > >
> > > > > > > > > > + 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
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > --
> > > > > > > > To unsubscribe from this list: send the line "unsubscribe
> > > > linux-
> > > > > > mmc"
> > > > > > > in
> > > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > > > info.html
> > > > > > >
> > > > > >
> > > > >
> > > > >
> > > >
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-mmc"
> in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization
2011-03-21 9:50 ` Nath, Arindam
@ 2011-03-23 6:58 ` Subhash Jadavani
0 siblings, 0 replies; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-23 6:58 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Monday, March 21, 2011 3:21 PM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs initialization
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Monday, March 21, 2011 3:14 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 09/12] mmc: sd: add support for tuning during
> > uhs initialization
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Arindam Nath
> > > Sent: Friday, March 04, 2011 5:03 PM
> > > To: cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> henry.su@amd.com;
> > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > Subject: [PATCH v2 09/12] mmc: sd: add support for tuning during
> uhs
> > > initialization
> > >
> > > 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. 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 Seletc (bit 7) of Host Control2 register to 0.
> > > Before exiting the tuning procedure, we disable Buffer Read Ready
> > > interrupt.
> > >
> > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > ---
> > > drivers/mmc/core/sd.c | 4 +
> > > drivers/mmc/host/sdhci.c | 137
> > > ++++++++++++++++++++++++++++++++++++++++++++-
> > > 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, 149 insertions(+), 1 deletions(-)
> > >
> > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > index be01397..1e2d157 100644
> > > --- a/drivers/mmc/core/sd.c
> > > +++ b/drivers/mmc/core/sd.c
> > > @@ -637,6 +637,10 @@ static int mmc_sd_init_uhs_card(struct
> mmc_card
> > > *card)
> > > /* Set current limit for the card */
> > > err = sd_set_current_limit(card, status);
> > >
> > > + /* SPI mode doesn't define CMD19 */
> > > + if (!mmc_host_is_spi(card->host) && card->host->ops-
> > > >execute_tuning)
> > > + card->host->ops->execute_tuning(card->host);
> >
> > As you have mentioned, Host Controller needs tuning only in case of
> > SDR104
> > mode and for SDR50 mode. This means we should not call execute_tuning
> > ops if
> > bus speed mode is other than SD104/SDR50. Host driver should not be
> > responsible for putting the check if the current bus speed mode is
> > SDR104/SDR50 before start tuning.
>
> But since tuning procedure needs to be performed by the Host Controller
> itself, isn't it better for the controller itself to check which bus
> speed mode it is operating on currently before deciding on whether to
> carry on tuning or not?
Hmmm. Yes, this is definitely arguable from both side. Core layer also knows
which bus speed mode it is operating and putting check in core layer means
host controller driver just need to execute tuning without checking
anything. But yes, it's up to you then which you feel is better.
>
> Thanks,
> Arindam
>
> >
> > > +
> > > out:
> > > kfree(status);
> > >
> > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > > index 245cc39..8f4f102 100644
> > > --- a/drivers/mmc/host/sdhci.c
> > > +++ b/drivers/mmc/host/sdhci.c
> > > @@ -39,6 +39,8 @@
> > > #define SDHCI_USE_LEDS_CLASS
> > > #endif
> > >
> > > +#define MAX_TUNING_LOOP 40
> > > +
> > > static unsigned int debug_quirks = 0;
> > >
> > > static void sdhci_prepare_data(struct sdhci_host *, struct
> mmc_data
> > > *);
> > > @@ -985,7 +987,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);
> > > @@ -1485,6 +1489,119 @@ static int sdhci_get_max_current_180(struct
> > > mmc_host *mmc)
> > > return max_current_180;
> > > }
> > >
> > > +static void sdhci_execute_tuning(struct mmc_host *mmc)
> > > +{
> > > + struct sdhci_host *host;
> > > + u16 ctrl;
> > > + int tuning_loop_counter = MAX_TUNING_LOOP;
> > > + unsigned long flags;
> > > + unsigned long timeout;
> > > +
> > > + host = mmc_priv(mmc);
> > > +
> > > + spin_lock_irqsave(&host->lock, flags);
> > > +
> > > + 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_SDR104) ||
> > > + ((ctrl & SDHCI_CTRL_UHS_SDR50) &&
> > > + (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> > > + ctrl |= SDHCI_CTRL_EXEC_TUNING;
> > > + else {
> > > + spin_unlock_irqrestore(&host->lock, flags);
> > > + return;
> > > + }
> > > +
> > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > +
> > > + /*
> > > + * As per the Host Controller spec v3.00, tuning command
> > > + * generates Buffer Read Ready interrupt, so enable that.
> > > + */
> > > + sdhci_unmask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > +
> > > + /*
> > > + * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the
> > > number
> > > + * of loops reaches 40 times or a timeout of 150ms occurs.
> > > + */
> > > + timeout = 150;
> > > + do {
> > > + 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;
> > > + sdhci_send_command(host, &cmd);
> > > +
> > > + host->cmd = NULL;
> > > + host->mrq = NULL;
> > > +
> > > + spin_unlock_irqrestore(&host->lock, flags);
> > > +
> > > + /* Wait for Buffer Read Ready interrupt */
> > > + wait_event_interruptible_timeout(host->buf_ready_int,
> > > + (host->tuning_done == 1),
> > > + msecs_to_jiffies(50));
> > > + spin_lock_irqsave(&host->lock, flags);
> > > +
> > > + if (!host->tuning_done) {
> > > + printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
> > > + " failed, falling back to fixed sampling"
> > > + " clock\n");
> > > + ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> > > + ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
> > > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> > > +
> > > + 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");
> > > + }
> > > +
> > > +out:
> > > + sdhci_mask_irqs(host, SDHCI_INT_DATA_AVAIL);
> > > + spin_unlock_irqrestore(&host->lock, flags);
> > > +}
> > > +
> > > static const struct mmc_host_ops sdhci_ops = {
> > > .request = sdhci_request,
> > > .set_ios = sdhci_set_ios,
> > > @@ -1492,6 +1609,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,
> > > .get_max_current_180 = sdhci_get_max_current_180,
> > > + .execute_tuning = sdhci_execute_tuning,
> > > };
> > >
> > >
> > >
> >
> /**********************************************************************
> > > *******\
> > > @@ -1703,6 +1821,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
> > > @@ -2126,6 +2254,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;
> > > @@ -2269,6 +2401,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 5bf244d..4746879 100644
> > > --- a/drivers/mmc/host/sdhci.h
> > > +++ b/drivers/mmc/host/sdhci.h
> > > @@ -160,6 +160,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
> > > @@ -187,6 +189,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 e84cd05..651e40b 100644
> > > --- a/include/linux/mmc/host.h
> > > +++ b/include/linux/mmc/host.h
> > > @@ -129,6 +129,7 @@ struct mmc_host_ops {
> > >
> > > int (*start_signal_voltage_switch)(struct mmc_host *host);
> > > int (*get_max_current_180)(struct mmc_host *mmc);
> > > + void (*execute_tuning)(struct mmc_host *host);
> > > };
> > >
> > > struct mmc_card;
> > > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> > > index 612301f..9194f9e 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 282d158..5203b97 100644
> > > --- a/include/linux/mmc/sdhci.h
> > > +++ b/include/linux/mmc/sdhci.h
> > > @@ -109,6 +109,7 @@ struct sdhci_host {
> > > #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable
> > */
> > > #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this
> req. */
> > > #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive
> */
> > > +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning
> > */
> > >
> > > unsigned int version; /* SDHCI spec. version */
> > >
> > > @@ -147,6 +148,9 @@ struct sdhci_host {
> > >
> > > struct mmc_command *saved_abort_cmd; /* Abort command saved
> > > for data error recovery */
> > >
> > > + 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
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-23 6:35 ` Nath, Arindam
@ 2011-03-23 7:23 ` Subhash Jadavani
2011-03-23 14:02 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-23 7:23 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Wednesday, March 23, 2011 12:06 PM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Wednesday, March 23, 2011 11:56 AM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode selection
> >
> >
> >
> > > -----Original Message-----
> > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > Sent: Wednesday, March 23, 2011 11:48 AM
> > > To: Subhash Jadavani; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> speed
> > > mode selection
> > >
> > > Hi Subhash,
> > >
> > >
> > > > -----Original Message-----
> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > Sent: Wednesday, March 23, 2011 11:44 AM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > speed
> > > > mode selection
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > Sent: Wednesday, March 23, 2011 11:35 AM
> > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > > speed
> > > > > mode selection
> > > > >
> > > > > Hi Subhash,
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > Sent: Monday, March 21, 2011 12:13 PM
> > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs
> bus
> > > > speed
> > > > > > mode selection
> > > > > >
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf
> Of
> > > > > Arindam
> > > > > > > Nath
> > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > To: cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > henry.su@amd.com;
> > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > > speed
> > > > > mode
> > > > > > > selection
> > > > > > >
> > > > > > > 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. Since MMC_TIMING_UHS_SDR25 is same
> > as
> > > > > > > MMC_TIMING_SD_HS, we don't need to set the SDHCI_CTRL_HISPD
> > > flag
> > > > > for
> > > > > > > UHS SDR25 again. We also 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 set the clock frequency
> > for
> > > > the
> > > > > > > UHS-I mode selected.
> > > > > > >
> > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > ---
> > > > > > > drivers/mmc/core/sd.c | 91
> > > > > > > ++++++++++++++++++++++++++++++++++++++++++++++
> > > > > > > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > > > > > > drivers/mmc/host/sdhci.h | 5 +++
> > > > > > > include/linux/mmc/card.h | 16 ++++++++
> > > > > > > include/linux/mmc/host.h | 4 ++
> > > > > > > 5 files changed, 146 insertions(+), 2 deletions(-)
> > > > > > >
> > > > > > > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > > > > > > index f6a4fab..ec0d8e6 100644
> > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > @@ -464,6 +464,92 @@ static int
> sd_select_driver_type(struct
> > > > > mmc_card
> > > > > > > *card, u8 *status)
> > > > > > > return 0;
> > > > > > > }
> > > > > > >
> > > > > > > +static int sd_set_bus_speed_mode(struct mmc_card *card, u8
> > > > > *status)
> > > > > > > +{
> > > > > > > + unsigned int bus_speed, timing;
> > > > > > > + int err;
> > > > > > > +
> > > > > > > + /*
> > > > > > > + * If the host doesn't support any of the UHS-I
> > modes,
> > > > fallback
> > > > > > > on
> > > > > > > + * default speed.
> > > > > > > + */
> > > > > > > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 |
> > > > MMC_CAP_UHS_SDR25 |
> > > > > > > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> > > > MMC_CAP_UHS_DDR50)))
> > > > > > > + return 0;
> > > > > > > +
> > > > > > > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > > > > > > + if (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR104)
> > > > {
> > > > > > > + bus_speed = UHS_SDR104_BUS_SPEED;
> > > > > > > + timing = MMC_TIMING_UHS_SDR104;
> > > > > > > + card->sw_caps.uhs_max_dtr =
> > > UHS_SDR104_MAX_DTR;
> > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR50)
> > > > > > > {
> > > > > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > > > > + card->sw_caps.uhs_max_dtr =
> > > UHS_SDR50_MAX_DTR;
> > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_DDR50)
> > > > > > > {
> > > > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > > + card->sw_caps.uhs_max_dtr =
> > > UHS_DDR50_MAX_DTR;
> > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR25)
> > > > > > > {
> > > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > + card->sw_caps.uhs_max_dtr =
> > > UHS_SDR25_MAX_DTR;
> > > > > > > + }
> > > > > >
> > > > > > I would not agree that if Host cap is set to
> MMC_CAP_UHS_SDR104
> > > > > implies
> > > > > > that
> > > > > > DDR50 mode is supported by host. There may be a case where
> host
> > > > > > controller
> > > > > > only support SDR timing modes.
> > > > > > Basically if host should advertise all the supported modes
> > > (SDR104,
> > > > > > SDR50,
> > > > > > SDR25, SDR12 & DDR) by caps and then we should choose the
> > highest
> > > > > > performance mode depending on both the card and host's
> highest
> > > > > > performance
> > > > > > mode.
> > > > > >
> > > > > > So this should be the check:
> > > > > >
> > > > > > if ((card->host->caps & MMC_CAP_UHS_SDR104 &&
> > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
> > > > > > bus_speed = UHS_SDR104_BUS_SPEED;
> > > > > > timing = MMC_TIMING_UHS_SDR104;
> > > > > > card->sw_caps.uhs_max_dtr =
> > UHS_SDR104_MAX_DTR;
> > > > > > } else if ((card->host->caps & MMC_CAP_UHS_DDR50 &&
> > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > > > > > bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > timing = MMC_TIMING_UHS_DDR50;
> > > > > > card->sw_caps.uhs_max_dtr =
> > UHS_DDR50_MAX_DTR;
> > > > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR50 &&
> > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> > > > > > bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > > timing = MMC_TIMING_UHS_SDR50;
> > > > > > card->sw_caps.uhs_max_dtr =
> > UHS_SDR50_MAX_DTR;
> > > > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR25 &&
> > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > > > > > bus_speed = UHS_SDR50_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_SDR12 &&
> > > > > > (card->sw_caps.uhs_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;
> > > > > > }
> > > > >
> > > > > A host capable of operating at a higher bus speed mode should
> be
> > > able
> > > > > to support cards with a lower bus speed mode. In case of your
> > > > > conditions checks above, you are checking for the exact match
> for
> > > the
> > > > > bus speed modes, otherwise you will fall back on using default
> > bus
> > > > > speed mode, thus performance loss.
> > > >
> > > > Why should we implicitly assume that host supporting SDR104 mode
> > > would
> > > > also
> > > > support DDR50 mode? Is there any spec. which says so? If not then
> > we
> > > > should
> > > > rely on host capability to take the decision. DDR50 would require
> > > > different
> > > > timing implementation on the host side and host may choose not to
> > > > implement
> > > > DDR50 timing support then?
> > > >
> > > > I would agree that host supporting SDR104 mode should also be
> > > > supporting
> > > > SDR50/SDR25/SDR12 modes but it may not be true for DDR50 mode.
> > >
> > > Please refer to footnote of table 3-5 of the Physical Layer spec
> > v3.01.
> > > It says:
> > >
> > > "The card supports a UHS-I mode shall support all lower UHS-I
> modes."
> > >
> > > So if this is true for card, doesn't the same apply to host?
> >
> > Yes, this is the exact point I am stretching. Spec. never says the
> same
> > thing for host so we should not assume it. we already have luxury to
> > use
> > host caps then why should we assume something which may not be
> correct?
>
> I still think we should have a combination which works best based on
> the capabilities of the host and card, rather than falling back to
> default bus speed right away. I can try to separate out the case for
> DDR50, if you want that. Otherwise, I will wait for few more comments
> on the same.
Thanks. My code snippet was just an example. Yes, just separating out DDR50
should be enough.
>
> Thanks,
> Arindam
>
> >
> > >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > > >
> > > > > Thanks,
> > > > > Arindam
> > > > >
> > > > > >
> > > > > > Regards,
> > > > > > Subhash
> > > > > >
> > > > > >
> > > > > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR50) {
> > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR104)
> > > > ||
> > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR50))
> > > > {
> > > > > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > > > > + card->sw_caps.uhs_max_dtr =
> > > UHS_SDR50_MAX_DTR;
> > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_DDR50)
> > > > > > > {
> > > > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > > + card->sw_caps.uhs_max_dtr =
> > > UHS_DDR50_MAX_DTR;
> > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR25)
> > > > > > > {
> > > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > + card->sw_caps.uhs_max_dtr =
> > > UHS_SDR25_MAX_DTR;
> > > > > > > + }
> > > > > > > + } else if (card->host->caps & MMC_CAP_UHS_DDR50) {
> > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR104)
> > > > ||
> > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR50)
> > > > ||
> > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_DDR50))
> > > > {
> > > > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > > + card->sw_caps.uhs_max_dtr =
> > > UHS_DDR50_MAX_DTR;
> > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR25)
> > > > > > > {
> > > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > + card->sw_caps.uhs_max_dtr =
> > > UHS_SDR25_MAX_DTR;
> > > > > > > + }
> > > > > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR25) {
> > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR104)
> > > > ||
> > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR50)
> > > > ||
> > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_DDR50)
> > > > ||
> > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > SD_MODE_UHS_SDR25))
> > > > {
> > > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > + card->sw_caps.uhs_max_dtr =
> > > UHS_SDR25_MAX_DTR;
> > > > > > > + }
> > > > > > > + }
> > > > > > > +
> > > > > > > + err = mmc_sd_switch(card, 1, 0, bus_speed, status);
> > > > > > > + if (err)
> > > > > > > + return err;
> > > > > > > +
> > > > > > > + if ((status[16] & 0xF) != bus_speed)
> > > > > > > + printk(KERN_WARNING "%s: Problem setting
> bus
> > > speed
> > > > > > > mode!\n",
> > > > > > > + mmc_hostname(card->host));
> > > > > > > + else {
> > > > > > > + if (bus_speed) {
> > > > > > > + mmc_set_timing(card->host, timing);
> > > > > > > + mmc_set_clock(card->host,
> > > > > > card->sw_caps.uhs_max_dtr);
> > > > > > > + }
> > > > > > > + }
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > /*
> > > > > > > * UHS-I specific initialization procedure
> > > > > > > */
> > > > > > > @@ -499,6 +585,11 @@ static int mmc_sd_init_uhs_card(struct
> > > > > mmc_card
> > > > > > > *card)
> > > > > > >
> > > > > > > /* Set the driver strength for the card */
> > > > > > > err = sd_select_driver_type(card, status);
> > > > > > > + if (err)
> > > > > > > + goto out;
> > > > > > > +
> > > > > > > + /* Set bus speed mode of the card */
> > > > > > > + err = sd_set_bus_speed_mode(card, status);
> > > > > > >
> > > > > > > out:
> > > > > > > kfree(status);
> > > > > > > diff --git a/drivers/mmc/host/sdhci.c
> > > b/drivers/mmc/host/sdhci.c
> > > > > > > index 5d3bb11..f127fa2 100644
> > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct
> > > mmc_host
> > > > > > *mmc,
> > > > > > > struct mmc_ios *ios)
> > > > > > > ctrl &= ~SDHCI_CTRL_HISPD;
> > > > > > >
> > > > > > > if (host->version >= SDHCI_SPEC_300) {
> > > > > > > - u16 ctrl_2;
> > > > > > > + u16 clk, ctrl_2;
> > > > > > > +
> > > > > > > + /* In case of UHS-I modes, set High Speed
> > > Enable */
> > > > > > > + if ((ios->timing == MMC_TIMING_UHS_SDR50)
> ||
> > > > > > > + (ios->timing == MMC_TIMING_UHS_SDR104)
> ||
> > > > > > > + (ios->timing == MMC_TIMING_UHS_DDR50))
> > > > > > > + ctrl |= SDHCI_CTRL_HISPD;
> > > > > > >
> > > > > > > ctrl_2 = sdhci_readw(host,
> > > SDHCI_HOST_CONTROL2);
> > > > > > > if (!(ctrl_2 &
> SDHCI_CTRL_PRESET_VAL_ENABLE))
> > {
> > > > > > > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct
> > mmc_host
> > > > > *mmc,
> > > > > > > struct mmc_ios *ios)
> > > > > > > * need to reset SD Clock Enable
> > before
> > > > changing
> > > > > > High
> > > > > > > * Speed Enable to avoid generating
> > > clock
> > > > gliches.
> > > > > > > */
> > > > > > > - u16 clk;
> > > > > > >
> > > > > > > /* Reset SD Clock Enable */
> > > > > > > clk = sdhci_readw(host,
> > > SDHCI_CLOCK_CONTROL);
> > > > > > > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct
> > > mmc_host
> > > > > > *mmc,
> > > > > > > struct mmc_ios *ios)
> > > > > > > clk |= SDHCI_CLOCK_CARD_EN;
> > > > > > > sdhci_writew(host, clk,
> > > SDHCI_CLOCK_CONTROL);
> > > > > > > }
> > > > > > > +
> > > > > > > + ctrl_2 = sdhci_readw(host,
> > > SDHCI_HOST_CONTROL2);
> > > > > > > +
> > > > > > > + /* Select Bus Speed Mode for host */
> > > > > > > + if (ios->timing == MMC_TIMING_UHS_SDR25)
> > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > > > > > > + else if (ios->timing ==
> MMC_TIMING_UHS_SDR50)
> > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > > > > > > + else if (ios->timing ==
> > MMC_TIMING_UHS_SDR104)
> > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> > > > > > > + else if (ios->timing ==
> MMC_TIMING_UHS_DDR50)
> > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > > > > > > +
> > > > > > > + /* Reset SD Clock Enable */
> > > > > > > + clk = sdhci_readw(host,
> SDHCI_CLOCK_CONTROL);
> > > > > > > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > > > > > > + sdhci_writew(host, clk,
> SDHCI_CLOCK_CONTROL);
> > > > > > > +
> > > > > > > + sdhci_writew(host, ctrl_2,
> > > SDHCI_HOST_CONTROL2);
> > > > > > > +
> > > > > > > + /* Re-enable SD Clock */
> > > > > > > + clk |= SDHCI_CLOCK_CARD_EN;
> > > > > > > + sdhci_writew(host, clk,
> SDHCI_CLOCK_CONTROL);
> > > > > > > } else
> > > > > > > sdhci_writeb(host, ctrl,
> > SDHCI_HOST_CONTROL1);
> > > > > > >
> > > > > > > diff --git a/drivers/mmc/host/sdhci.h
> > > b/drivers/mmc/host/sdhci.h
> > > > > > > index a407b5b..5bf244d 100644
> > > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > > @@ -150,6 +150,11 @@
> > > > > > > #define SDHCI_ACMD12_ERR 0x3C
> > > > > > >
> > > > > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > > > > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > > > > > > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > > > > > > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > > > > > > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > > > > > > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > > > > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > > > > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > > > > > > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > > > > > > diff --git a/include/linux/mmc/card.h
> > > b/include/linux/mmc/card.h
> > > > > > > index 2d7f7a3..0b24c41 100644
> > > > > > > --- a/include/linux/mmc/card.h
> > > > > > > +++ b/include/linux/mmc/card.h
> > > > > > > @@ -75,7 +75,23 @@ struct sd_ssr {
> > > > > > >
> > > > > > > struct sd_switch_caps {
> > > > > > > unsigned int hs_max_dtr;
> > > > > > > + unsigned int uhs_max_dtr;
> > > > > > > +#define UHS_SDR104_MAX_DTR 208000000
> > > > > > > +#define UHS_SDR50_MAX_DTR 100000000
> > > > > > > +#define UHS_DDR50_MAX_DTR 50000000
> > > > > > > +#define UHS_SDR25_MAX_DTR 50000000
> > > > > > > unsigned int uhs_bus_mode;
> > > > > > > +#define UHS_SDR12_BUS_SPEED 0
> > > > > > > +#define UHS_SDR25_BUS_SPEED 1
> > > > > > > +#define UHS_SDR50_BUS_SPEED 2
> > > > > > > +#define UHS_SDR104_BUS_SPEED 3
> > > > > > > +#define UHS_DDR50_BUS_SPEED 4
> > > > > > > +
> > > > > > > +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
> > > > > > > +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
> > > > > > > +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
> > > > > > > +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
> > > > > > > +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
> > > > > > > unsigned int uhs_drv_type;
> > > > > > > #define SD_DRIVER_TYPE_B 0x01
> > > > > > > #define SD_DRIVER_TYPE_A 0x02
> > > > > > > diff --git a/include/linux/mmc/host.h
> > > b/include/linux/mmc/host.h
> > > > > > > index bc2121e..4dfff6d 100644
> > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > @@ -50,6 +50,10 @@ struct mmc_ios {
> > > > > > > #define MMC_TIMING_LEGACY 0
> > > > > > > #define MMC_TIMING_MMC_HS 1
> > > > > > > #define MMC_TIMING_SD_HS 2
> > > > > > > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > > > > > > +#define MMC_TIMING_UHS_SDR50 3
> > > > > > > +#define MMC_TIMING_UHS_SDR104 4
> > > > > > > +#define MMC_TIMING_UHS_DDR50 5
> > > > > > >
> > > > > > > unsigned char ddr; /* dual
> data
> > > rate used
> > > > */
> > > > > > >
> > > > > > > --
> > > > > > > 1.7.1
> > > > > >
> > > > > >
> > > > >
> > > > >
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe
> linux-
> > > mmc"
> > > > in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > info.html
> > > >
> > >
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> mmc"
> > in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-23 7:23 ` Subhash Jadavani
@ 2011-03-23 14:02 ` Nath, Arindam
2011-03-24 7:25 ` Subhash Jadavani
0 siblings, 1 reply; 125+ messages in thread
From: Nath, Arindam @ 2011-03-23 14:02 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Wednesday, March 23, 2011 12:54 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
>
>
> > -----Original Message-----
> > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > Sent: Wednesday, March 23, 2011 12:06 PM
> > To: Subhash Jadavani; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode selection
> >
> >
> >
> > > -----Original Message-----
> > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > Sent: Wednesday, March 23, 2011 11:56 AM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> speed
> > > mode selection
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > Sent: Wednesday, March 23, 2011 11:48 AM
> > > > To: Subhash Jadavani; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > speed
> > > > mode selection
> > > >
> > > > Hi Subhash,
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > Sent: Wednesday, March 23, 2011 11:44 AM
> > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > > speed
> > > > > mode selection
> > > > >
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > Sent: Wednesday, March 23, 2011 11:35 AM
> > > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs
> bus
> > > > speed
> > > > > > mode selection
> > > > > >
> > > > > > Hi Subhash,
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > > Sent: Monday, March 21, 2011 12:13 PM
> > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > > > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs
> > bus
> > > > > speed
> > > > > > > mode selection
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > > -----Original Message-----
> > > > > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On Behalf
> > Of
> > > > > > Arindam
> > > > > > > > Nath
> > > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > > To: cjb@laptop.org
> > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > > henry.su@amd.com;
> > > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs
> bus
> > > > speed
> > > > > > mode
> > > > > > > > selection
> > > > > > > >
> > > > > > > > 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. Since MMC_TIMING_UHS_SDR25 is
> same
> > > as
> > > > > > > > MMC_TIMING_SD_HS, we don't need to set the
> SDHCI_CTRL_HISPD
> > > > flag
> > > > > > for
> > > > > > > > UHS SDR25 again. We also 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 set the clock
> frequency
> > > for
> > > > > the
> > > > > > > > UHS-I mode selected.
> > > > > > > >
> > > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > > ---
> > > > > > > > drivers/mmc/core/sd.c | 91
> > > > > > > > ++++++++++++++++++++++++++++++++++++++++++++++
> > > > > > > > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > > > > > > > drivers/mmc/host/sdhci.h | 5 +++
> > > > > > > > include/linux/mmc/card.h | 16 ++++++++
> > > > > > > > include/linux/mmc/host.h | 4 ++
> > > > > > > > 5 files changed, 146 insertions(+), 2 deletions(-)
> > > > > > > >
> > > > > > > > diff --git a/drivers/mmc/core/sd.c
> b/drivers/mmc/core/sd.c
> > > > > > > > index f6a4fab..ec0d8e6 100644
> > > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > > @@ -464,6 +464,92 @@ static int
> > sd_select_driver_type(struct
> > > > > > mmc_card
> > > > > > > > *card, u8 *status)
> > > > > > > > return 0;
> > > > > > > > }
> > > > > > > >
> > > > > > > > +static int sd_set_bus_speed_mode(struct mmc_card *card,
> u8
> > > > > > *status)
> > > > > > > > +{
> > > > > > > > + unsigned int bus_speed, timing;
> > > > > > > > + int err;
> > > > > > > > +
> > > > > > > > + /*
> > > > > > > > + * If the host doesn't support any of the UHS-I
> > > modes,
> > > > > fallback
> > > > > > > > on
> > > > > > > > + * default speed.
> > > > > > > > + */
> > > > > > > > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 |
> > > > > MMC_CAP_UHS_SDR25 |
> > > > > > > > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> > > > > MMC_CAP_UHS_DDR50)))
> > > > > > > > + return 0;
> > > > > > > > +
> > > > > > > > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > > > > > > > + if (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR104)
> > > > > {
> > > > > > > > + bus_speed = UHS_SDR104_BUS_SPEED;
> > > > > > > > + timing = MMC_TIMING_UHS_SDR104;
> > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > UHS_SDR104_MAX_DTR;
> > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR50)
> > > > > > > > {
> > > > > > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > UHS_SDR50_MAX_DTR;
> > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_DDR50)
> > > > > > > > {
> > > > > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > UHS_DDR50_MAX_DTR;
> > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR25)
> > > > > > > > {
> > > > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > UHS_SDR25_MAX_DTR;
> > > > > > > > + }
> > > > > > >
> > > > > > > I would not agree that if Host cap is set to
> > MMC_CAP_UHS_SDR104
> > > > > > implies
> > > > > > > that
> > > > > > > DDR50 mode is supported by host. There may be a case where
> > host
> > > > > > > controller
> > > > > > > only support SDR timing modes.
> > > > > > > Basically if host should advertise all the supported modes
> > > > (SDR104,
> > > > > > > SDR50,
> > > > > > > SDR25, SDR12 & DDR) by caps and then we should choose the
> > > highest
> > > > > > > performance mode depending on both the card and host's
> > highest
> > > > > > > performance
> > > > > > > mode.
> > > > > > >
> > > > > > > So this should be the check:
> > > > > > >
> > > > > > > if ((card->host->caps & MMC_CAP_UHS_SDR104 &&
> > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
> > > > > > > bus_speed = UHS_SDR104_BUS_SPEED;
> > > > > > > timing = MMC_TIMING_UHS_SDR104;
> > > > > > > card->sw_caps.uhs_max_dtr =
> > > UHS_SDR104_MAX_DTR;
> > > > > > > } else if ((card->host->caps & MMC_CAP_UHS_DDR50 &&
> > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > > > > > > bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > > timing = MMC_TIMING_UHS_DDR50;
> > > > > > > card->sw_caps.uhs_max_dtr =
> > > UHS_DDR50_MAX_DTR;
> > > > > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR50 &&
> > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> > > > > > > bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > > > timing = MMC_TIMING_UHS_SDR50;
> > > > > > > card->sw_caps.uhs_max_dtr =
> > > UHS_SDR50_MAX_DTR;
> > > > > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR25 &&
> > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > > > > > > bus_speed = UHS_SDR50_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_SDR12 &&
> > > > > > > (card->sw_caps.uhs_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;
> > > > > > > }
> > > > > >
> > > > > > A host capable of operating at a higher bus speed mode should
> > be
> > > > able
> > > > > > to support cards with a lower bus speed mode. In case of your
> > > > > > conditions checks above, you are checking for the exact match
> > for
> > > > the
> > > > > > bus speed modes, otherwise you will fall back on using
> default
> > > bus
> > > > > > speed mode, thus performance loss.
> > > > >
> > > > > Why should we implicitly assume that host supporting SDR104
> mode
> > > > would
> > > > > also
> > > > > support DDR50 mode? Is there any spec. which says so? If not
> then
> > > we
> > > > > should
> > > > > rely on host capability to take the decision. DDR50 would
> require
> > > > > different
> > > > > timing implementation on the host side and host may choose not
> to
> > > > > implement
> > > > > DDR50 timing support then?
> > > > >
> > > > > I would agree that host supporting SDR104 mode should also be
> > > > > supporting
> > > > > SDR50/SDR25/SDR12 modes but it may not be true for DDR50 mode.
> > > >
> > > > Please refer to footnote of table 3-5 of the Physical Layer spec
> > > v3.01.
> > > > It says:
> > > >
> > > > "The card supports a UHS-I mode shall support all lower UHS-I
> > modes."
> > > >
> > > > So if this is true for card, doesn't the same apply to host?
> > >
> > > Yes, this is the exact point I am stretching. Spec. never says the
> > same
> > > thing for host so we should not assume it. we already have luxury
> to
> > > use
> > > host caps then why should we assume something which may not be
> > correct?
> >
> > I still think we should have a combination which works best based on
> > the capabilities of the host and card, rather than falling back to
> > default bus speed right away. I can try to separate out the case for
> > DDR50, if you want that. Otherwise, I will wait for few more comments
> > on the same.
>
> Thanks. My code snippet was just an example. Yes, just separating out
> DDR50
> should be enough.
Section 3.9.3 of the Physical Layer spec v3.01 says:
"Host may use SDR50, DDR50 and SDR104 with either UHS50 card or UHS104 card."
So doesn't that mean that we should also check for all possible combinations of host and card when the host supports DDR50 mode? Just a thought.
Thanks,
Arindam
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > > >
> > > > Thanks,
> > > > Arindam
> > > >
> > > > >
> > > > > >
> > > > > > Thanks,
> > > > > > Arindam
> > > > > >
> > > > > > >
> > > > > > > Regards,
> > > > > > > Subhash
> > > > > > >
> > > > > > >
> > > > > > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR50)
> {
> > > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR104)
> > > > > ||
> > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR50))
> > > > > {
> > > > > > > > + bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > UHS_SDR50_MAX_DTR;
> > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_DDR50)
> > > > > > > > {
> > > > > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > UHS_DDR50_MAX_DTR;
> > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR25)
> > > > > > > > {
> > > > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > UHS_SDR25_MAX_DTR;
> > > > > > > > + }
> > > > > > > > + } else if (card->host->caps & MMC_CAP_UHS_DDR50)
> {
> > > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR104)
> > > > > ||
> > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR50)
> > > > > ||
> > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_DDR50))
> > > > > {
> > > > > > > > + bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > UHS_DDR50_MAX_DTR;
> > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR25)
> > > > > > > > {
> > > > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > UHS_SDR25_MAX_DTR;
> > > > > > > > + }
> > > > > > > > + } else if (card->host->caps & MMC_CAP_UHS_SDR25)
> {
> > > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR104)
> > > > > ||
> > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR50)
> > > > > ||
> > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_DDR50)
> > > > > ||
> > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > SD_MODE_UHS_SDR25))
> > > > > {
> > > > > > > > + bus_speed = UHS_SDR25_BUS_SPEED;
> > > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > UHS_SDR25_MAX_DTR;
> > > > > > > > + }
> > > > > > > > + }
> > > > > > > > +
> > > > > > > > + err = mmc_sd_switch(card, 1, 0, bus_speed,
> status);
> > > > > > > > + if (err)
> > > > > > > > + return err;
> > > > > > > > +
> > > > > > > > + if ((status[16] & 0xF) != bus_speed)
> > > > > > > > + printk(KERN_WARNING "%s: Problem setting
> > bus
> > > > speed
> > > > > > > > mode!\n",
> > > > > > > > + mmc_hostname(card->host));
> > > > > > > > + else {
> > > > > > > > + if (bus_speed) {
> > > > > > > > + mmc_set_timing(card->host,
> timing);
> > > > > > > > + mmc_set_clock(card->host,
> > > > > > > card->sw_caps.uhs_max_dtr);
> > > > > > > > + }
> > > > > > > > + }
> > > > > > > > +
> > > > > > > > + return 0;
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > /*
> > > > > > > > * UHS-I specific initialization procedure
> > > > > > > > */
> > > > > > > > @@ -499,6 +585,11 @@ static int
> mmc_sd_init_uhs_card(struct
> > > > > > mmc_card
> > > > > > > > *card)
> > > > > > > >
> > > > > > > > /* Set the driver strength for the card */
> > > > > > > > err = sd_select_driver_type(card, status);
> > > > > > > > + if (err)
> > > > > > > > + goto out;
> > > > > > > > +
> > > > > > > > + /* Set bus speed mode of the card */
> > > > > > > > + err = sd_set_bus_speed_mode(card, status);
> > > > > > > >
> > > > > > > > out:
> > > > > > > > kfree(status);
> > > > > > > > diff --git a/drivers/mmc/host/sdhci.c
> > > > b/drivers/mmc/host/sdhci.c
> > > > > > > > index 5d3bb11..f127fa2 100644
> > > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct
> > > > mmc_host
> > > > > > > *mmc,
> > > > > > > > struct mmc_ios *ios)
> > > > > > > > ctrl &= ~SDHCI_CTRL_HISPD;
> > > > > > > >
> > > > > > > > if (host->version >= SDHCI_SPEC_300) {
> > > > > > > > - u16 ctrl_2;
> > > > > > > > + u16 clk, ctrl_2;
> > > > > > > > +
> > > > > > > > + /* In case of UHS-I modes, set High Speed
> > > > Enable */
> > > > > > > > + if ((ios->timing == MMC_TIMING_UHS_SDR50)
> > ||
> > > > > > > > + (ios->timing ==
> MMC_TIMING_UHS_SDR104)
> > ||
> > > > > > > > + (ios->timing ==
> MMC_TIMING_UHS_DDR50))
> > > > > > > > + ctrl |= SDHCI_CTRL_HISPD;
> > > > > > > >
> > > > > > > > ctrl_2 = sdhci_readw(host,
> > > > SDHCI_HOST_CONTROL2);
> > > > > > > > if (!(ctrl_2 &
> > SDHCI_CTRL_PRESET_VAL_ENABLE))
> > > {
> > > > > > > > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct
> > > mmc_host
> > > > > > *mmc,
> > > > > > > > struct mmc_ios *ios)
> > > > > > > > * need to reset SD Clock Enable
> > > before
> > > > > changing
> > > > > > > High
> > > > > > > > * Speed Enable to avoid
> generating
> > > > clock
> > > > > gliches.
> > > > > > > > */
> > > > > > > > - u16 clk;
> > > > > > > >
> > > > > > > > /* Reset SD Clock Enable */
> > > > > > > > clk = sdhci_readw(host,
> > > > SDHCI_CLOCK_CONTROL);
> > > > > > > > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct
> > > > mmc_host
> > > > > > > *mmc,
> > > > > > > > struct mmc_ios *ios)
> > > > > > > > clk |= SDHCI_CLOCK_CARD_EN;
> > > > > > > > sdhci_writew(host, clk,
> > > > SDHCI_CLOCK_CONTROL);
> > > > > > > > }
> > > > > > > > +
> > > > > > > > + ctrl_2 = sdhci_readw(host,
> > > > SDHCI_HOST_CONTROL2);
> > > > > > > > +
> > > > > > > > + /* Select Bus Speed Mode for host */
> > > > > > > > + if (ios->timing == MMC_TIMING_UHS_SDR25)
> > > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > > > > > > > + else if (ios->timing ==
> > MMC_TIMING_UHS_SDR50)
> > > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > > > > > > > + else if (ios->timing ==
> > > MMC_TIMING_UHS_SDR104)
> > > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> > > > > > > > + else if (ios->timing ==
> > MMC_TIMING_UHS_DDR50)
> > > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > > > > > > > +
> > > > > > > > + /* Reset SD Clock Enable */
> > > > > > > > + clk = sdhci_readw(host,
> > SDHCI_CLOCK_CONTROL);
> > > > > > > > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > > > > > > > + sdhci_writew(host, clk,
> > SDHCI_CLOCK_CONTROL);
> > > > > > > > +
> > > > > > > > + sdhci_writew(host, ctrl_2,
> > > > SDHCI_HOST_CONTROL2);
> > > > > > > > +
> > > > > > > > + /* Re-enable SD Clock */
> > > > > > > > + clk |= SDHCI_CLOCK_CARD_EN;
> > > > > > > > + sdhci_writew(host, clk,
> > SDHCI_CLOCK_CONTROL);
> > > > > > > > } else
> > > > > > > > sdhci_writeb(host, ctrl,
> > > SDHCI_HOST_CONTROL1);
> > > > > > > >
> > > > > > > > diff --git a/drivers/mmc/host/sdhci.h
> > > > b/drivers/mmc/host/sdhci.h
> > > > > > > > index a407b5b..5bf244d 100644
> > > > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > > > @@ -150,6 +150,11 @@
> > > > > > > > #define SDHCI_ACMD12_ERR 0x3C
> > > > > > > >
> > > > > > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > > > > > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > > > > > > > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > > > > > > > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > > > > > > > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > > > > > > > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > > > > > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > > > > > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > > > > > > > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > > > > > > > diff --git a/include/linux/mmc/card.h
> > > > b/include/linux/mmc/card.h
> > > > > > > > index 2d7f7a3..0b24c41 100644
> > > > > > > > --- a/include/linux/mmc/card.h
> > > > > > > > +++ b/include/linux/mmc/card.h
> > > > > > > > @@ -75,7 +75,23 @@ struct sd_ssr {
> > > > > > > >
> > > > > > > > struct sd_switch_caps {
> > > > > > > > unsigned int hs_max_dtr;
> > > > > > > > + unsigned int uhs_max_dtr;
> > > > > > > > +#define UHS_SDR104_MAX_DTR 208000000
> > > > > > > > +#define UHS_SDR50_MAX_DTR 100000000
> > > > > > > > +#define UHS_DDR50_MAX_DTR 50000000
> > > > > > > > +#define UHS_SDR25_MAX_DTR 50000000
> > > > > > > > unsigned int uhs_bus_mode;
> > > > > > > > +#define UHS_SDR12_BUS_SPEED 0
> > > > > > > > +#define UHS_SDR25_BUS_SPEED 1
> > > > > > > > +#define UHS_SDR50_BUS_SPEED 2
> > > > > > > > +#define UHS_SDR104_BUS_SPEED 3
> > > > > > > > +#define UHS_DDR50_BUS_SPEED 4
> > > > > > > > +
> > > > > > > > +#define SD_MODE_UHS_SDR12 (1 <<
> UHS_SDR12_BUS_SPEED)
> > > > > > > > +#define SD_MODE_UHS_SDR25 (1 <<
> UHS_SDR25_BUS_SPEED)
> > > > > > > > +#define SD_MODE_UHS_SDR50 (1 <<
> UHS_SDR50_BUS_SPEED)
> > > > > > > > +#define SD_MODE_UHS_SDR104 (1 <<
> UHS_SDR104_BUS_SPEED)
> > > > > > > > +#define SD_MODE_UHS_DDR50 (1 <<
> UHS_DDR50_BUS_SPEED)
> > > > > > > > unsigned int uhs_drv_type;
> > > > > > > > #define SD_DRIVER_TYPE_B 0x01
> > > > > > > > #define SD_DRIVER_TYPE_A 0x02
> > > > > > > > diff --git a/include/linux/mmc/host.h
> > > > b/include/linux/mmc/host.h
> > > > > > > > index bc2121e..4dfff6d 100644
> > > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > > @@ -50,6 +50,10 @@ struct mmc_ios {
> > > > > > > > #define MMC_TIMING_LEGACY 0
> > > > > > > > #define MMC_TIMING_MMC_HS 1
> > > > > > > > #define MMC_TIMING_SD_HS 2
> > > > > > > > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > > > > > > > +#define MMC_TIMING_UHS_SDR50 3
> > > > > > > > +#define MMC_TIMING_UHS_SDR104 4
> > > > > > > > +#define MMC_TIMING_UHS_DDR50 5
> > > > > > > >
> > > > > > > > unsigned char ddr; /* dual
> > data
> > > > rate used
> > > > > */
> > > > > > > >
> > > > > > > > --
> > > > > > > > 1.7.1
> > > > > > >
> > > > > > >
> > > > > >
> > > > > >
> > > > > > --
> > > > > > To unsubscribe from this list: send the line "unsubscribe
> > linux-
> > > > mmc"
> > > > > in
> > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > info.html
> > > > >
> > > >
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-
> > mmc"
> > > in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at http://vger.kernel.org/majordomo-
> info.html
> > >
> >
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-23 14:02 ` Nath, Arindam
@ 2011-03-24 7:25 ` Subhash Jadavani
2011-03-24 8:42 ` Nath, Arindam
0 siblings, 1 reply; 125+ messages in thread
From: Subhash Jadavani @ 2011-03-24 7:25 UTC (permalink / raw)
To: 'Nath, Arindam', cjb
Cc: zhangfei.gao, prakity, linux-mmc, 'Su, Henry',
'Lu, Aaron', anath.amd
> -----Original Message-----
> From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> Sent: Wednesday, March 23, 2011 7:33 PM
> To: Subhash Jadavani; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
> Hi Subhash,
>
>
> > -----Original Message-----
> > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > Sent: Wednesday, March 23, 2011 12:54 PM
> > To: Nath, Arindam; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode selection
> >
> >
> >
> > > -----Original Message-----
> > > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > > Sent: Wednesday, March 23, 2011 12:06 PM
> > > To: Subhash Jadavani; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> speed
> > > mode selection
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > Sent: Wednesday, March 23, 2011 11:56 AM
> > > > To: Nath, Arindam; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > speed
> > > > mode selection
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > Sent: Wednesday, March 23, 2011 11:48 AM
> > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > > speed
> > > > > mode selection
> > > > >
> > > > > Hi Subhash,
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > Sent: Wednesday, March 23, 2011 11:44 AM
> > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs
> bus
> > > > speed
> > > > > > mode selection
> > > > > >
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > > Sent: Wednesday, March 23, 2011 11:35 AM
> > > > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > > > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs
> > bus
> > > > > speed
> > > > > > > mode selection
> > > > > > >
> > > > > > > Hi Subhash,
> > > > > > >
> > > > > > >
> > > > > > > > -----Original Message-----
> > > > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > > > Sent: Monday, March 21, 2011 12:13 PM
> > > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > anath.amd@gmail.com
> > > > > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for
> uhs
> > > bus
> > > > > > speed
> > > > > > > > mode selection
> > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > > -----Original Message-----
> > > > > > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On
> Behalf
> > > Of
> > > > > > > Arindam
> > > > > > > > > Nath
> > > > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > > > To: cjb@laptop.org
> > > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > > > henry.su@amd.com;
> > > > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > > > Subject: [PATCH v2 06/12] mmc: sd: add support for uhs
> > bus
> > > > > speed
> > > > > > > mode
> > > > > > > > > selection
> > > > > > > > >
> > > > > > > > > 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. Since MMC_TIMING_UHS_SDR25 is
> > same
> > > > as
> > > > > > > > > MMC_TIMING_SD_HS, we don't need to set the
> > SDHCI_CTRL_HISPD
> > > > > flag
> > > > > > > for
> > > > > > > > > UHS SDR25 again. We also 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 set the clock
> > frequency
> > > > for
> > > > > > the
> > > > > > > > > UHS-I mode selected.
> > > > > > > > >
> > > > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > > > ---
> > > > > > > > > drivers/mmc/core/sd.c | 91
> > > > > > > > > ++++++++++++++++++++++++++++++++++++++++++++++
> > > > > > > > > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > > > > > > > > drivers/mmc/host/sdhci.h | 5 +++
> > > > > > > > > include/linux/mmc/card.h | 16 ++++++++
> > > > > > > > > include/linux/mmc/host.h | 4 ++
> > > > > > > > > 5 files changed, 146 insertions(+), 2 deletions(-)
> > > > > > > > >
> > > > > > > > > diff --git a/drivers/mmc/core/sd.c
> > b/drivers/mmc/core/sd.c
> > > > > > > > > index f6a4fab..ec0d8e6 100644
> > > > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > > > @@ -464,6 +464,92 @@ static int
> > > sd_select_driver_type(struct
> > > > > > > mmc_card
> > > > > > > > > *card, u8 *status)
> > > > > > > > > return 0;
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > > +static int sd_set_bus_speed_mode(struct mmc_card
> *card,
> > u8
> > > > > > > *status)
> > > > > > > > > +{
> > > > > > > > > + unsigned int bus_speed, timing;
> > > > > > > > > + int err;
> > > > > > > > > +
> > > > > > > > > + /*
> > > > > > > > > + * If the host doesn't support any of the UHS-I
> > > > modes,
> > > > > > fallback
> > > > > > > > > on
> > > > > > > > > + * default speed.
> > > > > > > > > + */
> > > > > > > > > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 |
> > > > > > MMC_CAP_UHS_SDR25 |
> > > > > > > > > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> > > > > > MMC_CAP_UHS_DDR50)))
> > > > > > > > > + return 0;
> > > > > > > > > +
> > > > > > > > > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > > > > > > > > + if (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR104)
> > > > > > {
> > > > > > > > > + bus_speed =
> UHS_SDR104_BUS_SPEED;
> > > > > > > > > + timing = MMC_TIMING_UHS_SDR104;
> > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > UHS_SDR104_MAX_DTR;
> > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR50)
> > > > > > > > > {
> > > > > > > > > + bus_speed =
> UHS_SDR50_BUS_SPEED;
> > > > > > > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > UHS_SDR50_MAX_DTR;
> > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_DDR50)
> > > > > > > > > {
> > > > > > > > > + bus_speed =
> UHS_DDR50_BUS_SPEED;
> > > > > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > UHS_DDR50_MAX_DTR;
> > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR25)
> > > > > > > > > {
> > > > > > > > > + bus_speed =
> UHS_SDR25_BUS_SPEED;
> > > > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > UHS_SDR25_MAX_DTR;
> > > > > > > > > + }
> > > > > > > >
> > > > > > > > I would not agree that if Host cap is set to
> > > MMC_CAP_UHS_SDR104
> > > > > > > implies
> > > > > > > > that
> > > > > > > > DDR50 mode is supported by host. There may be a case
> where
> > > host
> > > > > > > > controller
> > > > > > > > only support SDR timing modes.
> > > > > > > > Basically if host should advertise all the supported
> modes
> > > > > (SDR104,
> > > > > > > > SDR50,
> > > > > > > > SDR25, SDR12 & DDR) by caps and then we should choose the
> > > > highest
> > > > > > > > performance mode depending on both the card and host's
> > > highest
> > > > > > > > performance
> > > > > > > > mode.
> > > > > > > >
> > > > > > > > So this should be the check:
> > > > > > > >
> > > > > > > > if ((card->host->caps & MMC_CAP_UHS_SDR104 &&
> > > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
> > > > > > > > bus_speed = UHS_SDR104_BUS_SPEED;
> > > > > > > > timing = MMC_TIMING_UHS_SDR104;
> > > > > > > > card->sw_caps.uhs_max_dtr =
> > > > UHS_SDR104_MAX_DTR;
> > > > > > > > } else if ((card->host->caps & MMC_CAP_UHS_DDR50 &&
> > > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > > > > > > > bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > > > timing = MMC_TIMING_UHS_DDR50;
> > > > > > > > card->sw_caps.uhs_max_dtr =
> > > > UHS_DDR50_MAX_DTR;
> > > > > > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR50 &&
> > > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> > > > > > > > bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > > > > timing = MMC_TIMING_UHS_SDR50;
> > > > > > > > card->sw_caps.uhs_max_dtr =
> > > > UHS_SDR50_MAX_DTR;
> > > > > > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR25 &&
> > > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > > > > > > > bus_speed = UHS_SDR50_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_SDR12 &&
> > > > > > > > (card->sw_caps.uhs_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;
> > > > > > > > }
> > > > > > >
> > > > > > > A host capable of operating at a higher bus speed mode
> should
> > > be
> > > > > able
> > > > > > > to support cards with a lower bus speed mode. In case of
> your
> > > > > > > conditions checks above, you are checking for the exact
> match
> > > for
> > > > > the
> > > > > > > bus speed modes, otherwise you will fall back on using
> > default
> > > > bus
> > > > > > > speed mode, thus performance loss.
> > > > > >
> > > > > > Why should we implicitly assume that host supporting SDR104
> > mode
> > > > > would
> > > > > > also
> > > > > > support DDR50 mode? Is there any spec. which says so? If not
> > then
> > > > we
> > > > > > should
> > > > > > rely on host capability to take the decision. DDR50 would
> > require
> > > > > > different
> > > > > > timing implementation on the host side and host may choose
> not
> > to
> > > > > > implement
> > > > > > DDR50 timing support then?
> > > > > >
> > > > > > I would agree that host supporting SDR104 mode should also be
> > > > > > supporting
> > > > > > SDR50/SDR25/SDR12 modes but it may not be true for DDR50
> mode.
> > > > >
> > > > > Please refer to footnote of table 3-5 of the Physical Layer
> spec
> > > > v3.01.
> > > > > It says:
> > > > >
> > > > > "The card supports a UHS-I mode shall support all lower UHS-I
> > > modes."
> > > > >
> > > > > So if this is true for card, doesn't the same apply to host?
> > > >
> > > > Yes, this is the exact point I am stretching. Spec. never says
> the
> > > same
> > > > thing for host so we should not assume it. we already have luxury
> > to
> > > > use
> > > > host caps then why should we assume something which may not be
> > > correct?
> > >
> > > I still think we should have a combination which works best based
> on
> > > the capabilities of the host and card, rather than falling back to
> > > default bus speed right away. I can try to separate out the case
> for
> > > DDR50, if you want that. Otherwise, I will wait for few more
> comments
> > > on the same.
> >
> > Thanks. My code snippet was just an example. Yes, just separating out
> > DDR50
> > should be enough.
>
> Section 3.9.3 of the Physical Layer spec v3.01 says:
>
> "Host may use SDR50, DDR50 and SDR104 with either UHS50 card or UHS104
> card."
>
> So doesn't that mean that we should also check for all possible
> combinations of host and card when the host supports DDR50 mode? Just a
> thought.
Yes, definitely. We should choose the highest performance mode supported by
both card and host. How about following checks?
if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
(card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
bus_speed = UHS_SDR104_BUS_SPEED;
timing = MMC_TIMING_UHS_SDR104;
card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
} else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
(card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
bus_speed = UHS_DDR50_BUS_SPEED;
timing = MMC_TIMING_UHS_DDR50;
card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50)) && (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
bus_speed = UHS_SDR50_BUS_SPEED;
timing = MMC_TIMING_UHS_SDR50;
card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) && (card->sw_caps.uhs_bus_mode &
SD_MODE_UHS_SDR25)) {
bus_speed = UHS_SDR50_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.uhs_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;
}
Here let's say if host sets both DDR50 and SDR50 caps and card also supports
the DDR50 and SDR50 then we have give preference to DDR50 mode as it would
require lower interface clock compared to SDR50. Although spec is not saying
anything about which one should be given a preference when both are
supported by card and host.
Does above code snippest looks fine?
>
> Thanks,
> Arindam
>
> >
> > >
> > > Thanks,
> > > Arindam
> > >
> > > >
> > > > >
> > > > > Thanks,
> > > > > Arindam
> > > > >
> > > > > >
> > > > > > >
> > > > > > > Thanks,
> > > > > > > Arindam
> > > > > > >
> > > > > > > >
> > > > > > > > Regards,
> > > > > > > > Subhash
> > > > > > > >
> > > > > > > >
> > > > > > > > > + } else if (card->host->caps &
> MMC_CAP_UHS_SDR50)
> > {
> > > > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR104)
> > > > > > ||
> > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR50))
> > > > > > {
> > > > > > > > > + bus_speed =
> UHS_SDR50_BUS_SPEED;
> > > > > > > > > + timing = MMC_TIMING_UHS_SDR50;
> > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > UHS_SDR50_MAX_DTR;
> > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_DDR50)
> > > > > > > > > {
> > > > > > > > > + bus_speed =
> UHS_DDR50_BUS_SPEED;
> > > > > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > UHS_DDR50_MAX_DTR;
> > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR25)
> > > > > > > > > {
> > > > > > > > > + bus_speed =
> UHS_SDR25_BUS_SPEED;
> > > > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > UHS_SDR25_MAX_DTR;
> > > > > > > > > + }
> > > > > > > > > + } else if (card->host->caps &
> MMC_CAP_UHS_DDR50)
> > {
> > > > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR104)
> > > > > > ||
> > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR50)
> > > > > > ||
> > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_DDR50))
> > > > > > {
> > > > > > > > > + bus_speed =
> UHS_DDR50_BUS_SPEED;
> > > > > > > > > + timing = MMC_TIMING_UHS_DDR50;
> > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > UHS_DDR50_MAX_DTR;
> > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR25)
> > > > > > > > > {
> > > > > > > > > + bus_speed =
> UHS_SDR25_BUS_SPEED;
> > > > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > UHS_SDR25_MAX_DTR;
> > > > > > > > > + }
> > > > > > > > > + } else if (card->host->caps &
> MMC_CAP_UHS_SDR25)
> > {
> > > > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR104)
> > > > > > ||
> > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR50)
> > > > > > ||
> > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_DDR50)
> > > > > > ||
> > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > SD_MODE_UHS_SDR25))
> > > > > > {
> > > > > > > > > + bus_speed =
> UHS_SDR25_BUS_SPEED;
> > > > > > > > > + timing = MMC_TIMING_UHS_SDR25;
> > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > UHS_SDR25_MAX_DTR;
> > > > > > > > > + }
> > > > > > > > > + }
> > > > > > > > > +
> > > > > > > > > + err = mmc_sd_switch(card, 1, 0, bus_speed,
> > status);
> > > > > > > > > + if (err)
> > > > > > > > > + return err;
> > > > > > > > > +
> > > > > > > > > + if ((status[16] & 0xF) != bus_speed)
> > > > > > > > > + printk(KERN_WARNING "%s: Problem
> setting
> > > bus
> > > > > speed
> > > > > > > > > mode!\n",
> > > > > > > > > + mmc_hostname(card->host));
> > > > > > > > > + else {
> > > > > > > > > + if (bus_speed) {
> > > > > > > > > + mmc_set_timing(card->host,
> > timing);
> > > > > > > > > + mmc_set_clock(card->host,
> > > > > > > > card->sw_caps.uhs_max_dtr);
> > > > > > > > > + }
> > > > > > > > > + }
> > > > > > > > > +
> > > > > > > > > + return 0;
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > /*
> > > > > > > > > * UHS-I specific initialization procedure
> > > > > > > > > */
> > > > > > > > > @@ -499,6 +585,11 @@ static int
> > mmc_sd_init_uhs_card(struct
> > > > > > > mmc_card
> > > > > > > > > *card)
> > > > > > > > >
> > > > > > > > > /* Set the driver strength for the card */
> > > > > > > > > err = sd_select_driver_type(card, status);
> > > > > > > > > + if (err)
> > > > > > > > > + goto out;
> > > > > > > > > +
> > > > > > > > > + /* Set bus speed mode of the card */
> > > > > > > > > + err = sd_set_bus_speed_mode(card, status);
> > > > > > > > >
> > > > > > > > > out:
> > > > > > > > > kfree(status);
> > > > > > > > > diff --git a/drivers/mmc/host/sdhci.c
> > > > > b/drivers/mmc/host/sdhci.c
> > > > > > > > > index 5d3bb11..f127fa2 100644
> > > > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > > > @@ -1278,7 +1278,13 @@ static void sdhci_set_ios(struct
> > > > > mmc_host
> > > > > > > > *mmc,
> > > > > > > > > struct mmc_ios *ios)
> > > > > > > > > ctrl &= ~SDHCI_CTRL_HISPD;
> > > > > > > > >
> > > > > > > > > if (host->version >= SDHCI_SPEC_300) {
> > > > > > > > > - u16 ctrl_2;
> > > > > > > > > + u16 clk, ctrl_2;
> > > > > > > > > +
> > > > > > > > > + /* In case of UHS-I modes, set High
> Speed
> > > > > Enable */
> > > > > > > > > + if ((ios->timing ==
> MMC_TIMING_UHS_SDR50)
> > > ||
> > > > > > > > > + (ios->timing ==
> > MMC_TIMING_UHS_SDR104)
> > > ||
> > > > > > > > > + (ios->timing ==
> > MMC_TIMING_UHS_DDR50))
> > > > > > > > > + ctrl |= SDHCI_CTRL_HISPD;
> > > > > > > > >
> > > > > > > > > ctrl_2 = sdhci_readw(host,
> > > > > SDHCI_HOST_CONTROL2);
> > > > > > > > > if (!(ctrl_2 &
> > > SDHCI_CTRL_PRESET_VAL_ENABLE))
> > > > {
> > > > > > > > > @@ -1300,7 +1306,6 @@ static void sdhci_set_ios(struct
> > > > mmc_host
> > > > > > > *mmc,
> > > > > > > > > struct mmc_ios *ios)
> > > > > > > > > * need to reset SD Clock
> Enable
> > > > before
> > > > > > changing
> > > > > > > > High
> > > > > > > > > * Speed Enable to avoid
> > generating
> > > > > clock
> > > > > > gliches.
> > > > > > > > > */
> > > > > > > > > - u16 clk;
> > > > > > > > >
> > > > > > > > > /* Reset SD Clock Enable */
> > > > > > > > > clk = sdhci_readw(host,
> > > > > SDHCI_CLOCK_CONTROL);
> > > > > > > > > @@ -1313,6 +1318,29 @@ static void sdhci_set_ios(struct
> > > > > mmc_host
> > > > > > > > *mmc,
> > > > > > > > > struct mmc_ios *ios)
> > > > > > > > > clk |= SDHCI_CLOCK_CARD_EN;
> > > > > > > > > sdhci_writew(host, clk,
> > > > > SDHCI_CLOCK_CONTROL);
> > > > > > > > > }
> > > > > > > > > +
> > > > > > > > > + ctrl_2 = sdhci_readw(host,
> > > > > SDHCI_HOST_CONTROL2);
> > > > > > > > > +
> > > > > > > > > + /* Select Bus Speed Mode for host */
> > > > > > > > > + if (ios->timing ==
> MMC_TIMING_UHS_SDR25)
> > > > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> > > > > > > > > + else if (ios->timing ==
> > > MMC_TIMING_UHS_SDR50)
> > > > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> > > > > > > > > + else if (ios->timing ==
> > > > MMC_TIMING_UHS_SDR104)
> > > > > > > > > + ctrl_2 |=
> SDHCI_CTRL_UHS_SDR104;
> > > > > > > > > + else if (ios->timing ==
> > > MMC_TIMING_UHS_DDR50)
> > > > > > > > > + ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> > > > > > > > > +
> > > > > > > > > + /* Reset SD Clock Enable */
> > > > > > > > > + clk = sdhci_readw(host,
> > > SDHCI_CLOCK_CONTROL);
> > > > > > > > > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > > > > > > > > + sdhci_writew(host, clk,
> > > SDHCI_CLOCK_CONTROL);
> > > > > > > > > +
> > > > > > > > > + sdhci_writew(host, ctrl_2,
> > > > > SDHCI_HOST_CONTROL2);
> > > > > > > > > +
> > > > > > > > > + /* Re-enable SD Clock */
> > > > > > > > > + clk |= SDHCI_CLOCK_CARD_EN;
> > > > > > > > > + sdhci_writew(host, clk,
> > > SDHCI_CLOCK_CONTROL);
> > > > > > > > > } else
> > > > > > > > > sdhci_writeb(host, ctrl,
> > > > SDHCI_HOST_CONTROL1);
> > > > > > > > >
> > > > > > > > > diff --git a/drivers/mmc/host/sdhci.h
> > > > > b/drivers/mmc/host/sdhci.h
> > > > > > > > > index a407b5b..5bf244d 100644
> > > > > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > > > > @@ -150,6 +150,11 @@
> > > > > > > > > #define SDHCI_ACMD12_ERR 0x3C
> > > > > > > > >
> > > > > > > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > > > > > > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > > > > > > > > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > > > > > > > > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > > > > > > > > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > > > > > > > > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > > > > > > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > > > > > > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > > > > > > > > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > > > > > > > > diff --git a/include/linux/mmc/card.h
> > > > > b/include/linux/mmc/card.h
> > > > > > > > > index 2d7f7a3..0b24c41 100644
> > > > > > > > > --- a/include/linux/mmc/card.h
> > > > > > > > > +++ b/include/linux/mmc/card.h
> > > > > > > > > @@ -75,7 +75,23 @@ struct sd_ssr {
> > > > > > > > >
> > > > > > > > > struct sd_switch_caps {
> > > > > > > > > unsigned int hs_max_dtr;
> > > > > > > > > + unsigned int uhs_max_dtr;
> > > > > > > > > +#define UHS_SDR104_MAX_DTR 208000000
> > > > > > > > > +#define UHS_SDR50_MAX_DTR 100000000
> > > > > > > > > +#define UHS_DDR50_MAX_DTR 50000000
> > > > > > > > > +#define UHS_SDR25_MAX_DTR 50000000
> > > > > > > > > unsigned int uhs_bus_mode;
> > > > > > > > > +#define UHS_SDR12_BUS_SPEED 0
> > > > > > > > > +#define UHS_SDR25_BUS_SPEED 1
> > > > > > > > > +#define UHS_SDR50_BUS_SPEED 2
> > > > > > > > > +#define UHS_SDR104_BUS_SPEED 3
> > > > > > > > > +#define UHS_DDR50_BUS_SPEED 4
> > > > > > > > > +
> > > > > > > > > +#define SD_MODE_UHS_SDR12 (1 <<
> > UHS_SDR12_BUS_SPEED)
> > > > > > > > > +#define SD_MODE_UHS_SDR25 (1 <<
> > UHS_SDR25_BUS_SPEED)
> > > > > > > > > +#define SD_MODE_UHS_SDR50 (1 <<
> > UHS_SDR50_BUS_SPEED)
> > > > > > > > > +#define SD_MODE_UHS_SDR104 (1 <<
> > UHS_SDR104_BUS_SPEED)
> > > > > > > > > +#define SD_MODE_UHS_DDR50 (1 <<
> > UHS_DDR50_BUS_SPEED)
> > > > > > > > > unsigned int uhs_drv_type;
> > > > > > > > > #define SD_DRIVER_TYPE_B 0x01
> > > > > > > > > #define SD_DRIVER_TYPE_A 0x02
> > > > > > > > > diff --git a/include/linux/mmc/host.h
> > > > > b/include/linux/mmc/host.h
> > > > > > > > > index bc2121e..4dfff6d 100644
> > > > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > > > @@ -50,6 +50,10 @@ struct mmc_ios {
> > > > > > > > > #define MMC_TIMING_LEGACY 0
> > > > > > > > > #define MMC_TIMING_MMC_HS 1
> > > > > > > > > #define MMC_TIMING_SD_HS 2
> > > > > > > > > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > > > > > > > > +#define MMC_TIMING_UHS_SDR50 3
> > > > > > > > > +#define MMC_TIMING_UHS_SDR104 4
> > > > > > > > > +#define MMC_TIMING_UHS_DDR50 5
> > > > > > > > >
> > > > > > > > > unsigned char ddr; /* dual
> > > data
> > > > > rate used
> > > > > > */
> > > > > > > > >
> > > > > > > > > --
> > > > > > > > > 1.7.1
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > --
> > > > > > > To unsubscribe from this list: send the line "unsubscribe
> > > linux-
> > > > > mmc"
> > > > > > in
> > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > > info.html
> > > > > >
> > > > >
> > > > >
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe
> linux-
> > > mmc"
> > > > in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > info.html
> > > >
> > >
> >
> >
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection
2011-03-24 7:25 ` Subhash Jadavani
@ 2011-03-24 8:42 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-24 8:42 UTC (permalink / raw)
To: Subhash Jadavani, cjb@laptop.org
Cc: zhangfei.gao@gmail.com, prakity@marvell.com,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Subhash,
> -----Original Message-----
> From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> Sent: Thursday, March 24, 2011 12:55 PM
> To: Nath, Arindam; cjb@laptop.org
> Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> mode selection
>
>
>
> > -----Original Message-----
> > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > Sent: Wednesday, March 23, 2011 7:33 PM
> > To: Subhash Jadavani; cjb@laptop.org
> > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus speed
> > mode selection
> >
> > Hi Subhash,
> >
> >
> > > -----Original Message-----
> > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > Sent: Wednesday, March 23, 2011 12:54 PM
> > > To: Nath, Arindam; cjb@laptop.org
> > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> speed
> > > mode selection
> > >
> > >
> > >
> > > > -----Original Message-----
> > > > From: Nath, Arindam [mailto:Arindam.Nath@amd.com]
> > > > Sent: Wednesday, March 23, 2011 12:06 PM
> > > > To: Subhash Jadavani; cjb@laptop.org
> > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > speed
> > > > mode selection
> > > >
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > Sent: Wednesday, March 23, 2011 11:56 AM
> > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs bus
> > > speed
> > > > > mode selection
> > > > >
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > Sent: Wednesday, March 23, 2011 11:48 AM
> > > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> anath.amd@gmail.com
> > > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs
> bus
> > > > speed
> > > > > > mode selection
> > > > > >
> > > > > > Hi Subhash,
> > > > > >
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > > Sent: Wednesday, March 23, 2011 11:44 AM
> > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > anath.amd@gmail.com
> > > > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for uhs
> > bus
> > > > > speed
> > > > > > > mode selection
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > > -----Original Message-----
> > > > > > > > From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> > > > > > > > owner@vger.kernel.org] On Behalf Of Nath, Arindam
> > > > > > > > Sent: Wednesday, March 23, 2011 11:35 AM
> > > > > > > > To: Subhash Jadavani; cjb@laptop.org
> > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > anath.amd@gmail.com
> > > > > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for
> uhs
> > > bus
> > > > > > speed
> > > > > > > > mode selection
> > > > > > > >
> > > > > > > > Hi Subhash,
> > > > > > > >
> > > > > > > >
> > > > > > > > > -----Original Message-----
> > > > > > > > > From: Subhash Jadavani [mailto:subhashj@codeaurora.org]
> > > > > > > > > Sent: Monday, March 21, 2011 12:13 PM
> > > > > > > > > To: Nath, Arindam; cjb@laptop.org
> > > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com; linux-
> > > > > > > > > mmc@vger.kernel.org; Su, Henry; Lu, Aaron;
> > > > anath.amd@gmail.com
> > > > > > > > > Subject: RE: [PATCH v2 06/12] mmc: sd: add support for
> > uhs
> > > > bus
> > > > > > > speed
> > > > > > > > > mode selection
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > > -----Original Message-----
> > > > > > > > > > From: Arindam Nath [mailto:anath.amd@gmail.com] On
> > Behalf
> > > > Of
> > > > > > > > Arindam
> > > > > > > > > > Nath
> > > > > > > > > > Sent: Friday, March 04, 2011 5:03 PM
> > > > > > > > > > To: cjb@laptop.org
> > > > > > > > > > Cc: zhangfei.gao@gmail.com; prakity@marvell.com;
> > > > > > > > > > subhashj@codeaurora.org; linux-mmc@vger.kernel.org;
> > > > > > > > henry.su@amd.com;
> > > > > > > > > > aaron.lu@amd.com; anath.amd@gmail.com; Arindam Nath
> > > > > > > > > > Subject: [PATCH v2 06/12] mmc: sd: add support for
> uhs
> > > bus
> > > > > > speed
> > > > > > > > mode
> > > > > > > > > > selection
> > > > > > > > > >
> > > > > > > > > > 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. Since MMC_TIMING_UHS_SDR25
> is
> > > same
> > > > > as
> > > > > > > > > > MMC_TIMING_SD_HS, we don't need to set the
> > > SDHCI_CTRL_HISPD
> > > > > > flag
> > > > > > > > for
> > > > > > > > > > UHS SDR25 again. We also 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 set the clock
> > > frequency
> > > > > for
> > > > > > > the
> > > > > > > > > > UHS-I mode selected.
> > > > > > > > > >
> > > > > > > > > > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > > > > > > > > > ---
> > > > > > > > > > drivers/mmc/core/sd.c | 91
> > > > > > > > > > ++++++++++++++++++++++++++++++++++++++++++++++
> > > > > > > > > > drivers/mmc/host/sdhci.c | 32 +++++++++++++++-
> > > > > > > > > > drivers/mmc/host/sdhci.h | 5 +++
> > > > > > > > > > include/linux/mmc/card.h | 16 ++++++++
> > > > > > > > > > include/linux/mmc/host.h | 4 ++
> > > > > > > > > > 5 files changed, 146 insertions(+), 2 deletions(-)
> > > > > > > > > >
> > > > > > > > > > diff --git a/drivers/mmc/core/sd.c
> > > b/drivers/mmc/core/sd.c
> > > > > > > > > > index f6a4fab..ec0d8e6 100644
> > > > > > > > > > --- a/drivers/mmc/core/sd.c
> > > > > > > > > > +++ b/drivers/mmc/core/sd.c
> > > > > > > > > > @@ -464,6 +464,92 @@ static int
> > > > sd_select_driver_type(struct
> > > > > > > > mmc_card
> > > > > > > > > > *card, u8 *status)
> > > > > > > > > > return 0;
> > > > > > > > > > }
> > > > > > > > > >
> > > > > > > > > > +static int sd_set_bus_speed_mode(struct mmc_card
> > *card,
> > > u8
> > > > > > > > *status)
> > > > > > > > > > +{
> > > > > > > > > > + unsigned int bus_speed, timing;
> > > > > > > > > > + int err;
> > > > > > > > > > +
> > > > > > > > > > + /*
> > > > > > > > > > + * If the host doesn't support any of the
> UHS-I
> > > > > modes,
> > > > > > > fallback
> > > > > > > > > > on
> > > > > > > > > > + * default speed.
> > > > > > > > > > + */
> > > > > > > > > > + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 |
> > > > > > > MMC_CAP_UHS_SDR25 |
> > > > > > > > > > + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
> > > > > > > MMC_CAP_UHS_DDR50)))
> > > > > > > > > > + return 0;
> > > > > > > > > > +
> > > > > > > > > > + if (card->host->caps & MMC_CAP_UHS_SDR104) {
> > > > > > > > > > + if (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR104)
> > > > > > > {
> > > > > > > > > > + bus_speed =
> > UHS_SDR104_BUS_SPEED;
> > > > > > > > > > + timing =
> MMC_TIMING_UHS_SDR104;
> > > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > > UHS_SDR104_MAX_DTR;
> > > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode
> &
> > > > > > > SD_MODE_UHS_SDR50)
> > > > > > > > > > {
> > > > > > > > > > + bus_speed =
> > UHS_SDR50_BUS_SPEED;
> > > > > > > > > > + timing =
> MMC_TIMING_UHS_SDR50;
> > > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > > UHS_SDR50_MAX_DTR;
> > > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode
> &
> > > > > > > SD_MODE_UHS_DDR50)
> > > > > > > > > > {
> > > > > > > > > > + bus_speed =
> > UHS_DDR50_BUS_SPEED;
> > > > > > > > > > + timing =
> MMC_TIMING_UHS_DDR50;
> > > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > > UHS_DDR50_MAX_DTR;
> > > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode
> &
> > > > > > > SD_MODE_UHS_SDR25)
> > > > > > > > > > {
> > > > > > > > > > + bus_speed =
> > UHS_SDR25_BUS_SPEED;
> > > > > > > > > > + timing =
> MMC_TIMING_UHS_SDR25;
> > > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > > UHS_SDR25_MAX_DTR;
> > > > > > > > > > + }
> > > > > > > > >
> > > > > > > > > I would not agree that if Host cap is set to
> > > > MMC_CAP_UHS_SDR104
> > > > > > > > implies
> > > > > > > > > that
> > > > > > > > > DDR50 mode is supported by host. There may be a case
> > where
> > > > host
> > > > > > > > > controller
> > > > > > > > > only support SDR timing modes.
> > > > > > > > > Basically if host should advertise all the supported
> > modes
> > > > > > (SDR104,
> > > > > > > > > SDR50,
> > > > > > > > > SDR25, SDR12 & DDR) by caps and then we should choose
> the
> > > > > highest
> > > > > > > > > performance mode depending on both the card and host's
> > > > highest
> > > > > > > > > performance
> > > > > > > > > mode.
> > > > > > > > >
> > > > > > > > > So this should be the check:
> > > > > > > > >
> > > > > > > > > if ((card->host->caps & MMC_CAP_UHS_SDR104 &&
> > > > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
> > > > > > > > > bus_speed = UHS_SDR104_BUS_SPEED;
> > > > > > > > > timing = MMC_TIMING_UHS_SDR104;
> > > > > > > > > card->sw_caps.uhs_max_dtr =
> > > > > UHS_SDR104_MAX_DTR;
> > > > > > > > > } else if ((card->host->caps & MMC_CAP_UHS_DDR50 &&
> > > > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> > > > > > > > > bus_speed = UHS_DDR50_BUS_SPEED;
> > > > > > > > > timing = MMC_TIMING_UHS_DDR50;
> > > > > > > > > card->sw_caps.uhs_max_dtr =
> > > > > UHS_DDR50_MAX_DTR;
> > > > > > > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR50 &&
> > > > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR50)) {
> > > > > > > > > bus_speed = UHS_SDR50_BUS_SPEED;
> > > > > > > > > timing = MMC_TIMING_UHS_SDR50;
> > > > > > > > > card->sw_caps.uhs_max_dtr =
> > > > > UHS_SDR50_MAX_DTR;
> > > > > > > > > } else if ((card->host->caps & MMC_CAP_UHS_SDR25 &&
> > > > > > > > > (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR25)) {
> > > > > > > > > bus_speed = UHS_SDR50_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_SDR12 &&
> > > > > > > > > (card->sw_caps.uhs_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;
> > > > > > > > > }
> > > > > > > >
> > > > > > > > A host capable of operating at a higher bus speed mode
> > should
> > > > be
> > > > > > able
> > > > > > > > to support cards with a lower bus speed mode. In case of
> > your
> > > > > > > > conditions checks above, you are checking for the exact
> > match
> > > > for
> > > > > > the
> > > > > > > > bus speed modes, otherwise you will fall back on using
> > > default
> > > > > bus
> > > > > > > > speed mode, thus performance loss.
> > > > > > >
> > > > > > > Why should we implicitly assume that host supporting SDR104
> > > mode
> > > > > > would
> > > > > > > also
> > > > > > > support DDR50 mode? Is there any spec. which says so? If
> not
> > > then
> > > > > we
> > > > > > > should
> > > > > > > rely on host capability to take the decision. DDR50 would
> > > require
> > > > > > > different
> > > > > > > timing implementation on the host side and host may choose
> > not
> > > to
> > > > > > > implement
> > > > > > > DDR50 timing support then?
> > > > > > >
> > > > > > > I would agree that host supporting SDR104 mode should also
> be
> > > > > > > supporting
> > > > > > > SDR50/SDR25/SDR12 modes but it may not be true for DDR50
> > mode.
> > > > > >
> > > > > > Please refer to footnote of table 3-5 of the Physical Layer
> > spec
> > > > > v3.01.
> > > > > > It says:
> > > > > >
> > > > > > "The card supports a UHS-I mode shall support all lower UHS-I
> > > > modes."
> > > > > >
> > > > > > So if this is true for card, doesn't the same apply to host?
> > > > >
> > > > > Yes, this is the exact point I am stretching. Spec. never says
> > the
> > > > same
> > > > > thing for host so we should not assume it. we already have
> luxury
> > > to
> > > > > use
> > > > > host caps then why should we assume something which may not be
> > > > correct?
> > > >
> > > > I still think we should have a combination which works best based
> > on
> > > > the capabilities of the host and card, rather than falling back
> to
> > > > default bus speed right away. I can try to separate out the case
> > for
> > > > DDR50, if you want that. Otherwise, I will wait for few more
> > comments
> > > > on the same.
> > >
> > > Thanks. My code snippet was just an example. Yes, just separating
> out
> > > DDR50
> > > should be enough.
> >
> > Section 3.9.3 of the Physical Layer spec v3.01 says:
> >
> > "Host may use SDR50, DDR50 and SDR104 with either UHS50 card or
> UHS104
> > card."
> >
> > So doesn't that mean that we should also check for all possible
> > combinations of host and card when the host supports DDR50 mode? Just
> a
> > thought.
>
> Yes, definitely. We should choose the highest performance mode
> supported by
> both card and host. How about following checks?
>
> if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
> (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_SDR104)) {
> bus_speed = UHS_SDR104_BUS_SPEED;
> timing = MMC_TIMING_UHS_SDR104;
> card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
> } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
> (card->sw_caps.uhs_bus_mode & SD_MODE_UHS_DDR50)) {
> bus_speed = UHS_DDR50_BUS_SPEED;
> timing = MMC_TIMING_UHS_DDR50;
> card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
> } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
> MMC_CAP_UHS_SDR50)) && (card->sw_caps.uhs_bus_mode &
> SD_MODE_UHS_SDR50)) {
> bus_speed = UHS_SDR50_BUS_SPEED;
> timing = MMC_TIMING_UHS_SDR50;
> card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
> } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
> MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) && (card->sw_caps.uhs_bus_mode
> &
> SD_MODE_UHS_SDR25)) {
> bus_speed = UHS_SDR50_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.uhs_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;
> }
>
>
> Here let's say if host sets both DDR50 and SDR50 caps and card also
> supports
> the DDR50 and SDR50 then we have give preference to DDR50 mode as it
> would
> require lower interface clock compared to SDR50. Although spec is not
> saying
> anything about which one should be given a preference when both are
> supported by card and host.
>
> Does above code snippest looks fine?
I am okay with this. I just hope that somebody else doesn't have a different opinion at a later point of time. Anyways, I will make the changes as per your snippet above.
Thanks,
Arindam
>
> >
> > Thanks,
> > Arindam
> >
> > >
> > > >
> > > > Thanks,
> > > > Arindam
> > > >
> > > > >
> > > > > >
> > > > > > Thanks,
> > > > > > Arindam
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > Thanks,
> > > > > > > > Arindam
> > > > > > > >
> > > > > > > > >
> > > > > > > > > Regards,
> > > > > > > > > Subhash
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > > + } else if (card->host->caps &
> > MMC_CAP_UHS_SDR50)
> > > {
> > > > > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR104)
> > > > > > > ||
> > > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR50))
> > > > > > > {
> > > > > > > > > > + bus_speed =
> > UHS_SDR50_BUS_SPEED;
> > > > > > > > > > + timing =
> MMC_TIMING_UHS_SDR50;
> > > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > > UHS_SDR50_MAX_DTR;
> > > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode
> &
> > > > > > > SD_MODE_UHS_DDR50)
> > > > > > > > > > {
> > > > > > > > > > + bus_speed =
> > UHS_DDR50_BUS_SPEED;
> > > > > > > > > > + timing =
> MMC_TIMING_UHS_DDR50;
> > > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > > UHS_DDR50_MAX_DTR;
> > > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode
> &
> > > > > > > SD_MODE_UHS_SDR25)
> > > > > > > > > > {
> > > > > > > > > > + bus_speed =
> > UHS_SDR25_BUS_SPEED;
> > > > > > > > > > + timing =
> MMC_TIMING_UHS_SDR25;
> > > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > > UHS_SDR25_MAX_DTR;
> > > > > > > > > > + }
> > > > > > > > > > + } else if (card->host->caps &
> > MMC_CAP_UHS_DDR50)
> > > {
> > > > > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR104)
> > > > > > > ||
> > > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR50)
> > > > > > > ||
> > > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_DDR50))
> > > > > > > {
> > > > > > > > > > + bus_speed =
> > UHS_DDR50_BUS_SPEED;
> > > > > > > > > > + timing =
> MMC_TIMING_UHS_DDR50;
> > > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > > UHS_DDR50_MAX_DTR;
> > > > > > > > > > + } else if (card->sw_caps.uhs_bus_mode
> &
> > > > > > > SD_MODE_UHS_SDR25)
> > > > > > > > > > {
> > > > > > > > > > + bus_speed =
> > UHS_SDR25_BUS_SPEED;
> > > > > > > > > > + timing =
> MMC_TIMING_UHS_SDR25;
> > > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > > UHS_SDR25_MAX_DTR;
> > > > > > > > > > + }
> > > > > > > > > > + } else if (card->host->caps &
> > MMC_CAP_UHS_SDR25)
> > > {
> > > > > > > > > > + if ((card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR104)
> > > > > > > ||
> > > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR50)
> > > > > > > ||
> > > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_DDR50)
> > > > > > > ||
> > > > > > > > > > + (card->sw_caps.uhs_bus_mode &
> > > > > > SD_MODE_UHS_SDR25))
> > > > > > > {
> > > > > > > > > > + bus_speed =
> > UHS_SDR25_BUS_SPEED;
> > > > > > > > > > + timing =
> MMC_TIMING_UHS_SDR25;
> > > > > > > > > > + card->sw_caps.uhs_max_dtr =
> > > > > > UHS_SDR25_MAX_DTR;
> > > > > > > > > > + }
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + err = mmc_sd_switch(card, 1, 0, bus_speed,
> > > status);
> > > > > > > > > > + if (err)
> > > > > > > > > > + return err;
> > > > > > > > > > +
> > > > > > > > > > + if ((status[16] & 0xF) != bus_speed)
> > > > > > > > > > + printk(KERN_WARNING "%s: Problem
> > setting
> > > > bus
> > > > > > speed
> > > > > > > > > > mode!\n",
> > > > > > > > > > + mmc_hostname(card->host));
> > > > > > > > > > + else {
> > > > > > > > > > + if (bus_speed) {
> > > > > > > > > > + mmc_set_timing(card->host,
> > > timing);
> > > > > > > > > > + mmc_set_clock(card->host,
> > > > > > > > > card->sw_caps.uhs_max_dtr);
> > > > > > > > > > + }
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > /*
> > > > > > > > > > * UHS-I specific initialization procedure
> > > > > > > > > > */
> > > > > > > > > > @@ -499,6 +585,11 @@ static int
> > > mmc_sd_init_uhs_card(struct
> > > > > > > > mmc_card
> > > > > > > > > > *card)
> > > > > > > > > >
> > > > > > > > > > /* Set the driver strength for the card */
> > > > > > > > > > err = sd_select_driver_type(card, status);
> > > > > > > > > > + if (err)
> > > > > > > > > > + goto out;
> > > > > > > > > > +
> > > > > > > > > > + /* Set bus speed mode of the card */
> > > > > > > > > > + err = sd_set_bus_speed_mode(card, status);
> > > > > > > > > >
> > > > > > > > > > out:
> > > > > > > > > > kfree(status);
> > > > > > > > > > diff --git a/drivers/mmc/host/sdhci.c
> > > > > > b/drivers/mmc/host/sdhci.c
> > > > > > > > > > index 5d3bb11..f127fa2 100644
> > > > > > > > > > --- a/drivers/mmc/host/sdhci.c
> > > > > > > > > > +++ b/drivers/mmc/host/sdhci.c
> > > > > > > > > > @@ -1278,7 +1278,13 @@ static void
> sdhci_set_ios(struct
> > > > > > mmc_host
> > > > > > > > > *mmc,
> > > > > > > > > > struct mmc_ios *ios)
> > > > > > > > > > ctrl &= ~SDHCI_CTRL_HISPD;
> > > > > > > > > >
> > > > > > > > > > if (host->version >= SDHCI_SPEC_300) {
> > > > > > > > > > - u16 ctrl_2;
> > > > > > > > > > + u16 clk, ctrl_2;
> > > > > > > > > > +
> > > > > > > > > > + /* In case of UHS-I modes, set High
> > Speed
> > > > > > Enable */
> > > > > > > > > > + if ((ios->timing ==
> > MMC_TIMING_UHS_SDR50)
> > > > ||
> > > > > > > > > > + (ios->timing ==
> > > MMC_TIMING_UHS_SDR104)
> > > > ||
> > > > > > > > > > + (ios->timing ==
> > > MMC_TIMING_UHS_DDR50))
> > > > > > > > > > + ctrl |= SDHCI_CTRL_HISPD;
> > > > > > > > > >
> > > > > > > > > > ctrl_2 = sdhci_readw(host,
> > > > > > SDHCI_HOST_CONTROL2);
> > > > > > > > > > if (!(ctrl_2 &
> > > > SDHCI_CTRL_PRESET_VAL_ENABLE))
> > > > > {
> > > > > > > > > > @@ -1300,7 +1306,6 @@ static void
> sdhci_set_ios(struct
> > > > > mmc_host
> > > > > > > > *mmc,
> > > > > > > > > > struct mmc_ios *ios)
> > > > > > > > > > * need to reset SD Clock
> > Enable
> > > > > before
> > > > > > > changing
> > > > > > > > > High
> > > > > > > > > > * Speed Enable to avoid
> > > generating
> > > > > > clock
> > > > > > > gliches.
> > > > > > > > > > */
> > > > > > > > > > - u16 clk;
> > > > > > > > > >
> > > > > > > > > > /* Reset SD Clock Enable */
> > > > > > > > > > clk = sdhci_readw(host,
> > > > > > SDHCI_CLOCK_CONTROL);
> > > > > > > > > > @@ -1313,6 +1318,29 @@ static void
> sdhci_set_ios(struct
> > > > > > mmc_host
> > > > > > > > > *mmc,
> > > > > > > > > > struct mmc_ios *ios)
> > > > > > > > > > clk |= SDHCI_CLOCK_CARD_EN;
> > > > > > > > > > sdhci_writew(host, clk,
> > > > > > SDHCI_CLOCK_CONTROL);
> > > > > > > > > > }
> > > > > > > > > > +
> > > > > > > > > > + ctrl_2 = sdhci_readw(host,
> > > > > > SDHCI_HOST_CONTROL2);
> > > > > > > > > > +
> > > > > > > > > > + /* Select Bus Speed Mode for host */
> > > > > > > > > > + if (ios->timing ==
> > MMC_TIMING_UHS_SDR25)
> > > > > > > > > > + ctrl_2 |=
> SDHCI_CTRL_UHS_SDR25;
> > > > > > > > > > + else if (ios->timing ==
> > > > MMC_TIMING_UHS_SDR50)
> > > > > > > > > > + ctrl_2 |=
> SDHCI_CTRL_UHS_SDR50;
> > > > > > > > > > + else if (ios->timing ==
> > > > > MMC_TIMING_UHS_SDR104)
> > > > > > > > > > + ctrl_2 |=
> > SDHCI_CTRL_UHS_SDR104;
> > > > > > > > > > + else if (ios->timing ==
> > > > MMC_TIMING_UHS_DDR50)
> > > > > > > > > > + ctrl_2 |=
> SDHCI_CTRL_UHS_DDR50;
> > > > > > > > > > +
> > > > > > > > > > + /* Reset SD Clock Enable */
> > > > > > > > > > + clk = sdhci_readw(host,
> > > > SDHCI_CLOCK_CONTROL);
> > > > > > > > > > + clk &= ~SDHCI_CLOCK_CARD_EN;
> > > > > > > > > > + sdhci_writew(host, clk,
> > > > SDHCI_CLOCK_CONTROL);
> > > > > > > > > > +
> > > > > > > > > > + sdhci_writew(host, ctrl_2,
> > > > > > SDHCI_HOST_CONTROL2);
> > > > > > > > > > +
> > > > > > > > > > + /* Re-enable SD Clock */
> > > > > > > > > > + clk |= SDHCI_CLOCK_CARD_EN;
> > > > > > > > > > + sdhci_writew(host, clk,
> > > > SDHCI_CLOCK_CONTROL);
> > > > > > > > > > } else
> > > > > > > > > > sdhci_writeb(host, ctrl,
> > > > > SDHCI_HOST_CONTROL1);
> > > > > > > > > >
> > > > > > > > > > diff --git a/drivers/mmc/host/sdhci.h
> > > > > > b/drivers/mmc/host/sdhci.h
> > > > > > > > > > index a407b5b..5bf244d 100644
> > > > > > > > > > --- a/drivers/mmc/host/sdhci.h
> > > > > > > > > > +++ b/drivers/mmc/host/sdhci.h
> > > > > > > > > > @@ -150,6 +150,11 @@
> > > > > > > > > > #define SDHCI_ACMD12_ERR 0x3C
> > > > > > > > > >
> > > > > > > > > > #define SDHCI_HOST_CONTROL2 0x3E
> > > > > > > > > > +#define SDHCI_CTRL_UHS_SDR12 0x0000
> > > > > > > > > > +#define SDHCI_CTRL_UHS_SDR25 0x0001
> > > > > > > > > > +#define SDHCI_CTRL_UHS_SDR50 0x0002
> > > > > > > > > > +#define SDHCI_CTRL_UHS_SDR104 0x0003
> > > > > > > > > > +#define SDHCI_CTRL_UHS_DDR50 0x0004
> > > > > > > > > > #define SDHCI_CTRL_VDD_180 0x0008
> > > > > > > > > > #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> > > > > > > > > > #define SDHCI_CTRL_DRV_TYPE_A 0x0010
> > > > > > > > > > diff --git a/include/linux/mmc/card.h
> > > > > > b/include/linux/mmc/card.h
> > > > > > > > > > index 2d7f7a3..0b24c41 100644
> > > > > > > > > > --- a/include/linux/mmc/card.h
> > > > > > > > > > +++ b/include/linux/mmc/card.h
> > > > > > > > > > @@ -75,7 +75,23 @@ struct sd_ssr {
> > > > > > > > > >
> > > > > > > > > > struct sd_switch_caps {
> > > > > > > > > > unsigned int hs_max_dtr;
> > > > > > > > > > + unsigned int uhs_max_dtr;
> > > > > > > > > > +#define UHS_SDR104_MAX_DTR 208000000
> > > > > > > > > > +#define UHS_SDR50_MAX_DTR 100000000
> > > > > > > > > > +#define UHS_DDR50_MAX_DTR 50000000
> > > > > > > > > > +#define UHS_SDR25_MAX_DTR 50000000
> > > > > > > > > > unsigned int uhs_bus_mode;
> > > > > > > > > > +#define UHS_SDR12_BUS_SPEED 0
> > > > > > > > > > +#define UHS_SDR25_BUS_SPEED 1
> > > > > > > > > > +#define UHS_SDR50_BUS_SPEED 2
> > > > > > > > > > +#define UHS_SDR104_BUS_SPEED 3
> > > > > > > > > > +#define UHS_DDR50_BUS_SPEED 4
> > > > > > > > > > +
> > > > > > > > > > +#define SD_MODE_UHS_SDR12 (1 <<
> > > UHS_SDR12_BUS_SPEED)
> > > > > > > > > > +#define SD_MODE_UHS_SDR25 (1 <<
> > > UHS_SDR25_BUS_SPEED)
> > > > > > > > > > +#define SD_MODE_UHS_SDR50 (1 <<
> > > UHS_SDR50_BUS_SPEED)
> > > > > > > > > > +#define SD_MODE_UHS_SDR104 (1 <<
> > > UHS_SDR104_BUS_SPEED)
> > > > > > > > > > +#define SD_MODE_UHS_DDR50 (1 <<
> > > UHS_DDR50_BUS_SPEED)
> > > > > > > > > > unsigned int uhs_drv_type;
> > > > > > > > > > #define SD_DRIVER_TYPE_B 0x01
> > > > > > > > > > #define SD_DRIVER_TYPE_A 0x02
> > > > > > > > > > diff --git a/include/linux/mmc/host.h
> > > > > > b/include/linux/mmc/host.h
> > > > > > > > > > index bc2121e..4dfff6d 100644
> > > > > > > > > > --- a/include/linux/mmc/host.h
> > > > > > > > > > +++ b/include/linux/mmc/host.h
> > > > > > > > > > @@ -50,6 +50,10 @@ struct mmc_ios {
> > > > > > > > > > #define MMC_TIMING_LEGACY 0
> > > > > > > > > > #define MMC_TIMING_MMC_HS 1
> > > > > > > > > > #define MMC_TIMING_SD_HS 2
> > > > > > > > > > +#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
> > > > > > > > > > +#define MMC_TIMING_UHS_SDR50 3
> > > > > > > > > > +#define MMC_TIMING_UHS_SDR104 4
> > > > > > > > > > +#define MMC_TIMING_UHS_DDR50 5
> > > > > > > > > >
> > > > > > > > > > unsigned char ddr; /*
> dual
> > > > data
> > > > > > rate used
> > > > > > > */
> > > > > > > > > >
> > > > > > > > > > --
> > > > > > > > > > 1.7.1
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > --
> > > > > > > > To unsubscribe from this list: send the line "unsubscribe
> > > > linux-
> > > > > > mmc"
> > > > > > > in
> > > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > > > info.html
> > > > > > >
> > > > > >
> > > > > >
> > > > > > --
> > > > > > To unsubscribe from this list: send the line "unsubscribe
> > linux-
> > > > mmc"
> > > > > in
> > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > More majordomo info at http://vger.kernel.org/majordomo-
> > > info.html
> > > > >
> > > >
> > >
> > >
> >
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-04 11:32 ` [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
` (4 preceding siblings ...)
2011-03-16 21:39 ` Philip Rakity
@ 2011-03-24 10:52 ` zhangfei gao
2011-03-24 10:59 ` Nath, Arindam
5 siblings, 1 reply; 125+ messages in thread
From: zhangfei gao @ 2011-03-24 10:52 UTC (permalink / raw)
To: Arindam Nath
Cc: cjb, prakity, subhashj, linux-mmc, henry.su, aaron.lu, anath.amd
On Fri, Mar 4, 2011 at 6:32 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 support 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.
>
> Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> ---
> drivers/mmc/core/sd.c | 37 ++++++++++-
> drivers/mmc/core/sd_ops.c | 32 ++++++++++
> drivers/mmc/core/sd_ops.h | 1 +
> drivers/mmc/host/sdhci.c | 148 +++++++++++++++++++++++++++++++++++++++++----
> drivers/mmc/host/sdhci.h | 18 +++++-
> include/linux/mmc/host.h | 10 +++
> include/linux/mmc/sd.h | 1 +
> 7 files changed, 229 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index b3f8a3c..3e82599 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -408,6 +408,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
> @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
> if (!err)
> ocr |= 1 << 30;
>
> - err = mmc_send_app_op_cond(host, ocr, NULL);
> + /* If the host can supply more than 150mA, XPC should be set to 1. */
> + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> + MMC_CAP_SET_XPC_180))
> + ocr |= 1 << 28;
> +
> + 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)) {
> + err = mmc_start_voltage_switch(host);
> + if (err)
> + return err;
> + }
> +
> if (mmc_host_is_spi(host))
> err = mmc_send_cid(host, cid);
> else
> @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> }
>
> /*
> + * 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))
> + host->ocr |= (1 << 24);
> +
> + /*
> * Detect and init the card.
> */
> err = mmc_sd_init_card(host, host->ocr, NULL);
> - if (err)
> - goto err;
> + if (err == -EAGAIN) {
> + /*
> + * Retry initialization with S18R set to 0.
> + */
> + host->ocr &= ~(1 << 24);
> + err = mmc_sd_init_card(host, host->ocr, NULL);
> + if (err)
> + goto err;
> + }
>
> mmc_release_host(host);
> err = mmc_add_card(host->card);
> diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> index 797cdb5..8a23e2e 100644
> --- a/drivers/mmc/core/sd_ops.c
> +++ b/drivers/mmc/core/sd_ops.c
> @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
> return 0;
> }
>
> +int mmc_start_voltage_switch(struct mmc_host *host)
> +{
> + struct mmc_command cmd;
> + int err;
> +
> + BUG_ON(!host);
> +
> + /*
> + * If the host does not provide signal voltage switch procedure, we
> + * set S18R to 0, and then retry initialization sequence.
> + */
> + if (!host->ops->start_signal_voltage_switch)
> + return -EAGAIN;
> +
> + 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;
> +
> + err = host->ops->start_signal_voltage_switch(host);
> +
> + return err;
> +}
> +
> int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> {
> struct mmc_command cmd;
> diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> index ffc2305..3cfba59 100644
> --- a/drivers/mmc/core/sd_ops.h
> +++ b/drivers/mmc/core/sd_ops.h
> @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
> int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> u8 value, u8 *resp);
> int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> +int mmc_start_voltage_switch(struct mmc_host *host);
>
> #endif
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 8914a25..5487a0b 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -85,6 +85,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",
> @@ -1337,11 +1339,70 @@ out:
> spin_unlock_irqrestore(&host->lock, flags);
> }
>
> +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> +{
> + struct sdhci_host *host;
> + u8 pwr;
> + u16 clk, ctrl;
> + u32 present_state;
> +
> + host = mmc_priv(mmc);
> +
> + /* Stop SDCLK */
> + host = mmc_priv(mmc);
> + 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_readw(host, SDHCI_HOST_CONTROL2);
> + ctrl |= SDHCI_CTRL_VDD_180;
> + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
Hi, Arindam
How about adding one call back for switching signal voltage here,
since some controller can not provide voltage by itself, but rely on
external pmic to provide voltage.
For example add one call back in sdhci_ops
int (*set_signal_voltage)(struct sdhci_host *host, int
signal_voltage);
And here could call set_signal_voltage.
if (host->ops->set_signal_voltage)
ret = host->ops->set_signal_voltage(host, ios->signal_voltage);
Thanks a lot.
> +
> + /* 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. Stop
> + * power to 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);
> +
> + return -EAGAIN;
> +}
> +
> 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,
> };
>
> /*****************************************************************************\
> @@ -1799,7 +1860,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);
> @@ -1822,12 +1885,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);
>
> + if (host->version >= SDHCI_SPEC_300)
> + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> +
> 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;
> @@ -1838,7 +1904,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) &&
> @@ -1892,10 +1959,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;
> @@ -1911,7 +1978,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);
> @@ -1923,7 +1990,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;
>
> /*
> @@ -1950,21 +2017,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)
> @@ -2024,7 +2146,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 223762c..95d70e6 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -69,6 +69,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
> @@ -147,7 +149,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
> @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -117,6 +117,8 @@ struct mmc_host_ops {
>
> /* optional callback for HC quirks */
> void (*init_card)(struct mmc_host *host, struct mmc_card *card);
> +
> + int (*start_signal_voltage_switch)(struct mmc_host *host);
> };
>
> struct mmc_card;
> @@ -173,6 +175,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 178363b..3ba5aa6 100644
> --- a/include/linux/mmc/sd.h
> +++ b/include/linux/mmc/sd.h
> @@ -17,6 +17,7 @@
> /* This is basically the same command as for MMC with some quirks. */
> #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
> #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
> +#define SD_SWITCH_VOLTAGE 11 /* ac R1 */
>
> /* class 10 */
> #define SD_SWITCH 6 /* adtc [31:0] See below R1 */
> --
> 1.7.1
>
>
^ permalink raw reply [flat|nested] 125+ messages in thread
* RE: [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure
2011-03-24 10:52 ` zhangfei gao
@ 2011-03-24 10:59 ` Nath, Arindam
0 siblings, 0 replies; 125+ messages in thread
From: Nath, Arindam @ 2011-03-24 10:59 UTC (permalink / raw)
To: zhangfei gao
Cc: cjb@laptop.org, prakity@marvell.com, subhashj@codeaurora.org,
linux-mmc@vger.kernel.org, Su, Henry, Lu, Aaron,
anath.amd@gmail.com
Hi Zhangfei,
> -----Original Message-----
> From: zhangfei gao [mailto:zhangfei.gao@gmail.com]
> Sent: Thursday, March 24, 2011 4:22 PM
> To: Nath, Arindam
> Cc: cjb@laptop.org; prakity@marvell.com; subhashj@codeaurora.org;
> linux-mmc@vger.kernel.org; Su, Henry; Lu, Aaron; anath.amd@gmail.com
> Subject: Re: [PATCH v2 02/12] mmc: sd: add support for signal voltage
> switch procedure
>
> On Fri, Mar 4, 2011 at 6:32 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 support 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.
> >
> > Signed-off-by: Arindam Nath <arindam.nath@amd.com>
> > ---
> > drivers/mmc/core/sd.c | 37 ++++++++++-
> > drivers/mmc/core/sd_ops.c | 32 ++++++++++
> > drivers/mmc/core/sd_ops.h | 1 +
> > drivers/mmc/host/sdhci.c | 148
> +++++++++++++++++++++++++++++++++++++++++----
> > drivers/mmc/host/sdhci.h | 18 +++++-
> > include/linux/mmc/host.h | 10 +++
> > include/linux/mmc/sd.h | 1 +
> > 7 files changed, 229 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> > index b3f8a3c..3e82599 100644
> > --- a/drivers/mmc/core/sd.c
> > +++ b/drivers/mmc/core/sd.c
> > @@ -408,6 +408,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
> > @@ -427,10 +428,25 @@ int mmc_sd_get_cid(struct mmc_host *host, u32
> ocr, u32 *cid)
> > if (!err)
> > ocr |= 1 << 30;
> >
> > - err = mmc_send_app_op_cond(host, ocr, NULL);
> > + /* If the host can supply more than 150mA, XPC should be set
> to 1. */
> > + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
> > + MMC_CAP_SET_XPC_180))
> > + ocr |= 1 << 28;
> > +
> > + 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)) {
> > + err = mmc_start_voltage_switch(host);
> > + if (err)
> > + return err;
> > + }
> > +
> > if (mmc_host_is_spi(host))
> > err = mmc_send_cid(host, cid);
> > else
> > @@ -827,11 +843,26 @@ int mmc_attach_sd(struct mmc_host *host)
> > }
> >
> > /*
> > + * 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))
> > + host->ocr |= (1 << 24);
> > +
> > + /*
> > * Detect and init the card.
> > */
> > err = mmc_sd_init_card(host, host->ocr, NULL);
> > - if (err)
> > - goto err;
> > + if (err == -EAGAIN) {
> > + /*
> > + * Retry initialization with S18R set to 0.
> > + */
> > + host->ocr &= ~(1 << 24);
> > + err = mmc_sd_init_card(host, host->ocr, NULL);
> > + if (err)
> > + goto err;
> > + }
> >
> > mmc_release_host(host);
> > err = mmc_add_card(host->card);
> > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
> > index 797cdb5..8a23e2e 100644
> > --- a/drivers/mmc/core/sd_ops.c
> > +++ b/drivers/mmc/core/sd_ops.c
> > @@ -146,6 +146,38 @@ int mmc_app_set_bus_width(struct mmc_card *card,
> int width)
> > return 0;
> > }
> >
> > +int mmc_start_voltage_switch(struct mmc_host *host)
> > +{
> > + struct mmc_command cmd;
> > + int err;
> > +
> > + BUG_ON(!host);
> > +
> > + /*
> > + * If the host does not provide signal voltage switch
> procedure, we
> > + * set S18R to 0, and then retry initialization sequence.
> > + */
> > + if (!host->ops->start_signal_voltage_switch)
> > + return -EAGAIN;
> > +
> > + 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;
> > +
> > + err = host->ops->start_signal_voltage_switch(host);
> > +
> > + return err;
> > +}
> > +
> > int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> > {
> > struct mmc_command cmd;
> > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
> > index ffc2305..3cfba59 100644
> > --- a/drivers/mmc/core/sd_ops.h
> > +++ b/drivers/mmc/core/sd_ops.h
> > @@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32
> *scr);
> > int mmc_sd_switch(struct mmc_card *card, int mode, int group,
> > u8 value, u8 *resp);
> > int mmc_app_sd_status(struct mmc_card *card, void *ssr);
> > +int mmc_start_voltage_switch(struct mmc_host *host);
> >
> > #endif
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 8914a25..5487a0b 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -85,6 +85,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",
> > @@ -1337,11 +1339,70 @@ out:
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
> >
> > +static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
> > +{
> > + struct sdhci_host *host;
> > + u8 pwr;
> > + u16 clk, ctrl;
> > + u32 present_state;
> > +
> > + host = mmc_priv(mmc);
> > +
> > + /* Stop SDCLK */
> > + host = mmc_priv(mmc);
> > + 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_readw(host, SDHCI_HOST_CONTROL2);
> > + ctrl |= SDHCI_CTRL_VDD_180;
> > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
>
> Hi, Arindam
>
> How about adding one call back for switching signal voltage here,
> since some controller can not provide voltage by itself, but rely on
> external pmic to provide voltage.
> For example add one call back in sdhci_ops
> int (*set_signal_voltage)(struct sdhci_host *host, int
> signal_voltage);
> And here could call set_signal_voltage.
> if (host->ops->set_signal_voltage)
> ret = host->ops->set_signal_voltage(host, ios->signal_voltage);
Similar suggestion was provided by Subhash. I will be setting a variable called signal_voltage which will hold either 0 or 1 (3.3V or 1.8V). So controllers can probably take the necessary action based on the value of this variable. I will be adding this support in V3 patchset.
Thanks,
Arindam
>
> Thanks a lot.
>
> > +
> > + /* 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. Stop
> > + * power to 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);
> > +
> > + return -EAGAIN;
> > +}
> > +
> > 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,
> > };
> >
> >
> /*********************************************************************
> ********\
> > @@ -1799,7 +1860,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);
> > @@ -1822,12 +1885,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);
> >
> > + if (host->version >= SDHCI_SPEC_300)
> > + caps[1] = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> > +
> > 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;
> > @@ -1838,7 +1904,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) &&
> > @@ -1892,10 +1959,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;
> > @@ -1911,7 +1978,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);
> > @@ -1923,7 +1990,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;
> >
> > /*
> > @@ -1950,21 +2017,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)
> > @@ -2024,7 +2146,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 223762c..95d70e6 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -69,6 +69,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
> > @@ -147,7 +149,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
> > @@ -168,9 +171,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 bcb793e..ad7daa3 100644
> > --- a/include/linux/mmc/host.h
> > +++ b/include/linux/mmc/host.h
> > @@ -117,6 +117,8 @@ struct mmc_host_ops {
> >
> > /* optional callback for HC quirks */
> > void (*init_card)(struct mmc_host *host, struct mmc_card
> *card);
> > +
> > + int (*start_signal_voltage_switch)(struct mmc_host
> *host);
> > };
> >
> > struct mmc_card;
> > @@ -173,6 +175,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 178363b..3ba5aa6 100644
> > --- a/include/linux/mmc/sd.h
> > +++ b/include/linux/mmc/sd.h
> > @@ -17,6 +17,7 @@
> > /* This is basically the same command as for MMC with some quirks.
> */
> > #define SD_SEND_RELATIVE_ADDR 3 /* bcr R6
> */
> > #define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7
> */
> > +#define SD_SWITCH_VOLTAGE 11 /* ac R1
> */
> >
> > /* class 10 */
> > #define SD_SWITCH 6 /* adtc [31:0] See below R1
> */
> > --
> > 1.7.1
> >
> >
^ permalink raw reply [flat|nested] 125+ messages in thread
end of thread, other threads:[~2011-03-24 11:00 UTC | newest]
Thread overview: 125+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-04 11:32 [PATCH v2 00/12] add support for host controller v3.00 Arindam Nath
2011-03-04 11:32 ` [PATCH v2 01/12] mmc: sdhci: add support for auto CMD23 Arindam Nath
2011-03-09 12:22 ` subhashj
2011-03-09 12:55 ` Nath, Arindam
2011-03-15 11:23 ` Subhash Jadavani
2011-03-15 11:35 ` Nath, Arindam
2011-03-15 11:52 ` Subhash Jadavani
2011-03-16 6:07 ` Nath, Arindam
2011-03-04 11:32 ` [PATCH v2 02/12] mmc: sd: add support for signal voltage switch procedure Arindam Nath
2011-03-04 11:47 ` Wolfram Sang
2011-03-04 11:52 ` Nath, Arindam
2011-03-09 10:44 ` subhashj
2011-03-10 6:30 ` subhashj
2011-03-10 8:05 ` Nath, Arindam
2011-03-09 12:45 ` zhangfei gao
2011-03-10 8:11 ` Nath, Arindam
2011-03-15 10:18 ` Subhash Jadavani
2011-03-15 10:32 ` Nath, Arindam
2011-03-15 11:18 ` Subhash Jadavani
2011-03-15 11:28 ` Nath, Arindam
2011-03-15 11:58 ` Subhash Jadavani
2011-03-16 3:03 ` zhangfei gao
2011-03-16 6:30 ` Nath, Arindam
2011-03-16 10:44 ` zhangfei gao
2011-03-16 10:48 ` Nath, Arindam
2011-03-16 21:39 ` Philip Rakity
2011-03-17 4:18 ` Nath, Arindam
2011-03-24 10:52 ` zhangfei gao
2011-03-24 10:59 ` Nath, Arindam
2011-03-04 11:32 ` [PATCH v2 03/12] mmc: sd: query function modes for uhs cards Arindam Nath
2011-03-09 14:08 ` subhashj
2011-03-09 14:31 ` Nath, Arindam
2011-03-09 18:04 ` subhashj
2011-03-09 18:16 ` Nath, Arindam
2011-03-04 11:32 ` [PATCH v2 04/12] mmc: sd: add support for driver type selection Arindam Nath
2011-03-09 5:33 ` Philip Rakity
2011-03-09 8:11 ` Nath, Arindam
2011-03-10 6:57 ` subhashj
2011-03-10 8:31 ` Nath, Arindam
2011-03-10 10:28 ` subhashj
2011-03-10 10:44 ` Nath, Arindam
2011-03-10 11:25 ` subhashj
2011-03-10 11:34 ` Nath, Arindam
2011-03-04 11:32 ` [PATCH v2 05/12] mmc: sdhci: reset sdclk before setting high speed enable Arindam Nath
2011-03-05 4:57 ` Philip Rakity
2011-03-05 5:07 ` Nath, Arindam
2011-03-04 11:32 ` [PATCH v2 06/12] mmc: sd: add support for uhs bus speed mode selection Arindam Nath
2011-03-10 8:00 ` subhashj
2011-03-10 8:36 ` Nath, Arindam
2011-03-10 10:07 ` subhashj
2011-03-10 10:15 ` Nath, Arindam
2011-03-21 6:42 ` Subhash Jadavani
2011-03-23 6:04 ` Nath, Arindam
2011-03-23 6:14 ` Subhash Jadavani
2011-03-23 6:17 ` Nath, Arindam
2011-03-23 6:26 ` Subhash Jadavani
2011-03-23 6:35 ` Nath, Arindam
2011-03-23 7:23 ` Subhash Jadavani
2011-03-23 14:02 ` Nath, Arindam
2011-03-24 7:25 ` Subhash Jadavani
2011-03-24 8:42 ` Nath, Arindam
2011-03-04 11:32 ` [PATCH v2 07/12] mmc: sd: set current limit for uhs cards Arindam Nath
2011-03-09 21:41 ` Philip Rakity
2011-03-10 3:12 ` Nath, Arindam
2011-03-10 8:16 ` subhashj
2011-03-10 8:43 ` Nath, Arindam
2011-03-10 9:45 ` subhashj
2011-03-16 14:26 ` Philip Rakity
2011-03-16 14:32 ` Nath, Arindam
2011-03-16 14:51 ` Philip Rakity
2011-03-16 15:00 ` Nath, Arindam
2011-03-16 15:18 ` Philip Rakity
2011-03-16 15:24 ` Nath, Arindam
2011-03-16 15:31 ` Philip Rakity
2011-03-16 15:33 ` Nath, Arindam
2011-03-16 15:34 ` Philip Rakity
2011-03-21 7:43 ` Subhash Jadavani
2011-03-21 7:54 ` Nath, Arindam
2011-03-04 11:32 ` [PATCH v2 08/12] mmc: sd: report correct speed and capacity of " Arindam Nath
2011-03-10 11:48 ` subhashj
2011-03-10 13:28 ` Nath, Arindam
2011-03-10 13:41 ` subhashj
2011-03-04 11:32 ` [PATCH v2 09/12] mmc: sd: add support for tuning during uhs initialization Arindam Nath
2011-03-04 18:27 ` Philip Rakity
2011-03-04 18:48 ` Nath, Arindam
2011-03-04 18:34 ` Philip Rakity
2011-03-04 18:54 ` Nath, Arindam
2011-03-15 10:32 ` zhangfei gao
2011-03-15 10:43 ` Nath, Arindam
2011-03-16 2:51 ` zhangfei gao
2011-03-16 6:20 ` Nath, Arindam
2011-03-16 10:18 ` zhangfei gao
2011-03-21 9:43 ` Subhash Jadavani
2011-03-21 9:50 ` Nath, Arindam
2011-03-23 6:58 ` Subhash Jadavani
2011-03-21 10:58 ` Subhash Jadavani
2011-03-21 11:07 ` Nath, Arindam
2011-03-23 6:08 ` Subhash Jadavani
2011-03-23 6:14 ` Nath, Arindam
2011-03-23 6:19 ` Subhash Jadavani
2011-03-23 6:22 ` Nath, Arindam
2011-03-23 6:34 ` Subhash Jadavani
2011-03-23 6:41 ` Nath, Arindam
2011-03-23 6:45 ` Subhash Jadavani
2011-03-23 6:48 ` Nath, Arindam
2011-03-04 11:32 ` [PATCH v2 10/12] mmc: sdhci: enable preset value after " Arindam Nath
2011-03-10 13:23 ` subhashj
2011-03-10 13:30 ` Nath, Arindam
2011-03-10 13:45 ` subhashj
2011-03-10 13:49 ` Nath, Arindam
2011-03-10 14:03 ` subhashj
2011-03-10 14:07 ` Nath, Arindam
2011-03-10 14:12 ` subhashj
2011-03-10 14:15 ` Nath, Arindam
2011-03-10 14:19 ` subhashj
2011-03-10 14:24 ` Nath, Arindam
2011-03-04 11:32 ` [PATCH v2 11/12] mmc: sdhci: add support for programmable clock mode Arindam Nath
2011-03-04 11:32 ` [PATCH v2 12/12] mmc: sdhci: add support for retuning mode 1 Arindam Nath
2011-03-10 13:57 ` subhashj
2011-03-10 14:00 ` Nath, Arindam
2011-03-04 15:16 ` [PATCH v2 00/12] add support for host controller v3.00 Chris Ball
2011-03-04 15:55 ` Nath, Arindam
2011-03-11 13:13 ` subhashj
2011-03-11 15:29 ` Nath, Arindam
2011-03-11 16:06 ` Chris Ball
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).