|
@@ -263,55 +263,13 @@ out:
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Host is being removed. Free up the current card.
|
|
|
- */
|
|
|
-static void mmc_sd_remove(struct mmc_host *host)
|
|
|
-{
|
|
|
- BUG_ON(!host);
|
|
|
- BUG_ON(!host->card);
|
|
|
-
|
|
|
- mmc_remove_card(host->card);
|
|
|
- host->card = NULL;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Card detection callback from host.
|
|
|
- */
|
|
|
-static void mmc_sd_detect(struct mmc_host *host)
|
|
|
-{
|
|
|
- int err;
|
|
|
-
|
|
|
- BUG_ON(!host);
|
|
|
- BUG_ON(!host->card);
|
|
|
-
|
|
|
- mmc_claim_host(host);
|
|
|
-
|
|
|
- /*
|
|
|
- * Just check if our card has been removed.
|
|
|
- */
|
|
|
- err = mmc_send_status(host->card, NULL);
|
|
|
-
|
|
|
- mmc_release_host(host);
|
|
|
-
|
|
|
- if (err != MMC_ERR_NONE) {
|
|
|
- mmc_remove_card(host->card);
|
|
|
- host->card = NULL;
|
|
|
-
|
|
|
- mmc_claim_host(host);
|
|
|
- mmc_detach_bus(host);
|
|
|
- mmc_release_host(host);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static const struct mmc_bus_ops mmc_sd_ops = {
|
|
|
- .remove = mmc_sd_remove,
|
|
|
- .detect = mmc_sd_detect,
|
|
|
-};
|
|
|
-
|
|
|
-/*
|
|
|
- * Starting point for SD card init.
|
|
|
+ * Handle the detection and initialisation of a card.
|
|
|
+ *
|
|
|
+ * In the case of a resume, "curcard" will contain the card
|
|
|
+ * we're trying to reinitialise.
|
|
|
*/
|
|
|
-int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
|
|
+static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
|
|
+ struct mmc_card *oldcard)
|
|
|
{
|
|
|
struct mmc_card *card;
|
|
|
int err;
|
|
@@ -321,34 +279,6 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
|
|
BUG_ON(!host);
|
|
|
BUG_ON(!host->claimed);
|
|
|
|
|
|
- mmc_attach_bus(host, &mmc_sd_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;
|
|
|
- }
|
|
|
-
|
|
|
- if (ocr & MMC_VDD_165_195) {
|
|
|
- printk(KERN_WARNING "%s: SD card claims to support the "
|
|
|
- "incompletely defined 'low voltage range'. This "
|
|
|
- "will be ignored.\n", mmc_hostname(host));
|
|
|
- ocr &= ~MMC_VDD_165_195;
|
|
|
- }
|
|
|
-
|
|
|
- host->ocr = mmc_select_voltage(host, ocr);
|
|
|
-
|
|
|
- /*
|
|
|
- * Can we support the voltage(s) of the card(s)?
|
|
|
- */
|
|
|
- if (!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
|
|
@@ -363,11 +293,13 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
|
|
* of the ocr to indicate that we can handle
|
|
|
* block-addressed SDHC cards.
|
|
|
*/
|
|
|
- err = mmc_send_if_cond(host, host->ocr);
|
|
|
+ err = mmc_send_if_cond(host, ocr);
|
|
|
if (err == MMC_ERR_NONE)
|
|
|
- ocr = host->ocr | (1 << 30);
|
|
|
+ ocr |= 1 << 30;
|
|
|
|
|
|
- mmc_send_app_op_cond(host, ocr, NULL);
|
|
|
+ err = mmc_send_app_op_cond(host, ocr, NULL);
|
|
|
+ if (err != MMC_ERR_NONE)
|
|
|
+ goto err;
|
|
|
|
|
|
/*
|
|
|
* Fetch CID from card.
|
|
@@ -376,15 +308,22 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
|
|
if (err != MMC_ERR_NONE)
|
|
|
goto err;
|
|
|
|
|
|
- /*
|
|
|
- * Allocate card structure.
|
|
|
- */
|
|
|
- card = mmc_alloc_card(host);
|
|
|
- if (IS_ERR(card))
|
|
|
- goto err;
|
|
|
+ if (oldcard) {
|
|
|
+ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
|
|
|
+ goto err;
|
|
|
|
|
|
- card->type = MMC_TYPE_SD;
|
|
|
- memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
|
|
+ card = oldcard;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * Allocate card structure.
|
|
|
+ */
|
|
|
+ card = mmc_alloc_card(host);
|
|
|
+ if (IS_ERR(card))
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ card->type = MMC_TYPE_SD;
|
|
|
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
|
* Set card RCA.
|
|
@@ -395,35 +334,42 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
|
|
|
|
|
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;
|
|
|
+ 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);
|
|
|
+ mmc_decode_csd(card);
|
|
|
+ mmc_decode_cid(card);
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
|
- * Fetch SCR from card.
|
|
|
+ * Select card, as all following commands rely on that.
|
|
|
*/
|
|
|
err = mmc_select_card(card);
|
|
|
if (err != MMC_ERR_NONE)
|
|
|
goto free_card;
|
|
|
|
|
|
- err = mmc_app_send_scr(card, card->raw_scr);
|
|
|
- if (err != MMC_ERR_NONE)
|
|
|
- goto free_card;
|
|
|
+ if (!oldcard) {
|
|
|
+ /*
|
|
|
+ * Fetch SCR from card.
|
|
|
+ */
|
|
|
+ err = mmc_app_send_scr(card, card->raw_scr);
|
|
|
+ if (err != MMC_ERR_NONE)
|
|
|
+ goto free_card;
|
|
|
|
|
|
- mmc_decode_scr(card);
|
|
|
+ mmc_decode_scr(card);
|
|
|
|
|
|
- /*
|
|
|
- * Fetch switch information from card.
|
|
|
- */
|
|
|
- err = mmc_read_switch(card);
|
|
|
- if (err != MMC_ERR_NONE)
|
|
|
- goto free_card;
|
|
|
+ /*
|
|
|
+ * Fetch switch information from card.
|
|
|
+ */
|
|
|
+ err = mmc_read_switch(card);
|
|
|
+ if (err != MMC_ERR_NONE)
|
|
|
+ goto free_card;
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
|
* Attempt to change to high-speed (if supported)
|
|
@@ -458,11 +404,164 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
|
|
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
|
|
|
}
|
|
|
|
|
|
- host->card = card;
|
|
|
+ if (!oldcard)
|
|
|
+ host->card = card;
|
|
|
+
|
|
|
+ return MMC_ERR_NONE;
|
|
|
+
|
|
|
+free_card:
|
|
|
+ if (!oldcard)
|
|
|
+ mmc_remove_card(card);
|
|
|
+err:
|
|
|
+
|
|
|
+ return MMC_ERR_FAILED;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Host is being removed. Free up the current card.
|
|
|
+ */
|
|
|
+static void mmc_sd_remove(struct mmc_host *host)
|
|
|
+{
|
|
|
+ BUG_ON(!host);
|
|
|
+ BUG_ON(!host->card);
|
|
|
+
|
|
|
+ mmc_remove_card(host->card);
|
|
|
+ host->card = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Card detection callback from host.
|
|
|
+ */
|
|
|
+static void mmc_sd_detect(struct mmc_host *host)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ BUG_ON(!host);
|
|
|
+ BUG_ON(!host->card);
|
|
|
+
|
|
|
+ mmc_claim_host(host);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Just check if our card has been removed.
|
|
|
+ */
|
|
|
+ err = mmc_send_status(host->card, NULL);
|
|
|
|
|
|
mmc_release_host(host);
|
|
|
|
|
|
- err = mmc_register_card(card);
|
|
|
+ if (err != MMC_ERR_NONE) {
|
|
|
+ mmc_remove_card(host->card);
|
|
|
+ host->card = NULL;
|
|
|
+
|
|
|
+ mmc_claim_host(host);
|
|
|
+ mmc_detach_bus(host);
|
|
|
+ mmc_release_host(host);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_MMC_UNSAFE_RESUME
|
|
|
+
|
|
|
+/*
|
|
|
+ * Suspend callback from host.
|
|
|
+ */
|
|
|
+static void mmc_sd_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_sd_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_sd_suspend NULL
|
|
|
+#define mmc_sd_resume NULL
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+static const struct mmc_bus_ops mmc_sd_ops = {
|
|
|
+ .remove = mmc_sd_remove,
|
|
|
+ .detect = mmc_sd_detect,
|
|
|
+ .suspend = mmc_sd_suspend,
|
|
|
+ .resume = mmc_sd_resume,
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Starting point for SD card init.
|
|
|
+ */
|
|
|
+int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ BUG_ON(!host);
|
|
|
+ BUG_ON(!host->claimed);
|
|
|
+
|
|
|
+ mmc_attach_bus(host, &mmc_sd_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;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ocr & MMC_VDD_165_195) {
|
|
|
+ printk(KERN_WARNING "%s: SD card claims to support the "
|
|
|
+ "incompletely defined 'low voltage range'. This "
|
|
|
+ "will be ignored.\n", mmc_hostname(host));
|
|
|
+ ocr &= ~MMC_VDD_165_195;
|
|
|
+ }
|
|
|
+
|
|
|
+ host->ocr = mmc_select_voltage(host, ocr);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Can we support the voltage(s) of the card(s)?
|
|
|
+ */
|
|
|
+ if (!host->ocr)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Detect and init the card.
|
|
|
+ */
|
|
|
+ err = mmc_sd_init_card(host, host->ocr, NULL);
|
|
|
+ if (err != MMC_ERR_NONE)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ mmc_release_host(host);
|
|
|
+
|
|
|
+ err = mmc_register_card(host->card);
|
|
|
if (err)
|
|
|
goto reclaim_host;
|
|
|
|
|
@@ -470,8 +569,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
|
|
|
|
|
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);
|