diff options
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/Kconfig | 4 | ||||
-rw-r--r-- | drivers/mmc/core/bus.c | 1 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 29 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 2 | ||||
-rw-r--r-- | drivers/mmc/core/debugfs.c | 1 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 1 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 29 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.c | 1 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 22 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 69 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_bus.c | 8 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_cis.c | 168 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_io.c | 56 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_ops.c | 36 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_ops.h | 1 |
15 files changed, 286 insertions, 142 deletions
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index ab37a6d9d32a..bb22ffd76ef8 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig | |||
@@ -3,7 +3,7 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | config MMC_UNSAFE_RESUME | 5 | config MMC_UNSAFE_RESUME |
6 | bool "Allow unsafe resume (DANGEROUS)" | 6 | bool "Assume MMC/SD cards are non-removable (DANGEROUS)" |
7 | help | 7 | help |
8 | If you say Y here, the MMC layer will assume that all cards | 8 | If you say Y here, the MMC layer will assume that all cards |
9 | stayed in their respective slots during the suspend. The | 9 | stayed in their respective slots during the suspend. The |
@@ -14,3 +14,5 @@ config MMC_UNSAFE_RESUME | |||
14 | This option is usually just for embedded systems which use | 14 | This option is usually just for embedded systems which use |
15 | a MMC/SD card for rootfs. Most people should say N here. | 15 | a MMC/SD card for rootfs. Most people should say N here. |
16 | 16 | ||
17 | This option sets a default which can be overridden by the | ||
18 | module parameter "removable=0" or "removable=1". | ||
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index bdb165f93046..49d9dcaeca49 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c | |||
@@ -13,6 +13,7 @@ | |||
13 | 13 | ||
14 | #include <linux/device.h> | 14 | #include <linux/device.h> |
15 | #include <linux/err.h> | 15 | #include <linux/err.h> |
16 | #include <linux/slab.h> | ||
16 | 17 | ||
17 | #include <linux/mmc/card.h> | 18 | #include <linux/mmc/card.h> |
18 | #include <linux/mmc/host.h> | 19 | #include <linux/mmc/host.h> |
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 7dab2e5f4bc9..3168ebd616b2 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -48,6 +48,22 @@ int use_spi_crc = 1; | |||
48 | module_param(use_spi_crc, bool, 0); | 48 | module_param(use_spi_crc, bool, 0); |
49 | 49 | ||
50 | /* | 50 | /* |
51 | * We normally treat cards as removed during suspend if they are not | ||
52 | * known to be on a non-removable bus, to avoid the risk of writing | ||
53 | * back data to a different card after resume. Allow this to be | ||
54 | * overridden if necessary. | ||
55 | */ | ||
56 | #ifdef CONFIG_MMC_UNSAFE_RESUME | ||
57 | int mmc_assume_removable; | ||
58 | #else | ||
59 | int mmc_assume_removable = 1; | ||
60 | #endif | ||
61 | module_param_named(removable, mmc_assume_removable, bool, 0644); | ||
62 | MODULE_PARM_DESC( | ||
63 | removable, | ||
64 | "MMC/SD cards are removable and may be removed during suspend"); | ||
65 | |||
66 | /* | ||
51 | * Internal function. Schedule delayed work in the MMC work queue. | 67 | * Internal function. Schedule delayed work in the MMC work queue. |
52 | */ | 68 | */ |
53 | static int mmc_schedule_delayed_work(struct delayed_work *work, | 69 | static int mmc_schedule_delayed_work(struct delayed_work *work, |
@@ -1073,6 +1089,7 @@ void mmc_rescan(struct work_struct *work) | |||
1073 | mmc_claim_host(host); | 1089 | mmc_claim_host(host); |
1074 | 1090 | ||
1075 | mmc_power_up(host); | 1091 | mmc_power_up(host); |
1092 | sdio_reset(host); | ||
1076 | mmc_go_idle(host); | 1093 | mmc_go_idle(host); |
1077 | 1094 | ||
1078 | mmc_send_if_cond(host, host->ocr_avail); | 1095 | mmc_send_if_cond(host, host->ocr_avail); |
@@ -1135,6 +1152,9 @@ void mmc_stop_host(struct mmc_host *host) | |||
1135 | cancel_delayed_work(&host->detect); | 1152 | cancel_delayed_work(&host->detect); |
1136 | mmc_flush_scheduled_work(); | 1153 | mmc_flush_scheduled_work(); |
1137 | 1154 | ||
1155 | /* clear pm flags now and let card drivers set them as needed */ | ||
1156 | host->pm_flags = 0; | ||
1157 | |||
1138 | mmc_bus_get(host); | 1158 | mmc_bus_get(host); |
1139 | if (host->bus_ops && !host->bus_dead) { | 1159 | if (host->bus_ops && !host->bus_dead) { |
1140 | if (host->bus_ops->remove) | 1160 | if (host->bus_ops->remove) |
@@ -1257,12 +1277,13 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state) | |||
1257 | mmc_claim_host(host); | 1277 | mmc_claim_host(host); |
1258 | mmc_detach_bus(host); | 1278 | mmc_detach_bus(host); |
1259 | mmc_release_host(host); | 1279 | mmc_release_host(host); |
1280 | host->pm_flags = 0; | ||
1260 | err = 0; | 1281 | err = 0; |
1261 | } | 1282 | } |
1262 | } | 1283 | } |
1263 | mmc_bus_put(host); | 1284 | mmc_bus_put(host); |
1264 | 1285 | ||
1265 | if (!err) | 1286 | if (!err && !(host->pm_flags & MMC_PM_KEEP_POWER)) |
1266 | mmc_power_off(host); | 1287 | mmc_power_off(host); |
1267 | 1288 | ||
1268 | return err; | 1289 | return err; |
@@ -1280,8 +1301,10 @@ int mmc_resume_host(struct mmc_host *host) | |||
1280 | 1301 | ||
1281 | mmc_bus_get(host); | 1302 | mmc_bus_get(host); |
1282 | if (host->bus_ops && !host->bus_dead) { | 1303 | if (host->bus_ops && !host->bus_dead) { |
1283 | mmc_power_up(host); | 1304 | if (!(host->pm_flags & MMC_PM_KEEP_POWER)) { |
1284 | mmc_select_voltage(host, host->ocr); | 1305 | mmc_power_up(host); |
1306 | mmc_select_voltage(host, host->ocr); | ||
1307 | } | ||
1285 | BUG_ON(!host->bus_ops->resume); | 1308 | BUG_ON(!host->bus_ops->resume); |
1286 | err = host->bus_ops->resume(host); | 1309 | err = host->bus_ops->resume(host); |
1287 | if (err) { | 1310 | if (err) { |
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 67ae6abc4230..a811c52a1659 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h | |||
@@ -54,7 +54,9 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr); | |||
54 | int mmc_attach_sd(struct mmc_host *host, u32 ocr); | 54 | int mmc_attach_sd(struct mmc_host *host, u32 ocr); |
55 | int mmc_attach_sdio(struct mmc_host *host, u32 ocr); | 55 | int mmc_attach_sdio(struct mmc_host *host, u32 ocr); |
56 | 56 | ||
57 | /* Module parameters */ | ||
57 | extern int use_spi_crc; | 58 | extern int use_spi_crc; |
59 | extern int mmc_assume_removable; | ||
58 | 60 | ||
59 | /* Debugfs information for hosts and cards */ | 61 | /* Debugfs information for hosts and cards */ |
60 | void mmc_add_host_debugfs(struct mmc_host *host); | 62 | void mmc_add_host_debugfs(struct mmc_host *host); |
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 96d10f40fb23..53cb380c0987 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/debugfs.h> | 10 | #include <linux/debugfs.h> |
11 | #include <linux/fs.h> | 11 | #include <linux/fs.h> |
12 | #include <linux/seq_file.h> | 12 | #include <linux/seq_file.h> |
13 | #include <linux/slab.h> | ||
13 | #include <linux/stat.h> | 14 | #include <linux/stat.h> |
14 | 15 | ||
15 | #include <linux/mmc/card.h> | 16 | #include <linux/mmc/card.h> |
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index a268d12f1af0..47353909e345 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/idr.h> | 16 | #include <linux/idr.h> |
17 | #include <linux/pagemap.h> | 17 | #include <linux/pagemap.h> |
18 | #include <linux/leds.h> | 18 | #include <linux/leds.h> |
19 | #include <linux/slab.h> | ||
19 | 20 | ||
20 | #include <linux/mmc/host.h> | 21 | #include <linux/mmc/host.h> |
21 | 22 | ||
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index bfefce365ae7..89f7a25b7ac1 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c | |||
@@ -11,6 +11,7 @@ | |||
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/err.h> | 13 | #include <linux/err.h> |
14 | #include <linux/slab.h> | ||
14 | 15 | ||
15 | #include <linux/mmc/host.h> | 16 | #include <linux/mmc/host.h> |
16 | #include <linux/mmc/card.h> | 17 | #include <linux/mmc/card.h> |
@@ -207,7 +208,7 @@ static int mmc_read_ext_csd(struct mmc_card *card) | |||
207 | } | 208 | } |
208 | 209 | ||
209 | card->ext_csd.rev = ext_csd[EXT_CSD_REV]; | 210 | card->ext_csd.rev = ext_csd[EXT_CSD_REV]; |
210 | if (card->ext_csd.rev > 3) { | 211 | if (card->ext_csd.rev > 5) { |
211 | printk(KERN_ERR "%s: unrecognised EXT_CSD structure " | 212 | printk(KERN_ERR "%s: unrecognised EXT_CSD structure " |
212 | "version %d\n", mmc_hostname(card->host), | 213 | "version %d\n", mmc_hostname(card->host), |
213 | card->ext_csd.rev); | 214 | card->ext_csd.rev); |
@@ -225,7 +226,7 @@ static int mmc_read_ext_csd(struct mmc_card *card) | |||
225 | mmc_card_set_blockaddr(card); | 226 | mmc_card_set_blockaddr(card); |
226 | } | 227 | } |
227 | 228 | ||
228 | switch (ext_csd[EXT_CSD_CARD_TYPE]) { | 229 | switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { |
229 | case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: | 230 | case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: |
230 | card->ext_csd.hs_max_dtr = 52000000; | 231 | card->ext_csd.hs_max_dtr = 52000000; |
231 | break; | 232 | break; |
@@ -237,7 +238,6 @@ static int mmc_read_ext_csd(struct mmc_card *card) | |||
237 | printk(KERN_WARNING "%s: card is mmc v4 but doesn't " | 238 | printk(KERN_WARNING "%s: card is mmc v4 but doesn't " |
238 | "support any high-speed modes.\n", | 239 | "support any high-speed modes.\n", |
239 | mmc_hostname(card->host)); | 240 | mmc_hostname(card->host)); |
240 | goto out; | ||
241 | } | 241 | } |
242 | 242 | ||
243 | if (card->ext_csd.rev >= 3) { | 243 | if (card->ext_csd.rev >= 3) { |
@@ -602,25 +602,6 @@ static int mmc_awake(struct mmc_host *host) | |||
602 | return err; | 602 | return err; |
603 | } | 603 | } |
604 | 604 | ||
605 | #ifdef CONFIG_MMC_UNSAFE_RESUME | ||
606 | |||
607 | static const struct mmc_bus_ops mmc_ops = { | ||
608 | .awake = mmc_awake, | ||
609 | .sleep = mmc_sleep, | ||
610 | .remove = mmc_remove, | ||
611 | .detect = mmc_detect, | ||
612 | .suspend = mmc_suspend, | ||
613 | .resume = mmc_resume, | ||
614 | .power_restore = mmc_power_restore, | ||
615 | }; | ||
616 | |||
617 | static void mmc_attach_bus_ops(struct mmc_host *host) | ||
618 | { | ||
619 | mmc_attach_bus(host, &mmc_ops); | ||
620 | } | ||
621 | |||
622 | #else | ||
623 | |||
624 | static const struct mmc_bus_ops mmc_ops = { | 605 | static const struct mmc_bus_ops mmc_ops = { |
625 | .awake = mmc_awake, | 606 | .awake = mmc_awake, |
626 | .sleep = mmc_sleep, | 607 | .sleep = mmc_sleep, |
@@ -645,15 +626,13 @@ static void mmc_attach_bus_ops(struct mmc_host *host) | |||
645 | { | 626 | { |
646 | const struct mmc_bus_ops *bus_ops; | 627 | const struct mmc_bus_ops *bus_ops; |
647 | 628 | ||
648 | if (host->caps & MMC_CAP_NONREMOVABLE) | 629 | if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable) |
649 | bus_ops = &mmc_ops_unsafe; | 630 | bus_ops = &mmc_ops_unsafe; |
650 | else | 631 | else |
651 | bus_ops = &mmc_ops; | 632 | bus_ops = &mmc_ops; |
652 | mmc_attach_bus(host, bus_ops); | 633 | mmc_attach_bus(host, bus_ops); |
653 | } | 634 | } |
654 | 635 | ||
655 | #endif | ||
656 | |||
657 | /* | 636 | /* |
658 | * Starting point for MMC card init. | 637 | * Starting point for MMC card init. |
659 | */ | 638 | */ |
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index d2cb5c634392..326447c9ede8 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c | |||
@@ -9,6 +9,7 @@ | |||
9 | * your option) any later version. | 9 | * your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/slab.h> | ||
12 | #include <linux/types.h> | 13 | #include <linux/types.h> |
13 | #include <linux/scatterlist.h> | 14 | #include <linux/scatterlist.h> |
14 | 15 | ||
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 10b2a4d20f5a..5eac21df4809 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c | |||
@@ -11,6 +11,7 @@ | |||
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/err.h> | 13 | #include <linux/err.h> |
14 | #include <linux/slab.h> | ||
14 | 15 | ||
15 | #include <linux/mmc/host.h> | 16 | #include <linux/mmc/host.h> |
16 | #include <linux/mmc/card.h> | 17 | #include <linux/mmc/card.h> |
@@ -606,23 +607,6 @@ static void mmc_sd_power_restore(struct mmc_host *host) | |||
606 | mmc_release_host(host); | 607 | mmc_release_host(host); |
607 | } | 608 | } |
608 | 609 | ||
609 | #ifdef CONFIG_MMC_UNSAFE_RESUME | ||
610 | |||
611 | static const struct mmc_bus_ops mmc_sd_ops = { | ||
612 | .remove = mmc_sd_remove, | ||
613 | .detect = mmc_sd_detect, | ||
614 | .suspend = mmc_sd_suspend, | ||
615 | .resume = mmc_sd_resume, | ||
616 | .power_restore = mmc_sd_power_restore, | ||
617 | }; | ||
618 | |||
619 | static void mmc_sd_attach_bus_ops(struct mmc_host *host) | ||
620 | { | ||
621 | mmc_attach_bus(host, &mmc_sd_ops); | ||
622 | } | ||
623 | |||
624 | #else | ||
625 | |||
626 | static const struct mmc_bus_ops mmc_sd_ops = { | 610 | static const struct mmc_bus_ops mmc_sd_ops = { |
627 | .remove = mmc_sd_remove, | 611 | .remove = mmc_sd_remove, |
628 | .detect = mmc_sd_detect, | 612 | .detect = mmc_sd_detect, |
@@ -643,15 +627,13 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host) | |||
643 | { | 627 | { |
644 | const struct mmc_bus_ops *bus_ops; | 628 | const struct mmc_bus_ops *bus_ops; |
645 | 629 | ||
646 | if (host->caps & MMC_CAP_NONREMOVABLE) | 630 | if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable) |
647 | bus_ops = &mmc_sd_ops_unsafe; | 631 | bus_ops = &mmc_sd_ops_unsafe; |
648 | else | 632 | else |
649 | bus_ops = &mmc_sd_ops; | 633 | bus_ops = &mmc_sd_ops; |
650 | mmc_attach_bus(host, bus_ops); | 634 | mmc_attach_bus(host, bus_ops); |
651 | } | 635 | } |
652 | 636 | ||
653 | #endif | ||
654 | |||
655 | /* | 637 | /* |
656 | * Starting point for SD card init. | 638 | * Starting point for SD card init. |
657 | */ | 639 | */ |
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index cdb845b68ab5..2dd4cfe7ca17 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c | |||
@@ -188,6 +188,40 @@ static int sdio_disable_cd(struct mmc_card *card) | |||
188 | } | 188 | } |
189 | 189 | ||
190 | /* | 190 | /* |
191 | * Devices that remain active during a system suspend are | ||
192 | * put back into 1-bit mode. | ||
193 | */ | ||
194 | static int sdio_disable_wide(struct mmc_card *card) | ||
195 | { | ||
196 | int ret; | ||
197 | u8 ctrl; | ||
198 | |||
199 | if (!(card->host->caps & MMC_CAP_4_BIT_DATA)) | ||
200 | return 0; | ||
201 | |||
202 | if (card->cccr.low_speed && !card->cccr.wide_bus) | ||
203 | return 0; | ||
204 | |||
205 | ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); | ||
206 | if (ret) | ||
207 | return ret; | ||
208 | |||
209 | if (!(ctrl & SDIO_BUS_WIDTH_4BIT)) | ||
210 | return 0; | ||
211 | |||
212 | ctrl &= ~SDIO_BUS_WIDTH_4BIT; | ||
213 | ctrl |= SDIO_BUS_ASYNC_INT; | ||
214 | |||
215 | ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL); | ||
216 | if (ret) | ||
217 | return ret; | ||
218 | |||
219 | mmc_set_bus_width(card->host, MMC_BUS_WIDTH_1); | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | /* | ||
191 | * Test if the card supports high-speed mode and, if so, switch to it. | 225 | * Test if the card supports high-speed mode and, if so, switch to it. |
192 | */ | 226 | */ |
193 | static int sdio_enable_hs(struct mmc_card *card) | 227 | static int sdio_enable_hs(struct mmc_card *card) |
@@ -224,7 +258,7 @@ static int sdio_enable_hs(struct mmc_card *card) | |||
224 | * we're trying to reinitialise. | 258 | * we're trying to reinitialise. |
225 | */ | 259 | */ |
226 | static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, | 260 | static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, |
227 | struct mmc_card *oldcard) | 261 | struct mmc_card *oldcard, int powered_resume) |
228 | { | 262 | { |
229 | struct mmc_card *card; | 263 | struct mmc_card *card; |
230 | int err; | 264 | int err; |
@@ -235,9 +269,11 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, | |||
235 | /* | 269 | /* |
236 | * Inform the card of the voltage | 270 | * Inform the card of the voltage |
237 | */ | 271 | */ |
238 | err = mmc_send_io_op_cond(host, host->ocr, &ocr); | 272 | if (!powered_resume) { |
239 | if (err) | 273 | err = mmc_send_io_op_cond(host, host->ocr, &ocr); |
240 | goto err; | 274 | if (err) |
275 | goto err; | ||
276 | } | ||
241 | 277 | ||
242 | /* | 278 | /* |
243 | * For SPI, enable CRC as appropriate. | 279 | * For SPI, enable CRC as appropriate. |
@@ -262,7 +298,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, | |||
262 | /* | 298 | /* |
263 | * For native busses: set card RCA and quit open drain mode. | 299 | * For native busses: set card RCA and quit open drain mode. |
264 | */ | 300 | */ |
265 | if (!mmc_host_is_spi(host)) { | 301 | if (!powered_resume && !mmc_host_is_spi(host)) { |
266 | err = mmc_send_relative_addr(host, &card->rca); | 302 | err = mmc_send_relative_addr(host, &card->rca); |
267 | if (err) | 303 | if (err) |
268 | goto remove; | 304 | goto remove; |
@@ -273,7 +309,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, | |||
273 | /* | 309 | /* |
274 | * Select card, as all following commands rely on that. | 310 | * Select card, as all following commands rely on that. |
275 | */ | 311 | */ |
276 | if (!mmc_host_is_spi(host)) { | 312 | if (!powered_resume && !mmc_host_is_spi(host)) { |
277 | err = mmc_select_card(card); | 313 | err = mmc_select_card(card); |
278 | if (err) | 314 | if (err) |
279 | goto remove; | 315 | goto remove; |
@@ -425,6 +461,12 @@ static int mmc_sdio_suspend(struct mmc_host *host) | |||
425 | } | 461 | } |
426 | } | 462 | } |
427 | 463 | ||
464 | if (!err && host->pm_flags & MMC_PM_KEEP_POWER) { | ||
465 | mmc_claim_host(host); | ||
466 | sdio_disable_wide(host->card); | ||
467 | mmc_release_host(host); | ||
468 | } | ||
469 | |||
428 | return err; | 470 | return err; |
429 | } | 471 | } |
430 | 472 | ||
@@ -437,7 +479,13 @@ static int mmc_sdio_resume(struct mmc_host *host) | |||
437 | 479 | ||
438 | /* Basic card reinitialization. */ | 480 | /* Basic card reinitialization. */ |
439 | mmc_claim_host(host); | 481 | mmc_claim_host(host); |
440 | err = mmc_sdio_init_card(host, host->ocr, host->card); | 482 | err = mmc_sdio_init_card(host, host->ocr, host->card, |
483 | (host->pm_flags & MMC_PM_KEEP_POWER)); | ||
484 | if (!err) | ||
485 | /* We may have switched to 1-bit mode during suspend. */ | ||
486 | err = sdio_enable_wide(host->card); | ||
487 | if (!err && host->sdio_irqs) | ||
488 | mmc_signal_sdio_irq(host); | ||
441 | mmc_release_host(host); | 489 | mmc_release_host(host); |
442 | 490 | ||
443 | /* | 491 | /* |
@@ -507,7 +555,7 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | |||
507 | /* | 555 | /* |
508 | * Detect and init the card. | 556 | * Detect and init the card. |
509 | */ | 557 | */ |
510 | err = mmc_sdio_init_card(host, host->ocr, NULL); | 558 | err = mmc_sdio_init_card(host, host->ocr, NULL, 0); |
511 | if (err) | 559 | if (err) |
512 | goto err; | 560 | goto err; |
513 | card = host->card; | 561 | card = host->card; |
@@ -516,7 +564,8 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | |||
516 | * The number of functions on the card is encoded inside | 564 | * The number of functions on the card is encoded inside |
517 | * the ocr. | 565 | * the ocr. |
518 | */ | 566 | */ |
519 | card->sdio_funcs = funcs = (ocr & 0x70000000) >> 28; | 567 | funcs = (ocr & 0x70000000) >> 28; |
568 | card->sdio_funcs = 0; | ||
520 | 569 | ||
521 | /* | 570 | /* |
522 | * If needed, disconnect card detection pull-up resistor. | 571 | * If needed, disconnect card detection pull-up resistor. |
@@ -528,7 +577,7 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | |||
528 | /* | 577 | /* |
529 | * Initialize (but don't add) all present functions. | 578 | * Initialize (but don't add) all present functions. |
530 | */ | 579 | */ |
531 | for (i = 0;i < funcs;i++) { | 580 | for (i = 0; i < funcs; i++, card->sdio_funcs++) { |
532 | err = sdio_init_func(host->card, i + 1); | 581 | err = sdio_init_func(host->card, i + 1); |
533 | if (err) | 582 | if (err) |
534 | goto remove; | 583 | goto remove; |
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index d37464e296a5..4a890dcb95ab 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c | |||
@@ -13,6 +13,7 @@ | |||
13 | 13 | ||
14 | #include <linux/device.h> | 14 | #include <linux/device.h> |
15 | #include <linux/err.h> | 15 | #include <linux/err.h> |
16 | #include <linux/slab.h> | ||
16 | 17 | ||
17 | #include <linux/mmc/card.h> | 18 | #include <linux/mmc/card.h> |
18 | #include <linux/mmc/sdio_func.h> | 19 | #include <linux/mmc/sdio_func.h> |
@@ -248,12 +249,15 @@ int sdio_add_func(struct sdio_func *func) | |||
248 | /* | 249 | /* |
249 | * Unregister a SDIO function with the driver model, and | 250 | * Unregister a SDIO function with the driver model, and |
250 | * (eventually) free it. | 251 | * (eventually) free it. |
252 | * This function can be called through error paths where sdio_add_func() was | ||
253 | * never executed (because a failure occurred at an earlier point). | ||
251 | */ | 254 | */ |
252 | void sdio_remove_func(struct sdio_func *func) | 255 | void sdio_remove_func(struct sdio_func *func) |
253 | { | 256 | { |
254 | if (sdio_func_present(func)) | 257 | if (!sdio_func_present(func)) |
255 | device_del(&func->dev); | 258 | return; |
256 | 259 | ||
260 | device_del(&func->dev); | ||
257 | put_device(&func->dev); | 261 | put_device(&func->dev); |
258 | } | 262 | } |
259 | 263 | ||
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index f85dcd536508..541bdb89e0c5 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c | |||
@@ -14,6 +14,7 @@ | |||
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/slab.h> | ||
17 | 18 | ||
18 | #include <linux/mmc/host.h> | 19 | #include <linux/mmc/host.h> |
19 | #include <linux/mmc/card.h> | 20 | #include <linux/mmc/card.h> |
@@ -97,26 +98,56 @@ static const unsigned char speed_val[16] = | |||
97 | static const unsigned int speed_unit[8] = | 98 | static const unsigned int speed_unit[8] = |
98 | { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; | 99 | { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; |
99 | 100 | ||
100 | /* FUNCE tuples with these types get passed to SDIO drivers */ | 101 | |
101 | static const unsigned char funce_type_whitelist[] = { | 102 | typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, |
102 | 4 /* CISTPL_FUNCE_LAN_NODE_ID used in Broadcom cards */ | 103 | const unsigned char *, unsigned); |
104 | |||
105 | struct cis_tpl { | ||
106 | unsigned char code; | ||
107 | unsigned char min_size; | ||
108 | tpl_parse_t *parse; | ||
103 | }; | 109 | }; |
104 | 110 | ||
105 | static int cistpl_funce_whitelisted(unsigned char type) | 111 | static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func, |
112 | const char *tpl_descr, | ||
113 | const struct cis_tpl *tpl, int tpl_count, | ||
114 | unsigned char code, | ||
115 | const unsigned char *buf, unsigned size) | ||
106 | { | 116 | { |
107 | int i; | 117 | int i, ret; |
108 | 118 | ||
109 | for (i = 0; i < ARRAY_SIZE(funce_type_whitelist); i++) { | 119 | /* look for a matching code in the table */ |
110 | if (funce_type_whitelist[i] == type) | 120 | for (i = 0; i < tpl_count; i++, tpl++) { |
111 | return 1; | 121 | if (tpl->code == code) |
122 | break; | ||
112 | } | 123 | } |
113 | return 0; | 124 | if (i < tpl_count) { |
125 | if (size >= tpl->min_size) { | ||
126 | if (tpl->parse) | ||
127 | ret = tpl->parse(card, func, buf, size); | ||
128 | else | ||
129 | ret = -EILSEQ; /* known tuple, not parsed */ | ||
130 | } else { | ||
131 | /* invalid tuple */ | ||
132 | ret = -EINVAL; | ||
133 | } | ||
134 | if (ret && ret != -EILSEQ && ret != -ENOENT) { | ||
135 | printk(KERN_ERR "%s: bad %s tuple 0x%02x (%u bytes)\n", | ||
136 | mmc_hostname(card->host), tpl_descr, code, size); | ||
137 | } | ||
138 | } else { | ||
139 | /* unknown tuple */ | ||
140 | ret = -ENOENT; | ||
141 | } | ||
142 | |||
143 | return ret; | ||
114 | } | 144 | } |
115 | 145 | ||
116 | static int cistpl_funce_common(struct mmc_card *card, | 146 | static int cistpl_funce_common(struct mmc_card *card, struct sdio_func *func, |
117 | const unsigned char *buf, unsigned size) | 147 | const unsigned char *buf, unsigned size) |
118 | { | 148 | { |
119 | if (size < 0x04 || buf[0] != 0) | 149 | /* Only valid for the common CIS (function 0) */ |
150 | if (func) | ||
120 | return -EINVAL; | 151 | return -EINVAL; |
121 | 152 | ||
122 | /* TPLFE_FN0_BLK_SIZE */ | 153 | /* TPLFE_FN0_BLK_SIZE */ |
@@ -129,20 +160,24 @@ static int cistpl_funce_common(struct mmc_card *card, | |||
129 | return 0; | 160 | return 0; |
130 | } | 161 | } |
131 | 162 | ||
132 | static int cistpl_funce_func(struct sdio_func *func, | 163 | static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func, |
133 | const unsigned char *buf, unsigned size) | 164 | const unsigned char *buf, unsigned size) |
134 | { | 165 | { |
135 | unsigned vsn; | 166 | unsigned vsn; |
136 | unsigned min_size; | 167 | unsigned min_size; |
137 | 168 | ||
138 | /* let SDIO drivers take care of whitelisted FUNCE tuples */ | 169 | /* Only valid for the individual function's CIS (1-7) */ |
139 | if (cistpl_funce_whitelisted(buf[0])) | 170 | if (!func) |
140 | return -EILSEQ; | 171 | return -EINVAL; |
141 | 172 | ||
173 | /* | ||
174 | * This tuple has a different length depending on the SDIO spec | ||
175 | * version. | ||
176 | */ | ||
142 | vsn = func->card->cccr.sdio_vsn; | 177 | vsn = func->card->cccr.sdio_vsn; |
143 | min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; | 178 | min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; |
144 | 179 | ||
145 | if (size < min_size || buf[0] != 1) | 180 | if (size < min_size) |
146 | return -EINVAL; | 181 | return -EINVAL; |
147 | 182 | ||
148 | /* TPLFE_MAX_BLK_SIZE */ | 183 | /* TPLFE_MAX_BLK_SIZE */ |
@@ -157,39 +192,32 @@ static int cistpl_funce_func(struct sdio_func *func, | |||
157 | return 0; | 192 | return 0; |
158 | } | 193 | } |
159 | 194 | ||
195 | /* | ||
196 | * Known TPLFE_TYPEs table for CISTPL_FUNCE tuples. | ||
197 | * | ||
198 | * Note that, unlike PCMCIA, CISTPL_FUNCE tuples are not parsed depending | ||
199 | * on the TPLFID_FUNCTION value of the previous CISTPL_FUNCID as on SDIO | ||
200 | * TPLFID_FUNCTION is always hardcoded to 0x0C. | ||
201 | */ | ||
202 | static const struct cis_tpl cis_tpl_funce_list[] = { | ||
203 | { 0x00, 4, cistpl_funce_common }, | ||
204 | { 0x01, 0, cistpl_funce_func }, | ||
205 | { 0x04, 1+1+6, /* CISTPL_FUNCE_LAN_NODE_ID */ }, | ||
206 | }; | ||
207 | |||
160 | static int cistpl_funce(struct mmc_card *card, struct sdio_func *func, | 208 | static int cistpl_funce(struct mmc_card *card, struct sdio_func *func, |
161 | const unsigned char *buf, unsigned size) | 209 | const unsigned char *buf, unsigned size) |
162 | { | 210 | { |
163 | int ret; | 211 | if (size < 1) |
164 | 212 | return -EINVAL; | |
165 | /* | ||
166 | * There should be two versions of the CISTPL_FUNCE tuple, | ||
167 | * one for the common CIS (function 0) and a version used by | ||
168 | * the individual function's CIS (1-7). Yet, the later has a | ||
169 | * different length depending on the SDIO spec version. | ||
170 | */ | ||
171 | if (func) | ||
172 | ret = cistpl_funce_func(func, buf, size); | ||
173 | else | ||
174 | ret = cistpl_funce_common(card, buf, size); | ||
175 | |||
176 | if (ret && ret != -EILSEQ) { | ||
177 | printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u " | ||
178 | "type %u\n", mmc_hostname(card->host), size, buf[0]); | ||
179 | } | ||
180 | 213 | ||
181 | return ret; | 214 | return cis_tpl_parse(card, func, "CISTPL_FUNCE", |
215 | cis_tpl_funce_list, | ||
216 | ARRAY_SIZE(cis_tpl_funce_list), | ||
217 | buf[0], buf, size); | ||
182 | } | 218 | } |
183 | 219 | ||
184 | typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, | 220 | /* Known TPL_CODEs table for CIS tuples */ |
185 | const unsigned char *, unsigned); | ||
186 | |||
187 | struct cis_tpl { | ||
188 | unsigned char code; | ||
189 | unsigned char min_size; | ||
190 | tpl_parse_t *parse; | ||
191 | }; | ||
192 | |||
193 | static const struct cis_tpl cis_tpl_list[] = { | 221 | static const struct cis_tpl cis_tpl_list[] = { |
194 | { 0x15, 3, cistpl_vers_1 }, | 222 | { 0x15, 3, cistpl_vers_1 }, |
195 | { 0x20, 4, cistpl_manfid }, | 223 | { 0x20, 4, cistpl_manfid }, |
@@ -268,46 +296,38 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) | |||
268 | break; | 296 | break; |
269 | } | 297 | } |
270 | 298 | ||
271 | for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++) | 299 | /* Try to parse the CIS tuple */ |
272 | if (cis_tpl_list[i].code == tpl_code) | 300 | ret = cis_tpl_parse(card, func, "CIS", |
273 | break; | 301 | cis_tpl_list, ARRAY_SIZE(cis_tpl_list), |
274 | if (i < ARRAY_SIZE(cis_tpl_list)) { | 302 | tpl_code, this->data, tpl_link); |
275 | const struct cis_tpl *tpl = cis_tpl_list + i; | 303 | if (ret == -EILSEQ || ret == -ENOENT) { |
276 | if (tpl_link < tpl->min_size) { | ||
277 | printk(KERN_ERR | ||
278 | "%s: bad CIS tuple 0x%02x" | ||
279 | " (length = %u, expected >= %u)\n", | ||
280 | mmc_hostname(card->host), | ||
281 | tpl_code, tpl_link, tpl->min_size); | ||
282 | ret = -EINVAL; | ||
283 | } else if (tpl->parse) { | ||
284 | ret = tpl->parse(card, func, | ||
285 | this->data, tpl_link); | ||
286 | } | ||
287 | /* | 304 | /* |
288 | * We don't need the tuple anymore if it was | 305 | * The tuple is unknown or known but not parsed. |
289 | * successfully parsed by the SDIO core or if it is | 306 | * Queue the tuple for the function driver. |
290 | * not going to be parsed by SDIO drivers. | ||
291 | */ | 307 | */ |
292 | if (!ret || ret != -EILSEQ) | ||
293 | kfree(this); | ||
294 | } else { | ||
295 | /* unknown tuple */ | ||
296 | ret = -EILSEQ; | ||
297 | } | ||
298 | |||
299 | if (ret == -EILSEQ) { | ||
300 | /* this tuple is unknown to the core or whitelisted */ | ||
301 | this->next = NULL; | 308 | this->next = NULL; |
302 | this->code = tpl_code; | 309 | this->code = tpl_code; |
303 | this->size = tpl_link; | 310 | this->size = tpl_link; |
304 | *prev = this; | 311 | *prev = this; |
305 | prev = &this->next; | 312 | prev = &this->next; |
306 | printk(KERN_DEBUG | 313 | |
307 | "%s: queuing CIS tuple 0x%02x length %u\n", | 314 | if (ret == -ENOENT) { |
308 | mmc_hostname(card->host), tpl_code, tpl_link); | 315 | /* warn about unknown tuples */ |
316 | printk(KERN_WARNING "%s: queuing unknown" | ||
317 | " CIS tuple 0x%02x (%u bytes)\n", | ||
318 | mmc_hostname(card->host), | ||
319 | tpl_code, tpl_link); | ||
320 | } | ||
321 | |||
309 | /* keep on analyzing tuples */ | 322 | /* keep on analyzing tuples */ |
310 | ret = 0; | 323 | ret = 0; |
324 | } else { | ||
325 | /* | ||
326 | * We don't need the tuple anymore if it was | ||
327 | * successfully parsed by the SDIO core or if it is | ||
328 | * not going to be queued for a driver. | ||
329 | */ | ||
330 | kfree(this); | ||
311 | } | 331 | } |
312 | 332 | ||
313 | ptr += tpl_link; | 333 | ptr += tpl_link; |
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index f9aa8a7deffa..ff27c8c71355 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c | |||
@@ -189,7 +189,12 @@ static inline unsigned int sdio_max_byte_size(struct sdio_func *func) | |||
189 | { | 189 | { |
190 | unsigned mval = min(func->card->host->max_seg_size, | 190 | unsigned mval = min(func->card->host->max_seg_size, |
191 | func->card->host->max_blk_size); | 191 | func->card->host->max_blk_size); |
192 | mval = min(mval, func->max_blksize); | 192 | |
193 | if (mmc_blksz_for_byte_mode(func->card)) | ||
194 | mval = min(mval, func->cur_blksize); | ||
195 | else | ||
196 | mval = min(mval, func->max_blksize); | ||
197 | |||
193 | return min(mval, 512u); /* maximum size for byte mode */ | 198 | return min(mval, 512u); /* maximum size for byte mode */ |
194 | } | 199 | } |
195 | 200 | ||
@@ -635,3 +640,52 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, | |||
635 | *err_ret = ret; | 640 | *err_ret = ret; |
636 | } | 641 | } |
637 | EXPORT_SYMBOL_GPL(sdio_f0_writeb); | 642 | EXPORT_SYMBOL_GPL(sdio_f0_writeb); |
643 | |||
644 | /** | ||
645 | * sdio_get_host_pm_caps - get host power management capabilities | ||
646 | * @func: SDIO function attached to host | ||
647 | * | ||
648 | * Returns a capability bitmask corresponding to power management | ||
649 | * features supported by the host controller that the card function | ||
650 | * might rely upon during a system suspend. The host doesn't need | ||
651 | * to be claimed, nor the function active, for this information to be | ||
652 | * obtained. | ||
653 | */ | ||
654 | mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func) | ||
655 | { | ||
656 | BUG_ON(!func); | ||
657 | BUG_ON(!func->card); | ||
658 | |||
659 | return func->card->host->pm_caps; | ||
660 | } | ||
661 | EXPORT_SYMBOL_GPL(sdio_get_host_pm_caps); | ||
662 | |||
663 | /** | ||
664 | * sdio_set_host_pm_flags - set wanted host power management capabilities | ||
665 | * @func: SDIO function attached to host | ||
666 | * | ||
667 | * Set a capability bitmask corresponding to wanted host controller | ||
668 | * power management features for the upcoming suspend state. | ||
669 | * This must be called, if needed, each time the suspend method of | ||
670 | * the function driver is called, and must contain only bits that | ||
671 | * were returned by sdio_get_host_pm_caps(). | ||
672 | * The host doesn't need to be claimed, nor the function active, | ||
673 | * for this information to be set. | ||
674 | */ | ||
675 | int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags) | ||
676 | { | ||
677 | struct mmc_host *host; | ||
678 | |||
679 | BUG_ON(!func); | ||
680 | BUG_ON(!func->card); | ||
681 | |||
682 | host = func->card->host; | ||
683 | |||
684 | if (flags & ~host->pm_caps) | ||
685 | return -EINVAL; | ||
686 | |||
687 | /* function suspend methods are serialized, hence no lock needed */ | ||
688 | host->pm_flags |= flags; | ||
689 | return 0; | ||
690 | } | ||
691 | EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags); | ||
diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index 4eb7825fd1a7..dea36d9c22e6 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c | |||
@@ -67,13 +67,13 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) | |||
67 | return err; | 67 | return err; |
68 | } | 68 | } |
69 | 69 | ||
70 | int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, | 70 | static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn, |
71 | unsigned addr, u8 in, u8* out) | 71 | unsigned addr, u8 in, u8 *out) |
72 | { | 72 | { |
73 | struct mmc_command cmd; | 73 | struct mmc_command cmd; |
74 | int err; | 74 | int err; |
75 | 75 | ||
76 | BUG_ON(!card); | 76 | BUG_ON(!host); |
77 | BUG_ON(fn > 7); | 77 | BUG_ON(fn > 7); |
78 | 78 | ||
79 | /* sanity check */ | 79 | /* sanity check */ |
@@ -90,11 +90,11 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, | |||
90 | cmd.arg |= in; | 90 | cmd.arg |= in; |
91 | cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; | 91 | cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; |
92 | 92 | ||
93 | err = mmc_wait_for_cmd(card->host, &cmd, 0); | 93 | err = mmc_wait_for_cmd(host, &cmd, 0); |
94 | if (err) | 94 | if (err) |
95 | return err; | 95 | return err; |
96 | 96 | ||
97 | if (mmc_host_is_spi(card->host)) { | 97 | if (mmc_host_is_spi(host)) { |
98 | /* host driver already reported errors */ | 98 | /* host driver already reported errors */ |
99 | } else { | 99 | } else { |
100 | if (cmd.resp[0] & R5_ERROR) | 100 | if (cmd.resp[0] & R5_ERROR) |
@@ -106,7 +106,7 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, | |||
106 | } | 106 | } |
107 | 107 | ||
108 | if (out) { | 108 | if (out) { |
109 | if (mmc_host_is_spi(card->host)) | 109 | if (mmc_host_is_spi(host)) |
110 | *out = (cmd.resp[0] >> 8) & 0xFF; | 110 | *out = (cmd.resp[0] >> 8) & 0xFF; |
111 | else | 111 | else |
112 | *out = cmd.resp[0] & 0xFF; | 112 | *out = cmd.resp[0] & 0xFF; |
@@ -115,6 +115,13 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, | |||
115 | return 0; | 115 | return 0; |
116 | } | 116 | } |
117 | 117 | ||
118 | int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, | ||
119 | unsigned addr, u8 in, u8 *out) | ||
120 | { | ||
121 | BUG_ON(!card); | ||
122 | return mmc_io_rw_direct_host(card->host, write, fn, addr, in, out); | ||
123 | } | ||
124 | |||
118 | int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, | 125 | int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, |
119 | unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz) | 126 | unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz) |
120 | { | 127 | { |
@@ -182,3 +189,20 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, | |||
182 | return 0; | 189 | return 0; |
183 | } | 190 | } |
184 | 191 | ||
192 | int sdio_reset(struct mmc_host *host) | ||
193 | { | ||
194 | int ret; | ||
195 | u8 abort; | ||
196 | |||
197 | /* SDIO Simplified Specification V2.0, 4.4 Reset for SDIO */ | ||
198 | |||
199 | ret = mmc_io_rw_direct_host(host, 0, 0, SDIO_CCCR_ABORT, 0, &abort); | ||
200 | if (ret) | ||
201 | abort = 0x08; | ||
202 | else | ||
203 | abort |= 0x08; | ||
204 | |||
205 | ret = mmc_io_rw_direct_host(host, 1, 0, SDIO_CCCR_ABORT, abort, NULL); | ||
206 | return ret; | ||
207 | } | ||
208 | |||
diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h index e2e74b0d17d8..12a4d3ab174c 100644 --- a/drivers/mmc/core/sdio_ops.h +++ b/drivers/mmc/core/sdio_ops.h | |||
@@ -17,6 +17,7 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, | |||
17 | unsigned addr, u8 in, u8* out); | 17 | unsigned addr, u8 in, u8* out); |
18 | int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, | 18 | int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, |
19 | unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz); | 19 | unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz); |
20 | int sdio_reset(struct mmc_host *host); | ||
20 | 21 | ||
21 | #endif | 22 | #endif |
22 | 23 | ||