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 | |
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>
-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 | ||||
-rw-r--r-- | include/linux/mmc/card.h | 2 | ||||
-rw-r--r-- | include/linux/mmc/host.h | 5 | ||||
-rw-r--r-- | include/linux/mmc/mmc.h | 2 |
8 files changed, 137 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 | ||
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 403aa505f27e..58f59174c64b 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h | |||
@@ -40,6 +40,8 @@ struct mmc_csd { | |||
40 | }; | 40 | }; |
41 | 41 | ||
42 | struct mmc_ext_csd { | 42 | struct mmc_ext_csd { |
43 | u8 rev; | ||
44 | unsigned int sa_timeout; /* Units: 100ns */ | ||
43 | unsigned int hs_max_dtr; | 45 | unsigned int hs_max_dtr; |
44 | unsigned int sectors; | 46 | unsigned int sectors; |
45 | }; | 47 | }; |
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c1cbe598d470..81bb42358595 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h | |||
@@ -149,6 +149,7 @@ struct mmc_host { | |||
149 | #define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */ | 149 | #define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */ |
150 | #define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */ | 150 | #define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */ |
151 | #define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ | 151 | #define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ |
152 | #define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ | ||
152 | 153 | ||
153 | /* host specific block data */ | 154 | /* host specific block data */ |
154 | unsigned int max_seg_size; /* see blk_queue_max_segment_size */ | 155 | unsigned int max_seg_size; /* see blk_queue_max_segment_size */ |
@@ -240,6 +241,10 @@ struct regulator; | |||
240 | int mmc_regulator_get_ocrmask(struct regulator *supply); | 241 | int mmc_regulator_get_ocrmask(struct regulator *supply); |
241 | int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit); | 242 | int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit); |
242 | 243 | ||
244 | int mmc_card_awake(struct mmc_host *host); | ||
245 | int mmc_card_sleep(struct mmc_host *host); | ||
246 | int mmc_card_can_sleep(struct mmc_host *host); | ||
247 | |||
243 | int mmc_host_enable(struct mmc_host *host); | 248 | int mmc_host_enable(struct mmc_host *host); |
244 | int mmc_host_disable(struct mmc_host *host); | 249 | int mmc_host_disable(struct mmc_host *host); |
245 | int mmc_host_lazy_disable(struct mmc_host *host); | 250 | int mmc_host_lazy_disable(struct mmc_host *host); |
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 14b81f3e5232..b2b40951c16c 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h | |||
@@ -31,6 +31,7 @@ | |||
31 | #define MMC_ALL_SEND_CID 2 /* bcr R2 */ | 31 | #define MMC_ALL_SEND_CID 2 /* bcr R2 */ |
32 | #define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ | 32 | #define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ |
33 | #define MMC_SET_DSR 4 /* bc [31:16] RCA */ | 33 | #define MMC_SET_DSR 4 /* bc [31:16] RCA */ |
34 | #define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */ | ||
34 | #define MMC_SWITCH 6 /* ac [31:0] See below R1b */ | 35 | #define MMC_SWITCH 6 /* ac [31:0] See below R1b */ |
35 | #define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ | 36 | #define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ |
36 | #define MMC_SEND_EXT_CSD 8 /* adtc R1 */ | 37 | #define MMC_SEND_EXT_CSD 8 /* adtc R1 */ |
@@ -254,6 +255,7 @@ struct _mmc_csd { | |||
254 | #define EXT_CSD_CARD_TYPE 196 /* RO */ | 255 | #define EXT_CSD_CARD_TYPE 196 /* RO */ |
255 | #define EXT_CSD_REV 192 /* RO */ | 256 | #define EXT_CSD_REV 192 /* RO */ |
256 | #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ | 257 | #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ |
258 | #define EXT_CSD_S_A_TIMEOUT 217 | ||
257 | 259 | ||
258 | /* | 260 | /* |
259 | * EXT_CSD field definitions | 261 | * EXT_CSD field definitions |