* [PATCH V6 0/2] HS200 Mode support for device and host
@ 2011-12-19 6:36 Girish K S
2011-12-19 6:36 ` [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5 Girish K S
2011-12-19 6:36 ` [PATCH V6 2/2] mmc: host: Adds support for eMMC 4.5 HS200 mode Girish K S
0 siblings, 2 replies; 9+ messages in thread
From: Girish K S @ 2011-12-19 6:36 UTC (permalink / raw)
To: linux-mmc; +Cc: patches, linux-samsung-soc, Girish K S
Changes in v6:
split the patch into 2 and classified them as a patch for device
and host. Modified to include the review comments.
Changes in v5:
Reduced the case statements for better code readability. Removed
unused macro definitions. Modified the tuning function prototype
and definition to support tuning for both SD and eMMC cards.
Changes in v4:
Rebased onto chris-mmc/mmc-next branch. This patch is successfully
applied on commit with id de022ed3fdc14808299b2fa66dbb1ed5ab921912.
Changes in v3:
In the previous commits of chris-mmc/mmc-next branch, the patch with
commit id (c0f22a2c92e357e7cb3988b0b13034d70b7461f9) defines caps2 for
more capabilities. This patch version deletes the member ext_caps(created
in my earlier patch) from struct mmc_host and reuses already accepted
caps2 member.
Changes in v2:
Rebased to latest chris-mmc/mmc-next branch. Resolved indentation
problems identified in review. This patch has to be applied before
the patch released for modifying the printk messages.
Changes in v1:
Case statements in switch that produce same result have
been combined to reduce repeated assignments.
patch recreated after rebase to chris balls mmc-next branch.
Girish K S (2):
mmc: core: HS200 mode support for eMMC 4.5
mmc: host: Adds support for eMMC 4.5 HS200 mode
drivers/mmc/core/bus.c | 3 +-
drivers/mmc/core/debugfs.c | 3 +
drivers/mmc/core/mmc.c | 147 +++++++++++++++++++++++++++++++++++++++++---
drivers/mmc/core/sd.c | 3 +-
drivers/mmc/core/sdio.c | 4 +-
drivers/mmc/host/sdhci.c | 43 ++++++++++---
drivers/mmc/host/sdhci.h | 1 +
include/linux/mmc/card.h | 3 +
include/linux/mmc/host.h | 11 +++-
include/linux/mmc/mmc.h | 66 +++++++++++++++++++-
include/linux/mmc/sdhci.h | 1 +
11 files changed, 261 insertions(+), 24 deletions(-)
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5
2011-12-19 6:36 [PATCH V6 0/2] HS200 Mode support for device and host Girish K S
@ 2011-12-19 6:36 ` Girish K S
2011-12-19 10:10 ` Subhash Jadavani
2011-12-19 17:01 ` Philip Rakity
2011-12-19 6:36 ` [PATCH V6 2/2] mmc: host: Adds support for eMMC 4.5 HS200 mode Girish K S
1 sibling, 2 replies; 9+ messages in thread
From: Girish K S @ 2011-12-19 6:36 UTC (permalink / raw)
To: linux-mmc; +Cc: patches, linux-samsung-soc, Girish K S, Chris Ball
This patch adds the support of the HS200 bus speed for eMMC 4.5 devices.
The eMMC 4.5 devices have support for 200MHz bus speed. The function
prototype of the tuning function is modified to handle the tuning command
number which is different in sd and mmc case.
cc: Chris Ball <cjb@laptop.org>
Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
---
drivers/mmc/core/bus.c | 3 +-
drivers/mmc/core/debugfs.c | 3 +
drivers/mmc/core/mmc.c | 147 +++++++++++++++++++++++++++++++++++++++++---
drivers/mmc/core/sd.c | 3 +-
drivers/mmc/core/sdio.c | 4 +-
include/linux/mmc/card.h | 3 +
include/linux/mmc/mmc.h | 66 +++++++++++++++++++-
7 files changed, 216 insertions(+), 13 deletions(-)
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index f8a228a..5d011a3 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -303,10 +303,11 @@ int mmc_add_card(struct mmc_card *card)
mmc_card_ddr_mode(card) ? "DDR " : "",
type);
} else {
- printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
+ pr_info("%s: new %s%s%s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_card_uhs(card) ? "ultra high speed " :
(mmc_card_highspeed(card) ? "high speed " : ""),
+ (mmc_card_hs200(card) ? "HS200 " : ""),
mmc_card_ddr_mode(card) ? "DDR " : "",
type, card->rca);
}
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 027615d..9ab5b17 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -135,6 +135,9 @@ static int mmc_ios_show(struct seq_file *s, void *data)
case MMC_TIMING_UHS_DDR50:
str = "sd uhs DDR50";
break;
+ case MMC_TIMING_MMC_HS200:
+ str = "mmc high-speed SDR200";
+ break;
default:
str = "invalid";
break;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index f0a9f1f..d5d93de 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -286,6 +286,27 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
}
card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
+ case EXT_CSD_CARD_TYPE_SDR_ALL:
+ case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V:
+ case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V:
+ case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52:
+ card->ext_csd.hs_max_dtr = 200000000;
+ card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
+ break;
+ case EXT_CSD_CARD_TYPE_SDR_1_2V_ALL:
+ case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V:
+ case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V:
+ case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52:
+ card->ext_csd.hs_max_dtr = 200000000;
+ card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
+ break;
+ case EXT_CSD_CARD_TYPE_SDR_1_8V_ALL:
+ case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V:
+ case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V:
+ case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52:
+ card->ext_csd.hs_max_dtr = 200000000;
+ card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
+ break;
case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 52000000;
@@ -700,6 +721,52 @@ static int mmc_select_powerclass(struct mmc_card *card,
}
/*
+ * Selects the desired buswidth and switch to the HS200 mode
+ * if bus width set without error
+ */
+static int mmc_select_hs200(struct mmc_card *card)
+{
+ int err = 0;
+ struct mmc_host *host;
+ u32 bus_width = MMC_BUS_WIDTH_4;
+
+ BUG_ON(!card);
+
+ host = card->host;
+
+ /*
+ * Host is capable of 8bit transfer, then switch
+ * the device to work in 8bit transfer mode. If the
+ * mmc switch command returns error then switch to
+ * 4bit transfer mode. On success set the corresponding
+ * bus width on the host.
+ */
+ if (host->caps & MMC_CAP_8_BIT_DATA) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_8,
+ card->ext_csd.generic_cmd6_time);
+ bus_width = MMC_BUS_WIDTH_8;
+ }
+
+ /* If the 8bit mode fails switch to 4 bit mode */
+ if (err)
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_4,
+ card->ext_csd.generic_cmd6_time);
+
+ /* switch to HS200 mode if bus width set successfully */
+ if (!err) {
+ mmc_set_bus_width(card->host, bus_width);
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, 2, 0);
+ }
+
+ return err;
+}
+
+/*
* Handle the detection and initialisation of a card.
*
* In the case of a resume, "oldcard" will contain the card
@@ -905,11 +972,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
/*
* Activate high speed (if supported)
*/
- if ((card->ext_csd.hs_max_dtr != 0) &&
- (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, 1,
- card->ext_csd.generic_cmd6_time);
+ if (card->ext_csd.hs_max_dtr != 0) {
+ err = 0;
+ if ((card->ext_csd.hs_max_dtr > 52000000) &&
+ (host->caps2 & MMC_CAP2_HS200))
+ err = mmc_select_hs200(card);
+ else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, 1, 0);
+
if (err && err != -EBADMSG)
goto free_card;
@@ -918,8 +989,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
mmc_hostname(card->host));
err = 0;
} else {
- mmc_card_set_highspeed(card);
- mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+ if ((card->ext_csd.hs_max_dtr > 52000000) &&
+ (host->caps2 & MMC_CAP2_HS200)) {
+ mmc_card_set_hs200(card);
+ mmc_set_timing(card->host,
+ MMC_TIMING_MMC_HS200);
+ } else {
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+ }
}
}
@@ -944,7 +1022,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/
max_dtr = (unsigned int)-1;
- if (mmc_card_highspeed(card)) {
+ if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
if (max_dtr > card->ext_csd.hs_max_dtr)
max_dtr = card->ext_csd.hs_max_dtr;
} else if (max_dtr > card->csd.max_dtr) {
@@ -970,9 +1048,60 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * Indicate HS200 SDR mode (if supported).
+ */
+ if (mmc_card_hs200(card)) {
+ u32 ext_csd_bits;
+ u32 bus_width = card->host->ios.bus_width;
+
+ /*
+ * For devices supporting HS200 mode, the bus width has
+ * to be set before executing the tuning function. If
+ * set before tuning, then device will respond with CRC
+ * errors for responses on CMD line. So for HS200 the
+ * sequence will be
+ * 1. set bus width 4bit / 8 bit (1 bit not supported)
+ * 2. switch to HS200 mode
+ * 3. set the clock to > 52Mhz <=200MHz and
+ * 4. execute tuning for HS200
+ */
+ if ((host->caps2 & MMC_CAP2_HS200) &&
+ card->host->ops->execute_tuning)
+ err = card->host->ops->execute_tuning(card->host,
+ MMC_SEND_TUNING_BLOCK_HS200);
+ if (err) {
+ pr_warning("tuning execution failed\n");
+ goto err;
+ }
+
+ ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
+ EXT_CSD_BUS_WIDTH_8 :
+ EXT_CSD_BUS_WIDTH_4;
+ err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
+ if (err) {
+ pr_err("%s: power class selection to bus width"
+ "%d failed\n", mmc_hostname(card->host),
+ 1 << bus_width);
+ goto err;
+ }
+
+ if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)
+ && (host->caps2 & MMC_CAP2_HS200_1_2V_SDR)) {
+ err = mmc_set_signal_voltage(host,
+ MMC_SIGNAL_VOLTAGE_120, 0);
+
+ if (err)
+ goto err;
+ mmc_set_timing(card->host, MMC_TIMING_MMC_HS200);
+ mmc_set_bus_width(card->host, bus_width);
+ }
+ }
+
+ /*
* Activate wide bus and DDR (if supported).
*/
- if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
+ if (!mmc_card_hs200(card) &&
+ (card->csd.mmca_vsn >= CSD_SPEC_VER_3) &&
(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
static unsigned ext_csd_bits[][2] = {
{ EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 6f27d35..a775131 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -661,7 +661,8 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
/* SPI mode doesn't define CMD19 */
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
- err = card->host->ops->execute_tuning(card->host);
+ err = card->host->ops->execute_tuning(card->host, \
+ MMC_SEND_TUNING_BLOCK);
out:
kfree(status);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index b77f770..bd7bacc 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -14,6 +14,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
@@ -556,7 +557,8 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
/* Initialize and start re-tuning timer */
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
- err = card->host->ops->execute_tuning(card->host);
+ err = card->host->ops->execute_tuning(card->host,
+ MMC_SEND_TUNING_BLOCK);
out:
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 9478a6b..9f22ba5 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -216,6 +216,7 @@ struct mmc_card {
#define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */
#define MMC_CARD_SDXC (1<<6) /* card is SDXC */
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */
+#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
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 */
@@ -374,6 +375,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
+#define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200)
#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_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
@@ -384,6 +386,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#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_hs200(c) ((c)->state |= MMC_STATE_HIGHSPEED_200)
#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_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 665548e..2c8cc69 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -51,6 +51,7 @@
#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 */
+#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
/* class 3 */
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
@@ -339,13 +340,76 @@ struct _mmc_csd {
#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
-#define EXT_CSD_CARD_TYPE_MASK 0xF /* Mask out reserved bits */
+#define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */
#define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */
/* DDR mode @1.8V or 3V I/O */
#define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */
/* DDR mode @1.2V I/O */
#define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \
| EXT_CSD_CARD_TYPE_DDR_1_2V)
+#define EXT_CSD_CARD_TYPE_SDR_1_8V (1<<4) /* Card can run at 200MHz */
+#define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */
+ /* SDR mode @1.2V I/O */
+
+#define EXT_CSD_CARD_TYPE_SDR_200 (EXT_CSD_CARD_TYPE_SDR_1_8V \
+ | EXT_CSD_CARD_TYPE_SDR_1_2V)
+
+#define EXT_CSD_CARD_TYPE_SDR_ALL (EXT_CSD_CARD_TYPE_SDR_200 \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_1_2V_ALL (EXT_CSD_CARD_TYPE_SDR_1_2V \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_1_8V_ALL (EXT_CSD_CARD_TYPE_SDR_1_8V \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_1_2V \
+ | EXT_CSD_CARD_TYPE_DDR_1_8V \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_1_8V \
+ | EXT_CSD_CARD_TYPE_DDR_1_8V \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_1_2V \
+ | EXT_CSD_CARD_TYPE_DDR_1_2V \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_1_8V \
+ | EXT_CSD_CARD_TYPE_DDR_1_2V \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52 (EXT_CSD_CARD_TYPE_SDR_1_2V \
+ | EXT_CSD_CARD_TYPE_DDR_52 \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52 (EXT_CSD_CARD_TYPE_SDR_1_8V \
+ | EXT_CSD_CARD_TYPE_DDR_52 \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_200 \
+ | EXT_CSD_CARD_TYPE_DDR_1_8V \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_200 \
+ | EXT_CSD_CARD_TYPE_DDR_1_2V \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
+
+#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52 (EXT_CSD_CARD_TYPE_SDR_200 \
+ | EXT_CSD_CARD_TYPE_DDR_52 \
+ | EXT_CSD_CARD_TYPE_52 \
+ | EXT_CSD_CARD_TYPE_26)
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH V6 2/2] mmc: host: Adds support for eMMC 4.5 HS200 mode
2011-12-19 6:36 [PATCH V6 0/2] HS200 Mode support for device and host Girish K S
2011-12-19 6:36 ` [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5 Girish K S
@ 2011-12-19 6:36 ` Girish K S
2011-12-19 16:54 ` Philip Rakity
1 sibling, 1 reply; 9+ messages in thread
From: Girish K S @ 2011-12-19 6:36 UTC (permalink / raw)
To: linux-mmc; +Cc: patches, linux-samsung-soc, Girish K S, Chris Ball
This patch adds support for the HS200 mode on the host side.
Also enables the tuning feature required when the HS200 mode
is selected.
cc: Chris Ball <cjb@laptop.org>
Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
---
drivers/mmc/host/sdhci.c | 43 +++++++++++++++++++++++++++++++++----------
drivers/mmc/host/sdhci.h | 1 +
include/linux/mmc/host.h | 11 ++++++++++-
include/linux/mmc/sdhci.h | 1 +
4 files changed, 45 insertions(+), 11 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index ab6018f..049d51d 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -49,7 +49,7 @@ static void sdhci_finish_data(struct sdhci_host *);
static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
static void sdhci_finish_command(struct sdhci_host *);
-static int sdhci_execute_tuning(struct mmc_host *mmc);
+static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static void sdhci_tuning_timer(unsigned long data);
#ifdef CONFIG_PM_RUNTIME
@@ -1016,7 +1016,8 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
flags |= SDHCI_CMD_INDEX;
/* CMD19 is special in that the Data Present Select should be set */
- if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
+ if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
flags |= SDHCI_CMD_DATA;
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
@@ -1287,7 +1288,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
if ((host->flags & SDHCI_NEEDS_RETUNING) &&
!(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
spin_unlock_irqrestore(&host->lock, flags);
- sdhci_execute_tuning(mmc);
+ sdhci_execute_tuning(mmc, mrq->cmd->opcode);
spin_lock_irqsave(&host->lock, flags);
/* Restore original mmc_request structure */
@@ -1375,7 +1376,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
(ios->timing == MMC_TIMING_UHS_SDR104) ||
(ios->timing == MMC_TIMING_UHS_DDR50) ||
(ios->timing == MMC_TIMING_UHS_SDR25) ||
- (ios->timing == MMC_TIMING_UHS_SDR12))
+ (ios->timing == MMC_TIMING_UHS_SDR12) ||
+ (ios->timing == MMC_TIMING_MMC_HS200))
ctrl |= SDHCI_CTRL_HISPD;
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
@@ -1435,6 +1437,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (ios->timing == MMC_TIMING_UHS_DDR50)
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+ else if (ios->timing == MMC_TIMING_MMC_HS200)
+ ctrl_2 |= SDHCI_CTRL_HS_SDR200;
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}
@@ -1673,7 +1677,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return err;
}
-static int sdhci_execute_tuning(struct mmc_host *mmc)
+static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host;
u16 ctrl;
@@ -1694,10 +1698,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
* 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 the Host Controller supports the HS200 mode then tuning
+ * function has to be executed.
*/
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
(((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
- (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
+ (host->flags & SDHCI_SDR50_NEEDS_TUNING)) ||
+ (host->flags & SDHCI_HS200_NEEDS_TUNING))
ctrl |= SDHCI_CTRL_EXEC_TUNING;
else {
spin_unlock(&host->lock);
@@ -1733,7 +1740,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
if (!tuning_loop_counter && !timeout)
break;
- cmd.opcode = MMC_SEND_TUNING_BLOCK;
+ cmd.opcode = opcode;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
cmd.retries = 0;
@@ -1748,7 +1755,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
* block to the Host Controller. So we set the block size
* to 64 here.
*/
- sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
+ if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
+ if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
+ SDHCI_BLOCK_SIZE);
+ else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+ SDHCI_BLOCK_SIZE);
+ } else {
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+ SDHCI_BLOCK_SIZE);
+ }
/*
* The tuning block is sent by the card to the host controller.
@@ -2131,12 +2148,14 @@ static void sdhci_show_adma_error(struct sdhci_host *host) { }
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
{
+ u32 command;
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) {
+ command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
+ if ((command == MMC_SEND_TUNING_BLOCK) ||
+ (command == MMC_SEND_TUNING_BLOCK_HS200)) {
host->tuning_done = 1;
wake_up(&host->buf_ready_int);
return;
@@ -2741,6 +2760,10 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_USE_SDR50_TUNING)
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
+ /* Does the host needs tuning for HS200? */
+ if (mmc->caps2 & MMC_CAP2_HS200)
+ host->flags |= SDHCI_HS200_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;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index a04d4d0..46fd2ac 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -158,6 +158,7 @@
#define SDHCI_CTRL_UHS_SDR50 0x0002
#define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004
+#define SDHCI_CTRL_HS_SDR200 0x0005 /*reserved value in SDIO spec */
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 9a03d03..606c8c3 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -56,10 +56,13 @@ struct mmc_ios {
#define MMC_TIMING_UHS_SDR50 3
#define MMC_TIMING_UHS_SDR104 4
#define MMC_TIMING_UHS_DDR50 5
+#define MMC_TIMING_MMC_HS200 6
#define MMC_SDR_MODE 0
#define MMC_1_2V_DDR_MODE 1
#define MMC_1_8V_DDR_MODE 2
+#define MMC_1_2V_SDR_MODE 3
+#define MMC_1_8V_SDR_MODE 4
unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
@@ -148,7 +151,9 @@ struct mmc_host_ops {
void (*init_card)(struct mmc_host *host, struct mmc_card *card);
int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
- int (*execute_tuning)(struct mmc_host *host);
+
+ /* The tuning command opcode value is different for SD and eMMC cards */
+ int (*execute_tuning)(struct mmc_host *host, u32 opcode);
void (*enable_preset_value)(struct mmc_host *host, bool enable);
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
void (*hw_reset)(struct mmc_host *host);
@@ -242,6 +247,10 @@ struct mmc_host {
#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */
#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */
#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */
+#define MMC_CAP2_HS200_1_8V_SDR (1 << 4) /* can support */
+#define MMC_CAP2_HS200_1_2V_SDR (1 << 5) /* can support */
+#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
+ MMC_CAP2_HS200_1_2V_SDR)
mmc_pm_flag_t pm_caps; /* supported pm features */
unsigned int power_notify_type;
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index e4b6935..d9a2222 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -121,6 +121,7 @@ struct sdhci_host {
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
+#define SDHCI_HS200_NEEDS_TUNING (1<<10) /* HS200 needs tuning */
unsigned int version; /* SDHCI spec. version */
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* RE: [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5
2011-12-19 6:36 ` [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5 Girish K S
@ 2011-12-19 10:10 ` Subhash Jadavani
2011-12-19 10:49 ` Girish K S
2011-12-19 17:01 ` Philip Rakity
1 sibling, 1 reply; 9+ messages in thread
From: Subhash Jadavani @ 2011-12-19 10:10 UTC (permalink / raw)
To: 'Girish K S', linux-mmc
Cc: patches, linux-samsung-soc, 'Chris Ball'
Hi Girish,
Overall this patch looks good. I have only 2 minor comments inline below.
Regards,
Subhash
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> owner@vger.kernel.org] On Behalf Of Girish K S
> Sent: Monday, December 19, 2011 12:07 PM
> To: linux-mmc@vger.kernel.org
> Cc: patches@linaro.org; linux-samsung-soc@vger.kernel.org; Girish K S;
> Chris Ball
> Subject: [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5
>
> This patch adds the support of the HS200 bus speed for eMMC 4.5
> devices.
> The eMMC 4.5 devices have support for 200MHz bus speed. The function
> prototype of the tuning function is modified to handle the tuning
> command
> number which is different in sd and mmc case.
>
> cc: Chris Ball <cjb@laptop.org>
> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
> ---
> drivers/mmc/core/bus.c | 3 +-
> drivers/mmc/core/debugfs.c | 3 +
> drivers/mmc/core/mmc.c | 147
> +++++++++++++++++++++++++++++++++++++++++---
> drivers/mmc/core/sd.c | 3 +-
> drivers/mmc/core/sdio.c | 4 +-
> include/linux/mmc/card.h | 3 +
> include/linux/mmc/mmc.h | 66 +++++++++++++++++++-
> 7 files changed, 216 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> index f8a228a..5d011a3 100644
> --- a/drivers/mmc/core/bus.c
> +++ b/drivers/mmc/core/bus.c
> @@ -303,10 +303,11 @@ int mmc_add_card(struct mmc_card *card)
> mmc_card_ddr_mode(card) ? "DDR " : "",
> type);
> } else {
> - printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
> + pr_info("%s: new %s%s%s%s card at address %04x\n",
> mmc_hostname(card->host),
> mmc_card_uhs(card) ? "ultra high speed " :
> (mmc_card_highspeed(card) ? "high speed " : ""),
> + (mmc_card_hs200(card) ? "HS200 " : ""),
> mmc_card_ddr_mode(card) ? "DDR " : "",
> type, card->rca);
> }
> diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
> index 027615d..9ab5b17 100644
> --- a/drivers/mmc/core/debugfs.c
> +++ b/drivers/mmc/core/debugfs.c
> @@ -135,6 +135,9 @@ static int mmc_ios_show(struct seq_file *s, void
> *data)
> case MMC_TIMING_UHS_DDR50:
> str = "sd uhs DDR50";
> break;
> + case MMC_TIMING_MMC_HS200:
> + str = "mmc high-speed SDR200";
> + break;
> default:
> str = "invalid";
> break;
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index f0a9f1f..d5d93de 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -286,6 +286,27 @@ static int mmc_read_ext_csd(struct mmc_card *card,
> u8 *ext_csd)
> }
> card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
> switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
> + case EXT_CSD_CARD_TYPE_SDR_ALL:
> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V:
> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V:
> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52:
> + card->ext_csd.hs_max_dtr = 200000000;
> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
> + break;
> + case EXT_CSD_CARD_TYPE_SDR_1_2V_ALL:
> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V:
> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V:
> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52:
> + card->ext_csd.hs_max_dtr = 200000000;
> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
> + break;
> + case EXT_CSD_CARD_TYPE_SDR_1_8V_ALL:
> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V:
> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V:
> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52:
> + card->ext_csd.hs_max_dtr = 200000000;
> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
> + break;
> case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
> EXT_CSD_CARD_TYPE_26:
> card->ext_csd.hs_max_dtr = 52000000;
> @@ -700,6 +721,52 @@ static int mmc_select_powerclass(struct mmc_card
> *card,
> }
>
> /*
> + * Selects the desired buswidth and switch to the HS200 mode
> + * if bus width set without error
> + */
> +static int mmc_select_hs200(struct mmc_card *card)
> +{
> + int err = 0;
> + struct mmc_host *host;
> + u32 bus_width = MMC_BUS_WIDTH_4;
> +
> + BUG_ON(!card);
> +
> + host = card->host;
> +
> + /*
> + * Host is capable of 8bit transfer, then switch
> + * the device to work in 8bit transfer mode. If the
> + * mmc switch command returns error then switch to
> + * 4bit transfer mode. On success set the corresponding
> + * bus width on the host.
> + */
> + if (host->caps & MMC_CAP_8_BIT_DATA) {
> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> + EXT_CSD_BUS_WIDTH,
> + EXT_CSD_BUS_WIDTH_8,
> + card->ext_csd.generic_cmd6_time);
> + bus_width = MMC_BUS_WIDTH_8;
> + }
> +
> + /* If the 8bit mode fails switch to 4 bit mode */
> + if (err)
> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> + EXT_CSD_BUS_WIDTH,
> + EXT_CSD_BUS_WIDTH_4,
> + card->ext_csd.generic_cmd6_time);
Don't you need to set the bus_width = MMC_BUS_WIDTH_4 after sending the
switch?
Let's say if the host->caps says it support the 8-bit mode but mmc_switch()
fails but your bus_width will still be MMC_BUS_WIDTH_8. Now you try to set
the bus width to 4-bit which will succeed but bus_width variable is not
initialize to 4-bit yet.
> +
> + /* switch to HS200 mode if bus width set successfully */
> + if (!err) {
> + mmc_set_bus_width(card->host, bus_width);
> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> + EXT_CSD_HS_TIMING, 2, 0);
> + }
> +
> + return err;
> +}
> +
> +/*
> * Handle the detection and initialisation of a card.
> *
> * In the case of a resume, "oldcard" will contain the card
> @@ -905,11 +972,15 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
> /*
> * Activate high speed (if supported)
> */
> - if ((card->ext_csd.hs_max_dtr != 0) &&
> - (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
> - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> - EXT_CSD_HS_TIMING, 1,
> - card->ext_csd.generic_cmd6_time);
> + if (card->ext_csd.hs_max_dtr != 0) {
> + err = 0;
> + if ((card->ext_csd.hs_max_dtr > 52000000) &&
> + (host->caps2 & MMC_CAP2_HS200))
> + err = mmc_select_hs200(card);
> + else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> + EXT_CSD_HS_TIMING, 1, 0);
> +
> if (err && err != -EBADMSG)
> goto free_card;
>
> @@ -918,8 +989,15 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
> mmc_hostname(card->host));
> err = 0;
> } else {
> - mmc_card_set_highspeed(card);
> - mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
> + if ((card->ext_csd.hs_max_dtr > 52000000) &&
> + (host->caps2 & MMC_CAP2_HS200)) {
> + mmc_card_set_hs200(card);
> + mmc_set_timing(card->host,
> + MMC_TIMING_MMC_HS200);
> + } else {
> + mmc_card_set_highspeed(card);
> + mmc_set_timing(card->host,
MMC_TIMING_MMC_HS);
> + }
> }
> }
>
> @@ -944,7 +1022,7 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
> */
> max_dtr = (unsigned int)-1;
>
> - if (mmc_card_highspeed(card)) {
> + if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
> if (max_dtr > card->ext_csd.hs_max_dtr)
> max_dtr = card->ext_csd.hs_max_dtr;
> } else if (max_dtr > card->csd.max_dtr) {
> @@ -970,9 +1048,60 @@ static int mmc_init_card(struct mmc_host *host,
> u32 ocr,
> }
>
> /*
> + * Indicate HS200 SDR mode (if supported).
> + */
> + if (mmc_card_hs200(card)) {
> + u32 ext_csd_bits;
> + u32 bus_width = card->host->ios.bus_width;
> +
> + /*
> + * For devices supporting HS200 mode, the bus width has
> + * to be set before executing the tuning function. If
> + * set before tuning, then device will respond with CRC
> + * errors for responses on CMD line. So for HS200 the
> + * sequence will be
> + * 1. set bus width 4bit / 8 bit (1 bit not supported)
> + * 2. switch to HS200 mode
> + * 3. set the clock to > 52Mhz <=200MHz and
> + * 4. execute tuning for HS200
> + */
> + if ((host->caps2 & MMC_CAP2_HS200) &&
> + card->host->ops->execute_tuning)
> + err = card->host->ops->execute_tuning(card->host,
> + MMC_SEND_TUNING_BLOCK_HS200);
> + if (err) {
> + pr_warning("tuning execution failed\n");
> + goto err;
> + }
> +
> + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
> + EXT_CSD_BUS_WIDTH_8 :
> + EXT_CSD_BUS_WIDTH_4;
> + err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
> + if (err) {
> + pr_err("%s: power class selection to bus width"
> + "%d failed\n", mmc_hostname(card->host),
> + 1 << bus_width);
> + goto err;
> + }
> +
> + if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)
> + && (host->caps2 & MMC_CAP2_HS200_1_2V_SDR)) {
> + err = mmc_set_signal_voltage(host,
> + MMC_SIGNAL_VOLTAGE_120, 0);
Shouldn't this voltage switch be done in mmc_select_hs200() before selecting
the bus width and timings?
> +
> + if (err)
> + goto err;
> + mmc_set_timing(card->host, MMC_TIMING_MMC_HS200);
> + mmc_set_bus_width(card->host, bus_width);
> + }
> + }
> +
> + /*
> * Activate wide bus and DDR (if supported).
> */
> - if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
> + if (!mmc_card_hs200(card) &&
> + (card->csd.mmca_vsn >= CSD_SPEC_VER_3) &&
> (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
> static unsigned ext_csd_bits[][2] = {
> { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 6f27d35..a775131 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -661,7 +661,8 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> *card)
>
> /* SPI mode doesn't define CMD19 */
> if (!mmc_host_is_spi(card->host) && card->host->ops-
> >execute_tuning)
> - err = card->host->ops->execute_tuning(card->host);
> + err = card->host->ops->execute_tuning(card->host, \
> +
MMC_SEND_TUNING_BLOCK);
>
> out:
> kfree(status);
> diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
> index b77f770..bd7bacc 100644
> --- a/drivers/mmc/core/sdio.c
> +++ b/drivers/mmc/core/sdio.c
> @@ -14,6 +14,7 @@
>
> #include <linux/mmc/host.h>
> #include <linux/mmc/card.h>
> +#include <linux/mmc/mmc.h>
> #include <linux/mmc/sdio.h>
> #include <linux/mmc/sdio_func.h>
> #include <linux/mmc/sdio_ids.h>
> @@ -556,7 +557,8 @@ static int mmc_sdio_init_uhs_card(struct mmc_card
> *card)
>
> /* Initialize and start re-tuning timer */
> if (!mmc_host_is_spi(card->host) && card->host->ops-
> >execute_tuning)
> - err = card->host->ops->execute_tuning(card->host);
> + err = card->host->ops->execute_tuning(card->host,
> +
MMC_SEND_TUNING_BLOCK);
>
> out:
>
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 9478a6b..9f22ba5 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -216,6 +216,7 @@ struct mmc_card {
> #define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra
> high speed mode */
> #define MMC_CARD_SDXC (1<<6) /* card is SDXC */
> #define MMC_CARD_REMOVED (1<<7) /* card has been
> removed */
> +#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in
> HS200 mode */
> 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 */
> @@ -374,6 +375,7 @@ static inline void __maybe_unused
> remove_quirk(struct mmc_card *card, int data)
> #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
> #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
> #define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
> +#define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200)
> #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_uhs(c) ((c)->state &
> MMC_STATE_ULTRAHIGHSPEED)
> @@ -384,6 +386,7 @@ static inline void __maybe_unused
> remove_quirk(struct mmc_card *card, int data)
> #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_hs200(c) ((c)->state |=
> MMC_STATE_HIGHSPEED_200)
> #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_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 665548e..2c8cc69 100644
> --- a/include/linux/mmc/mmc.h
> +++ b/include/linux/mmc/mmc.h
> @@ -51,6 +51,7 @@
> #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
> */
> +#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
>
> /* class 3 */
> #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1
> */
> @@ -339,13 +340,76 @@ struct _mmc_csd {
>
> #define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
> #define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
> -#define EXT_CSD_CARD_TYPE_MASK 0xF /* Mask out reserved bits */
> +#define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */
> #define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz
> */
> /* DDR mode @1.8V or 3V I/O */
> #define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz
> */
> /* DDR mode @1.2V I/O */
> #define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \
> | EXT_CSD_CARD_TYPE_DDR_1_2V)
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V (1<<4) /* Card can run at
> 200MHz */
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at
> 200MHz */
> + /* SDR mode @1.2V I/O */
> +
> +#define EXT_CSD_CARD_TYPE_SDR_200 (EXT_CSD_CARD_TYPE_SDR_1_8V \
> + | EXT_CSD_CARD_TYPE_SDR_1_2V)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_ALL (EXT_CSD_CARD_TYPE_SDR_200 \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_ALL
> (EXT_CSD_CARD_TYPE_SDR_1_2V \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_ALL
> (EXT_CSD_CARD_TYPE_SDR_1_8V \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V
> (EXT_CSD_CARD_TYPE_SDR_1_2V \
> + | EXT_CSD_CARD_TYPE_DDR_1_8V
\
> + | EXT_CSD_CARD_TYPE_52
\
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V
> (EXT_CSD_CARD_TYPE_SDR_1_8V \
> + | EXT_CSD_CARD_TYPE_DDR_1_8V
\
> + | EXT_CSD_CARD_TYPE_52
\
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V
> (EXT_CSD_CARD_TYPE_SDR_1_2V \
> + | EXT_CSD_CARD_TYPE_DDR_1_2V
\
> + | EXT_CSD_CARD_TYPE_52
\
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V
> (EXT_CSD_CARD_TYPE_SDR_1_8V \
> + | EXT_CSD_CARD_TYPE_DDR_1_2V
\
> + | EXT_CSD_CARD_TYPE_52
\
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52
> (EXT_CSD_CARD_TYPE_SDR_1_2V \
> + | EXT_CSD_CARD_TYPE_DDR_52
\
> + | EXT_CSD_CARD_TYPE_52
\
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52
> (EXT_CSD_CARD_TYPE_SDR_1_8V \
> + | EXT_CSD_CARD_TYPE_DDR_52
\
> + | EXT_CSD_CARD_TYPE_52
\
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V
> (EXT_CSD_CARD_TYPE_SDR_200 \
> + | EXT_CSD_CARD_TYPE_DDR_1_8V
\
> + | EXT_CSD_CARD_TYPE_52
\
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V
> (EXT_CSD_CARD_TYPE_SDR_200 \
> + | EXT_CSD_CARD_TYPE_DDR_1_2V
\
> + | EXT_CSD_CARD_TYPE_52
\
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52 (EXT_CSD_CARD_TYPE_SDR_200
> \
> + | EXT_CSD_CARD_TYPE_DDR_52
\
> + | EXT_CSD_CARD_TYPE_52
\
> + | EXT_CSD_CARD_TYPE_26)
>
> #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
> #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
> --
> 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] 9+ messages in thread
* Re: [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5
2011-12-19 10:10 ` Subhash Jadavani
@ 2011-12-19 10:49 ` Girish K S
2011-12-19 10:53 ` Subhash Jadavani
0 siblings, 1 reply; 9+ messages in thread
From: Girish K S @ 2011-12-19 10:49 UTC (permalink / raw)
To: Subhash Jadavani; +Cc: linux-mmc, patches, linux-samsung-soc, Chris Ball
On 19 December 2011 15:40, Subhash Jadavani <subhashj@codeaurora.org> wrote:
> Hi Girish,
>
> Overall this patch looks good. I have only 2 minor comments inline below.
>
> Regards,
> Subhash
>
>
>> -----Original Message-----
>> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
>> owner@vger.kernel.org] On Behalf Of Girish K S
>> Sent: Monday, December 19, 2011 12:07 PM
>> To: linux-mmc@vger.kernel.org
>> Cc: patches@linaro.org; linux-samsung-soc@vger.kernel.org; Girish K S;
>> Chris Ball
>> Subject: [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5
>>
>> This patch adds the support of the HS200 bus speed for eMMC 4.5
>> devices.
>> The eMMC 4.5 devices have support for 200MHz bus speed. The function
>> prototype of the tuning function is modified to handle the tuning
>> command
>> number which is different in sd and mmc case.
>>
>> cc: Chris Ball <cjb@laptop.org>
>> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
>> ---
>> drivers/mmc/core/bus.c | 3 +-
>> drivers/mmc/core/debugfs.c | 3 +
>> drivers/mmc/core/mmc.c | 147
>> +++++++++++++++++++++++++++++++++++++++++---
>> drivers/mmc/core/sd.c | 3 +-
>> drivers/mmc/core/sdio.c | 4 +-
>> include/linux/mmc/card.h | 3 +
>> include/linux/mmc/mmc.h | 66 +++++++++++++++++++-
>> 7 files changed, 216 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
>> index f8a228a..5d011a3 100644
>> --- a/drivers/mmc/core/bus.c
>> +++ b/drivers/mmc/core/bus.c
>> @@ -303,10 +303,11 @@ int mmc_add_card(struct mmc_card *card)
>> mmc_card_ddr_mode(card) ? "DDR " : "",
>> type);
>> } else {
>> - printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
>> + pr_info("%s: new %s%s%s%s card at address %04x\n",
>> mmc_hostname(card->host),
>> mmc_card_uhs(card) ? "ultra high speed " :
>> (mmc_card_highspeed(card) ? "high speed " : ""),
>> + (mmc_card_hs200(card) ? "HS200 " : ""),
>> mmc_card_ddr_mode(card) ? "DDR " : "",
>> type, card->rca);
>> }
>> diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
>> index 027615d..9ab5b17 100644
>> --- a/drivers/mmc/core/debugfs.c
>> +++ b/drivers/mmc/core/debugfs.c
>> @@ -135,6 +135,9 @@ static int mmc_ios_show(struct seq_file *s, void
>> *data)
>> case MMC_TIMING_UHS_DDR50:
>> str = "sd uhs DDR50";
>> break;
>> + case MMC_TIMING_MMC_HS200:
>> + str = "mmc high-speed SDR200";
>> + break;
>> default:
>> str = "invalid";
>> break;
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index f0a9f1f..d5d93de 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -286,6 +286,27 @@ static int mmc_read_ext_csd(struct mmc_card *card,
>> u8 *ext_csd)
>> }
>> card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
>> switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
>> + case EXT_CSD_CARD_TYPE_SDR_ALL:
>> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V:
>> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V:
>> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52:
>> + card->ext_csd.hs_max_dtr = 200000000;
>> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
>> + break;
>> + case EXT_CSD_CARD_TYPE_SDR_1_2V_ALL:
>> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V:
>> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V:
>> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52:
>> + card->ext_csd.hs_max_dtr = 200000000;
>> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
>> + break;
>> + case EXT_CSD_CARD_TYPE_SDR_1_8V_ALL:
>> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V:
>> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V:
>> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52:
>> + card->ext_csd.hs_max_dtr = 200000000;
>> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
>> + break;
>> case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
>> EXT_CSD_CARD_TYPE_26:
>> card->ext_csd.hs_max_dtr = 52000000;
>> @@ -700,6 +721,52 @@ static int mmc_select_powerclass(struct mmc_card
>> *card,
>> }
>>
>> /*
>> + * Selects the desired buswidth and switch to the HS200 mode
>> + * if bus width set without error
>> + */
>> +static int mmc_select_hs200(struct mmc_card *card)
>> +{
>> + int err = 0;
>> + struct mmc_host *host;
>> + u32 bus_width = MMC_BUS_WIDTH_4;
>> +
>> + BUG_ON(!card);
>> +
>> + host = card->host;
>> +
>> + /*
>> + * Host is capable of 8bit transfer, then switch
>> + * the device to work in 8bit transfer mode. If the
>> + * mmc switch command returns error then switch to
>> + * 4bit transfer mode. On success set the corresponding
>> + * bus width on the host.
>> + */
>> + if (host->caps & MMC_CAP_8_BIT_DATA) {
>> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> + EXT_CSD_BUS_WIDTH,
>> + EXT_CSD_BUS_WIDTH_8,
>> + card->ext_csd.generic_cmd6_time);
>> + bus_width = MMC_BUS_WIDTH_8;
>> + }
>> +
>> + /* If the 8bit mode fails switch to 4 bit mode */
>> + if (err)
>> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> + EXT_CSD_BUS_WIDTH,
>> + EXT_CSD_BUS_WIDTH_4,
>> + card->ext_csd.generic_cmd6_time);
>
> Don't you need to set the bus_width = MMC_BUS_WIDTH_4 after sending the
> switch?
> Let's say if the host->caps says it support the 8-bit mode but mmc_switch()
> fails but your bus_width will still be MMC_BUS_WIDTH_8. Now you try to set
> the bus width to 4-bit which will succeed but bus_width variable is not
> initialize to 4-bit yet.
Thanks just missed it.
>
>
>
>> +
>> + /* switch to HS200 mode if bus width set successfully */
>> + if (!err) {
>> + mmc_set_bus_width(card->host, bus_width);
>> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> + EXT_CSD_HS_TIMING, 2, 0);
>> + }
>> +
>> + return err;
>> +}
>> +
>> +/*
>> * Handle the detection and initialisation of a card.
>> *
>> * In the case of a resume, "oldcard" will contain the card
>> @@ -905,11 +972,15 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>> /*
>> * Activate high speed (if supported)
>> */
>> - if ((card->ext_csd.hs_max_dtr != 0) &&
>> - (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
>> - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> - EXT_CSD_HS_TIMING, 1,
>> - card->ext_csd.generic_cmd6_time);
>> + if (card->ext_csd.hs_max_dtr != 0) {
>> + err = 0;
>> + if ((card->ext_csd.hs_max_dtr > 52000000) &&
>> + (host->caps2 & MMC_CAP2_HS200))
>> + err = mmc_select_hs200(card);
>> + else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
>> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> + EXT_CSD_HS_TIMING, 1, 0);
>> +
>> if (err && err != -EBADMSG)
>> goto free_card;
>>
>> @@ -918,8 +989,15 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>> mmc_hostname(card->host));
>> err = 0;
>> } else {
>> - mmc_card_set_highspeed(card);
>> - mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
>> + if ((card->ext_csd.hs_max_dtr > 52000000) &&
>> + (host->caps2 & MMC_CAP2_HS200)) {
>> + mmc_card_set_hs200(card);
>> + mmc_set_timing(card->host,
>> + MMC_TIMING_MMC_HS200);
>> + } else {
>> + mmc_card_set_highspeed(card);
>> + mmc_set_timing(card->host,
> MMC_TIMING_MMC_HS);
>> + }
>> }
>> }
>>
>> @@ -944,7 +1022,7 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>> */
>> max_dtr = (unsigned int)-1;
>>
>> - if (mmc_card_highspeed(card)) {
>> + if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
>> if (max_dtr > card->ext_csd.hs_max_dtr)
>> max_dtr = card->ext_csd.hs_max_dtr;
>> } else if (max_dtr > card->csd.max_dtr) {
>> @@ -970,9 +1048,60 @@ static int mmc_init_card(struct mmc_host *host,
>> u32 ocr,
>> }
>>
>> /*
>> + * Indicate HS200 SDR mode (if supported).
>> + */
>> + if (mmc_card_hs200(card)) {
>> + u32 ext_csd_bits;
>> + u32 bus_width = card->host->ios.bus_width;
>> +
>> + /*
>> + * For devices supporting HS200 mode, the bus width has
>> + * to be set before executing the tuning function. If
>> + * set before tuning, then device will respond with CRC
>> + * errors for responses on CMD line. So for HS200 the
>> + * sequence will be
>> + * 1. set bus width 4bit / 8 bit (1 bit not supported)
>> + * 2. switch to HS200 mode
>> + * 3. set the clock to > 52Mhz <=200MHz and
>> + * 4. execute tuning for HS200
>> + */
>> + if ((host->caps2 & MMC_CAP2_HS200) &&
>> + card->host->ops->execute_tuning)
>> + err = card->host->ops->execute_tuning(card->host,
>> + MMC_SEND_TUNING_BLOCK_HS200);
>> + if (err) {
>> + pr_warning("tuning execution failed\n");
>> + goto err;
>> + }
>> +
>> + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
>> + EXT_CSD_BUS_WIDTH_8 :
>> + EXT_CSD_BUS_WIDTH_4;
>> + err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
>> + if (err) {
>> + pr_err("%s: power class selection to bus width"
>> + "%d failed\n", mmc_hostname(card->host),
>> + 1 << bus_width);
>> + goto err;
>> + }
>> +
>> + if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)
>> + && (host->caps2 & MMC_CAP2_HS200_1_2V_SDR)) {
>> + err = mmc_set_signal_voltage(host,
>> + MMC_SIGNAL_VOLTAGE_120, 0);
>
> Shouldn't this voltage switch be done in mmc_select_hs200() before selecting
> the bus width and timings?
Will it affect the functionality?. Since it it IO voltage I think it
can be done here. And I am setting the Host specific timing and
buswidth again.
is ur intention to avoid setting timing and buswidth again?
>
>
>
>
>
>> +
>> + if (err)
>> + goto err;
>> + mmc_set_timing(card->host, MMC_TIMING_MMC_HS200);
>> + mmc_set_bus_width(card->host, bus_width);
>> + }
>> + }
>> +
>> + /*
>> * Activate wide bus and DDR (if supported).
>> */
>> - if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
>> + if (!mmc_card_hs200(card) &&
>> + (card->csd.mmca_vsn >= CSD_SPEC_VER_3) &&
>> (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
>> static unsigned ext_csd_bits[][2] = {
>> { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },
>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>> index 6f27d35..a775131 100644
>> --- a/drivers/mmc/core/sd.c
>> +++ b/drivers/mmc/core/sd.c
>> @@ -661,7 +661,8 @@ static int mmc_sd_init_uhs_card(struct mmc_card
>> *card)
>>
>> /* SPI mode doesn't define CMD19 */
>> if (!mmc_host_is_spi(card->host) && card->host->ops-
>> >execute_tuning)
>> - err = card->host->ops->execute_tuning(card->host);
>> + err = card->host->ops->execute_tuning(card->host, \
>> +
> MMC_SEND_TUNING_BLOCK);
>>
>> out:
>> kfree(status);
>> diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
>> index b77f770..bd7bacc 100644
>> --- a/drivers/mmc/core/sdio.c
>> +++ b/drivers/mmc/core/sdio.c
>> @@ -14,6 +14,7 @@
>>
>> #include <linux/mmc/host.h>
>> #include <linux/mmc/card.h>
>> +#include <linux/mmc/mmc.h>
>> #include <linux/mmc/sdio.h>
>> #include <linux/mmc/sdio_func.h>
>> #include <linux/mmc/sdio_ids.h>
>> @@ -556,7 +557,8 @@ static int mmc_sdio_init_uhs_card(struct mmc_card
>> *card)
>>
>> /* Initialize and start re-tuning timer */
>> if (!mmc_host_is_spi(card->host) && card->host->ops-
>> >execute_tuning)
>> - err = card->host->ops->execute_tuning(card->host);
>> + err = card->host->ops->execute_tuning(card->host,
>> +
> MMC_SEND_TUNING_BLOCK);
>>
>> out:
>>
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 9478a6b..9f22ba5 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -216,6 +216,7 @@ struct mmc_card {
>> #define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra
>> high speed mode */
>> #define MMC_CARD_SDXC (1<<6) /* card is SDXC */
>> #define MMC_CARD_REMOVED (1<<7) /* card has been
>> removed */
>> +#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in
>> HS200 mode */
>> 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 */
>> @@ -374,6 +375,7 @@ static inline void __maybe_unused
>> remove_quirk(struct mmc_card *card, int data)
>> #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
>> #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
>> #define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
>> +#define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200)
>> #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_uhs(c) ((c)->state &
>> MMC_STATE_ULTRAHIGHSPEED)
>> @@ -384,6 +386,7 @@ static inline void __maybe_unused
>> remove_quirk(struct mmc_card *card, int data)
>> #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_hs200(c) ((c)->state |=
>> MMC_STATE_HIGHSPEED_200)
>> #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_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
>> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
>> index 665548e..2c8cc69 100644
>> --- a/include/linux/mmc/mmc.h
>> +++ b/include/linux/mmc/mmc.h
>> @@ -51,6 +51,7 @@
>> #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
>> */
>> +#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
>>
>> /* class 3 */
>> #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1
>> */
>> @@ -339,13 +340,76 @@ struct _mmc_csd {
>>
>> #define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
>> #define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
>> -#define EXT_CSD_CARD_TYPE_MASK 0xF /* Mask out reserved bits */
>> +#define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */
>> #define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz
>> */
>> /* DDR mode @1.8V or 3V I/O */
>> #define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz
>> */
>> /* DDR mode @1.2V I/O */
>> #define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \
>> | EXT_CSD_CARD_TYPE_DDR_1_2V)
>> +#define EXT_CSD_CARD_TYPE_SDR_1_8V (1<<4) /* Card can run at
>> 200MHz */
>> +#define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at
>> 200MHz */
>> + /* SDR mode @1.2V I/O */
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_200 (EXT_CSD_CARD_TYPE_SDR_1_8V \
>> + | EXT_CSD_CARD_TYPE_SDR_1_2V)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_ALL (EXT_CSD_CARD_TYPE_SDR_200 \
>> + | EXT_CSD_CARD_TYPE_52 \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_ALL
>> (EXT_CSD_CARD_TYPE_SDR_1_2V \
>> + | EXT_CSD_CARD_TYPE_52 \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_ALL
>> (EXT_CSD_CARD_TYPE_SDR_1_8V \
>> + | EXT_CSD_CARD_TYPE_52 \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V
>> (EXT_CSD_CARD_TYPE_SDR_1_2V \
>> + | EXT_CSD_CARD_TYPE_DDR_1_8V
> \
>> + | EXT_CSD_CARD_TYPE_52
> \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V
>> (EXT_CSD_CARD_TYPE_SDR_1_8V \
>> + | EXT_CSD_CARD_TYPE_DDR_1_8V
> \
>> + | EXT_CSD_CARD_TYPE_52
> \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V
>> (EXT_CSD_CARD_TYPE_SDR_1_2V \
>> + | EXT_CSD_CARD_TYPE_DDR_1_2V
> \
>> + | EXT_CSD_CARD_TYPE_52
> \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V
>> (EXT_CSD_CARD_TYPE_SDR_1_8V \
>> + | EXT_CSD_CARD_TYPE_DDR_1_2V
> \
>> + | EXT_CSD_CARD_TYPE_52
> \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52
>> (EXT_CSD_CARD_TYPE_SDR_1_2V \
>> + | EXT_CSD_CARD_TYPE_DDR_52
> \
>> + | EXT_CSD_CARD_TYPE_52
> \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52
>> (EXT_CSD_CARD_TYPE_SDR_1_8V \
>> + | EXT_CSD_CARD_TYPE_DDR_52
> \
>> + | EXT_CSD_CARD_TYPE_52
> \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V
>> (EXT_CSD_CARD_TYPE_SDR_200 \
>> + | EXT_CSD_CARD_TYPE_DDR_1_8V
> \
>> + | EXT_CSD_CARD_TYPE_52
> \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V
>> (EXT_CSD_CARD_TYPE_SDR_200 \
>> + | EXT_CSD_CARD_TYPE_DDR_1_2V
> \
>> + | EXT_CSD_CARD_TYPE_52
> \
>> + | EXT_CSD_CARD_TYPE_26)
>> +
>> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52 (EXT_CSD_CARD_TYPE_SDR_200
>> \
>> + | EXT_CSD_CARD_TYPE_DDR_52
> \
>> + | EXT_CSD_CARD_TYPE_52
> \
>> + | EXT_CSD_CARD_TYPE_26)
>>
>> #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
>> #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
>> --
>> 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] 9+ messages in thread
* RE: [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5
2011-12-19 10:49 ` Girish K S
@ 2011-12-19 10:53 ` Subhash Jadavani
0 siblings, 0 replies; 9+ messages in thread
From: Subhash Jadavani @ 2011-12-19 10:53 UTC (permalink / raw)
To: 'Girish K S'
Cc: linux-mmc, patches, linux-samsung-soc, 'Chris Ball'
> -----Original Message-----
> From: Girish K S [mailto:girish.shivananjappa@linaro.org]
> Sent: Monday, December 19, 2011 4:20 PM
> To: Subhash Jadavani
> Cc: linux-mmc@vger.kernel.org; patches@linaro.org; linux-samsung-
> soc@vger.kernel.org; Chris Ball
> Subject: Re: [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5
>
> On 19 December 2011 15:40, Subhash Jadavani <subhashj@codeaurora.org>
> wrote:
> > Hi Girish,
> >
> > Overall this patch looks good. I have only 2 minor comments inline
> below.
> >
> > Regards,
> > Subhash
> >
> >
> >> -----Original Message-----
> >> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-
> >> owner@vger.kernel.org] On Behalf Of Girish K S
> >> Sent: Monday, December 19, 2011 12:07 PM
> >> To: linux-mmc@vger.kernel.org
> >> Cc: patches@linaro.org; linux-samsung-soc@vger.kernel.org; Girish K
> S;
> >> Chris Ball
> >> Subject: [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5
> >>
> >> This patch adds the support of the HS200 bus speed for eMMC 4.5
> >> devices.
> >> The eMMC 4.5 devices have support for 200MHz bus speed. The function
> >> prototype of the tuning function is modified to handle the tuning
> >> command
> >> number which is different in sd and mmc case.
> >>
> >> cc: Chris Ball <cjb@laptop.org>
> >> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
> >> ---
> >> drivers/mmc/core/bus.c | 3 +-
> >> drivers/mmc/core/debugfs.c | 3 +
> >> drivers/mmc/core/mmc.c | 147
> >> +++++++++++++++++++++++++++++++++++++++++---
> >> drivers/mmc/core/sd.c | 3 +-
> >> drivers/mmc/core/sdio.c | 4 +-
> >> include/linux/mmc/card.h | 3 +
> >> include/linux/mmc/mmc.h | 66 +++++++++++++++++++-
> >> 7 files changed, 216 insertions(+), 13 deletions(-)
> >>
> >> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> >> index f8a228a..5d011a3 100644
> >> --- a/drivers/mmc/core/bus.c
> >> +++ b/drivers/mmc/core/bus.c
> >> @@ -303,10 +303,11 @@ int mmc_add_card(struct mmc_card *card)
> >> mmc_card_ddr_mode(card) ? "DDR " : "",
> >> type);
> >> } else {
> >> - printk(KERN_INFO "%s: new %s%s%s card at address
> %04x\n",
> >> + pr_info("%s: new %s%s%s%s card at address %04x\n",
> >> mmc_hostname(card->host),
> >> mmc_card_uhs(card) ? "ultra high speed " :
> >> (mmc_card_highspeed(card) ? "high speed " :
> ""),
> >> + (mmc_card_hs200(card) ? "HS200 " : ""),
> >> mmc_card_ddr_mode(card) ? "DDR " : "",
> >> type, card->rca);
> >> }
> >> diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
> >> index 027615d..9ab5b17 100644
> >> --- a/drivers/mmc/core/debugfs.c
> >> +++ b/drivers/mmc/core/debugfs.c
> >> @@ -135,6 +135,9 @@ static int mmc_ios_show(struct seq_file *s, void
> >> *data)
> >> case MMC_TIMING_UHS_DDR50:
> >> str = "sd uhs DDR50";
> >> break;
> >> + case MMC_TIMING_MMC_HS200:
> >> + str = "mmc high-speed SDR200";
> >> + break;
> >> default:
> >> str = "invalid";
> >> break;
> >> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> >> index f0a9f1f..d5d93de 100644
> >> --- a/drivers/mmc/core/mmc.c
> >> +++ b/drivers/mmc/core/mmc.c
> >> @@ -286,6 +286,27 @@ static int mmc_read_ext_csd(struct mmc_card
> *card,
> >> u8 *ext_csd)
> >> }
> >> card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
> >> switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
> >> + case EXT_CSD_CARD_TYPE_SDR_ALL:
> >> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V:
> >> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V:
> >> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52:
> >> + card->ext_csd.hs_max_dtr = 200000000;
> >> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
> >> + break;
> >> + case EXT_CSD_CARD_TYPE_SDR_1_2V_ALL:
> >> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V:
> >> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V:
> >> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52:
> >> + card->ext_csd.hs_max_dtr = 200000000;
> >> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
> >> + break;
> >> + case EXT_CSD_CARD_TYPE_SDR_1_8V_ALL:
> >> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V:
> >> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V:
> >> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52:
> >> + card->ext_csd.hs_max_dtr = 200000000;
> >> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
> >> + break;
> >> case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
> >> EXT_CSD_CARD_TYPE_26:
> >> card->ext_csd.hs_max_dtr = 52000000;
> >> @@ -700,6 +721,52 @@ static int mmc_select_powerclass(struct
> mmc_card
> >> *card,
> >> }
> >>
> >> /*
> >> + * Selects the desired buswidth and switch to the HS200 mode
> >> + * if bus width set without error
> >> + */
> >> +static int mmc_select_hs200(struct mmc_card *card)
> >> +{
> >> + int err = 0;
> >> + struct mmc_host *host;
> >> + u32 bus_width = MMC_BUS_WIDTH_4;
> >> +
> >> + BUG_ON(!card);
> >> +
> >> + host = card->host;
> >> +
> >> + /*
> >> + * Host is capable of 8bit transfer, then switch
> >> + * the device to work in 8bit transfer mode. If the
> >> + * mmc switch command returns error then switch to
> >> + * 4bit transfer mode. On success set the corresponding
> >> + * bus width on the host.
> >> + */
> >> + if (host->caps & MMC_CAP_8_BIT_DATA) {
> >> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> + EXT_CSD_BUS_WIDTH,
> >> + EXT_CSD_BUS_WIDTH_8,
> >> + card->ext_csd.generic_cmd6_time);
> >> + bus_width = MMC_BUS_WIDTH_8;
> >> + }
> >> +
> >> + /* If the 8bit mode fails switch to 4 bit mode */
> >> + if (err)
> >> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> + EXT_CSD_BUS_WIDTH,
> >> + EXT_CSD_BUS_WIDTH_4,
> >> + card->ext_csd.generic_cmd6_time);
> >
> > Don't you need to set the bus_width = MMC_BUS_WIDTH_4 after sending
> the
> > switch?
> > Let's say if the host->caps says it support the 8-bit mode but
> mmc_switch()
> > fails but your bus_width will still be MMC_BUS_WIDTH_8. Now you try
> to set
> > the bus width to 4-bit which will succeed but bus_width variable is
> not
> > initialize to 4-bit yet.
>
> Thanks just missed it.
>
> >
> >
> >
> >> +
> >> + /* switch to HS200 mode if bus width set successfully */
> >> + if (!err) {
> >> + mmc_set_bus_width(card->host, bus_width);
> >> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> + EXT_CSD_HS_TIMING, 2, 0);
> >> + }
> >> +
> >> + return err;
> >> +}
> >> +
> >> +/*
> >> * Handle the detection and initialisation of a card.
> >> *
> >> * In the case of a resume, "oldcard" will contain the card
> >> @@ -905,11 +972,15 @@ static int mmc_init_card(struct mmc_host
> *host,
> >> u32 ocr,
> >> /*
> >> * Activate high speed (if supported)
> >> */
> >> - if ((card->ext_csd.hs_max_dtr != 0) &&
> >> - (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
> >> - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> - EXT_CSD_HS_TIMING, 1,
> >> - card->ext_csd.generic_cmd6_time);
> >> + if (card->ext_csd.hs_max_dtr != 0) {
> >> + err = 0;
> >> + if ((card->ext_csd.hs_max_dtr > 52000000) &&
> >> + (host->caps2 & MMC_CAP2_HS200))
> >> + err = mmc_select_hs200(card);
> >> + else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
> >> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> + EXT_CSD_HS_TIMING, 1, 0);
> >> +
> >> if (err && err != -EBADMSG)
> >> goto free_card;
> >>
> >> @@ -918,8 +989,15 @@ static int mmc_init_card(struct mmc_host *host,
> >> u32 ocr,
> >> mmc_hostname(card->host));
> >> err = 0;
> >> } else {
> >> - mmc_card_set_highspeed(card);
> >> - mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
> >> + if ((card->ext_csd.hs_max_dtr > 52000000) &&
> >> + (host->caps2 & MMC_CAP2_HS200)) {
> >> + mmc_card_set_hs200(card);
> >> + mmc_set_timing(card->host,
> >> + MMC_TIMING_MMC_HS200);
> >> + } else {
> >> + mmc_card_set_highspeed(card);
> >> + mmc_set_timing(card->host,
> > MMC_TIMING_MMC_HS);
> >> + }
> >> }
> >> }
> >>
> >> @@ -944,7 +1022,7 @@ static int mmc_init_card(struct mmc_host *host,
> >> u32 ocr,
> >> */
> >> max_dtr = (unsigned int)-1;
> >>
> >> - if (mmc_card_highspeed(card)) {
> >> + if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
> >> if (max_dtr > card->ext_csd.hs_max_dtr)
> >> max_dtr = card->ext_csd.hs_max_dtr;
> >> } else if (max_dtr > card->csd.max_dtr) {
> >> @@ -970,9 +1048,60 @@ static int mmc_init_card(struct mmc_host
> *host,
> >> u32 ocr,
> >> }
> >>
> >> /*
> >> + * Indicate HS200 SDR mode (if supported).
> >> + */
> >> + if (mmc_card_hs200(card)) {
> >> + u32 ext_csd_bits;
> >> + u32 bus_width = card->host->ios.bus_width;
> >> +
> >> + /*
> >> + * For devices supporting HS200 mode, the bus width
> has
> >> + * to be set before executing the tuning function. If
> >> + * set before tuning, then device will respond with
> CRC
> >> + * errors for responses on CMD line. So for HS200 the
> >> + * sequence will be
> >> + * 1. set bus width 4bit / 8 bit (1 bit not supported)
> >> + * 2. switch to HS200 mode
> >> + * 3. set the clock to > 52Mhz <=200MHz and
> >> + * 4. execute tuning for HS200
> >> + */
> >> + if ((host->caps2 & MMC_CAP2_HS200) &&
> >> + card->host->ops->execute_tuning)
> >> + err = card->host->ops->execute_tuning(card-
> >host,
> >> + MMC_SEND_TUNING_BLOCK_HS200);
> >> + if (err) {
> >> + pr_warning("tuning execution failed\n");
> >> + goto err;
> >> + }
> >> +
> >> + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
> >> + EXT_CSD_BUS_WIDTH_8 :
> >> + EXT_CSD_BUS_WIDTH_4;
> >> + err = mmc_select_powerclass(card, ext_csd_bits,
> ext_csd);
> >> + if (err) {
> >> + pr_err("%s: power class selection to bus
> width"
> >> + "%d failed\n", mmc_hostname(card-
> >host),
> >> + 1 << bus_width);
> >> + goto err;
> >> + }
> >> +
> >> + if ((card->ext_csd.card_type &
> EXT_CSD_CARD_TYPE_SDR_1_2V)
> >> + && (host->caps2 & MMC_CAP2_HS200_1_2V_SDR)) {
> >> + err = mmc_set_signal_voltage(host,
> >> + MMC_SIGNAL_VOLTAGE_120, 0);
> >
> > Shouldn't this voltage switch be done in mmc_select_hs200() before
> selecting
> > the bus width and timings?
>
> Will it affect the functionality?. Since it it IO voltage I think it
> can be done here. And I am setting the Host specific timing and
> buswidth again.
> is ur intention to avoid setting timing and buswidth again?
Actually both. execute_tuning should be run with actual I/O voltage in which
card is going to run. But here you are first running the execute_tuning()
may be at 3.3v/1.8v then changing the I/O voltage which I guess doesn't look
correct.
SD3.01 specification has specifically mentioned that first the I/O voltage
switch should happen and then only execute_tuning() should happen. So it's
better to check if the eMMC4.5 spec. also implies this restriction anywhere
or not.
>
> >
> >
> >
> >
> >
> >> +
> >> + if (err)
> >> + goto err;
> >> + mmc_set_timing(card->host,
> MMC_TIMING_MMC_HS200);
> >> + mmc_set_bus_width(card->host, bus_width);
> >> + }
> >> + }
> >> +
> >> + /*
> >> * Activate wide bus and DDR (if supported).
> >> */
> >> - if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
> >> + if (!mmc_card_hs200(card) &&
> >> + (card->csd.mmca_vsn >= CSD_SPEC_VER_3) &&
> >> (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
> {
> >> static unsigned ext_csd_bits[][2] = {
> >> { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8
> },
> >> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> >> index 6f27d35..a775131 100644
> >> --- a/drivers/mmc/core/sd.c
> >> +++ b/drivers/mmc/core/sd.c
> >> @@ -661,7 +661,8 @@ static int mmc_sd_init_uhs_card(struct mmc_card
> >> *card)
> >>
> >> /* SPI mode doesn't define CMD19 */
> >> if (!mmc_host_is_spi(card->host) && card->host->ops-
> >> >execute_tuning)
> >> - err = card->host->ops->execute_tuning(card->host);
> >> + err = card->host->ops->execute_tuning(card->host,
> \
> >> +
> > MMC_SEND_TUNING_BLOCK);
> >>
> >> out:
> >> kfree(status);
> >> diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
> >> index b77f770..bd7bacc 100644
> >> --- a/drivers/mmc/core/sdio.c
> >> +++ b/drivers/mmc/core/sdio.c
> >> @@ -14,6 +14,7 @@
> >>
> >> #include <linux/mmc/host.h>
> >> #include <linux/mmc/card.h>
> >> +#include <linux/mmc/mmc.h>
> >> #include <linux/mmc/sdio.h>
> >> #include <linux/mmc/sdio_func.h>
> >> #include <linux/mmc/sdio_ids.h>
> >> @@ -556,7 +557,8 @@ static int mmc_sdio_init_uhs_card(struct
> mmc_card
> >> *card)
> >>
> >> /* Initialize and start re-tuning timer */
> >> if (!mmc_host_is_spi(card->host) && card->host->ops-
> >> >execute_tuning)
> >> - err = card->host->ops->execute_tuning(card->host);
> >> + err = card->host->ops->execute_tuning(card->host,
> >> +
> > MMC_SEND_TUNING_BLOCK);
> >>
> >> out:
> >>
> >> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> >> index 9478a6b..9f22ba5 100644
> >> --- a/include/linux/mmc/card.h
> >> +++ b/include/linux/mmc/card.h
> >> @@ -216,6 +216,7 @@ struct mmc_card {
> >> #define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in
> ultra
> >> high speed mode */
> >> #define MMC_CARD_SDXC (1<<6) /* card is
> SDXC */
> >> #define MMC_CARD_REMOVED (1<<7) /* card has been
> >> removed */
> >> +#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in
> >> HS200 mode */
> >> 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 */
> >> @@ -374,6 +375,7 @@ static inline void __maybe_unused
> >> remove_quirk(struct mmc_card *card, int data)
> >> #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
> >> #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
> >> #define mmc_card_highspeed(c) ((c)->state &
> MMC_STATE_HIGHSPEED)
> >> +#define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200)
> >> #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_uhs(c) ((c)->state &
> >> MMC_STATE_ULTRAHIGHSPEED)
> >> @@ -384,6 +386,7 @@ static inline void __maybe_unused
> >> remove_quirk(struct mmc_card *card, int data)
> >> #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_hs200(c) ((c)->state |=
> >> MMC_STATE_HIGHSPEED_200)
> >> #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_uhs(c) ((c)->state |=
> MMC_STATE_ULTRAHIGHSPEED)
> >> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> >> index 665548e..2c8cc69 100644
> >> --- a/include/linux/mmc/mmc.h
> >> +++ b/include/linux/mmc/mmc.h
> >> @@ -51,6 +51,7 @@
> >> #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
> >> */
> >> +#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
> >>
> >> /* class 3 */
> >> #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1
> >> */
> >> @@ -339,13 +340,76 @@ struct _mmc_csd {
> >>
> >> #define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
> >> #define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
> >> -#define EXT_CSD_CARD_TYPE_MASK 0xF /* Mask out reserved
> bits */
> >> +#define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved
> bits */
> >> #define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at
> 52MHz
> >> */
> >> /* DDR mode @1.8V or 3V
> I/O */
> >> #define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at
> 52MHz
> >> */
> >> /* DDR mode @1.2V I/O */
> >> #define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V
> \
> >> | EXT_CSD_CARD_TYPE_DDR_1_2V)
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_8V (1<<4) /* Card can run at
> >> 200MHz */
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at
> >> 200MHz */
> >> + /* SDR mode @1.2V I/O
> */
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_200 (EXT_CSD_CARD_TYPE_SDR_1_8V
> \
> >> + | EXT_CSD_CARD_TYPE_SDR_1_2V)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_ALL (EXT_CSD_CARD_TYPE_SDR_200
> \
> >> + | EXT_CSD_CARD_TYPE_52
> \
> >> + | EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_ALL
> >> (EXT_CSD_CARD_TYPE_SDR_1_2V \
> >> + | EXT_CSD_CARD_TYPE_52
> \
> >> + | EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_ALL
> >> (EXT_CSD_CARD_TYPE_SDR_1_8V \
> >> + | EXT_CSD_CARD_TYPE_52
> \
> >> + | EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V
> >> (EXT_CSD_CARD_TYPE_SDR_1_2V \
> >> + |
> EXT_CSD_CARD_TYPE_DDR_1_8V
> > \
> >> + | EXT_CSD_CARD_TYPE_52
> > \
> >> + |
> EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V
> >> (EXT_CSD_CARD_TYPE_SDR_1_8V \
> >> + |
> EXT_CSD_CARD_TYPE_DDR_1_8V
> > \
> >> + | EXT_CSD_CARD_TYPE_52
> > \
> >> + |
> EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V
> >> (EXT_CSD_CARD_TYPE_SDR_1_2V \
> >> + |
> EXT_CSD_CARD_TYPE_DDR_1_2V
> > \
> >> + | EXT_CSD_CARD_TYPE_52
> > \
> >> + |
> EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V
> >> (EXT_CSD_CARD_TYPE_SDR_1_8V \
> >> + |
> EXT_CSD_CARD_TYPE_DDR_1_2V
> > \
> >> + | EXT_CSD_CARD_TYPE_52
> > \
> >> + |
> EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52
> >> (EXT_CSD_CARD_TYPE_SDR_1_2V \
> >> + |
> EXT_CSD_CARD_TYPE_DDR_52
> > \
> >> + | EXT_CSD_CARD_TYPE_52
> > \
> >> + |
> EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52
> >> (EXT_CSD_CARD_TYPE_SDR_1_8V \
> >> + |
> EXT_CSD_CARD_TYPE_DDR_52
> > \
> >> + | EXT_CSD_CARD_TYPE_52
> > \
> >> + |
> EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V
> >> (EXT_CSD_CARD_TYPE_SDR_200 \
> >> + |
> EXT_CSD_CARD_TYPE_DDR_1_8V
> > \
> >> + | EXT_CSD_CARD_TYPE_52
> > \
> >> + |
> EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V
> >> (EXT_CSD_CARD_TYPE_SDR_200 \
> >> + |
> EXT_CSD_CARD_TYPE_DDR_1_2V
> > \
> >> + | EXT_CSD_CARD_TYPE_52
> > \
> >> + |
> EXT_CSD_CARD_TYPE_26)
> >> +
> >> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52
> (EXT_CSD_CARD_TYPE_SDR_200
> >> \
> >> + |
> EXT_CSD_CARD_TYPE_DDR_52
> > \
> >> + | EXT_CSD_CARD_TYPE_52
> > \
> >> + |
> EXT_CSD_CARD_TYPE_26)
> >>
> >> #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
> >> #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
> >> --
> >> 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] 9+ messages in thread
* Re: [PATCH V6 2/2] mmc: host: Adds support for eMMC 4.5 HS200 mode
2011-12-19 6:36 ` [PATCH V6 2/2] mmc: host: Adds support for eMMC 4.5 HS200 mode Girish K S
@ 2011-12-19 16:54 ` Philip Rakity
2011-12-20 4:40 ` Girish K S
0 siblings, 1 reply; 9+ messages in thread
From: Philip Rakity @ 2011-12-19 16:54 UTC (permalink / raw)
To: Girish K S
Cc: linux-mmc@vger.kernel.org, patches@linaro.org,
linux-samsung-soc@vger.kernel.org, Chris Ball
HI,
minor comments below.
Philip
On Dec 18, 2011, at 10:36 PM, Girish K S wrote:
> This patch adds support for the HS200 mode on the host side.
> Also enables the tuning feature required when the HS200 mode
> is selected.
>
> cc: Chris Ball <cjb@laptop.org>
> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
> ---
> drivers/mmc/host/sdhci.c | 43 +++++++++++++++++++++++++++++++++----------
> drivers/mmc/host/sdhci.h | 1 +
> include/linux/mmc/host.h | 11 ++++++++++-
> include/linux/mmc/sdhci.h | 1 +
> 4 files changed, 45 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index ab6018f..049d51d 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -49,7 +49,7 @@ static void sdhci_finish_data(struct sdhci_host *);
>
> static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
> static void sdhci_finish_command(struct sdhci_host *);
> -static int sdhci_execute_tuning(struct mmc_host *mmc);
> +static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
> static void sdhci_tuning_timer(unsigned long data);
>
> #ifdef CONFIG_PM_RUNTIME
> @@ -1016,7 +1016,8 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
> flags |= SDHCI_CMD_INDEX;
>
> /* CMD19 is special in that the Data Present Select should be set */
> - if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
> + if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
> + (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
> flags |= SDHCI_CMD_DATA;
>
> sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
> @@ -1287,7 +1288,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
> if ((host->flags & SDHCI_NEEDS_RETUNING) &&
> !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
> spin_unlock_irqrestore(&host->lock, flags);
> - sdhci_execute_tuning(mmc);
> + sdhci_execute_tuning(mmc, mrq->cmd->opcode);
> spin_lock_irqsave(&host->lock, flags);
>
> /* Restore original mmc_request structure */
> @@ -1375,7 +1376,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
> (ios->timing == MMC_TIMING_UHS_SDR104) ||
> (ios->timing == MMC_TIMING_UHS_DDR50) ||
> (ios->timing == MMC_TIMING_UHS_SDR25) ||
> - (ios->timing == MMC_TIMING_UHS_SDR12))
> + (ios->timing == MMC_TIMING_UHS_SDR12) ||
> + (ios->timing == MMC_TIMING_MMC_HS200))
> ctrl |= SDHCI_CTRL_HISPD;
move to first test so speed ordering is maintained.
>
> ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> @@ -1435,6 +1437,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
> ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> else if (ios->timing == MMC_TIMING_UHS_DDR50)
> ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> + else if (ios->timing == MMC_TIMING_MMC_HS200)
> + ctrl_2 |= SDHCI_CTRL_HS_SDR200;
> sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> }
same as above
>
> @@ -1673,7 +1677,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
> return err;
> }
>
> -static int sdhci_execute_tuning(struct mmc_host *mmc)
> +static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
> {
Can one deduce the type of tuning needed by the speed ? is the opcode really needed?
> struct sdhci_host *host;
> u16 ctrl;
> @@ -1694,10 +1698,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
> * 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 the Host Controller supports the HS200 mode then tuning
> + * function has to be executed.
> */
> if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
> (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
> - (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
> + (host->flags & SDHCI_SDR50_NEEDS_TUNING)) ||
> + (host->flags & SDHCI_HS200_NEEDS_TUNING))
> ctrl |= SDHCI_CTRL_EXEC_TUNING;
> else {
> spin_unlock(&host->lock);
> @@ -1733,7 +1740,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
> if (!tuning_loop_counter && !timeout)
> break;
>
> - cmd.opcode = MMC_SEND_TUNING_BLOCK;
> + cmd.opcode = opcode;
> cmd.arg = 0;
> cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> cmd.retries = 0;
> @@ -1748,7 +1755,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
> * block to the Host Controller. So we set the block size
> * to 64 here.
> */
> - sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
> + if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
> + if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
> + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
> + SDHCI_BLOCK_SIZE);
> + else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
> + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
> + SDHCI_BLOCK_SIZE);
> + } else {
> + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
> + SDHCI_BLOCK_SIZE);
> + }
>
> /*
> * The tuning block is sent by the card to the host controller.
> @@ -2131,12 +2148,14 @@ static void sdhci_show_adma_error(struct sdhci_host *host) { }
>
> static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
> {
> + u32 command;
> 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) {
> + command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
> + if ((command == MMC_SEND_TUNING_BLOCK) ||
> + (command == MMC_SEND_TUNING_BLOCK_HS200)) {
> host->tuning_done = 1;
> wake_up(&host->buf_ready_int);
> return;
> @@ -2741,6 +2760,10 @@ int sdhci_add_host(struct sdhci_host *host)
> if (caps[1] & SDHCI_USE_SDR50_TUNING)
> host->flags |= SDHCI_SDR50_NEEDS_TUNING;
>
> + /* Does the host needs tuning for HS200? */
> + if (mmc->caps2 & MMC_CAP2_HS200)
> + host->flags |= SDHCI_HS200_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;
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index a04d4d0..46fd2ac 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -158,6 +158,7 @@
> #define SDHCI_CTRL_UHS_SDR50 0x0002
> #define SDHCI_CTRL_UHS_SDR104 0x0003
> #define SDHCI_CTRL_UHS_DDR50 0x0004
> +#define SDHCI_CTRL_HS_SDR200 0x0005 /*reserved value in SDIO spec */
> #define SDHCI_CTRL_VDD_180 0x0008
> #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
> #define SDHCI_CTRL_DRV_TYPE_B 0x0000
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 9a03d03..606c8c3 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -56,10 +56,13 @@ struct mmc_ios {
> #define MMC_TIMING_UHS_SDR50 3
> #define MMC_TIMING_UHS_SDR104 4
> #define MMC_TIMING_UHS_DDR50 5
> +#define MMC_TIMING_MMC_HS200 6
>
> #define MMC_SDR_MODE 0
> #define MMC_1_2V_DDR_MODE 1
> #define MMC_1_8V_DDR_MODE 2
> +#define MMC_1_2V_SDR_MODE 3
> +#define MMC_1_8V_SDR_MODE 4
>
> unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
>
> @@ -148,7 +151,9 @@ struct mmc_host_ops {
> void (*init_card)(struct mmc_host *host, struct mmc_card *card);
>
> int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
> - int (*execute_tuning)(struct mmc_host *host);
> +
> + /* The tuning command opcode value is different for SD and eMMC cards */
> + int (*execute_tuning)(struct mmc_host *host, u32 opcode);
> void (*enable_preset_value)(struct mmc_host *host, bool enable);
> int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
> void (*hw_reset)(struct mmc_host *host);
> @@ -242,6 +247,10 @@ struct mmc_host {
> #define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */
> #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */
> #define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */
> +#define MMC_CAP2_HS200_1_8V_SDR (1 << 4) /* can support */
> +#define MMC_CAP2_HS200_1_2V_SDR (1 << 5) /* can support */
> +#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
> + MMC_CAP2_HS200_1_2V_SDR)
>
> mmc_pm_flag_t pm_caps; /* supported pm features */
> unsigned int power_notify_type;
> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> index e4b6935..d9a2222 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -121,6 +121,7 @@ struct sdhci_host {
> #define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
> #define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
> #define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
> +#define SDHCI_HS200_NEEDS_TUNING (1<<10) /* HS200 needs tuning */
>
> unsigned int version; /* SDHCI spec. version */
>
> --
> 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] 9+ messages in thread
* Re: [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5
2011-12-19 6:36 ` [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5 Girish K S
2011-12-19 10:10 ` Subhash Jadavani
@ 2011-12-19 17:01 ` Philip Rakity
1 sibling, 0 replies; 9+ messages in thread
From: Philip Rakity @ 2011-12-19 17:01 UTC (permalink / raw)
To: Girish K S
Cc: linux-mmc@vger.kernel.org, patches@linaro.org,
linux-samsung-soc@vger.kernel.org, Chris Ball
On Dec 18, 2011, at 10:36 PM, Girish K S wrote:
> This patch adds the support of the HS200 bus speed for eMMC 4.5 devices.
> The eMMC 4.5 devices have support for 200MHz bus speed. The function
> prototype of the tuning function is modified to handle the tuning command
> number which is different in sd and mmc case.
>
> cc: Chris Ball <cjb@laptop.org>
> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
> ---
> drivers/mmc/core/bus.c | 3 +-
> drivers/mmc/core/debugfs.c | 3 +
> drivers/mmc/core/mmc.c | 147 +++++++++++++++++++++++++++++++++++++++++---
> drivers/mmc/core/sd.c | 3 +-
> drivers/mmc/core/sdio.c | 4 +-
> include/linux/mmc/card.h | 3 +
> include/linux/mmc/mmc.h | 66 +++++++++++++++++++-
> 7 files changed, 216 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> index f8a228a..5d011a3 100644
> --- a/drivers/mmc/core/bus.c
> +++ b/drivers/mmc/core/bus.c
> @@ -303,10 +303,11 @@ int mmc_add_card(struct mmc_card *card)
> mmc_card_ddr_mode(card) ? "DDR " : "",
> type);
> } else {
> - printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
> + pr_info("%s: new %s%s%s%s card at address %04x\n",
> mmc_hostname(card->host),
> mmc_card_uhs(card) ? "ultra high speed " :
> (mmc_card_highspeed(card) ? "high speed " : ""),
> + (mmc_card_hs200(card) ? "HS200 " : ""),
> mmc_card_ddr_mode(card) ? "DDR " : "",
> type, card->rca);
> }
> diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
> index 027615d..9ab5b17 100644
> --- a/drivers/mmc/core/debugfs.c
> +++ b/drivers/mmc/core/debugfs.c
> @@ -135,6 +135,9 @@ static int mmc_ios_show(struct seq_file *s, void *data)
> case MMC_TIMING_UHS_DDR50:
> str = "sd uhs DDR50";
> break;
> + case MMC_TIMING_MMC_HS200:
> + str = "mmc high-speed SDR200";
> + break;
> default:
> str = "invalid";
> break;
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index f0a9f1f..d5d93de 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -286,6 +286,27 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
> }
> card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
> switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
> + case EXT_CSD_CARD_TYPE_SDR_ALL:
> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V:
> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V:
> + case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52:
> + card->ext_csd.hs_max_dtr = 200000000;
> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
> + break;
> + case EXT_CSD_CARD_TYPE_SDR_1_2V_ALL:
> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V:
> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V:
> + case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52:
> + card->ext_csd.hs_max_dtr = 200000000;
> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
> + break;
> + case EXT_CSD_CARD_TYPE_SDR_1_8V_ALL:
> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V:
> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V:
> + case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52:
> + card->ext_csd.hs_max_dtr = 200000000;
> + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
> + break;
> case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
> EXT_CSD_CARD_TYPE_26:
> card->ext_csd.hs_max_dtr = 52000000;
> @@ -700,6 +721,52 @@ static int mmc_select_powerclass(struct mmc_card *card,
> }
>
> /*
> + * Selects the desired buswidth and switch to the HS200 mode
> + * if bus width set without error
> + */
> +static int mmc_select_hs200(struct mmc_card *card)
> +{
> + int err = 0;
> + struct mmc_host *host;
> + u32 bus_width = MMC_BUS_WIDTH_4;
> +
> + BUG_ON(!card);
> +
> + host = card->host;
> +
Can you use BUS_WIDTH [183] section 7.4.42 to determine the supported bus widths of the device
to avoid the switch if 8 bit is not supported. CAP_8_BIT indicates controller supports 8 bit but the
BUS_WIDTH field indicates the card supports this.
> + /*
> + * Host is capable of 8bit transfer, then switch
> + * the device to work in 8bit transfer mode. If the
> + * mmc switch command returns error then switch to
> + * 4bit transfer mode. On success set the corresponding
> + * bus width on the host.
> + */
> + if (host->caps & MMC_CAP_8_BIT_DATA) {
> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> + EXT_CSD_BUS_WIDTH,
> + EXT_CSD_BUS_WIDTH_8,
> + card->ext_csd.generic_cmd6_time);
> + bus_width = MMC_BUS_WIDTH_8;
> + }
> +
> + /* If the 8bit mode fails switch to 4 bit mode */
> + if (err)
> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> + EXT_CSD_BUS_WIDTH,
> + EXT_CSD_BUS_WIDTH_4,
> + card->ext_csd.generic_cmd6_time);
> +
> + /* switch to HS200 mode if bus width set successfully */
> + if (!err) {
> + mmc_set_bus_width(card->host, bus_width);
> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> + EXT_CSD_HS_TIMING, 2, 0);
> + }
> +
> + return err;
> +}
> +
> +/*
> * Handle the detection and initialisation of a card.
> *
> * In the case of a resume, "oldcard" will contain the card
> @@ -905,11 +972,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
> /*
> * Activate high speed (if supported)
> */
> - if ((card->ext_csd.hs_max_dtr != 0) &&
> - (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
> - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> - EXT_CSD_HS_TIMING, 1,
> - card->ext_csd.generic_cmd6_time);
> + if (card->ext_csd.hs_max_dtr != 0) {
> + err = 0;
> + if ((card->ext_csd.hs_max_dtr > 52000000) &&
> + (host->caps2 & MMC_CAP2_HS200))
> + err = mmc_select_hs200(card);
> + else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> + EXT_CSD_HS_TIMING, 1, 0);
> +
> if (err && err != -EBADMSG)
> goto free_card;
>
> @@ -918,8 +989,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
> mmc_hostname(card->host));
> err = 0;
> } else {
> - mmc_card_set_highspeed(card);
> - mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
> + if ((card->ext_csd.hs_max_dtr > 52000000) &&
> + (host->caps2 & MMC_CAP2_HS200)) {
> + mmc_card_set_hs200(card);
> + mmc_set_timing(card->host,
> + MMC_TIMING_MMC_HS200);
> + } else {
> + mmc_card_set_highspeed(card);
> + mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
> + }
> }
> }
>
> @@ -944,7 +1022,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
> */
> max_dtr = (unsigned int)-1;
>
> - if (mmc_card_highspeed(card)) {
> + if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
> if (max_dtr > card->ext_csd.hs_max_dtr)
> max_dtr = card->ext_csd.hs_max_dtr;
> } else if (max_dtr > card->csd.max_dtr) {
> @@ -970,9 +1048,60 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
> }
>
> /*
> + * Indicate HS200 SDR mode (if supported).
> + */
> + if (mmc_card_hs200(card)) {
> + u32 ext_csd_bits;
> + u32 bus_width = card->host->ios.bus_width;
> +
> + /*
> + * For devices supporting HS200 mode, the bus width has
> + * to be set before executing the tuning function. If
> + * set before tuning, then device will respond with CRC
> + * errors for responses on CMD line. So for HS200 the
> + * sequence will be
> + * 1. set bus width 4bit / 8 bit (1 bit not supported)
> + * 2. switch to HS200 mode
> + * 3. set the clock to > 52Mhz <=200MHz and
> + * 4. execute tuning for HS200
> + */
> + if ((host->caps2 & MMC_CAP2_HS200) &&
> + card->host->ops->execute_tuning)
> + err = card->host->ops->execute_tuning(card->host,
> + MMC_SEND_TUNING_BLOCK_HS200);
> + if (err) {
> + pr_warning("tuning execution failed\n");
> + goto err;
> + }
> +
> + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
> + EXT_CSD_BUS_WIDTH_8 :
> + EXT_CSD_BUS_WIDTH_4;
> + err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
> + if (err) {
> + pr_err("%s: power class selection to bus width"
> + "%d failed\n", mmc_hostname(card->host),
> + 1 << bus_width);
> + goto err;
> + }
> +
> + if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)
> + && (host->caps2 & MMC_CAP2_HS200_1_2V_SDR)) {
> + err = mmc_set_signal_voltage(host,
> + MMC_SIGNAL_VOLTAGE_120, 0);
> +
> + if (err)
> + goto err;
> + mmc_set_timing(card->host, MMC_TIMING_MMC_HS200);
> + mmc_set_bus_width(card->host, bus_width);
> + }
> + }
> +
> + /*
> * Activate wide bus and DDR (if supported).
> */
> - if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
> + if (!mmc_card_hs200(card) &&
> + (card->csd.mmca_vsn >= CSD_SPEC_VER_3) &&
> (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
> static unsigned ext_csd_bits[][2] = {
> { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 6f27d35..a775131 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -661,7 +661,8 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
>
> /* SPI mode doesn't define CMD19 */
> if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
> - err = card->host->ops->execute_tuning(card->host);
> + err = card->host->ops->execute_tuning(card->host, \
> + MMC_SEND_TUNING_BLOCK);
>
> out:
> kfree(status);
> diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
> index b77f770..bd7bacc 100644
> --- a/drivers/mmc/core/sdio.c
> +++ b/drivers/mmc/core/sdio.c
> @@ -14,6 +14,7 @@
>
> #include <linux/mmc/host.h>
> #include <linux/mmc/card.h>
> +#include <linux/mmc/mmc.h>
> #include <linux/mmc/sdio.h>
> #include <linux/mmc/sdio_func.h>
> #include <linux/mmc/sdio_ids.h>
> @@ -556,7 +557,8 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
>
> /* Initialize and start re-tuning timer */
> if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
> - err = card->host->ops->execute_tuning(card->host);
> + err = card->host->ops->execute_tuning(card->host,
> + MMC_SEND_TUNING_BLOCK);
>
> out:
>
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 9478a6b..9f22ba5 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -216,6 +216,7 @@ struct mmc_card {
> #define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */
> #define MMC_CARD_SDXC (1<<6) /* card is SDXC */
> #define MMC_CARD_REMOVED (1<<7) /* card has been removed */
> +#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
> 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 */
> @@ -374,6 +375,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
> #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
> #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
> #define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
> +#define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200)
> #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_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
> @@ -384,6 +386,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
> #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_hs200(c) ((c)->state |= MMC_STATE_HIGHSPEED_200)
> #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_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index 665548e..2c8cc69 100644
> --- a/include/linux/mmc/mmc.h
> +++ b/include/linux/mmc/mmc.h
> @@ -51,6 +51,7 @@
> #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 */
> +#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
>
> /* class 3 */
> #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
> @@ -339,13 +340,76 @@ struct _mmc_csd {
>
> #define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
> #define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
> -#define EXT_CSD_CARD_TYPE_MASK 0xF /* Mask out reserved bits */
> +#define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */
> #define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */
> /* DDR mode @1.8V or 3V I/O */
> #define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */
> /* DDR mode @1.2V I/O */
> #define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \
> | EXT_CSD_CARD_TYPE_DDR_1_2V)
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V (1<<4) /* Card can run at 200MHz */
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */
> + /* SDR mode @1.2V I/O */
> +
> +#define EXT_CSD_CARD_TYPE_SDR_200 (EXT_CSD_CARD_TYPE_SDR_1_8V \
> + | EXT_CSD_CARD_TYPE_SDR_1_2V)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_ALL (EXT_CSD_CARD_TYPE_SDR_200 \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_ALL (EXT_CSD_CARD_TYPE_SDR_1_2V \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_ALL (EXT_CSD_CARD_TYPE_SDR_1_8V \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_1_2V \
> + | EXT_CSD_CARD_TYPE_DDR_1_8V \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_1_8V \
> + | EXT_CSD_CARD_TYPE_DDR_1_8V \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_1_2V \
> + | EXT_CSD_CARD_TYPE_DDR_1_2V \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_1_8V \
> + | EXT_CSD_CARD_TYPE_DDR_1_2V \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52 (EXT_CSD_CARD_TYPE_SDR_1_2V \
> + | EXT_CSD_CARD_TYPE_DDR_52 \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52 (EXT_CSD_CARD_TYPE_SDR_1_8V \
> + | EXT_CSD_CARD_TYPE_DDR_52 \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_200 \
> + | EXT_CSD_CARD_TYPE_DDR_1_8V \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_200 \
> + | EXT_CSD_CARD_TYPE_DDR_1_2V \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
> +
> +#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52 (EXT_CSD_CARD_TYPE_SDR_200 \
> + | EXT_CSD_CARD_TYPE_DDR_52 \
> + | EXT_CSD_CARD_TYPE_52 \
> + | EXT_CSD_CARD_TYPE_26)
>
> #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
> #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
> --
> 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] 9+ messages in thread
* Re: [PATCH V6 2/2] mmc: host: Adds support for eMMC 4.5 HS200 mode
2011-12-19 16:54 ` Philip Rakity
@ 2011-12-20 4:40 ` Girish K S
0 siblings, 0 replies; 9+ messages in thread
From: Girish K S @ 2011-12-20 4:40 UTC (permalink / raw)
To: Philip Rakity
Cc: linux-mmc@vger.kernel.org, patches@linaro.org,
linux-samsung-soc@vger.kernel.org, Chris Ball
On 19 December 2011 22:24, Philip Rakity <prakity@marvell.com> wrote:
>
> HI,
>
> minor comments below.
>
> Philip
>
> On Dec 18, 2011, at 10:36 PM, Girish K S wrote:
>
>> This patch adds support for the HS200 mode on the host side.
>> Also enables the tuning feature required when the HS200 mode
>> is selected.
>>
>> cc: Chris Ball <cjb@laptop.org>
>> Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
>> ---
>> drivers/mmc/host/sdhci.c | 43 +++++++++++++++++++++++++++++++++----------
>> drivers/mmc/host/sdhci.h | 1 +
>> include/linux/mmc/host.h | 11 ++++++++++-
>> include/linux/mmc/sdhci.h | 1 +
>> 4 files changed, 45 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index ab6018f..049d51d 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -49,7 +49,7 @@ static void sdhci_finish_data(struct sdhci_host *);
>>
>> static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
>> static void sdhci_finish_command(struct sdhci_host *);
>> -static int sdhci_execute_tuning(struct mmc_host *mmc);
>> +static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
>> static void sdhci_tuning_timer(unsigned long data);
>>
>> #ifdef CONFIG_PM_RUNTIME
>> @@ -1016,7 +1016,8 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
>> flags |= SDHCI_CMD_INDEX;
>>
>> /* CMD19 is special in that the Data Present Select should be set */
>> - if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
>> + if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
>> + (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
>> flags |= SDHCI_CMD_DATA;
>>
>> sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
>> @@ -1287,7 +1288,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
>> if ((host->flags & SDHCI_NEEDS_RETUNING) &&
>> !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
>> spin_unlock_irqrestore(&host->lock, flags);
>> - sdhci_execute_tuning(mmc);
>> + sdhci_execute_tuning(mmc, mrq->cmd->opcode);
>> spin_lock_irqsave(&host->lock, flags);
>>
>> /* Restore original mmc_request structure */
>> @@ -1375,7 +1376,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
>> (ios->timing == MMC_TIMING_UHS_SDR104) ||
>> (ios->timing == MMC_TIMING_UHS_DDR50) ||
>> (ios->timing == MMC_TIMING_UHS_SDR25) ||
>> - (ios->timing == MMC_TIMING_UHS_SDR12))
>> + (ios->timing == MMC_TIMING_UHS_SDR12) ||
>> + (ios->timing == MMC_TIMING_MMC_HS200))
>> ctrl |= SDHCI_CTRL_HISPD;
>
> move to first test so speed ordering is maintained.
Sure
>>
>> ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>> @@ -1435,6 +1437,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
>> ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
>> else if (ios->timing == MMC_TIMING_UHS_DDR50)
>> ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
>> + else if (ios->timing == MMC_TIMING_MMC_HS200)
>> + ctrl_2 |= SDHCI_CTRL_HS_SDR200;
>> sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
>> }
>
> same as above
Sure
>
>>
>> @@ -1673,7 +1677,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
>> return err;
>> }
>>
>> -static int sdhci_execute_tuning(struct mmc_host *mmc)
>> +static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
>> {
>
> Can one deduce the type of tuning needed by the speed ? is the opcode really needed?
Yes Its needed, because the SD specification defines tuining command
as 19 and eMMC specification defines the same tuning command as 21. If
opcode is not passed as the parameter then the host has to be informed
about the card type (whether SD or MMC).
You can check the previous review comments for the same by subhash.
>
>> struct sdhci_host *host;
>> u16 ctrl;
>> @@ -1694,10 +1698,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
>> * 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 the Host Controller supports the HS200 mode then tuning
>> + * function has to be executed.
>> */
>> if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
>> (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
>> - (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
>> + (host->flags & SDHCI_SDR50_NEEDS_TUNING)) ||
>> + (host->flags & SDHCI_HS200_NEEDS_TUNING))
>> ctrl |= SDHCI_CTRL_EXEC_TUNING;
>> else {
>> spin_unlock(&host->lock);
>> @@ -1733,7 +1740,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
>> if (!tuning_loop_counter && !timeout)
>> break;
>>
>> - cmd.opcode = MMC_SEND_TUNING_BLOCK;
>> + cmd.opcode = opcode;
>> cmd.arg = 0;
>> cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
>> cmd.retries = 0;
>> @@ -1748,7 +1755,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
>> * block to the Host Controller. So we set the block size
>> * to 64 here.
>> */
>> - sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
>> + if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
>> + if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
>> + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
>> + SDHCI_BLOCK_SIZE);
>> + else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
>> + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
>> + SDHCI_BLOCK_SIZE);
>> + } else {
>> + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
>> + SDHCI_BLOCK_SIZE);
>> + }
>>
>> /*
>> * The tuning block is sent by the card to the host controller.
>> @@ -2131,12 +2148,14 @@ static void sdhci_show_adma_error(struct sdhci_host *host) { }
>>
>> static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
>> {
>> + u32 command;
>> 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) {
>> + command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
>> + if ((command == MMC_SEND_TUNING_BLOCK) ||
>> + (command == MMC_SEND_TUNING_BLOCK_HS200)) {
>> host->tuning_done = 1;
>> wake_up(&host->buf_ready_int);
>> return;
>> @@ -2741,6 +2760,10 @@ int sdhci_add_host(struct sdhci_host *host)
>> if (caps[1] & SDHCI_USE_SDR50_TUNING)
>> host->flags |= SDHCI_SDR50_NEEDS_TUNING;
>>
>> + /* Does the host needs tuning for HS200? */
>> + if (mmc->caps2 & MMC_CAP2_HS200)
>> + host->flags |= SDHCI_HS200_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;
>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>> index a04d4d0..46fd2ac 100644
>> --- a/drivers/mmc/host/sdhci.h
>> +++ b/drivers/mmc/host/sdhci.h
>> @@ -158,6 +158,7 @@
>> #define SDHCI_CTRL_UHS_SDR50 0x0002
>> #define SDHCI_CTRL_UHS_SDR104 0x0003
>> #define SDHCI_CTRL_UHS_DDR50 0x0004
>> +#define SDHCI_CTRL_HS_SDR200 0x0005 /*reserved value in SDIO spec */
>> #define SDHCI_CTRL_VDD_180 0x0008
>> #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
>> #define SDHCI_CTRL_DRV_TYPE_B 0x0000
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index 9a03d03..606c8c3 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -56,10 +56,13 @@ struct mmc_ios {
>> #define MMC_TIMING_UHS_SDR50 3
>> #define MMC_TIMING_UHS_SDR104 4
>> #define MMC_TIMING_UHS_DDR50 5
>> +#define MMC_TIMING_MMC_HS200 6
>>
>> #define MMC_SDR_MODE 0
>> #define MMC_1_2V_DDR_MODE 1
>> #define MMC_1_8V_DDR_MODE 2
>> +#define MMC_1_2V_SDR_MODE 3
>> +#define MMC_1_8V_SDR_MODE 4
>>
>> unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
>>
>> @@ -148,7 +151,9 @@ struct mmc_host_ops {
>> void (*init_card)(struct mmc_host *host, struct mmc_card *card);
>>
>> int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
>> - int (*execute_tuning)(struct mmc_host *host);
>> +
>> + /* The tuning command opcode value is different for SD and eMMC cards */
>> + int (*execute_tuning)(struct mmc_host *host, u32 opcode);
>> void (*enable_preset_value)(struct mmc_host *host, bool enable);
>> int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
>> void (*hw_reset)(struct mmc_host *host);
>> @@ -242,6 +247,10 @@ struct mmc_host {
>> #define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */
>> #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */
>> #define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */
>> +#define MMC_CAP2_HS200_1_8V_SDR (1 << 4) /* can support */
>> +#define MMC_CAP2_HS200_1_2V_SDR (1 << 5) /* can support */
>> +#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
>> + MMC_CAP2_HS200_1_2V_SDR)
>>
>> mmc_pm_flag_t pm_caps; /* supported pm features */
>> unsigned int power_notify_type;
>> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
>> index e4b6935..d9a2222 100644
>> --- a/include/linux/mmc/sdhci.h
>> +++ b/include/linux/mmc/sdhci.h
>> @@ -121,6 +121,7 @@ struct sdhci_host {
>> #define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
>> #define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
>> #define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
>> +#define SDHCI_HS200_NEEDS_TUNING (1<<10) /* HS200 needs tuning */
>>
>> unsigned int version; /* SDHCI spec. version */
>>
>> --
>> 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] 9+ messages in thread
end of thread, other threads:[~2011-12-20 4:40 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-12-19 6:36 [PATCH V6 0/2] HS200 Mode support for device and host Girish K S
2011-12-19 6:36 ` [PATCH V6 1/2] mmc: core: HS200 mode support for eMMC 4.5 Girish K S
2011-12-19 10:10 ` Subhash Jadavani
2011-12-19 10:49 ` Girish K S
2011-12-19 10:53 ` Subhash Jadavani
2011-12-19 17:01 ` Philip Rakity
2011-12-19 6:36 ` [PATCH V6 2/2] mmc: host: Adds support for eMMC 4.5 HS200 mode Girish K S
2011-12-19 16:54 ` Philip Rakity
2011-12-20 4:40 ` Girish K S
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).