From: Michał Mirosław on
---
drivers/mmc/core/bus.c | 9 +++
drivers/mmc/core/core.c | 12 +++-
drivers/mmc/core/sdio.c | 178 ++++++++++++++++++++++++++++++++++++---------
include/linux/mmc/card.h | 1 +
4 files changed, 162 insertions(+), 38 deletions(-)

diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 49d9dca..7cd9749 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -37,6 +37,8 @@ static ssize_t mmc_type_show(struct device *dev,
return sprintf(buf, "SD\n");
case MMC_TYPE_SDIO:
return sprintf(buf, "SDIO\n");
+ case MMC_TYPE_SD_COMBO:
+ return sprintf(buf, "SDcombo\n");
default:
return -EFAULT;
}
@@ -74,6 +76,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
case MMC_TYPE_SDIO:
type = "SDIO";
break;
+ case MMC_TYPE_SD_COMBO:
+ type = "SDcombo";
+ break;
default:
type = NULL;
}
@@ -239,6 +244,10 @@ int mmc_add_card(struct mmc_card *card)
case MMC_TYPE_SDIO:
type = "SDIO";
break;
+ case MMC_TYPE_SD_COMBO:
+ type = "SD-combo";
+ if (mmc_card_blockaddr(card))
+ type = "SDHC-combo";
default:
type = "?";
break;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 3168ebd..87cf0de 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1099,8 +1099,15 @@ void mmc_rescan(struct work_struct *work)
*/
err = mmc_send_io_op_cond(host, 0, &ocr);
if (!err) {
- if (mmc_attach_sdio(host, ocr))
- mmc_power_off(host);
+ 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);
+ }
goto out;
}

@@ -1124,6 +1131,7 @@ void mmc_rescan(struct work_struct *work)
goto out;
}

+out_fail:
mmc_release_host(host);
mmc_power_off(host);

diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 2dd4cfe..aebb0b0 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -18,6 +18,7 @@

#include "core.h"
#include "bus.h"
+#include "sd.h"
#include "sdio_bus.h"
#include "mmc_ops.h"
#include "sd_ops.h"
@@ -144,10 +145,10 @@ static int sdio_enable_wide(struct mmc_card *card)
u8 ctrl;

if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
- return 0;
+ return -EOPNOTSUPP;

if (card->cccr.low_speed && !card->cccr.wide_bus)
- return 0;
+ return -EOPNOTSUPP;

ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
if (ret)
@@ -159,8 +160,6 @@ static int sdio_enable_wide(struct mmc_card *card)
if (ret)
return ret;

- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
-
return 0;
}

@@ -221,36 +220,98 @@ static int sdio_disable_wide(struct mmc_card *card)
return 0;
}

+
+static int sdio_enable_4bit_bus(struct mmc_card *card)
+{
+ int err;
+
+ if (card->type == MMC_TYPE_SD_COMBO) {
+ 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 -EOPNOTSUPP;
+ }
+
+ err = sdio_enable_wide(card);
+ if (err == -EOPNOTSUPP && card->type == MMC_TYPE_SD_COMBO)
+ 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.
*/
-static int sdio_enable_hs(struct mmc_card *card)
+static int mmc_sdio_switch_hs(struct mmc_card *card, int enable)
{
int ret;
u8 speed;

if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
- return 0;
+ return -EOPNOTSUPP;

if (!card->cccr.high_speed)
- return 0;
+ return -EOPNOTSUPP;

ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
if (ret)
return ret;

- speed |= SDIO_SPEED_EHS;
+ if (enable)
+ speed |= SDIO_SPEED_EHS;
+ else
+ speed &= ~SDIO_SPEED_EHS;

ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
if (ret)
return ret;

- mmc_card_set_highspeed(card);
- mmc_set_timing(card->host, MMC_TIMING_SD_HS);
-
return 0;
}

+static int sdio_enable_hs(struct mmc_card *card)
+{
+ int err;
+
+ err = mmc_sdio_switch_hs(card, true);
+ if (err)
+ return err;
+
+ if (card->type == MMC_TYPE_SD_COMBO) {
+ err = mmc_sd_switch_hs(card);
+ if (err == -EOPNOTSUPP)
+ mmc_sdio_switch_hs(card, false);
+ }
+
+ return err;
+}
+
+static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
+{
+ unsigned max_dtr;
+
+ 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.
+ */
+ max_dtr = 50000000;
+ } else {
+ 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;
+}
+
/*
* Handle the detection and initialisation of a card.
*
@@ -295,6 +356,34 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,

card->type = MMC_TYPE_SDIO;

+ if (!oldcard || oldcard->type == MMC_TYPE_SDIO) {
+ u32 cid[4];
+
+ err = mmc_sd_get_cid(host, host->ocr & ocr, cid);
+ if (!err) {
+ /* this is SD-combo card */
+ if (oldcard && oldcard->type == MMC_TYPE_SDIO) {
+ err = -ENOENT;
+ goto remove;
+ }
+ card->type = MMC_TYPE_SD_COMBO;
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ }
+ } else if (oldcard->type == MMC_TYPE_SD_COMBO) {
+ u32 cid[4];
+
+ err = mmc_sd_get_cid(host, host->ocr & ocr, cid);
+ if (err) {
+ /* this is SDIO-only card */
+ goto remove;
+ }
+
+ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
+ err = -ENOENT;
+ goto remove;
+ }
+ }
+
/*
* For native busses: set card RCA and quit open drain mode.
*/
@@ -307,6 +396,17 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
}

/*
+ * 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.
*/
if (!powered_resume && !mmc_host_is_spi(host)) {
@@ -333,41 +433,54 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
int same = (card->cis.vendor == oldcard->cis.vendor &&
card->cis.device == oldcard->cis.device);
mmc_remove_card(card);
- if (!same) {
- err = -ENOENT;
- goto err;
- }
+ if (!same)
+ return -ENOENT;
+
card = oldcard;
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).
*/
err = sdio_enable_hs(card);
- if (err)
+ if (!err)
+ mmc_sd_go_highspeed(card);
+ else if (err != -EOPNOTSUPP)
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);
- }
+ mmc_set_clock(host, mmc_sdio_get_max_clock(card));

/*
* Switch to wider bus (if supported).
*/
- err = sdio_enable_wide(card);
- if (err)
+ err = sdio_enable_4bit_bus(card);
+ if (!err)
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+ else if (err != -EOPNOTSUPP)
goto remove;

if (!oldcard)
@@ -568,13 +681,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
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.
*/
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index d02d2c6..dc570f5 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -92,6 +92,7 @@ struct mmc_card {
#define MMC_TYPE_MMC 0 /* MMC card */
#define MMC_TYPE_SD 1 /* SD 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 */
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
#define MMC_STATE_READONLY (1<<1) /* card is read-only */
--
1.6.4.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/