diff options
| author | Andy Fleming <afleming@freescale.com> | 2012-10-31 19:02:38 +0000 | 
|---|---|---|
| committer | Andy Fleming <afleming@freescale.com> | 2012-11-27 17:48:46 -0600 | 
| commit | 7798f6dbd5e1a3030ed81a81da5dfb57c3307cac (patch) | |
| tree | 44d6edde9d71397c2d4e1bfa82dbf0954048f117 | |
| parent | ed80c931ba7781e6605b9bdaa2a0d58ef365fe71 (diff) | |
| download | olio-uboot-2014.01-7798f6dbd5e1a3030ed81a81da5dfb57c3307cac.tar.xz olio-uboot-2014.01-7798f6dbd5e1a3030ed81a81da5dfb57c3307cac.zip | |
mmc: Properly determine maximum supported bus width
At some point, a confusion arose about the use of the bit
definitions in host_caps for bus widths, and the value
in ext_csd. By coincidence, a simple shift could convert
between one and the other:
MMC_MODE_1BIT = 0, EXT_CSD_BUS_WIDTH_1 = 0
MMC_MODE_4BIT = 0x100, EXT_CSD_BUS_WIDTH_4 = 1
MMC_MODE_8BIT = 0x200, EXT_CSD_BUS_WIDTH_8 = 2
However, as host_caps is a bitmask of supported things,
there is not, in fact, a one-to-one correspondence. host_caps
is capable of containing MODE_4BIT | MODE_8BIT, so nonsensical
things were happening where we would try to set the bus width
to 12.
The new code clarifies the very different namespaces:
host_caps/card_caps = bitmask (MMC_MODE_*)
ext CSD fields are just an index (EXT_CSD_BUS_WIDTH_*)
mmc->bus_width integer number of bits (1, 4, 8)
We create arrays to map between the namespaces, like in Linux.
Signed-off-by: Andy Fleming <afleming@freescale.com>
Tested-by: Jaehoon Chung <jh80.chung@samsung.com>
Tested-by: Stephen Warren <swarren@nvidia.com>
| -rw-r--r-- | drivers/mmc/mmc.c | 47 | 
1 files changed, 35 insertions, 12 deletions
| diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 59dc5894c..72e8ce6da 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -868,7 +868,7 @@ static void mmc_set_bus_width(struct mmc *mmc, uint width)  static int mmc_startup(struct mmc *mmc)  { -	int err, width; +	int err;  	uint mult, freq;  	u64 cmult, csize, capacity;  	struct mmc_cmd cmd; @@ -1087,21 +1087,44 @@ static int mmc_startup(struct mmc *mmc)  		else  			mmc->tran_speed = 25000000;  	} else { -		width = ((mmc->host_caps & MMC_MODE_MASK_WIDTH_BITS) >> -			 MMC_MODE_WIDTH_BITS_SHIFT); -		for (; width >= 0; width--) { -			/* Set the card to use 4 bit*/ +		int idx; + +		/* An array of possible bus widths in order of preference */ +		static unsigned ext_csd_bits[] = { +			EXT_CSD_BUS_WIDTH_8, +			EXT_CSD_BUS_WIDTH_4, +			EXT_CSD_BUS_WIDTH_1, +		}; + +		/* An array to map CSD bus widths to host cap bits */ +		static unsigned ext_to_hostcaps[] = { +			[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, +			[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, +		}; + +		/* An array to map chosen bus width to an integer */ +		static unsigned widths[] = { +			8, 4, 1, +		}; + +		for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { +			unsigned int extw = ext_csd_bits[idx]; + +			/* +			 * Check to make sure the controller supports +			 * this bus width, if it's more than 1 +			 */ +			if (extw != EXT_CSD_BUS_WIDTH_1 && +					!(mmc->host_caps & ext_to_hostcaps[extw])) +				continue; +  			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, -					EXT_CSD_BUS_WIDTH, width); +					EXT_CSD_BUS_WIDTH, extw);  			if (err)  				continue; -			if (!width) { -				mmc_set_bus_width(mmc, 1); -				break; -			} else -				mmc_set_bus_width(mmc, 4 * width); +			mmc_set_bus_width(mmc, widths[idx]);  			err = mmc_send_ext_csd(mmc, test_csd);  			if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \ @@ -1115,7 +1138,7 @@ static int mmc_startup(struct mmc *mmc)  				 && memcmp(&ext_csd[EXT_CSD_SEC_CNT], \  					&test_csd[EXT_CSD_SEC_CNT], 4) == 0) { -				mmc->card_caps |= width; +				mmc->card_caps |= ext_to_hostcaps[extw];  				break;  			}  		} |