}
/*
- * Read and decode extended CSD. Switch to high-speed and wide bus
- * if supported.
+ * Read and decode extended CSD.
*/
-static int mmc_process_ext_csd(struct mmc_card *card)
+static int mmc_read_ext_csd(struct mmc_card *card)
{
int err;
u8 *ext_csd;
goto out;
}
- if (card->host->caps & MMC_CAP_MMC_HIGHSPEED) {
- /* Activate highspeed support. */
+out:
+ kfree(ext_csd);
+
+ return err;
+}
+
+/*
+ * Handle the detection and initialisation of a card.
+ *
+ * In the case of a resume, "curcard" will contain the card
+ * we're trying to reinitialise.
+ */
+static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *oldcard)
+{
+ struct mmc_card *card;
+ int err;
+ u32 cid[4];
+ unsigned int max_dtr;
+
+ BUG_ON(!host);
+ BUG_ON(!host->claimed);
+
+ /*
+ * Since we're changing the OCR value, we seem to
+ * need to tell some cards to go back to the idle
+ * state. We wait 1ms to give cards time to
+ * respond.
+ */
+ mmc_go_idle(host);
+
+ /* The extra bit indicates that we support high capacity */
+ err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
+ if (err != MMC_ERR_NONE)
+ goto err;
+
+ /*
+ * Fetch CID from card.
+ */
+ err = mmc_all_send_cid(host, cid);
+ if (err != MMC_ERR_NONE)
+ goto err;
+
+ if (oldcard) {
+ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
+ goto err;
+
+ card = oldcard;
+ } else {
+ /*
+ * Allocate card structure.
+ */
+ card = mmc_alloc_card(host);
+ if (IS_ERR(card))
+ goto err;
+
+ card->type = MMC_TYPE_MMC;
+ card->rca = 1;
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ }
+
+ /*
+ * Set card RCA.
+ */
+ err = mmc_set_relative_addr(card);
+ if (err != MMC_ERR_NONE)
+ goto free_card;
+
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+
+ if (!oldcard) {
+ /*
+ * Fetch CSD from card.
+ */
+ err = mmc_send_csd(card, card->raw_csd);
+ if (err != MMC_ERR_NONE)
+ goto free_card;
+
+ mmc_decode_csd(card);
+ mmc_decode_cid(card);
+ }
+
+ /*
+ * Select card, as all following commands rely on that.
+ */
+ err = mmc_select_card(card);
+ if (err != MMC_ERR_NONE)
+ goto free_card;
+
+ if (!oldcard) {
+ /*
+ * Fetch and process extened CSD.
+ */
+ err = mmc_read_ext_csd(card);
+ if (err != MMC_ERR_NONE)
+ goto free_card;
+ }
+
+ /*
+ * 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);
- if (err != MMC_ERR_NONE) {
- printk(KERN_WARNING "%s: failed to switch "
- "card to mmc v4 high-speed mode.\n",
- mmc_hostname(card->host));
- err = MMC_ERR_NONE;
- goto out;
- }
+ if (err != MMC_ERR_NONE)
+ goto free_card;
mmc_card_set_highspeed(card);
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
}
- /* Check for host support for wide-bus modes. */
- if (card->host->caps & MMC_CAP_4_BIT_DATA) {
- /* Activate 4-bit support. */
+ /*
+ * Compute bus speed.
+ */
+ max_dtr = (unsigned int)-1;
+
+ if (mmc_card_highspeed(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) {
+ max_dtr = card->csd.max_dtr;
+ }
+
+ mmc_set_clock(host, max_dtr);
+
+ /*
+ * Activate wide bus (if supported).
+ */
+ if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
+ (host->caps & MMC_CAP_4_BIT_DATA)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
- if (err != MMC_ERR_NONE) {
- printk(KERN_WARNING "%s: failed to switch "
- "card to mmc v4 4-bit bus mode.\n",
- mmc_hostname(card->host));
- err = MMC_ERR_NONE;
- goto out;
- }
+ if (err != MMC_ERR_NONE)
+ goto free_card;
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
}
-out:
- kfree(ext_csd);
+ if (!oldcard)
+ host->card = card;
- return err;
+ return MMC_ERR_NONE;
+
+free_card:
+ if (!oldcard)
+ mmc_remove_card(card);
+err:
+
+ return MMC_ERR_FAILED;
}
/*
}
}
+#ifdef CONFIG_MMC_UNSAFE_RESUME
+
+/*
+ * Suspend callback from host.
+ */
+static void mmc_suspend(struct mmc_host *host)
+{
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+ mmc_deselect_cards(host);
+ host->card->state &= ~MMC_STATE_HIGHSPEED;
+ mmc_release_host(host);
+}
+
+/*
+ * Resume callback from host.
+ *
+ * This function tries to determine if the same card is still present
+ * and, if so, restore all state to it.
+ */
+static void mmc_resume(struct mmc_host *host)
+{
+ int err;
+
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+
+ err = mmc_sd_init_card(host, host->ocr, host->card);
+ if (err != MMC_ERR_NONE) {
+ mmc_remove_card(host->card);
+ host->card = NULL;
+
+ mmc_detach_bus(host);
+ }
+
+ mmc_release_host(host);
+}
+
+#else
+
+#define mmc_suspend NULL
+#define mmc_resume NULL
+
+#endif
+
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
+ .suspend = mmc_suspend,
+ .resume = mmc_resume,
};
/*
*/
int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
{
- struct mmc_card *card;
int err;
- u32 cid[4];
- unsigned int max_dtr;
BUG_ON(!host);
BUG_ON(!host->claimed);
mmc_attach_bus(host, &mmc_ops);
+ /*
+ * Sanity check the voltages that the card claims to
+ * support.
+ */
+ if (ocr & 0x7F) {
+ printk(KERN_WARNING "%s: card claims to support voltages "
+ "below the defined range. These will be ignored.\n",
+ mmc_hostname(host));
+ ocr &= ~0x7F;
+ }
+
host->ocr = mmc_select_voltage(host, ocr);
/*
goto err;
/*
- * Since we're changing the OCR value, we seem to
- * need to tell some cards to go back to the idle
- * state. We wait 1ms to give cards time to
- * respond.
- */
- mmc_go_idle(host);
-
- /* The extra bit indicates that we support high capacity */
- mmc_send_op_cond(host, host->ocr | (1 << 30), NULL);
-
- /*
- * Fetch CID from card.
+ * Detect and init the card.
*/
- err = mmc_all_send_cid(host, cid);
+ err = mmc_sd_init_card(host, host->ocr, NULL);
if (err != MMC_ERR_NONE)
goto err;
- /*
- * Allocate card structure.
- */
- card = mmc_alloc_card(host);
- if (IS_ERR(card))
- goto err;
-
- card->type = MMC_TYPE_MMC;
- card->rca = 1;
- memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
-
- /*
- * Set card RCA.
- */
- err = mmc_set_relative_addr(card);
- if (err != MMC_ERR_NONE)
- goto free_card;
-
- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
-
- /*
- * Fetch CSD from card.
- */
- err = mmc_send_csd(card, card->raw_csd);
- if (err != MMC_ERR_NONE)
- goto free_card;
-
- mmc_decode_csd(card);
- mmc_decode_cid(card);
-
- /*
- * Fetch and process extened CSD.
- * This will switch into high-speed and wide bus modes,
- * as available.
- */
- err = mmc_select_card(card);
- if (err != MMC_ERR_NONE)
- goto free_card;
-
- err = mmc_process_ext_csd(card);
- if (err != MMC_ERR_NONE)
- goto free_card;
-
- /*
- * Compute bus speed.
- */
- max_dtr = (unsigned int)-1;
-
- if (mmc_card_highspeed(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) {
- max_dtr = card->csd.max_dtr;
- }
-
- mmc_set_clock(host, max_dtr);
-
- host->card = card;
-
mmc_release_host(host);
- err = mmc_register_card(card);
+ err = mmc_register_card(host->card);
if (err)
goto reclaim_host;
reclaim_host:
mmc_claim_host(host);
-free_card:
- mmc_remove_card(card);
+ mmc_remove_card(host->card);
host->card = NULL;
err:
mmc_detach_bus(host);