|
@@ -217,6 +217,134 @@ static int sdio_enable_hs(struct mmc_card *card)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Handle the detection and initialisation of a card.
|
|
|
+ *
|
|
|
+ * In the case of a resume, "oldcard" will contain the card
|
|
|
+ * we're trying to reinitialise.
|
|
|
+ */
|
|
|
+static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|
|
+ struct mmc_card *oldcard)
|
|
|
+{
|
|
|
+ struct mmc_card *card;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ BUG_ON(!host);
|
|
|
+ WARN_ON(!host->claimed);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Inform the card of the voltage
|
|
|
+ */
|
|
|
+ err = mmc_send_io_op_cond(host, host->ocr, &ocr);
|
|
|
+ if (err)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For SPI, enable CRC as appropriate.
|
|
|
+ */
|
|
|
+ if (mmc_host_is_spi(host)) {
|
|
|
+ err = mmc_spi_set_crc(host, use_spi_crc);
|
|
|
+ if (err)
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Allocate card structure.
|
|
|
+ */
|
|
|
+ card = mmc_alloc_card(host, NULL);
|
|
|
+ if (IS_ERR(card)) {
|
|
|
+ err = PTR_ERR(card);
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ card->type = MMC_TYPE_SDIO;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For native busses: set card RCA and quit open drain mode.
|
|
|
+ */
|
|
|
+ if (!mmc_host_is_spi(host)) {
|
|
|
+ err = mmc_send_relative_addr(host, &card->rca);
|
|
|
+ if (err)
|
|
|
+ goto remove;
|
|
|
+
|
|
|
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Select card, as all following commands rely on that.
|
|
|
+ */
|
|
|
+ if (!mmc_host_is_spi(host)) {
|
|
|
+ err = mmc_select_card(card);
|
|
|
+ if (err)
|
|
|
+ goto remove;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Read the common registers.
|
|
|
+ */
|
|
|
+ err = sdio_read_cccr(card);
|
|
|
+ if (err)
|
|
|
+ goto remove;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Read the common CIS tuples.
|
|
|
+ */
|
|
|
+ err = sdio_read_common_cis(card);
|
|
|
+ if (err)
|
|
|
+ goto remove;
|
|
|
+
|
|
|
+ if (oldcard) {
|
|
|
+ int same = (card->cis.vendor == oldcard->cis.vendor &&
|
|
|
+ card->cis.device == oldcard->cis.device);
|
|
|
+ mmc_remove_card(card);
|
|
|
+ if (!same) {
|
|
|
+ err = -ENOENT;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ card = oldcard;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Switch to high-speed (if supported).
|
|
|
+ */
|
|
|
+ err = sdio_enable_hs(card);
|
|
|
+ if (err)
|
|
|
+ goto remove;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Change to the card's maximum speed.
|
|
|
+ */
|
|
|
+ if (mmc_card_highspeed(card)) {
|
|
|
+ /*
|
|
|
+ * The SDIO specification doesn't mention how
|
|
|
+ * the CIS transfer speed register relates to
|
|
|
+ * high-speed, but it seems that 50 MHz is
|
|
|
+ * mandatory.
|
|
|
+ */
|
|
|
+ mmc_set_clock(host, 50000000);
|
|
|
+ } else {
|
|
|
+ mmc_set_clock(host, card->cis.max_dtr);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Switch to wider bus (if supported).
|
|
|
+ */
|
|
|
+ err = sdio_enable_wide(card);
|
|
|
+ if (err)
|
|
|
+ goto remove;
|
|
|
+
|
|
|
+ if (!oldcard)
|
|
|
+ host->card = card;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+remove:
|
|
|
+ if (!oldcard)
|
|
|
+ mmc_remove_card(card);
|
|
|
+
|
|
|
+err:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Host is being removed. Free up the current card.
|
|
|
*/
|
|
@@ -266,10 +394,74 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * SDIO suspend. We need to suspend all functions separately.
|
|
|
+ * Therefore all registered functions must have drivers with suspend
|
|
|
+ * and resume methods. Failing that we simply remove the whole card.
|
|
|
+ */
|
|
|
+static void mmc_sdio_suspend(struct mmc_host *host)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* make sure all registered functions can suspend/resume */
|
|
|
+ for (i = 0; i < host->card->sdio_funcs; i++) {
|
|
|
+ struct sdio_func *func = host->card->sdio_func[i];
|
|
|
+ if (func && sdio_func_present(func) && func->dev.driver) {
|
|
|
+ const struct dev_pm_ops *pmops = func->dev.driver->pm;
|
|
|
+ if (!pmops || !pmops->suspend || !pmops->resume) {
|
|
|
+ /* just remove the entire card in that case */
|
|
|
+ mmc_sdio_remove(host);
|
|
|
+ mmc_claim_host(host);
|
|
|
+ mmc_detach_bus(host);
|
|
|
+ mmc_release_host(host);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* now suspend them */
|
|
|
+ for (i = 0; i < host->card->sdio_funcs; i++) {
|
|
|
+ struct sdio_func *func = host->card->sdio_func[i];
|
|
|
+ if (func && sdio_func_present(func) && func->dev.driver) {
|
|
|
+ const struct dev_pm_ops *pmops = func->dev.driver->pm;
|
|
|
+ pmops->suspend(&func->dev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void mmc_sdio_resume(struct mmc_host *host)
|
|
|
+{
|
|
|
+ int i, err;
|
|
|
+
|
|
|
+ BUG_ON(!host);
|
|
|
+ BUG_ON(!host->card);
|
|
|
+
|
|
|
+ mmc_claim_host(host);
|
|
|
+ err = mmc_sdio_init_card(host, host->ocr, host->card);
|
|
|
+ mmc_release_host(host);
|
|
|
+ if (err) {
|
|
|
+ mmc_sdio_remove(host);
|
|
|
+ mmc_claim_host(host);
|
|
|
+ mmc_detach_bus(host);
|
|
|
+ mmc_release_host(host);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* resume all functions */
|
|
|
+ for (i = 0; i < host->card->sdio_funcs; i++) {
|
|
|
+ struct sdio_func *func = host->card->sdio_func[i];
|
|
|
+ if (func && sdio_func_present(func) && func->dev.driver) {
|
|
|
+ const struct dev_pm_ops *pmops = func->dev.driver->pm;
|
|
|
+ pmops->resume(&func->dev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
static const struct mmc_bus_ops mmc_sdio_ops = {
|
|
|
.remove = mmc_sdio_remove,
|
|
|
.detect = mmc_sdio_detect,
|
|
|
+ .suspend = mmc_sdio_suspend,
|
|
|
+ .resume = mmc_sdio_resume,
|
|
|
};
|
|
|
|
|
|
|
|
@@ -309,103 +501,18 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Inform the card of the voltage
|
|
|
+ * Detect and init the card.
|
|
|
*/
|
|
|
- err = mmc_send_io_op_cond(host, host->ocr, &ocr);
|
|
|
+ err = mmc_sdio_init_card(host, host->ocr, NULL);
|
|
|
if (err)
|
|
|
goto err;
|
|
|
-
|
|
|
- /*
|
|
|
- * For SPI, enable CRC as appropriate.
|
|
|
- */
|
|
|
- if (mmc_host_is_spi(host)) {
|
|
|
- err = mmc_spi_set_crc(host, use_spi_crc);
|
|
|
- if (err)
|
|
|
- goto err;
|
|
|
- }
|
|
|
+ card = host->card;
|
|
|
|
|
|
/*
|
|
|
* The number of functions on the card is encoded inside
|
|
|
* the ocr.
|
|
|
*/
|
|
|
- funcs = (ocr & 0x70000000) >> 28;
|
|
|
-
|
|
|
- /*
|
|
|
- * Allocate card structure.
|
|
|
- */
|
|
|
- card = mmc_alloc_card(host, NULL);
|
|
|
- if (IS_ERR(card)) {
|
|
|
- err = PTR_ERR(card);
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- card->type = MMC_TYPE_SDIO;
|
|
|
- card->sdio_funcs = funcs;
|
|
|
-
|
|
|
- host->card = card;
|
|
|
-
|
|
|
- /*
|
|
|
- * For native busses: set card RCA and quit open drain mode.
|
|
|
- */
|
|
|
- if (!mmc_host_is_spi(host)) {
|
|
|
- err = mmc_send_relative_addr(host, &card->rca);
|
|
|
- if (err)
|
|
|
- goto remove;
|
|
|
-
|
|
|
- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Select card, as all following commands rely on that.
|
|
|
- */
|
|
|
- if (!mmc_host_is_spi(host)) {
|
|
|
- err = mmc_select_card(card);
|
|
|
- if (err)
|
|
|
- goto remove;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Read the common registers.
|
|
|
- */
|
|
|
- err = sdio_read_cccr(card);
|
|
|
- if (err)
|
|
|
- goto remove;
|
|
|
-
|
|
|
- /*
|
|
|
- * Read the common CIS tuples.
|
|
|
- */
|
|
|
- err = sdio_read_common_cis(card);
|
|
|
- if (err)
|
|
|
- goto remove;
|
|
|
-
|
|
|
- /*
|
|
|
- * Switch to high-speed (if supported).
|
|
|
- */
|
|
|
- err = sdio_enable_hs(card);
|
|
|
- if (err)
|
|
|
- goto remove;
|
|
|
-
|
|
|
- /*
|
|
|
- * Change to the card's maximum speed.
|
|
|
- */
|
|
|
- if (mmc_card_highspeed(card)) {
|
|
|
- /*
|
|
|
- * The SDIO specification doesn't mention how
|
|
|
- * the CIS transfer speed register relates to
|
|
|
- * high-speed, but it seems that 50 MHz is
|
|
|
- * mandatory.
|
|
|
- */
|
|
|
- mmc_set_clock(host, 50000000);
|
|
|
- } else {
|
|
|
- mmc_set_clock(host, card->cis.max_dtr);
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Switch to wider bus (if supported).
|
|
|
- */
|
|
|
- err = sdio_enable_wide(card);
|
|
|
- if (err)
|
|
|
- goto remove;
|
|
|
+ card->sdio_funcs = funcs = (ocr & 0x70000000) >> 28;
|
|
|
|
|
|
/*
|
|
|
* If needed, disconnect card detection pull-up resistor.
|