Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit da4bc4f2 authored by Adrian Hunter's avatar Adrian Hunter Committed by Ulf Hansson
Browse files

mmc: sdhci: Factor out tuning helper functions



Factor out some functions to tidy up the code in sdhci_execute_tuning.

Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent d0c3ab59
Loading
Loading
Loading
Loading
+124 −92
Original line number Diff line number Diff line
@@ -1952,64 +1952,9 @@ static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
	return 0;
}

static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
static void sdhci_start_tuning(struct sdhci_host *host)
{
	struct sdhci_host *host = mmc_priv(mmc);
	u16 ctrl;
	int tuning_loop_counter = MAX_TUNING_LOOP;
	int err = 0;
	unsigned long flags;
	unsigned int tuning_count = 0;
	bool hs400_tuning;

	spin_lock_irqsave(&host->lock, flags);

	hs400_tuning = host->flags & SDHCI_HS400_TUNING;
	host->flags &= ~SDHCI_HS400_TUNING;

	if (host->tuning_mode == SDHCI_TUNING_MODE_1)
		tuning_count = host->tuning_count;

	/*
	 * The Host Controller needs tuning in case of SDR104 and DDR50
	 * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
	 * the Capabilities register.
	 * If the Host Controller supports the HS200 mode then the
	 * tuning function has to be executed.
	 */
	switch (host->timing) {
	/* HS400 tuning is done in HS200 mode */
	case MMC_TIMING_MMC_HS400:
		err = -EINVAL;
		goto out_unlock;

	case MMC_TIMING_MMC_HS200:
		/*
		 * Periodic re-tuning for HS400 is not expected to be needed, so
		 * disable it here.
		 */
		if (hs400_tuning)
			tuning_count = 0;
		break;

	case MMC_TIMING_UHS_SDR104:
	case MMC_TIMING_UHS_DDR50:
		break;

	case MMC_TIMING_UHS_SDR50:
		if (host->flags & SDHCI_SDR50_NEEDS_TUNING)
			break;
		/* FALLTHROUGH */

	default:
		goto out_unlock;
	}

	if (host->ops->platform_execute_tuning) {
		spin_unlock_irqrestore(&host->lock, flags);
		err = host->ops->platform_execute_tuning(host, opcode);
		return err;
	}

	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
	ctrl |= SDHCI_CTRL_EXEC_TUNING;
@@ -2029,28 +1974,58 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
	 */
	sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE);
	sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE);
}

static void sdhci_end_tuning(struct sdhci_host *host)
{
	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
}

static void sdhci_reset_tuning(struct sdhci_host *host)
{
	u16 ctrl;

	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
	ctrl &= ~SDHCI_CTRL_TUNED_CLK;
	ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
}

static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode,
			       unsigned long flags)
{
	sdhci_reset_tuning(host);

	sdhci_do_reset(host, SDHCI_RESET_CMD);
	sdhci_do_reset(host, SDHCI_RESET_DATA);

	sdhci_end_tuning(host);

	spin_unlock_irqrestore(&host->lock, flags);
	mmc_abort_tuning(host->mmc, opcode);
	spin_lock_irqsave(&host->lock, flags);
}

/*
	 * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
	 * of loops reaches 40 times.
 * We use sdhci_send_tuning() because mmc_send_tuning() is not a good fit. SDHCI
 * tuning command does not have a data payload (or rather the hardware does it
 * automatically) so mmc_send_tuning() will return -EIO. Also the tuning command
 * interrupt setup is different to other commands and there is no timeout
 * interrupt so special handling is needed.
 */
	do {
static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode,
			      unsigned long flags)
{
	struct mmc_host *mmc = host->mmc;
	struct mmc_command cmd = {0};
	struct mmc_request mrq = {NULL};

	cmd.opcode = opcode;
		cmd.arg = 0;
	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
		cmd.retries = 0;
		cmd.data = NULL;
	cmd.mrq = &mrq;
		cmd.error = 0;

		if (tuning_loop_counter-- == 0)
			break;

	mrq.cmd = &cmd;

	/*
	 * In response to CMD19, the card sends 64 bytes of tuning
	 * block to the Host Controller. So we set the block size
@@ -2079,37 +2054,97 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
	sdhci_send_command(host, &cmd);

	host->cmd = NULL;

	sdhci_del_timer(host, &mrq);

	host->tuning_done = 0;

	spin_unlock_irqrestore(&host->lock, flags);

	/* Wait for Buffer Read Ready interrupt */
		wait_event_timeout(host->buf_ready_int,
					(host->tuning_done == 1),
	wait_event_timeout(host->buf_ready_int, (host->tuning_done == 1),
			   msecs_to_jiffies(50));

	spin_lock_irqsave(&host->lock, flags);
}

		if (!host->tuning_done) {
			pr_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);
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
	struct sdhci_host *host = mmc_priv(mmc);
	u16 ctrl;
	int tuning_loop_counter = MAX_TUNING_LOOP;
	int err = 0;
	unsigned long flags;
	unsigned int tuning_count = 0;
	bool hs400_tuning;

			sdhci_do_reset(host, SDHCI_RESET_CMD);
			sdhci_do_reset(host, SDHCI_RESET_DATA);
	spin_lock_irqsave(&host->lock, flags);

			sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
			sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
	hs400_tuning = host->flags & SDHCI_HS400_TUNING;
	host->flags &= ~SDHCI_HS400_TUNING;

	if (host->tuning_mode == SDHCI_TUNING_MODE_1)
		tuning_count = host->tuning_count;

	/*
	 * The Host Controller needs tuning in case of SDR104 and DDR50
	 * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
	 * the Capabilities register.
	 * If the Host Controller supports the HS200 mode then the
	 * tuning function has to be executed.
	 */
	switch (host->timing) {
	/* HS400 tuning is done in HS200 mode */
	case MMC_TIMING_MMC_HS400:
		err = -EINVAL;
		goto out_unlock;

	case MMC_TIMING_MMC_HS200:
		/*
		 * Periodic re-tuning for HS400 is not expected to be needed, so
		 * disable it here.
		 */
		if (hs400_tuning)
			tuning_count = 0;
		break;

	case MMC_TIMING_UHS_SDR104:
	case MMC_TIMING_UHS_DDR50:
		break;

	case MMC_TIMING_UHS_SDR50:
		if (host->flags & SDHCI_SDR50_NEEDS_TUNING)
			break;
		/* FALLTHROUGH */

	default:
		goto out_unlock;
	}

	if (host->ops->platform_execute_tuning) {
		spin_unlock_irqrestore(&host->lock, flags);
			mmc_abort_tuning(mmc, opcode);
			spin_lock_irqsave(&host->lock, flags);
		err = host->ops->platform_execute_tuning(host, opcode);
		return err;
	}

	sdhci_start_tuning(host);

	/*
	 * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
	 * of loops reaches 40 times.
	 */
	do {
		if (tuning_loop_counter-- == 0)
			break;

		sdhci_send_tuning(host, opcode, flags);

		if (!host->tuning_done) {
			pr_info(DRIVER_NAME ": Timeout waiting for Buffer Read Ready interrupt during tuning procedure, falling back to fixed sampling clock\n");
			sdhci_abort_tuning(host, opcode, flags);
			goto out;
		}

		host->tuning_done = 0;

		ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);

		/* eMMC spec does not require a delay between tuning cycles */
@@ -2121,18 +2156,15 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
	 * The Host Driver has exhausted the maximum number of loops allowed,
	 * so use fixed sampling frequency.
	 */
	if (tuning_loop_counter < 0) {
		ctrl &= ~SDHCI_CTRL_TUNED_CLK;
		ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
	}
	if (!(ctrl & SDHCI_CTRL_TUNED_CLK))
	if (tuning_loop_counter < 0)
		sdhci_reset_tuning(host);

	if (tuning_loop_counter < 0 || !(ctrl & SDHCI_CTRL_TUNED_CLK))
		pr_info(DRIVER_NAME ": Tuning procedure failed, falling back to fixed sampling clock\n");
out:
	host->mmc->retune_period = tuning_count;

	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
	sdhci_end_tuning(host);
out_unlock:
	spin_unlock_irqrestore(&host->lock, flags);
	return err;