Skip to content

Commit

Permalink
mmc: add mmc card sleep and awake support
Browse files Browse the repository at this point in the history
Add support for the new MMC command SLEEP_AWAKE.

Signed-off-by: Jarkko Lavinen <jarkko.lavinen@nokia.com>
Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
Acked-by: Matt Fleming <matt@console-pimps.org>
Cc: Ian Molton <ian@mnementh.co.uk>
Cc: "Roberto A. Foglietta" <roberto.foglietta@gmail.com>
Cc: Jarkko Lavinen <jarkko.lavinen@nokia.com>
Cc: Denis Karpov <ext-denis.2.karpov@nokia.com>
Cc: Pierre Ossman <pierre@ossman.eu>
Cc: Philip Langdale <philipl@overt.org>
Cc: "Madhusudhan" <madhu.cr@ti.com>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Jarkko Lavinen authored and Linus Torvalds committed Sep 23, 2009
1 parent eae1aee commit b1ebe38
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 5 deletions.
40 changes: 40 additions & 0 deletions drivers/mmc/core/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,46 @@ void mmc_power_restore_host(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_power_restore_host);

int mmc_card_awake(struct mmc_host *host)
{
int err = -ENOSYS;

mmc_bus_get(host);

if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
err = host->bus_ops->awake(host);

mmc_bus_put(host);

return err;
}
EXPORT_SYMBOL(mmc_card_awake);

int mmc_card_sleep(struct mmc_host *host)
{
int err = -ENOSYS;

mmc_bus_get(host);

if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
err = host->bus_ops->sleep(host);

mmc_bus_put(host);

return err;
}
EXPORT_SYMBOL(mmc_card_sleep);

int mmc_card_can_sleep(struct mmc_host *host)
{
struct mmc_card *card = host->card;

if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3)
return 1;
return 0;
}
EXPORT_SYMBOL(mmc_card_can_sleep);

#ifdef CONFIG_PM

/**
Expand Down
2 changes: 2 additions & 0 deletions drivers/mmc/core/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#define MMC_CMD_RETRIES 3

struct mmc_bus_ops {
int (*awake)(struct mmc_host *);
int (*sleep)(struct mmc_host *);
void (*remove)(struct mmc_host *);
void (*detect)(struct mmc_host *);
void (*suspend)(struct mmc_host *);
Expand Down
54 changes: 49 additions & 5 deletions drivers/mmc/core/mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ static int mmc_read_ext_csd(struct mmc_card *card)
{
int err;
u8 *ext_csd;
unsigned int ext_csd_struct;

BUG_ON(!card);

Expand Down Expand Up @@ -207,16 +206,16 @@ static int mmc_read_ext_csd(struct mmc_card *card)
goto out;
}

ext_csd_struct = ext_csd[EXT_CSD_REV];
if (ext_csd_struct > 3) {
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
if (card->ext_csd.rev > 3) {
printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
"version %d\n", mmc_hostname(card->host),
ext_csd_struct);
card->ext_csd.rev);
err = -EINVAL;
goto out;
}

if (ext_csd_struct >= 2) {
if (card->ext_csd.rev >= 2) {
card->ext_csd.sectors =
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
Expand All @@ -241,6 +240,15 @@ static int mmc_read_ext_csd(struct mmc_card *card)
goto out;
}

if (card->ext_csd.rev >= 3) {
u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT];

/* Sleep / awake timeout in 100ns units */
if (sa_shift > 0 && sa_shift <= 0x17)
card->ext_csd.sa_timeout =
1 << ext_csd[EXT_CSD_S_A_TIMEOUT];
}

out:
kfree(ext_csd);

Expand Down Expand Up @@ -557,9 +565,41 @@ static void mmc_power_restore(struct mmc_host *host)
mmc_release_host(host);
}

static int mmc_sleep(struct mmc_host *host)
{
struct mmc_card *card = host->card;
int err = -ENOSYS;

if (card && card->ext_csd.rev >= 3) {
err = mmc_card_sleepawake(host, 1);
if (err < 0)
pr_debug("%s: Error %d while putting card into sleep",
mmc_hostname(host), err);
}

return err;
}

static int mmc_awake(struct mmc_host *host)
{
struct mmc_card *card = host->card;
int err = -ENOSYS;

if (card && card->ext_csd.rev >= 3) {
err = mmc_card_sleepawake(host, 0);
if (err < 0)
pr_debug("%s: Error %d while awaking sleeping card",
mmc_hostname(host), err);
}

return err;
}

#ifdef CONFIG_MMC_UNSAFE_RESUME

static const struct mmc_bus_ops mmc_ops = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = mmc_suspend,
Expand All @@ -575,6 +615,8 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
#else

static const struct mmc_bus_ops mmc_ops = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = NULL,
Expand All @@ -583,6 +625,8 @@ static const struct mmc_bus_ops mmc_ops = {
};

static const struct mmc_bus_ops mmc_ops_unsafe = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = mmc_suspend,
Expand Down
36 changes: 36 additions & 0 deletions drivers/mmc/core/mmc_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,42 @@ int mmc_deselect_cards(struct mmc_host *host)
return _mmc_select_card(host, NULL);
}

int mmc_card_sleepawake(struct mmc_host *host, int sleep)
{
struct mmc_command cmd;
struct mmc_card *card = host->card;
int err;

if (sleep)
mmc_deselect_cards(host);

memset(&cmd, 0, sizeof(struct mmc_command));

cmd.opcode = MMC_SLEEP_AWAKE;
cmd.arg = card->rca << 16;
if (sleep)
cmd.arg |= 1 << 15;

cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
return err;

/*
* If the host does not wait while the card signals busy, then we will
* will have to wait the sleep/awake timeout. Note, we cannot use the
* SEND_STATUS command to poll the status because that command (and most
* others) is invalid while the card sleeps.
*/
if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));

if (!sleep)
err = mmc_select_card(card);

return err;
}

int mmc_go_idle(struct mmc_host *host)
{
int err;
Expand Down
1 change: 1 addition & 0 deletions drivers/mmc/core/mmc_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_cid(struct mmc_host *host, u32 *cid);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_card_sleepawake(struct mmc_host *host, int sleep);

#endif

2 changes: 2 additions & 0 deletions include/linux/mmc/card.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ struct mmc_csd {
};

struct mmc_ext_csd {
u8 rev;
unsigned int sa_timeout; /* Units: 100ns */
unsigned int hs_max_dtr;
unsigned int sectors;
};
Expand Down
5 changes: 5 additions & 0 deletions include/linux/mmc/host.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ struct mmc_host {
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
#define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */

/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
Expand Down Expand Up @@ -240,6 +241,10 @@ struct regulator;
int mmc_regulator_get_ocrmask(struct regulator *supply);
int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit);

int mmc_card_awake(struct mmc_host *host);
int mmc_card_sleep(struct mmc_host *host);
int mmc_card_can_sleep(struct mmc_host *host);

int mmc_host_enable(struct mmc_host *host);
int mmc_host_disable(struct mmc_host *host);
int mmc_host_lazy_disable(struct mmc_host *host);
Expand Down
2 changes: 2 additions & 0 deletions include/linux/mmc/mmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#define MMC_ALL_SEND_CID 2 /* bcr R2 */
#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */
#define MMC_SET_DSR 4 /* bc [31:16] RCA */
#define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */
#define MMC_SWITCH 6 /* ac [31:0] See below R1b */
#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */
#define MMC_SEND_EXT_CSD 8 /* adtc R1 */
Expand Down Expand Up @@ -254,6 +255,7 @@ struct _mmc_csd {
#define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_REV 192 /* RO */
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
#define EXT_CSD_S_A_TIMEOUT 217

/*
* EXT_CSD field definitions
Expand Down

0 comments on commit b1ebe38

Please sign in to comment.