mmc: Fix sd/sdio/mmc initialization frequency retries
Rewrite and clean up mmc_rescan() to properly retry frequencies lower than 400kHz. Failures can happen both in sd_send_* calls and mmc_attach_*. Break out "mmc_rescan_try_freq" from the frequency selection loop. Symmetrize claim/release logic in mmc_attach_* API, and move the sd_send_* calls there to make mmc_rescan easier to read. Signed-off-by: Andy Ross <andy.ross@windriver.com> Reviewed-and-Tested-by: Hein Tibosch <hein_tibosch@yahoo.es> Reviewed-by: Chris Ball <cjb@laptop.org> Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
		
							parent
							
								
									08c82dfad2
								
							
						
					
					
						commit
						807e8e4067
					
				| @ -1485,25 +1485,41 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) | |||||||
| } | } | ||||||
| EXPORT_SYMBOL(mmc_set_blocklen); | EXPORT_SYMBOL(mmc_set_blocklen); | ||||||
| 
 | 
 | ||||||
| void mmc_rescan(struct work_struct *work) | static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) | ||||||
| { | { | ||||||
| 	struct mmc_host *host = | 	host->f_init = freq; | ||||||
| 		container_of(work, struct mmc_host, detect.work); |  | ||||||
| 	u32 ocr; |  | ||||||
| 	int err; |  | ||||||
| 	unsigned long flags; |  | ||||||
| 	int i; |  | ||||||
| 	const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; |  | ||||||
| 
 | 
 | ||||||
| 	spin_lock_irqsave(&host->lock, flags); | #ifdef CONFIG_MMC_DEBUG | ||||||
|  | 	pr_info("%s: %s: trying to init card at %u Hz\n", | ||||||
|  | 		mmc_hostname(host), __func__, host->f_init); | ||||||
|  | #endif | ||||||
|  | 	mmc_power_up(host); | ||||||
|  | 	sdio_reset(host); | ||||||
|  | 	mmc_go_idle(host); | ||||||
| 
 | 
 | ||||||
| 	if (host->rescan_disable) { | 	mmc_send_if_cond(host, host->ocr_avail); | ||||||
| 		spin_unlock_irqrestore(&host->lock, flags); | 
 | ||||||
| 		return; | 	/* Order's important: probe SDIO, then SD, then MMC */ | ||||||
|  | 	if (!mmc_attach_sdio(host)) | ||||||
|  | 		return 0; | ||||||
|  | 	if (!mmc_attach_sd(host)) | ||||||
|  | 		return 0; | ||||||
|  | 	if (!mmc_attach_mmc(host)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	mmc_power_off(host); | ||||||
|  | 	return -EIO; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 	spin_unlock_irqrestore(&host->lock, flags); | void mmc_rescan(struct work_struct *work) | ||||||
|  | { | ||||||
|  | 	static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; | ||||||
|  | 	struct mmc_host *host = | ||||||
|  | 		container_of(work, struct mmc_host, detect.work); | ||||||
|  | 	int i; | ||||||
| 
 | 
 | ||||||
|  | 	if (host->rescan_disable) | ||||||
|  | 		return; | ||||||
| 
 | 
 | ||||||
| 	mmc_bus_get(host); | 	mmc_bus_get(host); | ||||||
| 
 | 
 | ||||||
| @ -1526,8 +1542,6 @@ void mmc_rescan(struct work_struct *work) | |||||||
| 		goto out; | 		goto out; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* detect a newly inserted card */ |  | ||||||
| 
 |  | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Only we can add a new handler, so it's safe to | 	 * Only we can add a new handler, so it's safe to | ||||||
| 	 * release the lock here. | 	 * release the lock here. | ||||||
| @ -1537,76 +1551,15 @@ void mmc_rescan(struct work_struct *work) | |||||||
| 	if (host->ops->get_cd && host->ops->get_cd(host) == 0) | 	if (host->ops->get_cd && host->ops->get_cd(host) == 0) | ||||||
| 		goto out; | 		goto out; | ||||||
| 
 | 
 | ||||||
|  | 	mmc_claim_host(host); | ||||||
| 	for (i = 0; i < ARRAY_SIZE(freqs); i++) { | 	for (i = 0; i < ARRAY_SIZE(freqs); i++) { | ||||||
| 		mmc_claim_host(host); | 		if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) | ||||||
| 
 | 			break; | ||||||
| 		if (freqs[i] >= host->f_min) | 		if (freqs[i] < host->f_min) | ||||||
| 			host->f_init = freqs[i]; | 			break; | ||||||
| 		else if (!i || freqs[i-1] > host->f_min) | 	} | ||||||
| 			host->f_init = host->f_min; |  | ||||||
| 		else { |  | ||||||
| 	mmc_release_host(host); | 	mmc_release_host(host); | ||||||
| 			goto out; |  | ||||||
| 		} |  | ||||||
| #ifdef CONFIG_MMC_DEBUG |  | ||||||
| 		pr_info("%s: %s: trying to init card at %u Hz\n", |  | ||||||
| 			mmc_hostname(host), __func__, host->f_init); |  | ||||||
| #endif |  | ||||||
| 		mmc_power_up(host); |  | ||||||
| 		sdio_reset(host); |  | ||||||
| 		mmc_go_idle(host); |  | ||||||
| 
 | 
 | ||||||
| 		mmc_send_if_cond(host, host->ocr_avail); |  | ||||||
| 
 |  | ||||||
| 		/*
 |  | ||||||
| 		 * First we search for SDIO... |  | ||||||
| 		 */ |  | ||||||
| 		err = mmc_send_io_op_cond(host, 0, &ocr); |  | ||||||
| 		if (!err) { |  | ||||||
| 			if (mmc_attach_sdio(host, ocr)) { |  | ||||||
| 				mmc_claim_host(host); |  | ||||||
| 				/*
 |  | ||||||
| 				 * Try SDMEM (but not MMC) even if SDIO |  | ||||||
| 				 * is broken. |  | ||||||
| 				 */ |  | ||||||
| 				mmc_power_up(host); |  | ||||||
| 				sdio_reset(host); |  | ||||||
| 				mmc_go_idle(host); |  | ||||||
| 				mmc_send_if_cond(host, host->ocr_avail); |  | ||||||
| 
 |  | ||||||
| 				if (mmc_send_app_op_cond(host, 0, &ocr)) |  | ||||||
| 					goto out_fail; |  | ||||||
| 
 |  | ||||||
| 				if (mmc_attach_sd(host, ocr)) |  | ||||||
| 					mmc_power_off(host); |  | ||||||
| 			} |  | ||||||
| 			goto out; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/*
 |  | ||||||
| 		 * ...then normal SD... |  | ||||||
| 		 */ |  | ||||||
| 		err = mmc_send_app_op_cond(host, 0, &ocr); |  | ||||||
| 		if (!err) { |  | ||||||
| 			if (mmc_attach_sd(host, ocr)) |  | ||||||
| 				mmc_power_off(host); |  | ||||||
| 			goto out; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/*
 |  | ||||||
| 		 * ...and finally MMC. |  | ||||||
| 		 */ |  | ||||||
| 		err = mmc_send_op_cond(host, 0, &ocr); |  | ||||||
| 		if (!err) { |  | ||||||
| 			if (mmc_attach_mmc(host, ocr)) |  | ||||||
| 				mmc_power_off(host); |  | ||||||
| 			goto out; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| out_fail: |  | ||||||
| 		mmc_release_host(host); |  | ||||||
| 		mmc_power_off(host); |  | ||||||
| 	} |  | ||||||
|  out: |  out: | ||||||
| 	if (host->caps & MMC_CAP_NEEDS_POLL) | 	if (host->caps & MMC_CAP_NEEDS_POLL) | ||||||
| 		mmc_schedule_delayed_work(&host->detect, HZ); | 		mmc_schedule_delayed_work(&host->detect, HZ); | ||||||
|  | |||||||
| @ -57,9 +57,9 @@ void mmc_rescan(struct work_struct *work); | |||||||
| void mmc_start_host(struct mmc_host *host); | void mmc_start_host(struct mmc_host *host); | ||||||
| void mmc_stop_host(struct mmc_host *host); | void mmc_stop_host(struct mmc_host *host); | ||||||
| 
 | 
 | ||||||
| int mmc_attach_mmc(struct mmc_host *host, u32 ocr); | int mmc_attach_mmc(struct mmc_host *host); | ||||||
| int mmc_attach_sd(struct mmc_host *host, u32 ocr); | int mmc_attach_sd(struct mmc_host *host); | ||||||
| int mmc_attach_sdio(struct mmc_host *host, u32 ocr); | int mmc_attach_sdio(struct mmc_host *host); | ||||||
| 
 | 
 | ||||||
| /* Module parameters */ | /* Module parameters */ | ||||||
| extern int use_spi_crc; | extern int use_spi_crc; | ||||||
|  | |||||||
| @ -755,13 +755,18 @@ static void mmc_attach_bus_ops(struct mmc_host *host) | |||||||
| /*
 | /*
 | ||||||
|  * Starting point for MMC card init. |  * Starting point for MMC card init. | ||||||
|  */ |  */ | ||||||
| int mmc_attach_mmc(struct mmc_host *host, u32 ocr) | int mmc_attach_mmc(struct mmc_host *host) | ||||||
| { | { | ||||||
| 	int err; | 	int err; | ||||||
|  | 	u32 ocr; | ||||||
| 
 | 
 | ||||||
| 	BUG_ON(!host); | 	BUG_ON(!host); | ||||||
| 	WARN_ON(!host->claimed); | 	WARN_ON(!host->claimed); | ||||||
| 
 | 
 | ||||||
|  | 	err = mmc_send_op_cond(host, 0, &ocr); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
| 	mmc_attach_bus_ops(host); | 	mmc_attach_bus_ops(host); | ||||||
| 	if (host->ocr_avail_mmc) | 	if (host->ocr_avail_mmc) | ||||||
| 		host->ocr_avail = host->ocr_avail_mmc; | 		host->ocr_avail = host->ocr_avail_mmc; | ||||||
| @ -804,20 +809,20 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) | |||||||
| 		goto err; | 		goto err; | ||||||
| 
 | 
 | ||||||
| 	mmc_release_host(host); | 	mmc_release_host(host); | ||||||
| 
 |  | ||||||
| 	err = mmc_add_card(host->card); | 	err = mmc_add_card(host->card); | ||||||
|  | 	mmc_claim_host(host); | ||||||
| 	if (err) | 	if (err) | ||||||
| 		goto remove_card; | 		goto remove_card; | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| remove_card: | remove_card: | ||||||
|  | 	mmc_release_host(host); | ||||||
| 	mmc_remove_card(host->card); | 	mmc_remove_card(host->card); | ||||||
| 	host->card = NULL; |  | ||||||
| 	mmc_claim_host(host); | 	mmc_claim_host(host); | ||||||
|  | 	host->card = NULL; | ||||||
| err: | err: | ||||||
| 	mmc_detach_bus(host); | 	mmc_detach_bus(host); | ||||||
| 	mmc_release_host(host); |  | ||||||
| 
 | 
 | ||||||
| 	printk(KERN_ERR "%s: error %d whilst initialising MMC card\n", | 	printk(KERN_ERR "%s: error %d whilst initialising MMC card\n", | ||||||
| 		mmc_hostname(host), err); | 		mmc_hostname(host), err); | ||||||
|  | |||||||
| @ -764,13 +764,18 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host) | |||||||
| /*
 | /*
 | ||||||
|  * Starting point for SD card init. |  * Starting point for SD card init. | ||||||
|  */ |  */ | ||||||
| int mmc_attach_sd(struct mmc_host *host, u32 ocr) | int mmc_attach_sd(struct mmc_host *host) | ||||||
| { | { | ||||||
| 	int err; | 	int err; | ||||||
|  | 	u32 ocr; | ||||||
| 
 | 
 | ||||||
| 	BUG_ON(!host); | 	BUG_ON(!host); | ||||||
| 	WARN_ON(!host->claimed); | 	WARN_ON(!host->claimed); | ||||||
| 
 | 
 | ||||||
|  | 	err = mmc_send_app_op_cond(host, 0, &ocr); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
| 	mmc_sd_attach_bus_ops(host); | 	mmc_sd_attach_bus_ops(host); | ||||||
| 	if (host->ocr_avail_sd) | 	if (host->ocr_avail_sd) | ||||||
| 		host->ocr_avail = host->ocr_avail_sd; | 		host->ocr_avail = host->ocr_avail_sd; | ||||||
| @ -823,20 +828,20 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) | |||||||
| 		goto err; | 		goto err; | ||||||
| 
 | 
 | ||||||
| 	mmc_release_host(host); | 	mmc_release_host(host); | ||||||
| 
 |  | ||||||
| 	err = mmc_add_card(host->card); | 	err = mmc_add_card(host->card); | ||||||
|  | 	mmc_claim_host(host); | ||||||
| 	if (err) | 	if (err) | ||||||
| 		goto remove_card; | 		goto remove_card; | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| remove_card: | remove_card: | ||||||
|  | 	mmc_release_host(host); | ||||||
| 	mmc_remove_card(host->card); | 	mmc_remove_card(host->card); | ||||||
| 	host->card = NULL; | 	host->card = NULL; | ||||||
| 	mmc_claim_host(host); | 	mmc_claim_host(host); | ||||||
| err: | err: | ||||||
| 	mmc_detach_bus(host); | 	mmc_detach_bus(host); | ||||||
| 	mmc_release_host(host); |  | ||||||
| 
 | 
 | ||||||
| 	printk(KERN_ERR "%s: error %d whilst initialising SD card\n", | 	printk(KERN_ERR "%s: error %d whilst initialising SD card\n", | ||||||
| 		mmc_hostname(host), err); | 		mmc_hostname(host), err); | ||||||
|  | |||||||
| @ -702,15 +702,19 @@ static const struct mmc_bus_ops mmc_sdio_ops = { | |||||||
| /*
 | /*
 | ||||||
|  * Starting point for SDIO card init. |  * Starting point for SDIO card init. | ||||||
|  */ |  */ | ||||||
| int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | int mmc_attach_sdio(struct mmc_host *host) | ||||||
| { | { | ||||||
| 	int err; | 	int err, i, funcs; | ||||||
| 	int i, funcs; | 	u32 ocr; | ||||||
| 	struct mmc_card *card; | 	struct mmc_card *card; | ||||||
| 
 | 
 | ||||||
| 	BUG_ON(!host); | 	BUG_ON(!host); | ||||||
| 	WARN_ON(!host->claimed); | 	WARN_ON(!host->claimed); | ||||||
| 
 | 
 | ||||||
|  | 	err = mmc_send_io_op_cond(host, 0, &ocr); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
| 	mmc_attach_bus(host, &mmc_sdio_ops); | 	mmc_attach_bus(host, &mmc_sdio_ops); | ||||||
| 	if (host->ocr_avail_sdio) | 	if (host->ocr_avail_sdio) | ||||||
| 		host->ocr_avail = host->ocr_avail_sdio; | 		host->ocr_avail = host->ocr_avail_sdio; | ||||||
| @ -783,12 +787,12 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | |||||||
| 			pm_runtime_enable(&card->sdio_func[i]->dev); | 			pm_runtime_enable(&card->sdio_func[i]->dev); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	mmc_release_host(host); |  | ||||||
| 
 |  | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * First add the card to the driver model... | 	 * First add the card to the driver model... | ||||||
| 	 */ | 	 */ | ||||||
|  | 	mmc_release_host(host); | ||||||
| 	err = mmc_add_card(host->card); | 	err = mmc_add_card(host->card); | ||||||
|  | 	mmc_claim_host(host); | ||||||
| 	if (err) | 	if (err) | ||||||
| 		goto remove_added; | 		goto remove_added; | ||||||
| 
 | 
 | ||||||
| @ -806,15 +810,17 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | |||||||
| 
 | 
 | ||||||
| remove_added: | remove_added: | ||||||
| 	/* Remove without lock if the device has been added. */ | 	/* Remove without lock if the device has been added. */ | ||||||
|  | 	mmc_release_host(host); | ||||||
| 	mmc_sdio_remove(host); | 	mmc_sdio_remove(host); | ||||||
| 	mmc_claim_host(host); | 	mmc_claim_host(host); | ||||||
| remove: | remove: | ||||||
| 	/* And with lock if it hasn't been added. */ | 	/* And with lock if it hasn't been added. */ | ||||||
|  | 	mmc_release_host(host); | ||||||
| 	if (host->card) | 	if (host->card) | ||||||
| 		mmc_sdio_remove(host); | 		mmc_sdio_remove(host); | ||||||
|  | 	mmc_claim_host(host); | ||||||
| err: | err: | ||||||
| 	mmc_detach_bus(host); | 	mmc_detach_bus(host); | ||||||
| 	mmc_release_host(host); |  | ||||||
| 
 | 
 | ||||||
| 	printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n", | 	printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n", | ||||||
| 		mmc_hostname(host), err); | 		mmc_hostname(host), err); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user