Commit 069c9f14 authored by Girish K S's avatar Girish K S Committed by Chris Ball

mmc: host: Adds support for eMMC 4.5 HS200 mode

This patch adds support for the HS200 mode on the host side.
Also enables the tuning feature required when the HS200 mode
is selected.
Signed-off-by: default avatarGirish K S <girish.shivananjappa@linaro.org>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent a4924c71
...@@ -49,7 +49,7 @@ static void sdhci_finish_data(struct sdhci_host *); ...@@ -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_send_command(struct sdhci_host *, struct mmc_command *);
static void sdhci_finish_command(struct sdhci_host *); 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); static void sdhci_tuning_timer(unsigned long data);
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
...@@ -1014,7 +1014,8 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) ...@@ -1014,7 +1014,8 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
flags |= SDHCI_CMD_INDEX; flags |= SDHCI_CMD_INDEX;
/* CMD19 is special in that the Data Present Select should be set */ /* 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; flags |= SDHCI_CMD_DATA;
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); 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) ...@@ -1287,7 +1288,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
if ((host->flags & SDHCI_NEEDS_RETUNING) && if ((host->flags & SDHCI_NEEDS_RETUNING) &&
!(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) { !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
sdhci_execute_tuning(mmc); sdhci_execute_tuning(mmc, mrq->cmd->opcode);
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
/* Restore original mmc_request structure */ /* Restore original mmc_request structure */
...@@ -1382,7 +1383,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) ...@@ -1382,7 +1383,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
unsigned int clock; unsigned int clock;
/* In case of UHS-I modes, set High Speed Enable */ /* In case of UHS-I modes, set High Speed Enable */
if ((ios->timing == MMC_TIMING_UHS_SDR50) || if ((ios->timing == MMC_TIMING_MMC_HS200) ||
(ios->timing == MMC_TIMING_UHS_SDR50) ||
(ios->timing == MMC_TIMING_UHS_SDR104) || (ios->timing == MMC_TIMING_UHS_SDR104) ||
(ios->timing == MMC_TIMING_UHS_DDR50) || (ios->timing == MMC_TIMING_UHS_DDR50) ||
(ios->timing == MMC_TIMING_UHS_SDR25)) (ios->timing == MMC_TIMING_UHS_SDR25))
...@@ -1435,7 +1437,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) ...@@ -1435,7 +1437,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */ /* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
if (ios->timing == MMC_TIMING_UHS_SDR12) if (ios->timing == MMC_TIMING_MMC_HS200)
ctrl_2 |= SDHCI_CTRL_HS_SDR200;
else if (ios->timing == MMC_TIMING_UHS_SDR12)
ctrl_2 |= SDHCI_CTRL_UHS_SDR12; ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
else if (ios->timing == MMC_TIMING_UHS_SDR25) else if (ios->timing == MMC_TIMING_UHS_SDR25)
ctrl_2 |= SDHCI_CTRL_UHS_SDR25; ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
...@@ -1682,7 +1686,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, ...@@ -1682,7 +1686,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return err; 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; struct sdhci_host *host;
u16 ctrl; u16 ctrl;
...@@ -1690,6 +1694,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc) ...@@ -1690,6 +1694,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
int tuning_loop_counter = MAX_TUNING_LOOP; int tuning_loop_counter = MAX_TUNING_LOOP;
unsigned long timeout; unsigned long timeout;
int err = 0; int err = 0;
bool requires_tuning_nonuhs = false;
host = mmc_priv(mmc); host = mmc_priv(mmc);
...@@ -1700,13 +1705,19 @@ static int sdhci_execute_tuning(struct mmc_host *mmc) ...@@ -1700,13 +1705,19 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* /*
* Host Controller needs tuning only in case of SDR104 mode * The Host Controller needs tuning only in case of SDR104 mode
* and for SDR50 mode when Use Tuning for SDR50 is set in * and for SDR50 mode when Use Tuning for SDR50 is set in the
* Capabilities register. * Capabilities register.
* If the Host Controller supports the HS200 mode then the
* tuning function has to be executed.
*/ */
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
(host->flags & SDHCI_SDR50_NEEDS_TUNING ||
host->flags & SDHCI_HS200_NEEDS_TUNING))
requires_tuning_nonuhs = true;
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) || if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
(((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) && requires_tuning_nonuhs)
(host->flags & SDHCI_SDR50_NEEDS_TUNING)))
ctrl |= SDHCI_CTRL_EXEC_TUNING; ctrl |= SDHCI_CTRL_EXEC_TUNING;
else { else {
spin_unlock(&host->lock); spin_unlock(&host->lock);
...@@ -1742,7 +1753,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc) ...@@ -1742,7 +1753,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
if (!tuning_loop_counter && !timeout) if (!tuning_loop_counter && !timeout)
break; break;
cmd.opcode = MMC_SEND_TUNING_BLOCK; cmd.opcode = opcode;
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
cmd.retries = 0; cmd.retries = 0;
...@@ -1757,7 +1768,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc) ...@@ -1757,7 +1768,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
* block to the Host Controller. So we set the block size * block to the Host Controller. So we set the block size
* to 64 here. * 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. * The tuning block is sent by the card to the host controller.
...@@ -2140,12 +2161,14 @@ static void sdhci_show_adma_error(struct sdhci_host *host) { } ...@@ -2140,12 +2161,14 @@ static void sdhci_show_adma_error(struct sdhci_host *host) { }
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
{ {
u32 command;
BUG_ON(intmask == 0); BUG_ON(intmask == 0);
/* CMD19 generates _only_ Buffer Read Ready interrupt */ /* CMD19 generates _only_ Buffer Read Ready interrupt */
if (intmask & SDHCI_INT_DATA_AVAIL) { if (intmask & SDHCI_INT_DATA_AVAIL) {
if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) == command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
MMC_SEND_TUNING_BLOCK) { if (command == MMC_SEND_TUNING_BLOCK ||
command == MMC_SEND_TUNING_BLOCK_HS200) {
host->tuning_done = 1; host->tuning_done = 1;
wake_up(&host->buf_ready_int); wake_up(&host->buf_ready_int);
return; return;
...@@ -2747,10 +2770,14 @@ int sdhci_add_host(struct sdhci_host *host) ...@@ -2747,10 +2770,14 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_SUPPORT_DDR50) if (caps[1] & SDHCI_SUPPORT_DDR50)
mmc->caps |= MMC_CAP_UHS_DDR50; mmc->caps |= MMC_CAP_UHS_DDR50;
/* Does the host needs tuning for SDR50? */ /* Does the host need tuning for SDR50? */
if (caps[1] & SDHCI_USE_SDR50_TUNING) if (caps[1] & SDHCI_USE_SDR50_TUNING)
host->flags |= SDHCI_SDR50_NEEDS_TUNING; host->flags |= SDHCI_SDR50_NEEDS_TUNING;
/* Does the host need 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 */ /* Driver Type(s) (A, C, D) supported by the host */
if (caps[1] & SDHCI_DRIVER_TYPE_A) if (caps[1] & SDHCI_DRIVER_TYPE_A)
mmc->caps |= MMC_CAP_DRIVER_TYPE_A; mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
......
...@@ -158,6 +158,7 @@ ...@@ -158,6 +158,7 @@
#define SDHCI_CTRL_UHS_SDR50 0x0002 #define SDHCI_CTRL_UHS_SDR50 0x0002
#define SDHCI_CTRL_UHS_SDR104 0x0003 #define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004 #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_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000 #define SDHCI_CTRL_DRV_TYPE_B 0x0000
......
...@@ -119,6 +119,7 @@ struct sdhci_host { ...@@ -119,6 +119,7 @@ struct sdhci_host {
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ #define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */ #define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq 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 */ unsigned int version; /* SDHCI spec. version */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment