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

Commit 08ee80cc authored by Philip Rakity's avatar Philip Rakity Committed by Chris Ball
Browse files

mmc: core: eMMC bus width may not work on all platforms



CMD19 -- The offical way to validate bus widths from the JEDEC spec
does not work on all platforms.  Some platforms that use PCI/PCIe
to connect their SD controllers are known to fail.

If the quirk MMC_BUS_WIDTH_TEST is not defined we try to figure out
the bus width by reading the ext_csd at different bus widths and
compare this against the ext_csd read in 1 bit mode.  If no ext_csd
is available we default to 1 bit operations.

Code has been tested on mmp2 against 8 bit eMMC and Transcend 2GB
card that is known to not work in 4 bit mode.  The physical pins
on the card are not present to support 4 bit operation.

Signed-off-by: default avatarPhilip Rakity <prakity@marvell.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 4f3d3e9b
Loading
Loading
Loading
Loading
+102 −10
Original line number Diff line number Diff line
@@ -174,14 +174,17 @@ static int mmc_decode_csd(struct mmc_card *card)
}

/*
 * Read and decode extended CSD.
 * Read extended CSD.
 */
static int mmc_read_ext_csd(struct mmc_card *card)
static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
{
	int err;
	u8 *ext_csd;

	BUG_ON(!card);
	BUG_ON(!new_ext_csd);

	*new_ext_csd = NULL;

	if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
		return 0;
@@ -199,12 +202,15 @@ static int mmc_read_ext_csd(struct mmc_card *card)

	err = mmc_send_ext_csd(card, ext_csd);
	if (err) {
		kfree(ext_csd);
		*new_ext_csd = NULL;

		/* If the host or the card can't do the switch,
		 * fail more gracefully. */
		if ((err != -EINVAL)
		 && (err != -ENOSYS)
		 && (err != -EFAULT))
			goto out;
			return err;

		/*
		 * High capacity cards should have this "magic" size
@@ -222,10 +228,24 @@ static int mmc_read_ext_csd(struct mmc_card *card)
				mmc_hostname(card->host));
			err = 0;
		}
	} else
		*new_ext_csd = ext_csd;

		goto out;
	return err;
}

/*
 * Decode extended CSD.
 */
static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
	int err = 0;

	BUG_ON(!card);

	if (!ext_csd)
		return 0;

	/* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
	if (card->csd.structure == 3) {
		int ext_csd_struct = ext_csd[EXT_CSD_STRUCTURE];
@@ -372,8 +392,69 @@ static int mmc_read_ext_csd(struct mmc_card *card)
		card->erased_byte = 0x0;

out:
	return err;
}

static inline void mmc_free_ext_csd(u8 *ext_csd)
{
	kfree(ext_csd);
}


static int mmc_compare_ext_csds(struct mmc_card *card, u8 *ext_csd,
			unsigned bus_width)
{
	u8 *bw_ext_csd;
	int err;

	err = mmc_get_ext_csd(card, &bw_ext_csd);
	if (err)
		return err;

	if ((ext_csd == NULL || bw_ext_csd == NULL)) {
		if (bus_width != MMC_BUS_WIDTH_1)
			err = -EINVAL;
		goto out;
	}

	if (bus_width == MMC_BUS_WIDTH_1)
		goto out;

	/* only compare read only fields */
	err = (!(ext_csd[EXT_CSD_PARTITION_SUPPORT] ==
			bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
		(ext_csd[EXT_CSD_ERASED_MEM_CONT] ==
			bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) &&
		(ext_csd[EXT_CSD_REV] ==
			bw_ext_csd[EXT_CSD_REV]) &&
		(ext_csd[EXT_CSD_STRUCTURE] ==
			bw_ext_csd[EXT_CSD_STRUCTURE]) &&
		(ext_csd[EXT_CSD_CARD_TYPE] ==
			bw_ext_csd[EXT_CSD_CARD_TYPE]) &&
		(ext_csd[EXT_CSD_S_A_TIMEOUT] ==
			bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) &&
		(ext_csd[EXT_CSD_HC_WP_GRP_SIZE] ==
			bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
		(ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] ==
			bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) &&
		(ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
			bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
		(ext_csd[EXT_CSD_SEC_TRIM_MULT] ==
			bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) &&
		(ext_csd[EXT_CSD_SEC_ERASE_MULT] ==
			bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) &&
		(ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] ==
			bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) &&
		(ext_csd[EXT_CSD_TRIM_MULT] ==
			bw_ext_csd[EXT_CSD_TRIM_MULT]) &&
		memcmp(&ext_csd[EXT_CSD_SEC_CNT],
		       &bw_ext_csd[EXT_CSD_SEC_CNT],
		       4) != 0);
	if (err)
		err = -EINVAL;

out:
	mmc_free_ext_csd(bw_ext_csd);
	return err;
}

@@ -438,6 +519,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
	u32 cid[4];
	unsigned int max_dtr;
	u32 rocr;
	u8 *ext_csd = NULL;

	BUG_ON(!host);
	WARN_ON(!host->claimed);
@@ -536,7 +618,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
		/*
		 * Fetch and process extended CSD.
		 */
		err = mmc_read_ext_csd(card);

		err = mmc_get_ext_csd(card, &ext_csd);
		if (err)
			goto free_card;
		err = mmc_read_ext_csd(card, ext_csd);
		if (err)
			goto free_card;

@@ -676,13 +762,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
					 0);
			if (!err) {
				mmc_set_bus_width(card->host, bus_width);

				/*
				 * If controller can't handle bus width test,
				 * use the highest bus width to maintain
				 * compatibility with previous MMC behavior.
				 * compare ext_csd previously read in 1 bit mode
				 * against ext_csd at new bus width
				 */
				if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
					break;
					err = mmc_compare_ext_csds(card,
						ext_csd,
						bus_width);
				else
					err = mmc_bus_test(card, bus_width);
				if (!err)
					break;
@@ -730,12 +820,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
	if (!oldcard)
		host->card = card;

	mmc_free_ext_csd(ext_csd);
	return 0;

free_card:
	if (!oldcard)
		mmc_remove_card(card);
err:
	mmc_free_ext_csd(ext_csd);

	return err;
}