Commit b1ebe384 authored by Jarkko Lavinen's avatar Jarkko Lavinen Committed by Linus Torvalds

mmc: add mmc card sleep and awake support

Add support for the new MMC command SLEEP_AWAKE.
Signed-off-by: default avatarJarkko Lavinen <jarkko.lavinen@nokia.com>
Signed-off-by: default avatarAdrian Hunter <adrian.hunter@nokia.com>
Acked-by: default avatarMatt 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent eae1aeee
...@@ -1185,6 +1185,46 @@ void mmc_power_restore_host(struct mmc_host *host) ...@@ -1185,6 +1185,46 @@ void mmc_power_restore_host(struct mmc_host *host)
} }
EXPORT_SYMBOL(mmc_power_restore_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 #ifdef CONFIG_PM
/** /**
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#define MMC_CMD_RETRIES 3 #define MMC_CMD_RETRIES 3
struct mmc_bus_ops { struct mmc_bus_ops {
int (*awake)(struct mmc_host *);
int (*sleep)(struct mmc_host *);
void (*remove)(struct mmc_host *); void (*remove)(struct mmc_host *);
void (*detect)(struct mmc_host *); void (*detect)(struct mmc_host *);
void (*suspend)(struct mmc_host *); void (*suspend)(struct mmc_host *);
......
...@@ -160,7 +160,6 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -160,7 +160,6 @@ static int mmc_read_ext_csd(struct mmc_card *card)
{ {
int err; int err;
u8 *ext_csd; u8 *ext_csd;
unsigned int ext_csd_struct;
BUG_ON(!card); BUG_ON(!card);
...@@ -207,16 +206,16 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -207,16 +206,16 @@ static int mmc_read_ext_csd(struct mmc_card *card)
goto out; goto out;
} }
ext_csd_struct = ext_csd[EXT_CSD_REV]; card->ext_csd.rev = ext_csd[EXT_CSD_REV];
if (ext_csd_struct > 3) { if (card->ext_csd.rev > 3) {
printk(KERN_ERR "%s: unrecognised EXT_CSD structure " printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
"version %d\n", mmc_hostname(card->host), "version %d\n", mmc_hostname(card->host),
ext_csd_struct); card->ext_csd.rev);
err = -EINVAL; err = -EINVAL;
goto out; goto out;
} }
if (ext_csd_struct >= 2) { if (card->ext_csd.rev >= 2) {
card->ext_csd.sectors = card->ext_csd.sectors =
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
...@@ -241,6 +240,15 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -241,6 +240,15 @@ static int mmc_read_ext_csd(struct mmc_card *card)
goto out; 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: out:
kfree(ext_csd); kfree(ext_csd);
...@@ -557,9 +565,41 @@ static void mmc_power_restore(struct mmc_host *host) ...@@ -557,9 +565,41 @@ static void mmc_power_restore(struct mmc_host *host)
mmc_release_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 #ifdef CONFIG_MMC_UNSAFE_RESUME
static const struct mmc_bus_ops mmc_ops = { static const struct mmc_bus_ops mmc_ops = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove, .remove = mmc_remove,
.detect = mmc_detect, .detect = mmc_detect,
.suspend = mmc_suspend, .suspend = mmc_suspend,
...@@ -575,6 +615,8 @@ static void mmc_attach_bus_ops(struct mmc_host *host) ...@@ -575,6 +615,8 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
#else #else
static const struct mmc_bus_ops mmc_ops = { static const struct mmc_bus_ops mmc_ops = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove, .remove = mmc_remove,
.detect = mmc_detect, .detect = mmc_detect,
.suspend = NULL, .suspend = NULL,
...@@ -583,6 +625,8 @@ static const struct mmc_bus_ops mmc_ops = { ...@@ -583,6 +625,8 @@ static const struct mmc_bus_ops mmc_ops = {
}; };
static const struct mmc_bus_ops mmc_ops_unsafe = { static const struct mmc_bus_ops mmc_ops_unsafe = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove, .remove = mmc_remove,
.detect = mmc_detect, .detect = mmc_detect,
.suspend = mmc_suspend, .suspend = mmc_suspend,
......
...@@ -57,6 +57,42 @@ int mmc_deselect_cards(struct mmc_host *host) ...@@ -57,6 +57,42 @@ int mmc_deselect_cards(struct mmc_host *host)
return _mmc_select_card(host, NULL); 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 mmc_go_idle(struct mmc_host *host)
{ {
int err; int err;
......
...@@ -25,6 +25,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status); ...@@ -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_send_cid(struct mmc_host *host, u32 *cid);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); 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_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
#endif #endif
...@@ -40,6 +40,8 @@ struct mmc_csd { ...@@ -40,6 +40,8 @@ struct mmc_csd {
}; };
struct mmc_ext_csd { struct mmc_ext_csd {
u8 rev;
unsigned int sa_timeout; /* Units: 100ns */
unsigned int hs_max_dtr; unsigned int hs_max_dtr;
unsigned int sectors; unsigned int sectors;
}; };
......
...@@ -149,6 +149,7 @@ struct mmc_host { ...@@ -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_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_DISABLE (1 << 7) /* Can the host be disabled */
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ #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 */ /* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */
...@@ -240,6 +241,10 @@ struct regulator; ...@@ -240,6 +241,10 @@ struct regulator;
int mmc_regulator_get_ocrmask(struct regulator *supply); int mmc_regulator_get_ocrmask(struct regulator *supply);
int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit); 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_enable(struct mmc_host *host);
int mmc_host_disable(struct mmc_host *host); int mmc_host_disable(struct mmc_host *host);
int mmc_host_lazy_disable(struct mmc_host *host); int mmc_host_lazy_disable(struct mmc_host *host);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#define MMC_ALL_SEND_CID 2 /* bcr R2 */ #define MMC_ALL_SEND_CID 2 /* bcr R2 */
#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ #define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */
#define MMC_SET_DSR 4 /* bc [31:16] RCA */ #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_SWITCH 6 /* ac [31:0] See below R1b */
#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ #define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */
#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ #define MMC_SEND_EXT_CSD 8 /* adtc R1 */
...@@ -254,6 +255,7 @@ struct _mmc_csd { ...@@ -254,6 +255,7 @@ struct _mmc_csd {
#define EXT_CSD_CARD_TYPE 196 /* RO */ #define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_REV 192 /* RO */ #define EXT_CSD_REV 192 /* RO */
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
#define EXT_CSD_S_A_TIMEOUT 217
/* /*
* EXT_CSD field definitions * EXT_CSD field definitions
......
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