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

Commit 0797e5f1 authored by Johan Rudholm's avatar Johan Rudholm Committed by Chris Ball
Browse files

mmc: core: Fixup signal voltage switch



When switching SD and SDIO cards from 3.3V to 1.8V signal levels, the
clock should be gated for 5 ms during the step. After enabling the
clock, the host should wait for at least 1 ms before checking for
failure. Failure by the card to switch is indicated by dat[0:3] being
pulled low. The host should check for this condition and power-cycle
the card if failure is indicated.

Add a retry mechanism for the SDIO case.

If the voltage switch fails repeatedly, give up and continue the
initialization using the original voltage.

This patch places a couple of requirements on the host driver:

 1) mmc_set_ios with ios.clock = 0 must gate the clock
 2) mmc_power_off must actually cut the power to the card
 3) The card_busy host_ops member must be implemented

if these requirements are not fulfilled, the 1.8V signal voltage switch
will still be attempted but may not be successful.

Signed-off-by: default avatarJohan Rudholm <johan.rudholm@stericsson.com>
Signed-off-by: default avatarKevin Liu <kliu5@marvell.com>
Acked-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Tested-by: default avatarWei WANG <wei_wang@realsil.com.cn>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 567c8903
Loading
Loading
Loading
Loading
+73 −10
Original line number Diff line number Diff line
@@ -1340,6 +1340,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
{
	struct mmc_command cmd = {0};
	int err = 0;
	u32 clock;

	BUG_ON(!host);

@@ -1347,7 +1348,19 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
	 * Send CMD11 only if the request is to switch the card to
	 * 1.8V signalling.
	 */
	if (signal_voltage != MMC_SIGNAL_VOLTAGE_330) {
	if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
		return __mmc_set_signal_voltage(host, signal_voltage);

	/*
	 * If we cannot switch voltages, return failure so the caller
	 * can continue without UHS mode
	 */
	if (!host->ops->start_signal_voltage_switch)
		return -EPERM;
	if (!host->ops->card_busy)
		pr_warning("%s: cannot verify signal voltage switch\n",
				mmc_hostname(host));

	cmd.opcode = SD_SWITCH_VOLTAGE;
	cmd.arg = 0;
	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
@@ -1358,9 +1371,59 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)

	if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
		return -EIO;

	mmc_host_clk_hold(host);
	/*
	 * The card should drive cmd and dat[0:3] low immediately
	 * after the response of cmd11, but wait 1 ms to be sure
	 */
	mmc_delay(1);
	if (host->ops->card_busy && !host->ops->card_busy(host)) {
		err = -EAGAIN;
		goto power_cycle;
	}
	/*
	 * During a signal voltage level switch, the clock must be gated
	 * for 5 ms according to the SD spec
	 */
	clock = host->ios.clock;
	host->ios.clock = 0;
	mmc_set_ios(host);

	return __mmc_set_signal_voltage(host, signal_voltage);
	if (__mmc_set_signal_voltage(host, signal_voltage)) {
		/*
		 * Voltages may not have been switched, but we've already
		 * sent CMD11, so a power cycle is required anyway
		 */
		err = -EAGAIN;
		goto power_cycle;
	}

	/* Keep clock gated for at least 5 ms */
	mmc_delay(5);
	host->ios.clock = clock;
	mmc_set_ios(host);

	/* Wait for at least 1 ms according to spec */
	mmc_delay(1);

	/*
	 * Failure to switch is indicated by the card holding
	 * dat[0:3] low
	 */
	if (host->ops->card_busy && host->ops->card_busy(host))
		err = -EAGAIN;

power_cycle:
	if (err) {
		pr_debug("%s: Signal voltage switch failed, "
			"power cycling card\n", mmc_hostname(host));
		mmc_power_cycle(host);
	}

	mmc_host_clk_release(host);

	return err;
}

/*
+16 −5
Original line number Diff line number Diff line
@@ -712,6 +712,14 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
{
	int err;
	u32 max_current;
	int retries = 10;

try_again:
	if (!retries) {
		ocr &= ~SD_OCR_S18R;
		pr_warning("%s: Skipping voltage switch\n",
			mmc_hostname(host));
	}

	/*
	 * Since we're changing the OCR value, we seem to
@@ -733,9 +741,10 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)

	/*
	 * If the host supports one of UHS-I modes, request the card
	 * to switch to 1.8V signaling level.
	 * to switch to 1.8V signaling level. If the card has failed
	 * repeatedly to switch however, skip this.
	 */
	if (mmc_host_uhs(host))
	if (retries && mmc_host_uhs(host))
		ocr |= SD_OCR_S18R;

	/*
@@ -746,7 +755,6 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
	if (max_current > 150)
		ocr |= SD_OCR_XPC;

try_again:
	err = mmc_send_app_op_cond(host, ocr, rocr);
	if (err)
		return err;
@@ -758,8 +766,11 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
	if (!mmc_host_is_spi(host) && rocr &&
	   ((*rocr & 0x41000000) == 0x41000000)) {
		err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
		if (err) {
			ocr &= ~SD_OCR_S18R;
		if (err == -EAGAIN) {
			retries--;
			goto try_again;
		} else if (err) {
			retries = 0;
			goto try_again;
		}
	}
+18 −2
Original line number Diff line number Diff line
@@ -584,10 +584,19 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
{
	struct mmc_card *card;
	int err;
	int retries = 10;

	BUG_ON(!host);
	WARN_ON(!host->claimed);

try_again:
	if (!retries) {
		pr_warning("%s: Skipping voltage switch\n",
				mmc_hostname(host));
		ocr &= ~R4_18V_PRESENT;
		host->ocr &= ~R4_18V_PRESENT;
	}

	/*
	 * Inform the card of the voltage
	 */
@@ -646,9 +655,16 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
	 * systems that claim 1.8v signalling in fact do not support
	 * it.
	 */
	if ((ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) {
	if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) {
		err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
		if (err) {
		if (err == -EAGAIN) {
			sdio_reset(host);
			mmc_go_idle(host);
			mmc_send_if_cond(host, host->ocr_avail);
			mmc_remove_card(card);
			retries--;
			goto try_again;
		} else if (err) {
			ocr &= ~R4_18V_PRESENT;
			host->ocr &= ~R4_18V_PRESENT;
		}