|
|
|
@@ -3,7 +3,7 @@
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
|
|
|
|
|
* SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
|
|
|
|
|
* SD support Copyright (C) 2005 Pierre Ossman, All Rights Reserved.
|
|
|
|
|
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
|
|
|
|
|
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
@@ -316,8 +316,6 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(mmc_set_data_timeout);
|
|
|
|
|
|
|
|
|
|
static int mmc_select_card(struct mmc_host *host, struct mmc_card *card);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* __mmc_claim_host - exclusively claim a host
|
|
|
|
|
* @host: mmc host to claim
|
|
|
|
@@ -329,11 +327,10 @@ static int mmc_select_card(struct mmc_host *host, struct mmc_card *card);
|
|
|
|
|
*
|
|
|
|
|
* Note: you should use mmc_card_claim_host or mmc_claim_host.
|
|
|
|
|
*/
|
|
|
|
|
int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card)
|
|
|
|
|
void mmc_claim_host(struct mmc_host *host)
|
|
|
|
|
{
|
|
|
|
|
DECLARE_WAITQUEUE(wait, current);
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
|
|
add_wait_queue(&host->wq, &wait);
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
@@ -349,17 +346,9 @@ int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card)
|
|
|
|
|
host->claimed = 1;
|
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
|
remove_wait_queue(&host->wq, &wait);
|
|
|
|
|
|
|
|
|
|
if (card != (void *)-1) {
|
|
|
|
|
err = mmc_select_card(host, card);
|
|
|
|
|
if (err != MMC_ERR_NONE)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(__mmc_claim_host);
|
|
|
|
|
EXPORT_SYMBOL(mmc_claim_host);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* mmc_release_host - release a host
|
|
|
|
@@ -396,23 +385,18 @@ static inline void mmc_set_ios(struct mmc_host *host)
|
|
|
|
|
host->ops->set_ios(host, ios);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int mmc_select_card(struct mmc_host *host, struct mmc_card *card)
|
|
|
|
|
static int mmc_select_card(struct mmc_card *card)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct mmc_command cmd;
|
|
|
|
|
|
|
|
|
|
BUG_ON(!host->claimed);
|
|
|
|
|
|
|
|
|
|
if (host->card_selected == card)
|
|
|
|
|
return MMC_ERR_NONE;
|
|
|
|
|
|
|
|
|
|
host->card_selected = card;
|
|
|
|
|
BUG_ON(!card->host->claimed);
|
|
|
|
|
|
|
|
|
|
cmd.opcode = MMC_SELECT_CARD;
|
|
|
|
|
cmd.arg = card->rca << 16;
|
|
|
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
|
|
|
|
|
|
|
|
|
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
|
|
|
|
|
err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES);
|
|
|
|
|
if (err != MMC_ERR_NONE)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
@@ -426,51 +410,26 @@ static int mmc_select_card(struct mmc_host *host, struct mmc_card *card)
|
|
|
|
|
* wider version.
|
|
|
|
|
*/
|
|
|
|
|
if (mmc_card_sd(card) &&
|
|
|
|
|
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
|
|
|
|
|
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) &&
|
|
|
|
|
(card->host->caps & MMC_CAP_4_BIT_DATA)) {
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Default bus width is 1 bit.
|
|
|
|
|
*/
|
|
|
|
|
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
|
|
|
|
|
|
|
|
|
if (host->caps & MMC_CAP_4_BIT_DATA) {
|
|
|
|
|
struct mmc_command cmd;
|
|
|
|
|
cmd.opcode = SD_APP_SET_BUS_WIDTH;
|
|
|
|
|
cmd.arg = SD_BUS_WIDTH_4;
|
|
|
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
|
|
|
|
|
|
|
|
|
err = mmc_wait_for_app_cmd(host, card->rca, &cmd,
|
|
|
|
|
CMD_RETRIES);
|
|
|
|
|
err = mmc_wait_for_app_cmd(card->host, card->rca,
|
|
|
|
|
&cmd, CMD_RETRIES);
|
|
|
|
|
if (err != MMC_ERR_NONE)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
host->ios.bus_width = MMC_BUS_WIDTH_4;
|
|
|
|
|
card->host->ios.bus_width = MMC_BUS_WIDTH_4;
|
|
|
|
|
mmc_set_ios(card->host);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mmc_set_ios(host);
|
|
|
|
|
|
|
|
|
|
return MMC_ERR_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Ensure that no card is selected.
|
|
|
|
|
*/
|
|
|
|
|
static void mmc_deselect_cards(struct mmc_host *host)
|
|
|
|
|
{
|
|
|
|
|
struct mmc_command cmd;
|
|
|
|
|
|
|
|
|
|
if (host->card_selected) {
|
|
|
|
|
host->card_selected = NULL;
|
|
|
|
|
|
|
|
|
|
cmd.opcode = MMC_SELECT_CARD;
|
|
|
|
|
cmd.arg = 0;
|
|
|
|
|
cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
|
|
|
|
|
|
|
|
|
|
mmc_wait_for_cmd(host, &cmd, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void mmc_delay(unsigned int ms)
|
|
|
|
|
{
|
|
|
|
@@ -732,27 +691,12 @@ static void mmc_decode_scr(struct mmc_card *card)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Locate a MMC card on this MMC host given a raw CID.
|
|
|
|
|
*/
|
|
|
|
|
static struct mmc_card *mmc_find_card(struct mmc_host *host, u32 *raw_cid)
|
|
|
|
|
{
|
|
|
|
|
struct mmc_card *card;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(card, &host->cards, node) {
|
|
|
|
|
if (memcmp(card->raw_cid, raw_cid, sizeof(card->raw_cid)) == 0)
|
|
|
|
|
return card;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Allocate a new MMC card, and assign a unique RCA.
|
|
|
|
|
* Allocate a new MMC card
|
|
|
|
|
*/
|
|
|
|
|
static struct mmc_card *
|
|
|
|
|
mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca)
|
|
|
|
|
mmc_alloc_card(struct mmc_host *host, u32 *raw_cid)
|
|
|
|
|
{
|
|
|
|
|
struct mmc_card *card, *c;
|
|
|
|
|
unsigned int rca = *frca;
|
|
|
|
|
struct mmc_card *card;
|
|
|
|
|
|
|
|
|
|
card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
|
|
|
|
|
if (!card)
|
|
|
|
@@ -761,17 +705,6 @@ mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca)
|
|
|
|
|
mmc_init_card(card, host);
|
|
|
|
|
memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid));
|
|
|
|
|
|
|
|
|
|
again:
|
|
|
|
|
list_for_each_entry(c, &host->cards, node)
|
|
|
|
|
if (c->rca == rca) {
|
|
|
|
|
rca++;
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
card->rca = rca;
|
|
|
|
|
|
|
|
|
|
*frca = rca;
|
|
|
|
|
|
|
|
|
|
return card;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -937,21 +870,19 @@ static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Discover cards by requesting their CID. If this command
|
|
|
|
|
* times out, it is not an error; there are no further cards
|
|
|
|
|
* to be discovered. Add new cards to the list.
|
|
|
|
|
* Discover the card by requesting its CID.
|
|
|
|
|
*
|
|
|
|
|
* Create a mmc_card entry for each discovered card, assigning
|
|
|
|
|
* Create a mmc_card entry for the discovered card, assigning
|
|
|
|
|
* it an RCA, and save the raw CID for decoding later.
|
|
|
|
|
*/
|
|
|
|
|
static void mmc_discover_cards(struct mmc_host *host)
|
|
|
|
|
static void mmc_discover_card(struct mmc_host *host)
|
|
|
|
|
{
|
|
|
|
|
struct mmc_card *card;
|
|
|
|
|
unsigned int first_rca = 1, err;
|
|
|
|
|
unsigned int err;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
struct mmc_command cmd;
|
|
|
|
|
|
|
|
|
|
BUG_ON(host->card);
|
|
|
|
|
|
|
|
|
|
cmd.opcode = MMC_ALL_SEND_CID;
|
|
|
|
|
cmd.arg = 0;
|
|
|
|
|
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
|
|
|
|
@@ -959,28 +890,23 @@ static void mmc_discover_cards(struct mmc_host *host)
|
|
|
|
|
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
|
|
|
|
|
if (err == MMC_ERR_TIMEOUT) {
|
|
|
|
|
err = MMC_ERR_NONE;
|
|
|
|
|
break;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (err != MMC_ERR_NONE) {
|
|
|
|
|
printk(KERN_ERR "%s: error requesting CID: %d\n",
|
|
|
|
|
mmc_hostname(host), err);
|
|
|
|
|
break;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
card = mmc_find_card(host, cmd.resp);
|
|
|
|
|
if (!card) {
|
|
|
|
|
card = mmc_alloc_card(host, cmd.resp, &first_rca);
|
|
|
|
|
if (IS_ERR(card)) {
|
|
|
|
|
err = PTR_ERR(card);
|
|
|
|
|
break;
|
|
|
|
|
host->card = mmc_alloc_card(host, cmd.resp);
|
|
|
|
|
if (IS_ERR(host->card)) {
|
|
|
|
|
err = PTR_ERR(host->card);
|
|
|
|
|
host->card = NULL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
list_add(&card->node, &host->cards);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
card->state &= ~MMC_STATE_DEAD;
|
|
|
|
|
|
|
|
|
|
if (host->mode == MMC_MODE_SD) {
|
|
|
|
|
card->type = MMC_TYPE_SD;
|
|
|
|
|
host->card->type = MMC_TYPE_SD;
|
|
|
|
|
|
|
|
|
|
cmd.opcode = SD_SEND_RELATIVE_ADDR;
|
|
|
|
|
cmd.arg = 0;
|
|
|
|
@@ -988,9 +914,9 @@ static void mmc_discover_cards(struct mmc_host *host)
|
|
|
|
|
|
|
|
|
|
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
|
|
|
|
|
if (err != MMC_ERR_NONE)
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
mmc_card_set_dead(host->card);
|
|
|
|
|
else {
|
|
|
|
|
card->rca = cmd.resp[0] >> 16;
|
|
|
|
|
host->card->rca = cmd.resp[0] >> 16;
|
|
|
|
|
|
|
|
|
|
if (!host->ops->get_ro) {
|
|
|
|
|
printk(KERN_WARNING "%s: host does not "
|
|
|
|
@@ -999,66 +925,73 @@ static void mmc_discover_cards(struct mmc_host *host)
|
|
|
|
|
mmc_hostname(host));
|
|
|
|
|
} else {
|
|
|
|
|
if (host->ops->get_ro(host))
|
|
|
|
|
mmc_card_set_readonly(card);
|
|
|
|
|
mmc_card_set_readonly(host->card);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
card->type = MMC_TYPE_MMC;
|
|
|
|
|
host->card->type = MMC_TYPE_MMC;
|
|
|
|
|
host->card->rca = 1;
|
|
|
|
|
|
|
|
|
|
cmd.opcode = MMC_SET_RELATIVE_ADDR;
|
|
|
|
|
cmd.arg = card->rca << 16;
|
|
|
|
|
cmd.arg = host->card->rca << 16;
|
|
|
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
|
|
|
|
|
|
|
|
|
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
|
|
|
|
|
if (err != MMC_ERR_NONE)
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
}
|
|
|
|
|
mmc_card_set_dead(host->card);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mmc_read_csds(struct mmc_host *host)
|
|
|
|
|
static void mmc_read_csd(struct mmc_host *host)
|
|
|
|
|
{
|
|
|
|
|
struct mmc_card *card;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(card, &host->cards, node) {
|
|
|
|
|
struct mmc_command cmd;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
|
|
|
|
|
continue;
|
|
|
|
|
if (!host->card)
|
|
|
|
|
return;
|
|
|
|
|
if (mmc_card_dead(host->card))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
cmd.opcode = MMC_SEND_CSD;
|
|
|
|
|
cmd.arg = card->rca << 16;
|
|
|
|
|
cmd.arg = host->card->rca << 16;
|
|
|
|
|
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
|
|
|
|
|
|
|
|
|
|
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
|
|
|
|
|
if (err != MMC_ERR_NONE) {
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
continue;
|
|
|
|
|
mmc_card_set_dead(host->card);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(card->raw_csd, cmd.resp, sizeof(card->raw_csd));
|
|
|
|
|
memcpy(host->card->raw_csd, cmd.resp, sizeof(host->card->raw_csd));
|
|
|
|
|
|
|
|
|
|
mmc_decode_csd(card);
|
|
|
|
|
mmc_decode_cid(card);
|
|
|
|
|
}
|
|
|
|
|
mmc_decode_csd(host->card);
|
|
|
|
|
mmc_decode_cid(host->card);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mmc_process_ext_csds(struct mmc_host *host)
|
|
|
|
|
static void mmc_process_ext_csd(struct mmc_host *host)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct mmc_card *card;
|
|
|
|
|
|
|
|
|
|
struct mmc_request mrq;
|
|
|
|
|
struct mmc_command cmd;
|
|
|
|
|
struct mmc_data data;
|
|
|
|
|
|
|
|
|
|
u8 *ext_csd;
|
|
|
|
|
struct scatterlist sg;
|
|
|
|
|
|
|
|
|
|
if (!host->card)
|
|
|
|
|
return;
|
|
|
|
|
if (mmc_card_dead(host->card))
|
|
|
|
|
return;
|
|
|
|
|
if (mmc_card_sd(host->card))
|
|
|
|
|
return;
|
|
|
|
|
if (host->card->csd.mmca_vsn < CSD_SPEC_VER_4)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* As the ext_csd is so large and mostly unused, we don't store the
|
|
|
|
|
* raw block in mmc_card.
|
|
|
|
|
*/
|
|
|
|
|
u8 *ext_csd;
|
|
|
|
|
ext_csd = kmalloc(512, GFP_KERNEL);
|
|
|
|
|
if (!ext_csd) {
|
|
|
|
|
printk("%s: could not allocate a buffer to receive the ext_csd."
|
|
|
|
@@ -1067,20 +1000,6 @@ static void mmc_process_ext_csds(struct mmc_host *host)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(card, &host->cards, node) {
|
|
|
|
|
if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
|
|
|
|
|
continue;
|
|
|
|
|
if (mmc_card_sd(card))
|
|
|
|
|
continue;
|
|
|
|
|
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
err = mmc_select_card(host, card);
|
|
|
|
|
if (err != MMC_ERR_NONE) {
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
|
|
|
|
|
|
|
|
|
cmd.opcode = MMC_SEND_EXT_CSD;
|
|
|
|
@@ -1089,7 +1008,7 @@ static void mmc_process_ext_csds(struct mmc_host *host)
|
|
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct mmc_data));
|
|
|
|
|
|
|
|
|
|
mmc_set_data_timeout(&data, card, 0);
|
|
|
|
|
mmc_set_data_timeout(&data, host->card, 0);
|
|
|
|
|
|
|
|
|
|
data.blksz = 512;
|
|
|
|
|
data.blocks = 1;
|
|
|
|
@@ -1107,42 +1026,42 @@ static void mmc_process_ext_csds(struct mmc_host *host)
|
|
|
|
|
mmc_wait_for_req(host, &mrq);
|
|
|
|
|
|
|
|
|
|
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
|
|
|
|
|
if (card->csd.capacity == (4096 * 512)) {
|
|
|
|
|
if (host->card->csd.capacity == (4096 * 512)) {
|
|
|
|
|
printk(KERN_ERR "%s: unable to read EXT_CSD "
|
|
|
|
|
"on a possible high capacity card. "
|
|
|
|
|
"Card will be ignored.\n",
|
|
|
|
|
mmc_hostname(card->host));
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
mmc_hostname(host));
|
|
|
|
|
mmc_card_set_dead(host->card);
|
|
|
|
|
} else {
|
|
|
|
|
printk(KERN_WARNING "%s: unable to read "
|
|
|
|
|
"EXT_CSD, performance might "
|
|
|
|
|
"suffer.\n",
|
|
|
|
|
mmc_hostname(card->host));
|
|
|
|
|
mmc_hostname(host));
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
card->ext_csd.sectors =
|
|
|
|
|
host->card->ext_csd.sectors =
|
|
|
|
|
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
|
|
|
|
|
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
|
|
|
|
|
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
|
|
|
|
|
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
|
|
|
|
|
if (card->ext_csd.sectors)
|
|
|
|
|
mmc_card_set_blockaddr(card);
|
|
|
|
|
if (host->card->ext_csd.sectors)
|
|
|
|
|
mmc_card_set_blockaddr(host->card);
|
|
|
|
|
|
|
|
|
|
switch (ext_csd[EXT_CSD_CARD_TYPE]) {
|
|
|
|
|
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
|
|
|
|
|
card->ext_csd.hs_max_dtr = 52000000;
|
|
|
|
|
host->card->ext_csd.hs_max_dtr = 52000000;
|
|
|
|
|
break;
|
|
|
|
|
case EXT_CSD_CARD_TYPE_26:
|
|
|
|
|
card->ext_csd.hs_max_dtr = 26000000;
|
|
|
|
|
host->card->ext_csd.hs_max_dtr = 26000000;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* MMC v4 spec says this cannot happen */
|
|
|
|
|
printk("%s: card is mmc v4 but doesn't support "
|
|
|
|
|
"any high-speed modes.\n",
|
|
|
|
|
mmc_hostname(card->host));
|
|
|
|
|
continue;
|
|
|
|
|
mmc_hostname(host));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (host->caps & MMC_CAP_MMC_HIGHSPEED) {
|
|
|
|
@@ -1158,11 +1077,11 @@ static void mmc_process_ext_csds(struct mmc_host *host)
|
|
|
|
|
if (err != MMC_ERR_NONE) {
|
|
|
|
|
printk("%s: failed to switch card to mmc v4 "
|
|
|
|
|
"high-speed mode.\n",
|
|
|
|
|
mmc_hostname(card->host));
|
|
|
|
|
continue;
|
|
|
|
|
mmc_hostname(host));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mmc_card_set_highspeed(card);
|
|
|
|
|
mmc_card_set_highspeed(host->card);
|
|
|
|
|
|
|
|
|
|
host->ios.timing = MMC_TIMING_MMC_HS;
|
|
|
|
|
mmc_set_ios(host);
|
|
|
|
@@ -1182,51 +1101,43 @@ static void mmc_process_ext_csds(struct mmc_host *host)
|
|
|
|
|
if (err != MMC_ERR_NONE) {
|
|
|
|
|
printk("%s: failed to switch card to "
|
|
|
|
|
"mmc v4 4-bit bus mode.\n",
|
|
|
|
|
mmc_hostname(card->host));
|
|
|
|
|
continue;
|
|
|
|
|
mmc_hostname(host));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
host->ios.bus_width = MMC_BUS_WIDTH_4;
|
|
|
|
|
mmc_set_ios(host);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
kfree(ext_csd);
|
|
|
|
|
|
|
|
|
|
mmc_deselect_cards(host);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mmc_read_scrs(struct mmc_host *host)
|
|
|
|
|
static void mmc_read_scr(struct mmc_host *host)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct mmc_card *card;
|
|
|
|
|
struct mmc_request mrq;
|
|
|
|
|
struct mmc_command cmd;
|
|
|
|
|
struct mmc_data data;
|
|
|
|
|
struct scatterlist sg;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(card, &host->cards, node) {
|
|
|
|
|
if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
|
|
|
|
|
continue;
|
|
|
|
|
if (!mmc_card_sd(card))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
err = mmc_select_card(host, card);
|
|
|
|
|
if (err != MMC_ERR_NONE) {
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!host->card)
|
|
|
|
|
return;
|
|
|
|
|
if (mmc_card_dead(host->card))
|
|
|
|
|
return;
|
|
|
|
|
if (!mmc_card_sd(host->card))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
|
|
|
|
|
|
|
|
|
cmd.opcode = MMC_APP_CMD;
|
|
|
|
|
cmd.arg = card->rca << 16;
|
|
|
|
|
cmd.arg = host->card->rca << 16;
|
|
|
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
|
|
|
|
|
|
|
|
|
err = mmc_wait_for_cmd(host, &cmd, 0);
|
|
|
|
|
if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) {
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
continue;
|
|
|
|
|
mmc_card_set_dead(host->card);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
|
|
|
@@ -1237,7 +1148,7 @@ static void mmc_read_scrs(struct mmc_host *host)
|
|
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct mmc_data));
|
|
|
|
|
|
|
|
|
|
mmc_set_data_timeout(&data, card, 0);
|
|
|
|
|
mmc_set_data_timeout(&data, host->card, 0);
|
|
|
|
|
|
|
|
|
|
data.blksz = 1 << 3;
|
|
|
|
|
data.blocks = 1;
|
|
|
|
@@ -1250,28 +1161,23 @@ static void mmc_read_scrs(struct mmc_host *host)
|
|
|
|
|
mrq.cmd = &cmd;
|
|
|
|
|
mrq.data = &data;
|
|
|
|
|
|
|
|
|
|
sg_init_one(&sg, (u8*)card->raw_scr, 8);
|
|
|
|
|
sg_init_one(&sg, (u8*)host->card->raw_scr, 8);
|
|
|
|
|
|
|
|
|
|
mmc_wait_for_req(host, &mrq);
|
|
|
|
|
|
|
|
|
|
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
continue;
|
|
|
|
|
mmc_card_set_dead(host->card);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
card->raw_scr[0] = ntohl(card->raw_scr[0]);
|
|
|
|
|
card->raw_scr[1] = ntohl(card->raw_scr[1]);
|
|
|
|
|
host->card->raw_scr[0] = ntohl(host->card->raw_scr[0]);
|
|
|
|
|
host->card->raw_scr[1] = ntohl(host->card->raw_scr[1]);
|
|
|
|
|
|
|
|
|
|
mmc_decode_scr(card);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mmc_deselect_cards(host);
|
|
|
|
|
mmc_decode_scr(host->card);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mmc_read_switch_caps(struct mmc_host *host)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct mmc_card *card;
|
|
|
|
|
struct mmc_request mrq;
|
|
|
|
|
struct mmc_command cmd;
|
|
|
|
|
struct mmc_data data;
|
|
|
|
@@ -1281,6 +1187,15 @@ static void mmc_read_switch_caps(struct mmc_host *host)
|
|
|
|
|
if (!(host->caps & MMC_CAP_SD_HIGHSPEED))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!host->card)
|
|
|
|
|
return;
|
|
|
|
|
if (mmc_card_dead(host->card))
|
|
|
|
|
return;
|
|
|
|
|
if (!mmc_card_sd(host->card))
|
|
|
|
|
return;
|
|
|
|
|
if (host->card->scr.sda_vsn < SCR_SPEC_VER_1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
status = kmalloc(64, GFP_KERNEL);
|
|
|
|
|
if (!status) {
|
|
|
|
|
printk(KERN_WARNING "%s: Unable to allocate buffer for "
|
|
|
|
@@ -1289,20 +1204,6 @@ static void mmc_read_switch_caps(struct mmc_host *host)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(card, &host->cards, node) {
|
|
|
|
|
if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
|
|
|
|
|
continue;
|
|
|
|
|
if (!mmc_card_sd(card))
|
|
|
|
|
continue;
|
|
|
|
|
if (card->scr.sda_vsn < SCR_SPEC_VER_1)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
err = mmc_select_card(host, card);
|
|
|
|
|
if (err != MMC_ERR_NONE) {
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
|
|
|
|
|
|
|
|
|
cmd.opcode = SD_SWITCH;
|
|
|
|
@@ -1311,7 +1212,7 @@ static void mmc_read_switch_caps(struct mmc_host *host)
|
|
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct mmc_data));
|
|
|
|
|
|
|
|
|
|
mmc_set_data_timeout(&data, card, 0);
|
|
|
|
|
mmc_set_data_timeout(&data, host->card, 0);
|
|
|
|
|
|
|
|
|
|
data.blksz = 64;
|
|
|
|
|
data.blocks = 1;
|
|
|
|
@@ -1331,12 +1232,12 @@ static void mmc_read_switch_caps(struct mmc_host *host)
|
|
|
|
|
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
|
|
|
|
|
printk("%s: unable to read switch capabilities, "
|
|
|
|
|
"performance might suffer.\n",
|
|
|
|
|
mmc_hostname(card->host));
|
|
|
|
|
continue;
|
|
|
|
|
mmc_hostname(host));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status[13] & 0x02)
|
|
|
|
|
card->sw_caps.hs_max_dtr = 50000000;
|
|
|
|
|
host->card->sw_caps.hs_max_dtr = 50000000;
|
|
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
|
|
|
|
|
|
|
|
@@ -1346,7 +1247,7 @@ static void mmc_read_switch_caps(struct mmc_host *host)
|
|
|
|
|
|
|
|
|
|
memset(&data, 0, sizeof(struct mmc_data));
|
|
|
|
|
|
|
|
|
|
mmc_set_data_timeout(&data, card, 0);
|
|
|
|
|
mmc_set_data_timeout(&data, host->card, 0);
|
|
|
|
|
|
|
|
|
|
data.blksz = 64;
|
|
|
|
|
data.blocks = 1;
|
|
|
|
@@ -1368,35 +1269,31 @@ static void mmc_read_switch_caps(struct mmc_host *host)
|
|
|
|
|
printk(KERN_WARNING "%s: Problem switching card "
|
|
|
|
|
"into high-speed mode!\n",
|
|
|
|
|
mmc_hostname(host));
|
|
|
|
|
continue;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mmc_card_set_highspeed(card);
|
|
|
|
|
mmc_card_set_highspeed(host->card);
|
|
|
|
|
|
|
|
|
|
host->ios.timing = MMC_TIMING_SD_HS;
|
|
|
|
|
mmc_set_ios(host);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
kfree(status);
|
|
|
|
|
|
|
|
|
|
mmc_deselect_cards(host);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned int mmc_calculate_clock(struct mmc_host *host)
|
|
|
|
|
{
|
|
|
|
|
struct mmc_card *card;
|
|
|
|
|
unsigned int max_dtr = host->f_max;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(card, &host->cards, node)
|
|
|
|
|
if (!mmc_card_dead(card)) {
|
|
|
|
|
if (mmc_card_highspeed(card) && mmc_card_sd(card)) {
|
|
|
|
|
if (max_dtr > card->sw_caps.hs_max_dtr)
|
|
|
|
|
max_dtr = card->sw_caps.hs_max_dtr;
|
|
|
|
|
} else if (mmc_card_highspeed(card) && !mmc_card_sd(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;
|
|
|
|
|
if (host->card && !mmc_card_dead(host->card)) {
|
|
|
|
|
if (mmc_card_highspeed(host->card) && mmc_card_sd(host->card)) {
|
|
|
|
|
if (max_dtr > host->card->sw_caps.hs_max_dtr)
|
|
|
|
|
max_dtr = host->card->sw_caps.hs_max_dtr;
|
|
|
|
|
} else if (mmc_card_highspeed(host->card) && !mmc_card_sd(host->card)) {
|
|
|
|
|
if (max_dtr > host->card->ext_csd.hs_max_dtr)
|
|
|
|
|
max_dtr = host->card->ext_csd.hs_max_dtr;
|
|
|
|
|
} else if (max_dtr > host->card->csd.max_dtr) {
|
|
|
|
|
max_dtr = host->card->csd.max_dtr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1415,32 +1312,26 @@ static unsigned int mmc_calculate_clock(struct mmc_host *host)
|
|
|
|
|
* A request for status does not cause a state change in data
|
|
|
|
|
* transfer mode.
|
|
|
|
|
*/
|
|
|
|
|
static void mmc_check_cards(struct mmc_host *host)
|
|
|
|
|
static void mmc_check_card(struct mmc_card *card)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *l, *n;
|
|
|
|
|
|
|
|
|
|
mmc_deselect_cards(host);
|
|
|
|
|
|
|
|
|
|
list_for_each_safe(l, n, &host->cards) {
|
|
|
|
|
struct mmc_card *card = mmc_list_to_card(l);
|
|
|
|
|
struct mmc_command cmd;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
BUG_ON(!card);
|
|
|
|
|
|
|
|
|
|
cmd.opcode = MMC_SEND_STATUS;
|
|
|
|
|
cmd.arg = card->rca << 16;
|
|
|
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
|
|
|
|
|
|
|
|
|
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
|
|
|
|
|
err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES);
|
|
|
|
|
if (err == MMC_ERR_NONE)
|
|
|
|
|
continue;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mmc_setup(struct mmc_host *host)
|
|
|
|
|
{
|
|
|
|
|
if (host->ios.power_mode != MMC_POWER_ON) {
|
|
|
|
|
int err;
|
|
|
|
|
u32 ocr;
|
|
|
|
|
|
|
|
|
@@ -1469,36 +1360,16 @@ static void mmc_setup(struct mmc_host *host)
|
|
|
|
|
|
|
|
|
|
host->ocr = mmc_select_voltage(host, ocr);
|
|
|
|
|
|
|
|
|
|
if (host->ocr == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
if (host->ocr)
|
|
|
|
|
mmc_idle_cards(host);
|
|
|
|
|
} else {
|
|
|
|
|
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
|
|
|
|
host->ios.clock = host->f_min;
|
|
|
|
|
mmc_set_ios(host);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We should remember the OCR mask from the existing
|
|
|
|
|
* cards, and detect the new cards OCR mask, combine
|
|
|
|
|
* the two and re-select the VDD. However, if we do
|
|
|
|
|
* change VDD, we should do an idle, and then do a
|
|
|
|
|
* full re-initialisation. We would need to notify
|
|
|
|
|
* drivers so that they can re-setup the cards as
|
|
|
|
|
* well, while keeping their queues at bay.
|
|
|
|
|
*
|
|
|
|
|
* For the moment, we take the easy way out - if the
|
|
|
|
|
* new cards don't like our currently selected VDD,
|
|
|
|
|
* they drop off the bus.
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (host->ocr == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Send the selected OCR multiple times... until the cards
|
|
|
|
@@ -1522,7 +1393,7 @@ static void mmc_setup(struct mmc_host *host)
|
|
|
|
|
mmc_send_op_cond(host, host->ocr | (1 << 30), NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mmc_discover_cards(host);
|
|
|
|
|
mmc_discover_card(host);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Ok, now switch to push-pull mode.
|
|
|
|
@@ -1530,13 +1401,19 @@ static void mmc_setup(struct mmc_host *host)
|
|
|
|
|
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
|
|
|
|
mmc_set_ios(host);
|
|
|
|
|
|
|
|
|
|
mmc_read_csds(host);
|
|
|
|
|
mmc_read_csd(host);
|
|
|
|
|
|
|
|
|
|
if (host->card && !mmc_card_dead(host->card)) {
|
|
|
|
|
err = mmc_select_card(host->card);
|
|
|
|
|
if (err != MMC_ERR_NONE)
|
|
|
|
|
mmc_card_set_dead(host->card);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (host->mode == MMC_MODE_SD) {
|
|
|
|
|
mmc_read_scrs(host);
|
|
|
|
|
mmc_read_scr(host);
|
|
|
|
|
mmc_read_switch_caps(host);
|
|
|
|
|
} else
|
|
|
|
|
mmc_process_ext_csds(host);
|
|
|
|
|
mmc_process_ext_csd(host);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1566,31 +1443,29 @@ static void mmc_rescan(struct work_struct *work)
|
|
|
|
|
{
|
|
|
|
|
struct mmc_host *host =
|
|
|
|
|
container_of(work, struct mmc_host, detect.work);
|
|
|
|
|
struct list_head *l, *n;
|
|
|
|
|
unsigned char power_mode;
|
|
|
|
|
|
|
|
|
|
mmc_claim_host(host);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check for removed cards and newly inserted ones. We check for
|
|
|
|
|
* Check for removed card and newly inserted ones. We check for
|
|
|
|
|
* removed cards first so we can intelligently re-select the VDD.
|
|
|
|
|
*/
|
|
|
|
|
power_mode = host->ios.power_mode;
|
|
|
|
|
if (power_mode == MMC_POWER_ON)
|
|
|
|
|
mmc_check_cards(host);
|
|
|
|
|
if (host->card) {
|
|
|
|
|
mmc_check_card(host->card);
|
|
|
|
|
|
|
|
|
|
mmc_release_host(host);
|
|
|
|
|
|
|
|
|
|
if (mmc_card_dead(host->card)) {
|
|
|
|
|
mmc_remove_card(host->card);
|
|
|
|
|
host->card = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mmc_setup(host);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Some broken cards process CMD1 even in stand-by state. There is
|
|
|
|
|
* no reply, but an ILLEGAL_COMMAND error is cached and returned
|
|
|
|
|
* after next command. We poll for card status here to clear any
|
|
|
|
|
* possibly pending error.
|
|
|
|
|
*/
|
|
|
|
|
if (power_mode == MMC_POWER_ON)
|
|
|
|
|
mmc_check_cards(host);
|
|
|
|
|
|
|
|
|
|
if (!list_empty(&host->cards)) {
|
|
|
|
|
if (host->card && !mmc_card_dead(host->card)) {
|
|
|
|
|
/*
|
|
|
|
|
* (Re-)calculate the fastest clock rate which the
|
|
|
|
|
* attached cards and the host support.
|
|
|
|
@@ -1601,31 +1476,28 @@ static void mmc_rescan(struct work_struct *work)
|
|
|
|
|
|
|
|
|
|
mmc_release_host(host);
|
|
|
|
|
|
|
|
|
|
list_for_each_safe(l, n, &host->cards) {
|
|
|
|
|
struct mmc_card *card = mmc_list_to_card(l);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If this is a new and good card, register it.
|
|
|
|
|
*/
|
|
|
|
|
if (!mmc_card_present(card) && !mmc_card_dead(card)) {
|
|
|
|
|
if (mmc_register_card(card))
|
|
|
|
|
mmc_card_set_dead(card);
|
|
|
|
|
if (host->card && !mmc_card_dead(host->card)) {
|
|
|
|
|
if (mmc_register_card(host->card))
|
|
|
|
|
mmc_card_set_dead(host->card);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If this card is dead, destroy it.
|
|
|
|
|
*/
|
|
|
|
|
if (mmc_card_dead(card)) {
|
|
|
|
|
list_del(&card->node);
|
|
|
|
|
mmc_remove_card(card);
|
|
|
|
|
}
|
|
|
|
|
if (host->card && mmc_card_dead(host->card)) {
|
|
|
|
|
mmc_remove_card(host->card);
|
|
|
|
|
host->card = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
/*
|
|
|
|
|
* If we discover that there are no cards on the
|
|
|
|
|
* bus, turn off the clock and power down.
|
|
|
|
|
*/
|
|
|
|
|
if (list_empty(&host->cards))
|
|
|
|
|
if (!host->card)
|
|
|
|
|
mmc_power_off(host);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1645,7 +1517,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
|
|
|
|
if (host) {
|
|
|
|
|
spin_lock_init(&host->lock);
|
|
|
|
|
init_waitqueue_head(&host->wq);
|
|
|
|
|
INIT_LIST_HEAD(&host->cards);
|
|
|
|
|
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -1694,8 +1565,6 @@ EXPORT_SYMBOL(mmc_add_host);
|
|
|
|
|
*/
|
|
|
|
|
void mmc_remove_host(struct mmc_host *host)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *l, *n;
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_MMC_DEBUG
|
|
|
|
|
mmc_claim_host(host);
|
|
|
|
|
host->removed = 1;
|
|
|
|
@@ -1704,10 +1573,9 @@ void mmc_remove_host(struct mmc_host *host)
|
|
|
|
|
|
|
|
|
|
mmc_flush_scheduled_work();
|
|
|
|
|
|
|
|
|
|
list_for_each_safe(l, n, &host->cards) {
|
|
|
|
|
struct mmc_card *card = mmc_list_to_card(l);
|
|
|
|
|
|
|
|
|
|
mmc_remove_card(card);
|
|
|
|
|
if (host->card) {
|
|
|
|
|
mmc_remove_card(host->card);
|
|
|
|
|
host->card = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mmc_power_off(host);
|
|
|
|
@@ -1738,14 +1606,11 @@ EXPORT_SYMBOL(mmc_free_host);
|
|
|
|
|
*/
|
|
|
|
|
int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *l, *n;
|
|
|
|
|
|
|
|
|
|
mmc_flush_scheduled_work();
|
|
|
|
|
|
|
|
|
|
list_for_each_safe(l, n, &host->cards) {
|
|
|
|
|
struct mmc_card *card = mmc_list_to_card(l);
|
|
|
|
|
|
|
|
|
|
mmc_remove_card(card);
|
|
|
|
|
if (host->card) {
|
|
|
|
|
mmc_remove_card(host->card);
|
|
|
|
|
host->card = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mmc_power_off(host);
|
|
|
|
|