diff options
Diffstat (limited to 'drivers/mmc/core/mmc.c')
| -rw-r--r-- | drivers/mmc/core/mmc.c | 215 | 
1 files changed, 196 insertions, 19 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index dbf421a6279c..59b9ba52e66a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -286,6 +286,27 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)  	}  	card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];  	switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { +	case EXT_CSD_CARD_TYPE_SDR_ALL: +	case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V: +	case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V: +	case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52: +		card->ext_csd.hs_max_dtr = 200000000; +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200; +		break; +	case EXT_CSD_CARD_TYPE_SDR_1_2V_ALL: +	case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V: +	case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V: +	case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52: +		card->ext_csd.hs_max_dtr = 200000000; +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V; +		break; +	case EXT_CSD_CARD_TYPE_SDR_1_8V_ALL: +	case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V: +	case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V: +	case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52: +		card->ext_csd.hs_max_dtr = 200000000; +		card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V; +		break;  	case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |  	     EXT_CSD_CARD_TYPE_26:  		card->ext_csd.hs_max_dtr = 52000000; @@ -348,7 +369,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)  				part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;  				mmc_part_add(card, part_size,  					EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx, -					"boot%d", idx, true); +					"boot%d", idx, true, +					MMC_BLK_DATA_AREA_BOOT);  			}  		}  	} @@ -435,7 +457,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)  					hc_wp_grp_sz);  				mmc_part_add(card, part_size << 19,  					EXT_CSD_PART_CONFIG_ACC_GP0 + idx, -					"gp%d", idx, false); +					"gp%d", idx, false, +					MMC_BLK_DATA_AREA_GP);  			}  		}  		card->ext_csd.sec_trim_mult = @@ -446,6 +469,14 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)  			ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];  		card->ext_csd.trim_timeout = 300 *  			ext_csd[EXT_CSD_TRIM_MULT]; + +		/* +		 * Note that the call to mmc_part_add above defaults to read +		 * only. If this default assumption is changed, the call must +		 * take into account the value of boot_locked below. +		 */ +		card->ext_csd.boot_ro_lock = ext_csd[EXT_CSD_BOOT_WP]; +		card->ext_csd.boot_ro_lockable = true;  	}  	if (card->ext_csd.rev >= 5) { @@ -690,6 +721,79 @@ static int mmc_select_powerclass(struct mmc_card *card,  }  /* + * Selects the desired buswidth and switch to the HS200 mode + * if bus width set without error + */ +static int mmc_select_hs200(struct mmc_card *card) +{ +	int idx, err = 0; +	struct mmc_host *host; +	static unsigned ext_csd_bits[] = { +		EXT_CSD_BUS_WIDTH_4, +		EXT_CSD_BUS_WIDTH_8, +	}; +	static unsigned bus_widths[] = { +		MMC_BUS_WIDTH_4, +		MMC_BUS_WIDTH_8, +	}; + +	BUG_ON(!card); + +	host = card->host; + +	if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V && +	    host->caps2 & MMC_CAP2_HS200_1_2V_SDR) +		if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 0)) +			err = mmc_set_signal_voltage(host, +						     MMC_SIGNAL_VOLTAGE_180, 0); + +	/* If fails try again during next card power cycle */ +	if (err) +		goto err; + +	idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0; + +	/* +	 * Unlike SD, MMC cards dont have a configuration register to notify +	 * supported bus width. So bus test command should be run to identify +	 * the supported bus width or compare the ext csd values of current +	 * bus width and ext csd values of 1 bit mode read earlier. +	 */ +	for (; idx >= 0; idx--) { + +		/* +		 * Host is capable of 8bit transfer, then switch +		 * the device to work in 8bit transfer mode. If the +		 * mmc switch command returns error then switch to +		 * 4bit transfer mode. On success set the corresponding +		 * bus width on the host. +		 */ +		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +				 EXT_CSD_BUS_WIDTH, +				 ext_csd_bits[idx], +				 card->ext_csd.generic_cmd6_time); +		if (err) +			continue; + +		mmc_set_bus_width(card->host, bus_widths[idx]); + +		if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) +			err = mmc_compare_ext_csds(card, bus_widths[idx]); +		else +			err = mmc_bus_test(card, bus_widths[idx]); +		if (!err) +			break; +	} + +	/* switch to HS200 mode if bus width set successfully */ +	if (!err) +		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +				 EXT_CSD_HS_TIMING, 2, 0); +err: +	return err; +} + +/*   * Handle the detection and initialisation of a card.   *   * In the case of a resume, "oldcard" will contain the card @@ -876,26 +980,34 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,  	 * set the notification byte in the ext_csd register of device  	 */  	if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) && -	    (card->poweroff_notify_state == MMC_NO_POWER_NOTIFICATION)) { +	    (card->ext_csd.rev >= 6)) {  		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,  				 EXT_CSD_POWER_OFF_NOTIFICATION,  				 EXT_CSD_POWER_ON,  				 card->ext_csd.generic_cmd6_time);  		if (err && err != -EBADMSG)  			goto free_card; -	} -	if (!err) -		card->poweroff_notify_state = MMC_POWERED_ON; +		/* +		 * The err can be -EBADMSG or 0, +		 * so check for success and update the flag +		 */ +		if (!err) +			card->poweroff_notify_state = MMC_POWERED_ON; +	}  	/*  	 * Activate high speed (if supported)  	 */ -	if ((card->ext_csd.hs_max_dtr != 0) && -		(host->caps & MMC_CAP_MMC_HIGHSPEED)) { -		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -				 EXT_CSD_HS_TIMING, 1, -				 card->ext_csd.generic_cmd6_time); +	if (card->ext_csd.hs_max_dtr != 0) { +		err = 0; +		if (card->ext_csd.hs_max_dtr > 52000000 && +		    host->caps2 & MMC_CAP2_HS200) +			err = mmc_select_hs200(card); +		else if	(host->caps & MMC_CAP_MMC_HIGHSPEED) +			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, +					 EXT_CSD_HS_TIMING, 1, 0); +  		if (err && err != -EBADMSG)  			goto free_card; @@ -904,8 +1016,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,  			       mmc_hostname(card->host));  			err = 0;  		} else { -			mmc_card_set_highspeed(card); -			mmc_set_timing(card->host, MMC_TIMING_MMC_HS); +			if (card->ext_csd.hs_max_dtr > 52000000 && +			    host->caps2 & MMC_CAP2_HS200) { +				mmc_card_set_hs200(card); +				mmc_set_timing(card->host, +					       MMC_TIMING_MMC_HS200); +			} else { +				mmc_card_set_highspeed(card); +				mmc_set_timing(card->host, MMC_TIMING_MMC_HS); +			}  		}  	} @@ -930,7 +1049,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,  	 */  	max_dtr = (unsigned int)-1; -	if (mmc_card_highspeed(card)) { +	if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {  		if (max_dtr > card->ext_csd.hs_max_dtr)  			max_dtr = card->ext_csd.hs_max_dtr;  	} else if (max_dtr > card->csd.max_dtr) { @@ -956,9 +1075,48 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,  	}  	/* +	 * Indicate HS200 SDR mode (if supported). +	 */ +	if (mmc_card_hs200(card)) { +		u32 ext_csd_bits; +		u32 bus_width = card->host->ios.bus_width; + +		/* +		 * For devices supporting HS200 mode, the bus width has +		 * to be set before executing the tuning function. If +		 * set before tuning, then device will respond with CRC +		 * errors for responses on CMD line. So for HS200 the +		 * sequence will be +		 * 1. set bus width 4bit / 8 bit (1 bit not supported) +		 * 2. switch to HS200 mode +		 * 3. set the clock to > 52Mhz <=200MHz and +		 * 4. execute tuning for HS200 +		 */ +		if ((host->caps2 & MMC_CAP2_HS200) && +		    card->host->ops->execute_tuning) +			err = card->host->ops->execute_tuning(card->host, +				MMC_SEND_TUNING_BLOCK_HS200); +		if (err) { +			pr_warning("%s: tuning execution failed\n", +				   mmc_hostname(card->host)); +			goto err; +		} + +		ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? +				EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; +		err = mmc_select_powerclass(card, ext_csd_bits, ext_csd); +		if (err) { +			pr_err("%s: power class selection to bus width %d failed\n", +				mmc_hostname(card->host), 1 << bus_width); +			goto err; +		} +	} + +	/*  	 * Activate wide bus and DDR (if supported).  	 */ -	if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && +	if (!mmc_card_hs200(card) && +	    (card->csd.mmca_vsn >= CSD_SPEC_VER_3) &&  	    (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {  		static unsigned ext_csd_bits[][2] = {  			{ EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, @@ -1044,7 +1202,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,  			 *  			 * WARNING: eMMC rules are NOT the same as SD DDR  			 */ -			if (ddr == EXT_CSD_CARD_TYPE_DDR_1_2V) { +			if (ddr == MMC_1_2V_DDR_MODE) {  				err = mmc_set_signal_voltage(host,  					MMC_SIGNAL_VOLTAGE_120, 0);  				if (err) @@ -1063,14 +1221,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,  	if ((host->caps2 & MMC_CAP2_CACHE_CTRL) &&  			card->ext_csd.cache_size > 0) {  		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, -				EXT_CSD_CACHE_CTRL, 1, 0); +				EXT_CSD_CACHE_CTRL, 1, +				card->ext_csd.generic_cmd6_time);  		if (err && err != -EBADMSG)  			goto free_card;  		/*  		 * Only if no error, cache is turned on successfully.  		 */ -		card->ext_csd.cache_ctrl = err ? 0 : 1; +		if (err) { +			pr_warning("%s: Cache is supported, " +					"but failed to turn on (%d)\n", +					mmc_hostname(card->host), err); +			card->ext_csd.cache_ctrl = 0; +			err = 0; +		} else { +			card->ext_csd.cache_ctrl = 1; +		}  	}  	if (!oldcard) @@ -1101,6 +1268,14 @@ static void mmc_remove(struct mmc_host *host)  }  /* + * Card detection - card is alive. + */ +static int mmc_alive(struct mmc_host *host) +{ +	return mmc_send_status(host->card, NULL); +} + +/*   * Card detection callback from host.   */  static void mmc_detect(struct mmc_host *host) @@ -1115,7 +1290,7 @@ static void mmc_detect(struct mmc_host *host)  	/*  	 * Just check if our card has been removed.  	 */ -	err = mmc_send_status(host->card, NULL); +	err = _mmc_detect_card_removed(host);  	mmc_release_host(host); @@ -1220,6 +1395,7 @@ static const struct mmc_bus_ops mmc_ops = {  	.suspend = NULL,  	.resume = NULL,  	.power_restore = mmc_power_restore, +	.alive = mmc_alive,  };  static const struct mmc_bus_ops mmc_ops_unsafe = { @@ -1230,6 +1406,7 @@ static const struct mmc_bus_ops mmc_ops_unsafe = {  	.suspend = mmc_suspend,  	.resume = mmc_resume,  	.power_restore = mmc_power_restore, +	.alive = mmc_alive,  };  static void mmc_attach_bus_ops(struct mmc_host *host)  |