#include "sd.h"
#include "sd_ops.h"
+#define UHS_SDR104_MIN_DTR (100 * 1000 * 1000)
+#define UHS_DDR50_MIN_DTR (50 * 1000 * 1000)
+#define UHS_SDR50_MIN_DTR (50 * 1000 * 1000)
+#define UHS_SDR25_MIN_DTR (25 * 1000 * 1000)
+#define UHS_SDR12_MIN_DTR (12.5 * 1000 * 1000)
+
+#define ENOCALLBACK 1
+
static const unsigned int tran_exp[] = {
10000, 100000, 1000000, 10000000,
0, 0, 0, 0
card->sw_caps.sd3_bus_mode = status[13];
/* Driver Strengths supported by the card */
card->sw_caps.sd3_drv_type = status[9];
+ card->sw_caps.sd3_curr_limit = status[7] | status[6] << 8;
}
out:
goto out;
if ((status[16] & 0xF) != 1) {
- pr_warn("%s: Problem switching card into high-speed mode!\n",
- mmc_hostname(card->host));
- err = 0;
+ pr_warn("%s: Problem switching card into high-speed mode!, status:%x\n",
+ mmc_hostname(card->host), (status[16] & 0xF));
+ err = -EBUSY;
} else {
err = 1;
}
}
if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
- (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
- card->sd_bus_speed = UHS_SDR104_BUS_SPEED;
- } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
- (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
- card->sd_bus_speed = UHS_DDR50_BUS_SPEED;
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104) &&
+ (card->host->f_max > UHS_SDR104_MIN_DTR)) {
+ card->sd_bus_speed = UHS_SDR104_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
- SD_MODE_UHS_SDR50)) {
- card->sd_bus_speed = UHS_SDR50_BUS_SPEED;
+ SD_MODE_UHS_SDR50) &&
+ (card->host->f_max > UHS_SDR50_MIN_DTR)) {
+ card->sd_bus_speed = UHS_SDR50_BUS_SPEED;
+ } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50) &&
+ (card->host->f_max > UHS_DDR50_MIN_DTR)) {
+ card->sd_bus_speed = UHS_DDR50_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
- (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
- card->sd_bus_speed = UHS_SDR25_BUS_SPEED;
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25) &&
+ (card->host->f_max > UHS_SDR25_MIN_DTR)) {
+ card->sd_bus_speed = UHS_SDR25_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
SD_MODE_UHS_SDR12)) {
- card->sd_bus_speed = UHS_SDR12_BUS_SPEED;
+ card->sd_bus_speed = UHS_SDR12_BUS_SPEED;
}
}
if (err)
return err;
- if ((status[16] & 0xF) != card->sd_bus_speed)
- pr_warn("%s: Problem setting bus speed mode!\n",
- mmc_hostname(card->host));
- else {
+ if ((status[16] & 0xF) != card->sd_bus_speed) {
+ pr_warn("%s: Problem setting bus speed mode(%u)! max_dtr:%u, timing:%u, status:%x\n",
+ mmc_hostname(card->host), card->sd_bus_speed,
+ card->sw_caps.uhs_max_dtr, timing, (status[16] & 0xF));
+ err = -EBUSY;
+ } else {
mmc_set_timing(card->host, timing);
- mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
+ if (card->host->ops->check_temp(card->host) &&
+ timing == MMC_TIMING_UHS_SDR104)
+ mmc_set_clock(card->host, UHS_SDR50_MAX_DTR);
+ else
+ mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
}
- return 0;
+ return err;
}
/* Get host's max current setting at its current voltage */
* when we set current limit to 200ma, the card will draw 200ma, and
* when we set current limit to 400/600/800ma, the card will draw its
* maximum 300ma from the host.
+ *
+ * The above is incorrect: if we try to set a current limit that is
+ * not supported by the card, the card can rightfully error out the
+ * attempt, and remain at the default current limit. This results
+ * in a 300mA card being limited to 200mA even though the host
+ * supports 800mA. Failures seen with SanDisk 8GB UHS cards with
+ * an iMX6 host. --rmk
*/
- if (max_current >= 800)
+ if (max_current >= 800 &&
+ card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_800)
current_limit = SD_SET_CURRENT_LIMIT_800;
- else if (max_current >= 600)
+ else if (max_current >= 600 &&
+ card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_600)
current_limit = SD_SET_CURRENT_LIMIT_600;
- else if (max_current >= 400)
+ else if (max_current >= 400 &&
+ card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400)
current_limit = SD_SET_CURRENT_LIMIT_400;
- else if (max_current >= 200)
+ else if (max_current >= 200 &&
+ card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200)
current_limit = SD_SET_CURRENT_LIMIT_200;
if (current_limit != SD_SET_CURRENT_NO_CHANGE) {
return 0;
}
+/**
+ * mmc_sd_change_bus_speed() - Change SD card bus frequency at runtime
+ * @host: pointer to mmc host structure
+ * @freq: pointer to desired frequency to be set
+ *
+ * Change the SD card bus frequency at runtime after the card is
+ * initialized. Callers are expected to make sure of the card's
+ * state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
+ *
+ * If the frequency to change is greater than max. supported by card,
+ * *freq is changed to max. supported by card and if it is less than min.
+ * supported by host, *freq is changed to min. supported by host.
+ */
+static int mmc_sd_change_bus_speed(struct mmc_host *host, unsigned long *freq)
+{
+ int err = 0;
+ struct mmc_card *card;
+
+ mmc_claim_host(host);
+ /*
+ * Assign card pointer after claiming host to avoid race
+ * conditions that may arise during removal of the card.
+ */
+ card = host->card;
+
+ /* sanity checks */
+ if (!card || !freq) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ mmc_set_clock(host, (unsigned int) (*freq));
+
+ if (!mmc_host_is_spi(card->host) && mmc_card_uhs(card)
+ && card->host->ops->execute_tuning) {
+ /*
+ * We try to probe host driver for tuning for any
+ * frequency, it is host driver responsibility to
+ * perform actual tuning only when required.
+ */
+ mmc_host_clk_hold(card->host);
+ err = card->host->ops->execute_tuning(card->host,
+ MMC_SEND_TUNING_BLOCK);
+ mmc_host_clk_release(card->host);
+
+ if (err) {
+ pr_warn("%s: %s: tuning execution failed %d. Restoring to previous clock %lu\n",
+ mmc_hostname(card->host), __func__, err,
+ host->clk_scaling.curr_freq);
+ mmc_set_clock(host, host->clk_scaling.curr_freq);
+ }
+ }
+
+out:
+ mmc_release_host(host);
+ return err;
+}
+
/*
* UHS-I specific initialization procedure
*/
if (!host->ops->get_ro)
return -1;
+ mmc_host_clk_hold(host);
ro = host->ops->get_ro(host);
+ mmc_host_clk_release(host);
return ro;
}
{
unsigned max_dtr = (unsigned int)-1;
- if (mmc_card_hs(card)) {
+ if (mmc_card_uhs(card)) {
+ if (max_dtr > card->sw_caps.uhs_max_dtr)
+ max_dtr = card->sw_caps.uhs_max_dtr;
+ } else if (mmc_card_hs(card)) {
if (max_dtr > card->sw_caps.hs_max_dtr)
max_dtr = card->sw_caps.hs_max_dtr;
} else if (max_dtr > card->csd.max_dtr) {
err = mmc_send_relative_addr(host, &card->rca);
if (err)
goto free_card;
+ host->card = card;
}
if (!oldcard) {
}
}
- host->card = card;
+ card->clk_scaling_highest = mmc_sd_get_max_clock(card);
+ card->clk_scaling_lowest = host->f_min;
+
return 0;
free_card:
- if (!oldcard)
+ if (!oldcard) {
+ host->card = NULL;
mmc_remove_card(card);
+ }
return err;
}
+static int mmc_sd_init_temp_control_clk_scaling(struct mmc_host *host)
+{
+ int ret;
+
+ if (host->ops->reg_temp_callback) {
+ ret = host->ops->reg_temp_callback(host);
+ } else {
+ pr_err("%s: %s: couldn't find init temp control clk scaling cb\n",
+ mmc_hostname(host), __func__);
+ ret = -ENOCALLBACK;
+ }
+ return ret;
+}
+
+static int mmc_sd_dereg_temp_control_clk_scaling(struct mmc_host *host)
+{
+ int ret;
+
+ if (host->ops->dereg_temp_callback) {
+ ret = host->ops->dereg_temp_callback(host);
+ } else {
+ pr_err("%s: %s: couldn't find dereg temp control clk scaling cb\n",
+ mmc_hostname(host), __func__);
+ ret = -ENOCALLBACK;
+ }
+ return ret;
+}
+
/*
* Host is being removed. Free up the current card.
*/
BUG_ON(!host);
BUG_ON(!host->card);
+ mmc_exit_clk_scaling(host);
+ mmc_sd_dereg_temp_control_clk_scaling(host);
mmc_remove_card(host->card);
+
+ mmc_claim_host(host);
host->card = NULL;
+ mmc_release_host(host);
}
/*
*/
static int mmc_sd_alive(struct mmc_host *host)
{
+ if (host->ops->get_cd && !host->ops->get_cd(host))
+ return -ENOMEDIUM;
+
return mmc_send_status(host->card, NULL);
}
BUG_ON(!host);
BUG_ON(!host->card);
- mmc_get_card(host->card);
+ /*
+ * Try to acquire claim host. If failed to get the lock in 2 sec,
+ * just return; This is to ensure that when this call is invoked
+ * due to pm_suspend, not to block suspend for longer duration.
+ */
+ pm_runtime_get_sync(&host->card->dev);
+ if (!mmc_try_claim_host(host, 2000)) {
+ pm_runtime_mark_last_busy(&host->card->dev);
+ pm_runtime_put_autosuspend(&host->card->dev);
+ return;
+ }
+
+ if (mmc_bus_needs_resume(host))
+ mmc_resume_bus(host);
+
+ if (host->ops->get_cd && !host->ops->get_cd(host)) {
+ err = -ENOMEDIUM;
+ mmc_card_set_removed(host->card);
+ mmc_card_clr_suspended(host->card);
+ goto out;
+ }
/*
* Just check if our card has been removed.
if (!retries) {
printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n",
__func__, mmc_hostname(host), err);
+ err = _mmc_detect_card_removed(host);
}
#else
err = _mmc_detect_card_removed(host);
#endif
+out:
mmc_put_card(host->card);
if (err) {
BUG_ON(!host);
BUG_ON(!host->card);
+ err = mmc_suspend_clk_scaling(host);
+ if (err) {
+ pr_err("%s: %s: fail to suspend clock scaling (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
mmc_claim_host(host);
if (mmc_card_suspended(host->card))
{
int err;
+ MMC_TRACE(host, "%s: Enter\n", __func__);
err = _mmc_sd_suspend(host);
if (!err) {
pm_runtime_disable(&host->card->dev);
pm_runtime_set_suspended(&host->card->dev);
- }
+ /* if suspend fails, force mmc_detect_change during resume */
+ } else if (mmc_bus_manual_resume(host))
+ host->ignore_bus_resume_flags = true;
+
+ MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
return err;
}
if (!mmc_card_suspended(host->card))
goto out;
+ if (host->ops->get_cd && !host->ops->get_cd(host)) {
+ mmc_card_clr_suspended(host->card);
+ goto out;
+ }
+
mmc_power_up(host, host->card->ocr);
#ifdef CONFIG_MMC_PARANOID_SD_INIT
retries = 5;
if (err) {
printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n",
mmc_hostname(host), err, retries);
- mdelay(5);
retries--;
+ mmc_power_off(host);
+ usleep_range(5000, 5500);
+ mmc_power_up(host, host->card->ocr);
+ mmc_select_voltage(host, host->card->ocr);
continue;
}
break;
#else
err = mmc_sd_init_card(host, host->card->ocr, host->card);
#endif
+ if (err) {
+ pr_err("%s: %s: mmc_sd_init_card_failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ mmc_power_off(host);
+ goto out;
+ }
mmc_card_clr_suspended(host->card);
+ if (host->card->sdr104_blocked)
+ goto out;
+ err = mmc_resume_clk_scaling(host);
+ if (err) {
+ pr_err("%s: %s: fail to resume clock scaling (%d)\n",
+ mmc_hostname(host), __func__, err);
+ goto out;
+ }
+
out:
mmc_release_host(host);
return err;
{
int err = 0;
+ MMC_TRACE(host, "%s: Enter\n", __func__);
if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
err = _mmc_sd_resume(host);
pm_runtime_set_active(&host->card->dev);
pm_runtime_mark_last_busy(&host->card->dev);
}
pm_runtime_enable(&host->card->dev);
+ MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
return err;
}
static int mmc_sd_reset(struct mmc_host *host)
{
+ if (host->ops->get_cd && !host->ops->get_cd(host))
+ return -ENOMEDIUM;
+
mmc_power_cycle(host, host->card->ocr);
return mmc_sd_init_card(host, host->card->ocr, host->card);
}
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
.alive = mmc_sd_alive,
- .shutdown = mmc_sd_suspend,
+ .change_bus_speed = mmc_sd_change_bus_speed,
.reset = mmc_sd_reset,
};
goto err;
}
+ /*
+ * Some SD cards claims an out of spec VDD voltage range. Let's treat
+ * these bits as being in-valid and especially also bit7.
+ */
+ ocr &= ~0x7FFF;
+
rocr = mmc_select_voltage(host, ocr);
/*
goto err;
}
+ if (mmc_sd_init_temp_control_clk_scaling(host))
+ pr_err("%s: failed to init temp control clk scaling\n",
+ mmc_hostname(host));
/*
* Detect and init the card.
*/
err = mmc_sd_init_card(host, rocr, NULL);
if (err) {
retries--;
+ mmc_power_off(host);
+ usleep_range(5000, 5500);
+ mmc_power_up(host, rocr);
+ mmc_select_voltage(host, rocr);
continue;
}
break;
goto remove_card;
mmc_claim_host(host);
+
+ err = mmc_init_clk_scaling(host);
+ if (err) {
+ mmc_release_host(host);
+ goto remove_card;
+ }
+
return 0;
remove_card: