diff options
author | Michal Miroslaw <mirq-linux@rere.qmqm.pl> | 2010-08-10 21:01:40 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-11 11:59:03 -0400 |
commit | 7310ece86ad7da027f85a37a0638164118a5d12f (patch) | |
tree | 122fd13237eba533876b08aadeee4b7128d9feb0 /drivers/mmc | |
parent | 71578a1eaa7b8b9bd3efc9c97d77ef2b63d5dc2b (diff) |
mmc: implement SD-combo (IO+mem) support
Signed-off-by: Michal Miroslaw <mirq-linux@rere.qmqm.pl>
Cc: Adrian Hunter <adrian.hunter@nokia.com>
Cc: Chris Ball <cjb@laptop.org>
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')
-rw-r--r-- | drivers/mmc/core/bus.c | 9 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 12 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 135 |
3 files changed, 133 insertions, 23 deletions
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 49d9dcaeca49..7cd9749dc21d 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c | |||
@@ -37,6 +37,8 @@ static ssize_t mmc_type_show(struct device *dev, | |||
37 | return sprintf(buf, "SD\n"); | 37 | return sprintf(buf, "SD\n"); |
38 | case MMC_TYPE_SDIO: | 38 | case MMC_TYPE_SDIO: |
39 | return sprintf(buf, "SDIO\n"); | 39 | return sprintf(buf, "SDIO\n"); |
40 | case MMC_TYPE_SD_COMBO: | ||
41 | return sprintf(buf, "SDcombo\n"); | ||
40 | default: | 42 | default: |
41 | return -EFAULT; | 43 | return -EFAULT; |
42 | } | 44 | } |
@@ -74,6 +76,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
74 | case MMC_TYPE_SDIO: | 76 | case MMC_TYPE_SDIO: |
75 | type = "SDIO"; | 77 | type = "SDIO"; |
76 | break; | 78 | break; |
79 | case MMC_TYPE_SD_COMBO: | ||
80 | type = "SDcombo"; | ||
81 | break; | ||
77 | default: | 82 | default: |
78 | type = NULL; | 83 | type = NULL; |
79 | } | 84 | } |
@@ -239,6 +244,10 @@ int mmc_add_card(struct mmc_card *card) | |||
239 | case MMC_TYPE_SDIO: | 244 | case MMC_TYPE_SDIO: |
240 | type = "SDIO"; | 245 | type = "SDIO"; |
241 | break; | 246 | break; |
247 | case MMC_TYPE_SD_COMBO: | ||
248 | type = "SD-combo"; | ||
249 | if (mmc_card_blockaddr(card)) | ||
250 | type = "SDHC-combo"; | ||
242 | default: | 251 | default: |
243 | type = "?"; | 252 | type = "?"; |
244 | break; | 253 | break; |
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 569e94da844c..b69ce91b11e1 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -1099,8 +1099,15 @@ void mmc_rescan(struct work_struct *work) | |||
1099 | */ | 1099 | */ |
1100 | err = mmc_send_io_op_cond(host, 0, &ocr); | 1100 | err = mmc_send_io_op_cond(host, 0, &ocr); |
1101 | if (!err) { | 1101 | if (!err) { |
1102 | if (mmc_attach_sdio(host, ocr)) | 1102 | if (mmc_attach_sdio(host, ocr)) { |
1103 | mmc_power_off(host); | 1103 | mmc_claim_host(host); |
1104 | /* try SDMEM (but not MMC) even if SDIO is broken */ | ||
1105 | if (mmc_send_app_op_cond(host, 0, &ocr)) | ||
1106 | goto out_fail; | ||
1107 | |||
1108 | if (mmc_attach_sd(host, ocr)) | ||
1109 | mmc_power_off(host); | ||
1110 | } | ||
1104 | goto out; | 1111 | goto out; |
1105 | } | 1112 | } |
1106 | 1113 | ||
@@ -1124,6 +1131,7 @@ void mmc_rescan(struct work_struct *work) | |||
1124 | goto out; | 1131 | goto out; |
1125 | } | 1132 | } |
1126 | 1133 | ||
1134 | out_fail: | ||
1127 | mmc_release_host(host); | 1135 | mmc_release_host(host); |
1128 | mmc_power_off(host); | 1136 | mmc_power_off(host); |
1129 | 1137 | ||
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 47d1708810bd..b0b6ce93e519 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c | |||
@@ -160,9 +160,7 @@ static int sdio_enable_wide(struct mmc_card *card) | |||
160 | if (ret) | 160 | if (ret) |
161 | return ret; | 161 | return ret; |
162 | 162 | ||
163 | mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); | 163 | return 1; |
164 | |||
165 | return 0; | ||
166 | } | 164 | } |
167 | 165 | ||
168 | /* | 166 | /* |
@@ -222,10 +220,34 @@ static int sdio_disable_wide(struct mmc_card *card) | |||
222 | return 0; | 220 | return 0; |
223 | } | 221 | } |
224 | 222 | ||
223 | |||
224 | static int sdio_enable_4bit_bus(struct mmc_card *card) | ||
225 | { | ||
226 | int err; | ||
227 | |||
228 | if (card->type == MMC_TYPE_SDIO) | ||
229 | return sdio_enable_wide(card); | ||
230 | |||
231 | if ((card->host->caps & MMC_CAP_4_BIT_DATA) && | ||
232 | (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { | ||
233 | err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); | ||
234 | if (err) | ||
235 | return err; | ||
236 | } else | ||
237 | return 0; | ||
238 | |||
239 | err = sdio_enable_wide(card); | ||
240 | if (err <= 0) | ||
241 | mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1); | ||
242 | |||
243 | return err; | ||
244 | } | ||
245 | |||
246 | |||
225 | /* | 247 | /* |
226 | * Test if the card supports high-speed mode and, if so, switch to it. | 248 | * Test if the card supports high-speed mode and, if so, switch to it. |
227 | */ | 249 | */ |
228 | static int sdio_enable_hs(struct mmc_card *card) | 250 | static int mmc_sdio_switch_hs(struct mmc_card *card, int enable) |
229 | { | 251 | { |
230 | int ret; | 252 | int ret; |
231 | u8 speed; | 253 | u8 speed; |
@@ -240,7 +262,10 @@ static int sdio_enable_hs(struct mmc_card *card) | |||
240 | if (ret) | 262 | if (ret) |
241 | return ret; | 263 | return ret; |
242 | 264 | ||
243 | speed |= SDIO_SPEED_EHS; | 265 | if (enable) |
266 | speed |= SDIO_SPEED_EHS; | ||
267 | else | ||
268 | speed &= ~SDIO_SPEED_EHS; | ||
244 | 269 | ||
245 | ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL); | 270 | ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL); |
246 | if (ret) | 271 | if (ret) |
@@ -249,6 +274,24 @@ static int sdio_enable_hs(struct mmc_card *card) | |||
249 | return 1; | 274 | return 1; |
250 | } | 275 | } |
251 | 276 | ||
277 | /* | ||
278 | * Enable SDIO/combo card's high-speed mode. Return 0/1 if [not]supported. | ||
279 | */ | ||
280 | static int sdio_enable_hs(struct mmc_card *card) | ||
281 | { | ||
282 | int ret; | ||
283 | |||
284 | ret = mmc_sdio_switch_hs(card, true); | ||
285 | if (ret <= 0 || card->type == MMC_TYPE_SDIO) | ||
286 | return ret; | ||
287 | |||
288 | ret = mmc_sd_switch_hs(card); | ||
289 | if (ret <= 0) | ||
290 | mmc_sdio_switch_hs(card, false); | ||
291 | |||
292 | return ret; | ||
293 | } | ||
294 | |||
252 | static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) | 295 | static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) |
253 | { | 296 | { |
254 | unsigned max_dtr; | 297 | unsigned max_dtr; |
@@ -265,6 +308,9 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) | |||
265 | max_dtr = card->cis.max_dtr; | 308 | max_dtr = card->cis.max_dtr; |
266 | } | 309 | } |
267 | 310 | ||
311 | if (card->type == MMC_TYPE_SD_COMBO) | ||
312 | max_dtr = min(max_dtr, mmc_sd_get_max_clock(card)); | ||
313 | |||
268 | return max_dtr; | 314 | return max_dtr; |
269 | } | 315 | } |
270 | 316 | ||
@@ -310,7 +356,24 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, | |||
310 | goto err; | 356 | goto err; |
311 | } | 357 | } |
312 | 358 | ||
313 | card->type = MMC_TYPE_SDIO; | 359 | err = mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid); |
360 | |||
361 | if (!err) { | ||
362 | card->type = MMC_TYPE_SD_COMBO; | ||
363 | |||
364 | if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || | ||
365 | memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) { | ||
366 | mmc_remove_card(card); | ||
367 | return -ENOENT; | ||
368 | } | ||
369 | } else { | ||
370 | card->type = MMC_TYPE_SDIO; | ||
371 | |||
372 | if (oldcard && oldcard->type != MMC_TYPE_SDIO) { | ||
373 | mmc_remove_card(card); | ||
374 | return -ENOENT; | ||
375 | } | ||
376 | } | ||
314 | 377 | ||
315 | /* | 378 | /* |
316 | * Call the optional HC's init_card function to handle quirks. | 379 | * Call the optional HC's init_card function to handle quirks. |
@@ -330,6 +393,17 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, | |||
330 | } | 393 | } |
331 | 394 | ||
332 | /* | 395 | /* |
396 | * Read CSD, before selecting the card | ||
397 | */ | ||
398 | if (!oldcard && card->type == MMC_TYPE_SD_COMBO) { | ||
399 | err = mmc_sd_get_csd(host, card); | ||
400 | if (err) | ||
401 | return err; | ||
402 | |||
403 | mmc_decode_cid(card); | ||
404 | } | ||
405 | |||
406 | /* | ||
333 | * Select card, as all following commands rely on that. | 407 | * Select card, as all following commands rely on that. |
334 | */ | 408 | */ |
335 | if (!powered_resume && !mmc_host_is_spi(host)) { | 409 | if (!powered_resume && !mmc_host_is_spi(host)) { |
@@ -356,14 +430,33 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, | |||
356 | int same = (card->cis.vendor == oldcard->cis.vendor && | 430 | int same = (card->cis.vendor == oldcard->cis.vendor && |
357 | card->cis.device == oldcard->cis.device); | 431 | card->cis.device == oldcard->cis.device); |
358 | mmc_remove_card(card); | 432 | mmc_remove_card(card); |
359 | if (!same) { | 433 | if (!same) |
360 | err = -ENOENT; | 434 | return -ENOENT; |
361 | goto err; | 435 | |
362 | } | ||
363 | card = oldcard; | 436 | card = oldcard; |
364 | return 0; | 437 | return 0; |
365 | } | 438 | } |
366 | 439 | ||
440 | if (card->type == MMC_TYPE_SD_COMBO) { | ||
441 | err = mmc_sd_setup_card(host, card, oldcard != NULL); | ||
442 | /* handle as SDIO-only card if memory init failed */ | ||
443 | if (err) { | ||
444 | mmc_go_idle(host); | ||
445 | if (mmc_host_is_spi(host)) | ||
446 | /* should not fail, as it worked previously */ | ||
447 | mmc_spi_set_crc(host, use_spi_crc); | ||
448 | card->type = MMC_TYPE_SDIO; | ||
449 | } else | ||
450 | card->dev.type = &sd_type; | ||
451 | } | ||
452 | |||
453 | /* | ||
454 | * If needed, disconnect card detection pull-up resistor. | ||
455 | */ | ||
456 | err = sdio_disable_cd(card); | ||
457 | if (err) | ||
458 | goto remove; | ||
459 | |||
367 | /* | 460 | /* |
368 | * Switch to high-speed (if supported). | 461 | * Switch to high-speed (if supported). |
369 | */ | 462 | */ |
@@ -381,8 +474,10 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, | |||
381 | /* | 474 | /* |
382 | * Switch to wider bus (if supported). | 475 | * Switch to wider bus (if supported). |
383 | */ | 476 | */ |
384 | err = sdio_enable_wide(card); | 477 | err = sdio_enable_4bit_bus(card); |
385 | if (err) | 478 | if (err > 0) |
479 | mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); | ||
480 | else if (err) | ||
386 | goto remove; | 481 | goto remove; |
387 | 482 | ||
388 | if (!oldcard) | 483 | if (!oldcard) |
@@ -496,9 +591,14 @@ static int mmc_sdio_resume(struct mmc_host *host) | |||
496 | mmc_claim_host(host); | 591 | mmc_claim_host(host); |
497 | err = mmc_sdio_init_card(host, host->ocr, host->card, | 592 | err = mmc_sdio_init_card(host, host->ocr, host->card, |
498 | (host->pm_flags & MMC_PM_KEEP_POWER)); | 593 | (host->pm_flags & MMC_PM_KEEP_POWER)); |
499 | if (!err) | 594 | if (!err) { |
500 | /* We may have switched to 1-bit mode during suspend. */ | 595 | /* We may have switched to 1-bit mode during suspend. */ |
501 | err = sdio_enable_wide(host->card); | 596 | err = sdio_enable_4bit_bus(host->card); |
597 | if (err > 0) { | ||
598 | mmc_set_bus_width(host, MMC_BUS_WIDTH_4); | ||
599 | err = 0; | ||
600 | } | ||
601 | } | ||
502 | if (!err && host->sdio_irqs) | 602 | if (!err && host->sdio_irqs) |
503 | mmc_signal_sdio_irq(host); | 603 | mmc_signal_sdio_irq(host); |
504 | mmc_release_host(host); | 604 | mmc_release_host(host); |
@@ -583,13 +683,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | |||
583 | card->sdio_funcs = 0; | 683 | card->sdio_funcs = 0; |
584 | 684 | ||
585 | /* | 685 | /* |
586 | * If needed, disconnect card detection pull-up resistor. | ||
587 | */ | ||
588 | err = sdio_disable_cd(card); | ||
589 | if (err) | ||
590 | goto remove; | ||
591 | |||
592 | /* | ||
593 | * Initialize (but don't add) all present functions. | 686 | * Initialize (but don't add) all present functions. |
594 | */ | 687 | */ |
595 | for (i = 0; i < funcs; i++, card->sdio_funcs++) { | 688 | for (i = 0; i < funcs; i++, card->sdio_funcs++) { |