diff options
author | Jarkko Lavinen <jarkko.lavinen@nokia.com> | 2009-09-22 19:44:34 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 10:39:33 -0400 |
commit | b1ebe38456f7fe61a88af2844361e763ac6ea5ae (patch) | |
tree | b45a2e7afe4f1f6d66d6d38dd1627fa2d6c0e7c7 /drivers/mmc/core | |
parent | eae1aeeed852aae37621b82a9e7f6c05096a18fd (diff) |
mmc: add mmc card sleep and awake support
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>
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/core.c | 40 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 2 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 54 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.c | 36 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.h | 1 |
5 files changed, 128 insertions, 5 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index be1fc013fbe9..828e60ea528c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -1185,6 +1185,46 @@ void mmc_power_restore_host(struct mmc_host *host) | |||
1185 | } | 1185 | } |
1186 | EXPORT_SYMBOL(mmc_power_restore_host); | 1186 | EXPORT_SYMBOL(mmc_power_restore_host); |
1187 | 1187 | ||
1188 | int mmc_card_awake(struct mmc_host *host) | ||
1189 | { | ||
1190 | int err = -ENOSYS; | ||
1191 | |||
1192 | mmc_bus_get(host); | ||
1193 | |||
1194 | if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) | ||
1195 | err = host->bus_ops->awake(host); | ||
1196 | |||
1197 | mmc_bus_put(host); | ||
1198 | |||
1199 | return err; | ||
1200 | } | ||
1201 | EXPORT_SYMBOL(mmc_card_awake); | ||
1202 | |||
1203 | int mmc_card_sleep(struct mmc_host *host) | ||
1204 | { | ||
1205 | int err = -ENOSYS; | ||
1206 | |||
1207 | mmc_bus_get(host); | ||
1208 | |||
1209 | if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) | ||
1210 | err = host->bus_ops->sleep(host); | ||
1211 | |||
1212 | mmc_bus_put(host); | ||
1213 | |||
1214 | return err; | ||
1215 | } | ||
1216 | EXPORT_SYMBOL(mmc_card_sleep); | ||
1217 | |||
1218 | int mmc_card_can_sleep(struct mmc_host *host) | ||
1219 | { | ||
1220 | struct mmc_card *card = host->card; | ||
1221 | |||
1222 | if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3) | ||
1223 | return 1; | ||
1224 | return 0; | ||
1225 | } | ||
1226 | EXPORT_SYMBOL(mmc_card_can_sleep); | ||
1227 | |||
1188 | #ifdef CONFIG_PM | 1228 | #ifdef CONFIG_PM |
1189 | 1229 | ||
1190 | /** | 1230 | /** |
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index f7eb4c4ca014..c386348f5f73 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h | |||
@@ -16,6 +16,8 @@ | |||
16 | #define MMC_CMD_RETRIES 3 | 16 | #define MMC_CMD_RETRIES 3 |
17 | 17 | ||
18 | struct mmc_bus_ops { | 18 | struct mmc_bus_ops { |
19 | int (*awake)(struct mmc_host *); | ||
20 | int (*sleep)(struct mmc_host *); | ||
19 | void (*remove)(struct mmc_host *); | 21 | void (*remove)(struct mmc_host *); |
20 | void (*detect)(struct mmc_host *); | 22 | void (*detect)(struct mmc_host *); |
21 | void (*suspend)(struct mmc_host *); | 23 | void (*suspend)(struct mmc_host *); |
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 27e842df6a6f..e0bfa9515c8a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c | |||
@@ -160,7 +160,6 @@ static int mmc_read_ext_csd(struct mmc_card *card) | |||
160 | { | 160 | { |
161 | int err; | 161 | int err; |
162 | u8 *ext_csd; | 162 | u8 *ext_csd; |
163 | unsigned int ext_csd_struct; | ||
164 | 163 | ||
165 | BUG_ON(!card); | 164 | BUG_ON(!card); |
166 | 165 | ||
@@ -207,16 +206,16 @@ static int mmc_read_ext_csd(struct mmc_card *card) | |||
207 | goto out; | 206 | goto out; |
208 | } | 207 | } |
209 | 208 | ||
210 | ext_csd_struct = ext_csd[EXT_CSD_REV]; | 209 | card->ext_csd.rev = ext_csd[EXT_CSD_REV]; |
211 | if (ext_csd_struct > 3) { | 210 | if (card->ext_csd.rev > 3) { |
212 | printk(KERN_ERR "%s: unrecognised EXT_CSD structure " | 211 | printk(KERN_ERR "%s: unrecognised EXT_CSD structure " |
213 | "version %d\n", mmc_hostname(card->host), | 212 | "version %d\n", mmc_hostname(card->host), |
214 | ext_csd_struct); | 213 | card->ext_csd.rev); |
215 | err = -EINVAL; | 214 | err = -EINVAL; |
216 | goto out; | 215 | goto out; |
217 | } | 216 | } |
218 | 217 | ||
219 | if (ext_csd_struct >= 2) { | 218 | if (card->ext_csd.rev >= 2) { |
220 | card->ext_csd.sectors = | 219 | card->ext_csd.sectors = |
221 | ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | | 220 | ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | |
222 | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | | 221 | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | |
@@ -241,6 +240,15 @@ static int mmc_read_ext_csd(struct mmc_card *card) | |||
241 | goto out; | 240 | goto out; |
242 | } | 241 | } |
243 | 242 | ||
243 | if (card->ext_csd.rev >= 3) { | ||
244 | u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT]; | ||
245 | |||
246 | /* Sleep / awake timeout in 100ns units */ | ||
247 | if (sa_shift > 0 && sa_shift <= 0x17) | ||
248 | card->ext_csd.sa_timeout = | ||
249 | 1 << ext_csd[EXT_CSD_S_A_TIMEOUT]; | ||
250 | } | ||
251 | |||
244 | out: | 252 | out: |
245 | kfree(ext_csd); | 253 | kfree(ext_csd); |
246 | 254 | ||
@@ -557,9 +565,41 @@ static void mmc_power_restore(struct mmc_host *host) | |||
557 | mmc_release_host(host); | 565 | mmc_release_host(host); |
558 | } | 566 | } |
559 | 567 | ||
568 | static int mmc_sleep(struct mmc_host *host) | ||
569 | { | ||
570 | struct mmc_card *card = host->card; | ||
571 | int err = -ENOSYS; | ||
572 | |||
573 | if (card && card->ext_csd.rev >= 3) { | ||
574 | err = mmc_card_sleepawake(host, 1); | ||
575 | if (err < 0) | ||
576 | pr_debug("%s: Error %d while putting card into sleep", | ||
577 | mmc_hostname(host), err); | ||
578 | } | ||
579 | |||
580 | return err; | ||
581 | } | ||
582 | |||
583 | static int mmc_awake(struct mmc_host *host) | ||
584 | { | ||
585 | struct mmc_card *card = host->card; | ||
586 | int err = -ENOSYS; | ||
587 | |||
588 | if (card && card->ext_csd.rev >= 3) { | ||
589 | err = mmc_card_sleepawake(host, 0); | ||
590 | if (err < 0) | ||
591 | pr_debug("%s: Error %d while awaking sleeping card", | ||
592 | mmc_hostname(host), err); | ||
593 | } | ||
594 | |||
595 | return err; | ||
596 | } | ||
597 | |||
560 | #ifdef CONFIG_MMC_UNSAFE_RESUME | 598 | #ifdef CONFIG_MMC_UNSAFE_RESUME |
561 | 599 | ||
562 | static const struct mmc_bus_ops mmc_ops = { | 600 | static const struct mmc_bus_ops mmc_ops = { |
601 | .awake = mmc_awake, | ||
602 | .sleep = mmc_sleep, | ||
563 | .remove = mmc_remove, | 603 | .remove = mmc_remove, |
564 | .detect = mmc_detect, | 604 | .detect = mmc_detect, |
565 | .suspend = mmc_suspend, | 605 | .suspend = mmc_suspend, |
@@ -575,6 +615,8 @@ static void mmc_attach_bus_ops(struct mmc_host *host) | |||
575 | #else | 615 | #else |
576 | 616 | ||
577 | static const struct mmc_bus_ops mmc_ops = { | 617 | static const struct mmc_bus_ops mmc_ops = { |
618 | .awake = mmc_awake, | ||
619 | .sleep = mmc_sleep, | ||
578 | .remove = mmc_remove, | 620 | .remove = mmc_remove, |
579 | .detect = mmc_detect, | 621 | .detect = mmc_detect, |
580 | .suspend = NULL, | 622 | .suspend = NULL, |
@@ -583,6 +625,8 @@ static const struct mmc_bus_ops mmc_ops = { | |||
583 | }; | 625 | }; |
584 | 626 | ||
585 | static const struct mmc_bus_ops mmc_ops_unsafe = { | 627 | static const struct mmc_bus_ops mmc_ops_unsafe = { |
628 | .awake = mmc_awake, | ||
629 | .sleep = mmc_sleep, | ||
586 | .remove = mmc_remove, | 630 | .remove = mmc_remove, |
587 | .detect = mmc_detect, | 631 | .detect = mmc_detect, |
588 | .suspend = mmc_suspend, | 632 | .suspend = mmc_suspend, |
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 34ce2703d29a..355c6042cf65 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c | |||
@@ -57,6 +57,42 @@ int mmc_deselect_cards(struct mmc_host *host) | |||
57 | return _mmc_select_card(host, NULL); | 57 | return _mmc_select_card(host, NULL); |
58 | } | 58 | } |
59 | 59 | ||
60 | int mmc_card_sleepawake(struct mmc_host *host, int sleep) | ||
61 | { | ||
62 | struct mmc_command cmd; | ||
63 | struct mmc_card *card = host->card; | ||
64 | int err; | ||
65 | |||
66 | if (sleep) | ||
67 | mmc_deselect_cards(host); | ||
68 | |||
69 | memset(&cmd, 0, sizeof(struct mmc_command)); | ||
70 | |||
71 | cmd.opcode = MMC_SLEEP_AWAKE; | ||
72 | cmd.arg = card->rca << 16; | ||
73 | if (sleep) | ||
74 | cmd.arg |= 1 << 15; | ||
75 | |||
76 | cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; | ||
77 | err = mmc_wait_for_cmd(host, &cmd, 0); | ||
78 | if (err) | ||
79 | return err; | ||
80 | |||
81 | /* | ||
82 | * If the host does not wait while the card signals busy, then we will | ||
83 | * will have to wait the sleep/awake timeout. Note, we cannot use the | ||
84 | * SEND_STATUS command to poll the status because that command (and most | ||
85 | * others) is invalid while the card sleeps. | ||
86 | */ | ||
87 | if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) | ||
88 | mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000)); | ||
89 | |||
90 | if (!sleep) | ||
91 | err = mmc_select_card(card); | ||
92 | |||
93 | return err; | ||
94 | } | ||
95 | |||
60 | int mmc_go_idle(struct mmc_host *host) | 96 | int mmc_go_idle(struct mmc_host *host) |
61 | { | 97 | { |
62 | int err; | 98 | int err; |
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 17854bf7cf0d..653eb8e84178 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h | |||
@@ -25,6 +25,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status); | |||
25 | int mmc_send_cid(struct mmc_host *host, u32 *cid); | 25 | int mmc_send_cid(struct mmc_host *host, u32 *cid); |
26 | int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); | 26 | int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); |
27 | int mmc_spi_set_crc(struct mmc_host *host, int use_crc); | 27 | int mmc_spi_set_crc(struct mmc_host *host, int use_crc); |
28 | int mmc_card_sleepawake(struct mmc_host *host, int sleep); | ||
28 | 29 | ||
29 | #endif | 30 | #endif |
30 | 31 | ||