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
5 changed files with 68 additions and 99 deletions
|
@ -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…
Add table
Reference in a new issue