summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorKrishna Konda <kkonda@codeaurora.org>2013-10-28 15:25:03 -0700
committerSubhash Jadavani <subhashj@codeaurora.org>2016-05-27 10:29:04 -0700
commitb0d11d554caef0ad130fd5d92187d0d8ea14aaff (patch)
tree8d2044bcda2a13421ed4fdc09b9821aa5c44d286 /drivers
parent21a9ae94b51d0c81bedcda704815e7ce153eed2a (diff)
mmc: sdhci-msm: improve tuning process
In newer hardware, the tuning process is not able to always find a reliable phase to use for sampling data. This is mostly due to hardware. This problem manifests itself as all successful tuning phases, which means that the phase choosen could be a bad one but is not identified as such at the time of tuning. So in order to work around this, rely on optional drive types implemented by the eMMC card, in addition to the mandatory drive type (50 ohm). By using drive types supported by the card, when all phases are sucessful in tuning, change drive type to a different value in the list of supported drive types and retune. This will continue for all tuning phases until a valid one is found. After that the drive type is reset to the default one, if changed. Change-Id: I348fb30daa43d97c58f83f7e4a22019f94ef4954 Signed-off-by: Krishna Konda <kkonda@codeaurora.org> Signed-off-by: Asutosh Das <asutoshd@codeaurora.org> [venkatg@codeaurora.org: rename cmd_timeout_ms to busy_timeout as part of 3.14 kernel upgrade] Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> [subhashj@codeaurora.org: fixed minor merge conflict & compilation error] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/core/mmc.c5
-rw-r--r--drivers/mmc/host/sdhci-msm.c76
2 files changed, 76 insertions, 5 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index dc70b2d7a895..619bd9b1ff13 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1428,6 +1428,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
card->type = MMC_TYPE_MMC;
card->rca = 1;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ host->card = card;
}
/*
@@ -1655,12 +1656,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
}
- if (!oldcard)
- host->card = card;
-
return 0;
free_card:
+ host->card = NULL;
if (!oldcard)
mmc_remove_card(card);
err:
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 87d9c793f468..e2419293a58d 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -146,6 +146,9 @@
#define CORE_VERSION_TARGET_MASK 0x000000FF
+#define NUM_TUNING_PHASES 16
+#define MAX_DRV_TYPES_SUPPORTED_HS200 3
+
static const u32 tuning_block_64[] = {
0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
@@ -831,11 +834,40 @@ out:
return ret;
}
+static void sdhci_msm_set_mmc_drv_type(struct sdhci_host *host, u32 opcode,
+ u8 drv_type)
+{
+ struct mmc_command cmd = {0};
+ struct mmc_request mrq = {NULL};
+ struct mmc_host *mmc = host->mmc;
+ u8 val = ((drv_type << 4) | 2);
+
+ cmd.opcode = MMC_SWITCH;
+ cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (EXT_CSD_HS_TIMING << 16) |
+ (val << 8) |
+ EXT_CSD_CMD_SET_NORMAL;
+ cmd.flags = MMC_CMD_AC | MMC_RSP_R1B;
+ /* 1 sec */
+ cmd.busy_timeout = 1000 * 1000;
+
+ memset(cmd.resp, 0, sizeof(cmd.resp));
+ cmd.retries = 3;
+
+ mrq.cmd = &cmd;
+ cmd.data = NULL;
+
+ mmc_wait_for_req(mmc, &mrq);
+ pr_debug("%s: %s: set card drive type to %d\n",
+ mmc_hostname(mmc), __func__,
+ drv_type);
+}
+
int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
{
unsigned long flags;
int tuning_seq_cnt = 3;
- u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
+ u8 phase, *data_buf, tuned_phases[NUM_TUNING_PHASES], tuned_phase_cnt;
const u32 *tuning_block_pattern = tuning_block_64;
int size = sizeof(tuning_block_64); /* Tuning pattern size in bytes */
int rc;
@@ -843,6 +875,9 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
struct mmc_ios ios = host->mmc->ios;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ u8 drv_type = 0;
+ bool drv_type_changed = false;
+ struct mmc_card *card = host->mmc->card;
/*
* Tuning is required for SDR104, HS200 and HS400 cards and
@@ -883,6 +918,8 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
}
retry:
+ tuned_phase_cnt = 0;
+
/* first of all reset the tuning block */
rc = msm_init_cm_dll(host);
if (rc)
@@ -921,11 +958,46 @@ retry:
!memcmp(data_buf, tuning_block_pattern, size)) {
/* tuning is successful at this tuning point */
tuned_phases[tuned_phase_cnt++] = phase;
- pr_debug("%s: %s: found good phase = %d\n",
+ pr_debug("%s: %s: found *** good *** phase = %d\n",
+ mmc_hostname(mmc), __func__, phase);
+ } else {
+ pr_debug("%s: %s: found ## bad ## phase = %d\n",
mmc_hostname(mmc), __func__, phase);
}
} while (++phase < 16);
+ if ((tuned_phase_cnt == NUM_TUNING_PHASES) && mmc_card_mmc(card)) {
+ /*
+ * If all phases pass then its a problem. So change the card's
+ * drive type to a different value, if supported and repeat
+ * tuning until at least one phase fails. Then set the original
+ * drive type back.
+ *
+ * If all the phases still pass after trying all possible
+ * drive types, then one of those 16 phases will be picked.
+ * This is no different from what was going on before the
+ * modification to change drive type and retune.
+ */
+ pr_debug("%s: tuned phases count: %d\n", mmc_hostname(mmc),
+ tuned_phase_cnt);
+
+ /* set drive type to other value . default setting is 0x0 */
+ while (++drv_type <= MAX_DRV_TYPES_SUPPORTED_HS200) {
+ if (card->ext_csd.raw_driver_strength &
+ (1 << drv_type)) {
+ sdhci_msm_set_mmc_drv_type(host, opcode,
+ drv_type);
+ if (!drv_type_changed)
+ drv_type_changed = true;
+ goto retry;
+ }
+ }
+ }
+
+ /* reset drive type to default (50 ohm) if changed */
+ if (drv_type_changed)
+ sdhci_msm_set_mmc_drv_type(host, opcode, 0);
+
if (tuned_phase_cnt) {
rc = msm_find_most_appropriate_phase(host, tuned_phases,
tuned_phase_cnt);