Commit 7310ece8 authored by Michal Miroslaw's avatar Michal Miroslaw Committed by Linus Torvalds

mmc: implement SD-combo (IO+mem) support

Signed-off-by: default avatarMichal Miroslaw <mirq-linux@rere.qmqm.pl>
Cc: Adrian Hunter <adrian.hunter@nokia.com>
Cc: Chris Ball <cjb@laptop.org>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 71578a1e
...@@ -37,6 +37,8 @@ static ssize_t mmc_type_show(struct device *dev, ...@@ -37,6 +37,8 @@ static ssize_t mmc_type_show(struct device *dev,
return sprintf(buf, "SD\n"); return sprintf(buf, "SD\n");
case MMC_TYPE_SDIO: case MMC_TYPE_SDIO:
return sprintf(buf, "SDIO\n"); return sprintf(buf, "SDIO\n");
case MMC_TYPE_SD_COMBO:
return sprintf(buf, "SDcombo\n");
default: default:
return -EFAULT; return -EFAULT;
} }
...@@ -74,6 +76,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -74,6 +76,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
case MMC_TYPE_SDIO: case MMC_TYPE_SDIO:
type = "SDIO"; type = "SDIO";
break; break;
case MMC_TYPE_SD_COMBO:
type = "SDcombo";
break;
default: default:
type = NULL; type = NULL;
} }
...@@ -239,6 +244,10 @@ int mmc_add_card(struct mmc_card *card) ...@@ -239,6 +244,10 @@ int mmc_add_card(struct mmc_card *card)
case MMC_TYPE_SDIO: case MMC_TYPE_SDIO:
type = "SDIO"; type = "SDIO";
break; break;
case MMC_TYPE_SD_COMBO:
type = "SD-combo";
if (mmc_card_blockaddr(card))
type = "SDHC-combo";
default: default:
type = "?"; type = "?";
break; break;
......
...@@ -1099,8 +1099,15 @@ void mmc_rescan(struct work_struct *work) ...@@ -1099,8 +1099,15 @@ void mmc_rescan(struct work_struct *work)
*/ */
err = mmc_send_io_op_cond(host, 0, &ocr); err = mmc_send_io_op_cond(host, 0, &ocr);
if (!err) { if (!err) {
if (mmc_attach_sdio(host, ocr)) if (mmc_attach_sdio(host, ocr)) {
mmc_claim_host(host);
/* try SDMEM (but not MMC) even if SDIO is broken */
if (mmc_send_app_op_cond(host, 0, &ocr))
goto out_fail;
if (mmc_attach_sd(host, ocr))
mmc_power_off(host); mmc_power_off(host);
}
goto out; goto out;
} }
...@@ -1124,6 +1131,7 @@ void mmc_rescan(struct work_struct *work) ...@@ -1124,6 +1131,7 @@ void mmc_rescan(struct work_struct *work)
goto out; goto out;
} }
out_fail:
mmc_release_host(host); mmc_release_host(host);
mmc_power_off(host); mmc_power_off(host);
......
...@@ -160,9 +160,7 @@ static int sdio_enable_wide(struct mmc_card *card) ...@@ -160,9 +160,7 @@ static int sdio_enable_wide(struct mmc_card *card)
if (ret) if (ret)
return ret; return ret;
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); return 1;
return 0;
} }
/* /*
...@@ -222,10 +220,34 @@ static int sdio_disable_wide(struct mmc_card *card) ...@@ -222,10 +220,34 @@ static int sdio_disable_wide(struct mmc_card *card)
return 0; return 0;
} }
static int sdio_enable_4bit_bus(struct mmc_card *card)
{
int err;
if (card->type == MMC_TYPE_SDIO)
return sdio_enable_wide(card);
if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
if (err)
return err;
} else
return 0;
err = sdio_enable_wide(card);
if (err <= 0)
mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1);
return err;
}
/* /*
* Test if the card supports high-speed mode and, if so, switch to it. * Test if the card supports high-speed mode and, if so, switch to it.
*/ */
static int sdio_enable_hs(struct mmc_card *card) static int mmc_sdio_switch_hs(struct mmc_card *card, int enable)
{ {
int ret; int ret;
u8 speed; u8 speed;
...@@ -240,7 +262,10 @@ static int sdio_enable_hs(struct mmc_card *card) ...@@ -240,7 +262,10 @@ static int sdio_enable_hs(struct mmc_card *card)
if (ret) if (ret)
return ret; return ret;
if (enable)
speed |= SDIO_SPEED_EHS; speed |= SDIO_SPEED_EHS;
else
speed &= ~SDIO_SPEED_EHS;
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL); ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
if (ret) if (ret)
...@@ -249,6 +274,24 @@ static int sdio_enable_hs(struct mmc_card *card) ...@@ -249,6 +274,24 @@ static int sdio_enable_hs(struct mmc_card *card)
return 1; return 1;
} }
/*
* Enable SDIO/combo card's high-speed mode. Return 0/1 if [not]supported.
*/
static int sdio_enable_hs(struct mmc_card *card)
{
int ret;
ret = mmc_sdio_switch_hs(card, true);
if (ret <= 0 || card->type == MMC_TYPE_SDIO)
return ret;
ret = mmc_sd_switch_hs(card);
if (ret <= 0)
mmc_sdio_switch_hs(card, false);
return ret;
}
static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
{ {
unsigned max_dtr; unsigned max_dtr;
...@@ -265,6 +308,9 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) ...@@ -265,6 +308,9 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
max_dtr = card->cis.max_dtr; max_dtr = card->cis.max_dtr;
} }
if (card->type == MMC_TYPE_SD_COMBO)
max_dtr = min(max_dtr, mmc_sd_get_max_clock(card));
return max_dtr; return max_dtr;
} }
...@@ -310,8 +356,25 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -310,8 +356,25 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto err; goto err;
} }
err = mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid);
if (!err) {
card->type = MMC_TYPE_SD_COMBO;
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
mmc_remove_card(card);
return -ENOENT;
}
} else {
card->type = MMC_TYPE_SDIO; card->type = MMC_TYPE_SDIO;
if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
mmc_remove_card(card);
return -ENOENT;
}
}
/* /*
* Call the optional HC's init_card function to handle quirks. * Call the optional HC's init_card function to handle quirks.
*/ */
...@@ -329,6 +392,17 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -329,6 +392,17 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
} }
/*
* Read CSD, before selecting the card
*/
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
err = mmc_sd_get_csd(host, card);
if (err)
return err;
mmc_decode_cid(card);
}
/* /*
* Select card, as all following commands rely on that. * Select card, as all following commands rely on that.
*/ */
...@@ -356,14 +430,33 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -356,14 +430,33 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
int same = (card->cis.vendor == oldcard->cis.vendor && int same = (card->cis.vendor == oldcard->cis.vendor &&
card->cis.device == oldcard->cis.device); card->cis.device == oldcard->cis.device);
mmc_remove_card(card); mmc_remove_card(card);
if (!same) { if (!same)
err = -ENOENT; return -ENOENT;
goto err;
}
card = oldcard; card = oldcard;
return 0; return 0;
} }
if (card->type == MMC_TYPE_SD_COMBO) {
err = mmc_sd_setup_card(host, card, oldcard != NULL);
/* handle as SDIO-only card if memory init failed */
if (err) {
mmc_go_idle(host);
if (mmc_host_is_spi(host))
/* should not fail, as it worked previously */
mmc_spi_set_crc(host, use_spi_crc);
card->type = MMC_TYPE_SDIO;
} else
card->dev.type = &sd_type;
}
/*
* If needed, disconnect card detection pull-up resistor.
*/
err = sdio_disable_cd(card);
if (err)
goto remove;
/* /*
* Switch to high-speed (if supported). * Switch to high-speed (if supported).
*/ */
...@@ -381,8 +474,10 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -381,8 +474,10 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
/* /*
* Switch to wider bus (if supported). * Switch to wider bus (if supported).
*/ */
err = sdio_enable_wide(card); err = sdio_enable_4bit_bus(card);
if (err) if (err > 0)
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
else if (err)
goto remove; goto remove;
if (!oldcard) if (!oldcard)
...@@ -496,9 +591,14 @@ static int mmc_sdio_resume(struct mmc_host *host) ...@@ -496,9 +591,14 @@ static int mmc_sdio_resume(struct mmc_host *host)
mmc_claim_host(host); mmc_claim_host(host);
err = mmc_sdio_init_card(host, host->ocr, host->card, err = mmc_sdio_init_card(host, host->ocr, host->card,
(host->pm_flags & MMC_PM_KEEP_POWER)); (host->pm_flags & MMC_PM_KEEP_POWER));
if (!err) if (!err) {
/* We may have switched to 1-bit mode during suspend. */ /* We may have switched to 1-bit mode during suspend. */
err = sdio_enable_wide(host->card); err = sdio_enable_4bit_bus(host->card);
if (err > 0) {
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
err = 0;
}
}
if (!err && host->sdio_irqs) if (!err && host->sdio_irqs)
mmc_signal_sdio_irq(host); mmc_signal_sdio_irq(host);
mmc_release_host(host); mmc_release_host(host);
...@@ -582,13 +682,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) ...@@ -582,13 +682,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
funcs = (ocr & 0x70000000) >> 28; funcs = (ocr & 0x70000000) >> 28;
card->sdio_funcs = 0; card->sdio_funcs = 0;
/*
* If needed, disconnect card detection pull-up resistor.
*/
err = sdio_disable_cd(card);
if (err)
goto remove;
/* /*
* Initialize (but don't add) all present functions. * Initialize (but don't add) all present functions.
*/ */
......
...@@ -93,6 +93,7 @@ struct mmc_card { ...@@ -93,6 +93,7 @@ struct mmc_card {
#define MMC_TYPE_MMC 0 /* MMC card */ #define MMC_TYPE_MMC 0 /* MMC card */
#define MMC_TYPE_SD 1 /* SD card */ #define MMC_TYPE_SD 1 /* SD card */
#define MMC_TYPE_SDIO 2 /* SDIO card */ #define MMC_TYPE_SDIO 2 /* SDIO card */
#define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */
unsigned int state; /* (our) card state */ unsigned int state; /* (our) card state */
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
#define MMC_STATE_READONLY (1<<1) /* card is read-only */ #define MMC_STATE_READONLY (1<<1) /* card is read-only */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment