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

Commit 1b30d1da authored by Ram Prakash Gupta's avatar Ram Prakash Gupta
Browse files

mmc: core: Core support for hs400 enhanced strobe



Add core support for hs400 enhanced strobe and flush
detect work as part of the mmc driver porting.

mmc: core: Fix voltage switching for ultra high speed card
Fix voltage switching for ultra high speed card.

Change-Id: Ie9e72c10b4beb06c59f0356c5258b8cfcb98d5b0
Signed-off-by: default avatarRam Prakash Gupta <rampraka@codeaurora.org>
parent 3c86f3f6
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -933,6 +933,19 @@ static inline void mmc_set_ios(struct mmc_host *host)
		 1 << ios->bus_width, ios->timing);

	host->ops->set_ios(host, ios);
	if (ios->old_rate != ios->clock) {
		if (likely(ios->clk_ts)) {
			char trace_info[80];

			snprintf(trace_info, 80,
				"%s: freq_KHz %d --> %d | t = %d",
				mmc_hostname(host), ios->old_rate / 1000,
				ios->clock / 1000, jiffies_to_msecs(
					(long)jiffies - (long)ios->clk_ts));
		}
		ios->old_rate = ios->clock;
		ios->clk_ts = jiffies;
	}
}

/*
@@ -1499,17 +1512,21 @@ int mmc_host_set_uhs_voltage(struct mmc_host *host)
	 * During a signal voltage level switch, the clock must be gated
	 * for 5 ms according to the SD spec
	 */
	host->card_clock_off = true;
	clock = host->ios.clock;
	host->ios.clock = 0;
	mmc_set_ios(host);

	if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180))
	if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)) {
		host->card_clock_off = false;
		return -EAGAIN;
	}

	/* Keep clock gated for at least 10 ms, though spec only says 5 ms */
	mmc_delay(10);
	host->ios.clock = clock;
	mmc_set_ios(host);
	host->card_clock_off = false;

	return 0;
}
@@ -2596,6 +2613,18 @@ int mmc_detect_card_removed(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_detect_card_removed);

/*
 * This should be called to make sure that detect work(mmc_rescan)
 * is completed.Drivers may use this function from async schedule/probe
 * contexts to make sure that the bootdevice detection is completed on
 * completion of async_schedule.
 */
void mmc_flush_detect_work(struct mmc_host *host)
{
	flush_delayed_work(&host->detect);
}
EXPORT_SYMBOL(mmc_flush_detect_work);

void mmc_rescan(struct work_struct *work)
{
	struct mmc_host *host =
+69 −15
Original line number Diff line number Diff line
@@ -1151,9 +1151,28 @@ static int mmc_select_hs400(struct mmc_card *card)
	/*
	 * HS400 mode requires 8-bit bus width
	 */
	if (card->ext_csd.strobe_support) {
		if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
		    host->caps & MMC_CAP_8_BIT_DATA))
			return 0;

		/* For Enhance Strobe flow. For non Enhance Strobe, signal
		 * voltage will not be set.
		 */
		if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
			err = mmc_set_signal_voltage(host,
					MMC_SIGNAL_VOLTAGE_120);

		if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V)
			err = mmc_set_signal_voltage(host,
					MMC_SIGNAL_VOLTAGE_180);
		if (err)
			return err;
	} else {
		if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
		    host->ios.bus_width == MMC_BUS_WIDTH_8))
			return 0;
	}

	/* Switch card to HS mode */
	val = EXT_CSD_TIMING_HS;
@@ -1170,10 +1189,6 @@ static int mmc_select_hs400(struct mmc_card *card)
	/* Set host controller to HS timing */
	mmc_set_timing(card->host, MMC_TIMING_MMC_HS);

	/* Prepare host to downgrade to HS timing */
	if (host->ops->hs400_downgrade)
		host->ops->hs400_downgrade(host);

	/* Reduce frequency to HS frequency */
	max_dtr = card->ext_csd.hs_max_dtr;
	mmc_set_clock(host, max_dtr);
@@ -1182,10 +1197,18 @@ static int mmc_select_hs400(struct mmc_card *card)
	if (err)
		goto out_err;

	val = EXT_CSD_DDR_BUS_WIDTH_8;
	if (card->ext_csd.strobe_support) {
		err = mmc_select_bus_width(card);
		if (IS_ERR_VALUE((unsigned long)err))
			return err;
		val |= EXT_CSD_BUS_WIDTH_STROBE;
	}

	/* Switch card to DDR */
	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
			 EXT_CSD_BUS_WIDTH,
			 EXT_CSD_DDR_BUS_WIDTH_8,
			 val,
			 card->ext_csd.generic_cmd6_time);
	if (err) {
		pr_err("%s: switch to bus width for hs400 failed, err:%d\n",
@@ -1210,13 +1233,33 @@ static int mmc_select_hs400(struct mmc_card *card)
	mmc_set_timing(host, MMC_TIMING_MMC_HS400);
	mmc_set_bus_speed(card);

	if (card->ext_csd.strobe_support && host->ops->enhanced_strobe) {
		mmc_host_clk_hold(host);
		err = host->ops->enhanced_strobe(host);
		if (!err)
			host->ios.enhanced_strobe = true;
		mmc_host_clk_release(host);
	} else if ((host->caps2 & MMC_CAP2_HS400_POST_TUNING) &&
			host->ops->execute_tuning) {
		mmc_host_clk_hold(host);
		err = host->ops->execute_tuning(host,
				MMC_SEND_TUNING_BLOCK_HS200);
		mmc_host_clk_release(host);

		if (err)
			pr_warn("%s: tuning execution failed\n",
				mmc_hostname(host));
	}

	/*
	 * Sending of CMD13 should be done after the host calibration
	 * for enhanced_strobe or HS400 mode is completed.
	 * Otherwise may see CMD13 timeouts or CRC errors.
	 */
	err = mmc_switch_status(card);
	if (err)
		goto out_err;

	if (host->ops->hs400_complete)
		host->ops->hs400_complete(host);

	return 0;

out_err:
@@ -1501,12 +1544,22 @@ static int mmc_select_timing(struct mmc_card *card)
	if (!mmc_can_ext_csd(card))
		goto bus_speed;

	if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)
	/* For Enhance Strobe HS400 flow */
	if (card->ext_csd.strobe_support &&
	    card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
	    card->host->caps & MMC_CAP_8_BIT_DATA) {
		err = mmc_select_hs400(card);
		if (err) {
			pr_err("%s: %s: mmc_select_hs400 failed : %d\n",
					mmc_hostname(card->host), __func__,
					err);
			err = mmc_select_hs400es(card);
	else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
		}
	} else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) {
		err = mmc_select_hs200(card);
	else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
	} else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) {
		err = mmc_select_hs(card);
	}

	if (err && err != -EBADMSG)
		return err;
@@ -1611,6 +1664,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;
	}

	/*
+3 −0
Original line number Diff line number Diff line
@@ -5093,6 +5093,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
		       mmc_hostname(host->mmc), __func__, ret);
		device_remove_file(&pdev->dev, &msm_host->auto_cmd21_attr);
	}

	if (sdhci_msm_is_bootdevice(&pdev->dev))
		mmc_flush_detect_work(host->mmc);
	/* Successful initialization */
	goto out;

+1 −0
Original line number Diff line number Diff line
@@ -180,5 +180,6 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd,
int mmc_hw_reset(struct mmc_host *host);
int mmc_sw_reset(struct mmc_host *host);
void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card);
extern void mmc_flush_detect_work(struct mmc_host *host);

#endif /* LINUX_MMC_CORE_H */