|
@@ -38,6 +38,8 @@
|
|
|
#define SDHCI_USE_LEDS_CLASS
|
|
|
#endif
|
|
|
|
|
|
+#define MAX_TUNING_LOOP 40
|
|
|
+
|
|
|
static unsigned int debug_quirks = 0;
|
|
|
|
|
|
static void sdhci_finish_data(struct sdhci_host *);
|
|
@@ -968,7 +970,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|
|
flags |= SDHCI_CMD_CRC;
|
|
|
if (cmd->flags & MMC_RSP_OPCODE)
|
|
|
flags |= SDHCI_CMD_INDEX;
|
|
|
- if (cmd->data)
|
|
|
+
|
|
|
+ /* CMD19 is special in that the Data Present Select should be set */
|
|
|
+ if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
|
|
|
flags |= SDHCI_CMD_DATA;
|
|
|
|
|
|
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
|
|
@@ -1501,12 +1505,157 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int sdhci_execute_tuning(struct mmc_host *mmc)
|
|
|
+{
|
|
|
+ struct sdhci_host *host;
|
|
|
+ u16 ctrl;
|
|
|
+ u32 ier;
|
|
|
+ int tuning_loop_counter = MAX_TUNING_LOOP;
|
|
|
+ unsigned long timeout;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ host = mmc_priv(mmc);
|
|
|
+
|
|
|
+ disable_irq(host->irq);
|
|
|
+ spin_lock(&host->lock);
|
|
|
+
|
|
|
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Host Controller needs tuning only in case of SDR104 mode
|
|
|
+ * and for SDR50 mode when Use Tuning for SDR50 is set in
|
|
|
+ * Capabilities register.
|
|
|
+ */
|
|
|
+ if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
|
|
|
+ (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
|
|
|
+ (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
|
|
|
+ ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
|
|
+ else {
|
|
|
+ spin_unlock(&host->lock);
|
|
|
+ enable_irq(host->irq);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * As per the Host Controller spec v3.00, tuning command
|
|
|
+ * generates Buffer Read Ready interrupt, so enable that.
|
|
|
+ *
|
|
|
+ * Note: The spec clearly says that when tuning sequence
|
|
|
+ * is being performed, the controller does not generate
|
|
|
+ * interrupts other than Buffer Read Ready interrupt. But
|
|
|
+ * to make sure we don't hit a controller bug, we _only_
|
|
|
+ * enable Buffer Read Ready interrupt here.
|
|
|
+ */
|
|
|
+ ier = sdhci_readl(host, SDHCI_INT_ENABLE);
|
|
|
+ sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
|
|
|
+ * of loops reaches 40 times or a timeout of 150ms occurs.
|
|
|
+ */
|
|
|
+ timeout = 150;
|
|
|
+ do {
|
|
|
+ struct mmc_command cmd = {0};
|
|
|
+ struct mmc_request mrq = {0};
|
|
|
+
|
|
|
+ if (!tuning_loop_counter && !timeout)
|
|
|
+ break;
|
|
|
+
|
|
|
+ cmd.opcode = MMC_SEND_TUNING_BLOCK;
|
|
|
+ cmd.arg = 0;
|
|
|
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
|
|
+ cmd.retries = 0;
|
|
|
+ cmd.data = NULL;
|
|
|
+ cmd.error = 0;
|
|
|
+
|
|
|
+ mrq.cmd = &cmd;
|
|
|
+ host->mrq = &mrq;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * In response to CMD19, the card sends 64 bytes of tuning
|
|
|
+ * block to the Host Controller. So we set the block size
|
|
|
+ * to 64 here.
|
|
|
+ */
|
|
|
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The tuning block is sent by the card to the host controller.
|
|
|
+ * So we set the TRNS_READ bit in the Transfer Mode register.
|
|
|
+ * This also takes care of setting DMA Enable and Multi Block
|
|
|
+ * Select in the same register to 0.
|
|
|
+ */
|
|
|
+ sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
|
|
|
+
|
|
|
+ sdhci_send_command(host, &cmd);
|
|
|
+
|
|
|
+ host->cmd = NULL;
|
|
|
+ host->mrq = NULL;
|
|
|
+
|
|
|
+ spin_unlock(&host->lock);
|
|
|
+ enable_irq(host->irq);
|
|
|
+
|
|
|
+ /* Wait for Buffer Read Ready interrupt */
|
|
|
+ wait_event_interruptible_timeout(host->buf_ready_int,
|
|
|
+ (host->tuning_done == 1),
|
|
|
+ msecs_to_jiffies(50));
|
|
|
+ disable_irq(host->irq);
|
|
|
+ spin_lock(&host->lock);
|
|
|
+
|
|
|
+ if (!host->tuning_done) {
|
|
|
+ printk(KERN_INFO DRIVER_NAME ": Timeout waiting for "
|
|
|
+ "Buffer Read Ready interrupt during tuning "
|
|
|
+ "procedure, falling back to fixed sampling "
|
|
|
+ "clock\n");
|
|
|
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
|
|
|
+ ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
|
|
|
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
|
+
|
|
|
+ err = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ host->tuning_done = 0;
|
|
|
+
|
|
|
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
+ tuning_loop_counter--;
|
|
|
+ timeout--;
|
|
|
+ mdelay(1);
|
|
|
+ } while (ctrl & SDHCI_CTRL_EXEC_TUNING);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The Host Driver has exhausted the maximum number of loops allowed,
|
|
|
+ * so use fixed sampling frequency.
|
|
|
+ */
|
|
|
+ if (!tuning_loop_counter || !timeout) {
|
|
|
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
|
|
|
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
|
+ } else {
|
|
|
+ if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
|
|
|
+ printk(KERN_INFO DRIVER_NAME ": Tuning procedure"
|
|
|
+ " failed, falling back to fixed sampling"
|
|
|
+ " clock\n");
|
|
|
+ err = -EIO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
|
|
|
+ spin_unlock(&host->lock);
|
|
|
+ enable_irq(host->irq);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static const struct mmc_host_ops sdhci_ops = {
|
|
|
.request = sdhci_request,
|
|
|
.set_ios = sdhci_set_ios,
|
|
|
.get_ro = sdhci_get_ro,
|
|
|
.enable_sdio_irq = sdhci_enable_sdio_irq,
|
|
|
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
|
|
|
+ .execute_tuning = sdhci_execute_tuning,
|
|
|
};
|
|
|
|
|
|
/*****************************************************************************\
|
|
@@ -1724,6 +1873,16 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
|
|
{
|
|
|
BUG_ON(intmask == 0);
|
|
|
|
|
|
+ /* CMD19 generates _only_ Buffer Read Ready interrupt */
|
|
|
+ if (intmask & SDHCI_INT_DATA_AVAIL) {
|
|
|
+ if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
|
|
|
+ MMC_SEND_TUNING_BLOCK) {
|
|
|
+ host->tuning_done = 1;
|
|
|
+ wake_up(&host->buf_ready_int);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (!host->data) {
|
|
|
/*
|
|
|
* The "data complete" interrupt is also used to
|
|
@@ -2160,6 +2319,10 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
|
if (caps[1] & SDHCI_SUPPORT_DDR50)
|
|
|
mmc->caps |= MMC_CAP_UHS_DDR50;
|
|
|
|
|
|
+ /* Does the host needs tuning for SDR50? */
|
|
|
+ if (caps[1] & SDHCI_USE_SDR50_TUNING)
|
|
|
+ host->flags |= SDHCI_SDR50_NEEDS_TUNING;
|
|
|
+
|
|
|
/* Driver Type(s) (A, C, D) supported by the host */
|
|
|
if (caps[1] & SDHCI_DRIVER_TYPE_A)
|
|
|
mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
|
|
@@ -2313,6 +2476,9 @@ int sdhci_add_host(struct sdhci_host *host)
|
|
|
|
|
|
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
|
|
|
|
|
|
+ if (host->version >= SDHCI_SPEC_300)
|
|
|
+ init_waitqueue_head(&host->buf_ready_int);
|
|
|
+
|
|
|
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
|
|
|
mmc_hostname(mmc), host);
|
|
|
if (ret)
|