diff options
author | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-01-02 08:14:23 -0500 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-01-17 12:30:12 -0500 |
commit | 88b060d6c03fcb9e4d2018b4349954c4242a5c7f (patch) | |
tree | 34954c1b7f1d4d978cae56ee40a9ab57829dccf2 | |
parent | f131ddc4bd1713385c70606555d4d63bed5ec3fd (diff) |
pcmcia: improve check for same card in slot after resume
During a suspend/resume cycle, an user may change the card in the
PCMCIA/CardBus slot. The pcmcia_core can at least look at the
socket state to check whether it is the same.
For PCMCIA devices, move the detection and handling of such a
change to ds.c.
For CardBus devices, the PCI hotplug interface doesn't offer a "rescan"
facility which also _removes_ devices no longer to be found behind a
bridge. Therefore, remove and re-add all devices unconditionally.
CC: Jesse Barnes <jbarnes@virtuousgeek.org>
CC: Linus Torvalds <torvalds@linux-foundation.org>
Tested-by: Wolfram Sang <w.sang@pengutronix.de>
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
-rw-r--r-- | drivers/pcmcia/cistpl.c | 1 | ||||
-rw-r--r-- | drivers/pcmcia/cs.c | 65 | ||||
-rw-r--r-- | drivers/pcmcia/ds.c | 16 | ||||
-rw-r--r-- | include/pcmcia/ss.h | 1 |
4 files changed, 47 insertions, 36 deletions
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 04bf1ba607f7..a8323cb2e34d 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c | |||
@@ -377,6 +377,7 @@ int verify_cis_cache(struct pcmcia_socket *s) | |||
377 | kfree(buf); | 377 | kfree(buf); |
378 | return 0; | 378 | return 0; |
379 | } | 379 | } |
380 | EXPORT_SYMBOL(verify_cis_cache); | ||
380 | 381 | ||
381 | /*====================================================================== | 382 | /*====================================================================== |
382 | 383 | ||
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 96d8d25c209d..8c51493d1f95 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c | |||
@@ -328,7 +328,7 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) | |||
328 | { | 328 | { |
329 | int ret; | 329 | int ret; |
330 | 330 | ||
331 | if (s->state & SOCKET_CARDBUS) | 331 | if ((s->state & SOCKET_CARDBUS) && (event != CS_EVENT_CARD_REMOVAL)) |
332 | return 0; | 332 | return 0; |
333 | 333 | ||
334 | dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n", | 334 | dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n", |
@@ -346,13 +346,6 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) | |||
346 | return ret; | 346 | return ret; |
347 | } | 347 | } |
348 | 348 | ||
349 | static void socket_remove_drivers(struct pcmcia_socket *skt) | ||
350 | { | ||
351 | dev_dbg(&skt->dev, "remove_drivers\n"); | ||
352 | |||
353 | send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); | ||
354 | } | ||
355 | |||
356 | static int socket_reset(struct pcmcia_socket *skt) | 349 | static int socket_reset(struct pcmcia_socket *skt) |
357 | { | 350 | { |
358 | int status, i; | 351 | int status, i; |
@@ -395,7 +388,7 @@ static void socket_shutdown(struct pcmcia_socket *s) | |||
395 | 388 | ||
396 | dev_dbg(&s->dev, "shutdown\n"); | 389 | dev_dbg(&s->dev, "shutdown\n"); |
397 | 390 | ||
398 | socket_remove_drivers(s); | 391 | send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); |
399 | s->state &= SOCKET_INUSE | SOCKET_PRESENT; | 392 | s->state &= SOCKET_INUSE | SOCKET_PRESENT; |
400 | msleep(shutdown_delay * 10); | 393 | msleep(shutdown_delay * 10); |
401 | s->state &= SOCKET_INUSE; | 394 | s->state &= SOCKET_INUSE; |
@@ -462,7 +455,8 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay) | |||
462 | return -EINVAL; | 455 | return -EINVAL; |
463 | } | 456 | } |
464 | skt->state |= SOCKET_CARDBUS; | 457 | skt->state |= SOCKET_CARDBUS; |
465 | } | 458 | } else |
459 | skt->state &= ~SOCKET_CARDBUS; | ||
466 | 460 | ||
467 | /* | 461 | /* |
468 | * Decode the card voltage requirements, and apply power to the card. | 462 | * Decode the card voltage requirements, and apply power to the card. |
@@ -544,6 +538,8 @@ static int socket_suspend(struct pcmcia_socket *skt) | |||
544 | if (skt->state & SOCKET_SUSPEND) | 538 | if (skt->state & SOCKET_SUSPEND) |
545 | return -EBUSY; | 539 | return -EBUSY; |
546 | 540 | ||
541 | skt->suspended_state = skt->state; | ||
542 | |||
547 | send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); | 543 | send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); |
548 | skt->socket = dead_socket; | 544 | skt->socket = dead_socket; |
549 | skt->ops->set_socket(skt, &skt->socket); | 545 | skt->ops->set_socket(skt, &skt->socket); |
@@ -566,38 +562,37 @@ static int socket_early_resume(struct pcmcia_socket *skt) | |||
566 | 562 | ||
567 | static int socket_late_resume(struct pcmcia_socket *skt) | 563 | static int socket_late_resume(struct pcmcia_socket *skt) |
568 | { | 564 | { |
569 | if (!(skt->state & SOCKET_PRESENT)) { | 565 | skt->state &= ~SOCKET_SUSPEND; |
570 | skt->state &= ~SOCKET_SUSPEND; | 566 | |
567 | if (!(skt->state & SOCKET_PRESENT)) | ||
571 | return socket_insert(skt); | 568 | return socket_insert(skt); |
569 | |||
570 | if (skt->resume_status) { | ||
571 | socket_shutdown(skt); | ||
572 | return 0; | ||
572 | } | 573 | } |
573 | 574 | ||
574 | if (skt->resume_status == 0) { | 575 | if (skt->suspended_state != skt->state) { |
575 | /* | 576 | dev_dbg(&skt->dev, |
576 | * FIXME: need a better check here for cardbus cards. | 577 | "suspend state 0x%x != resume state 0x%x\n", |
577 | */ | 578 | skt->suspended_state, skt->state); |
578 | if (verify_cis_cache(skt) != 0) { | 579 | |
579 | dev_dbg(&skt->dev, "cis mismatch - different card\n"); | ||
580 | socket_remove_drivers(skt); | ||
581 | destroy_cis_cache(skt); | ||
582 | kfree(skt->fake_cis); | ||
583 | skt->fake_cis = NULL; | ||
584 | /* | ||
585 | * Workaround: give DS time to schedule removal. | ||
586 | * Remove me once the 100ms delay is eliminated | ||
587 | * in ds.c | ||
588 | */ | ||
589 | msleep(200); | ||
590 | send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); | ||
591 | } else { | ||
592 | dev_dbg(&skt->dev, "cis matches cache\n"); | ||
593 | send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); | ||
594 | } | ||
595 | } else { | ||
596 | socket_shutdown(skt); | 580 | socket_shutdown(skt); |
581 | return socket_insert(skt); | ||
597 | } | 582 | } |
598 | 583 | ||
599 | skt->state &= ~SOCKET_SUSPEND; | 584 | #ifdef CONFIG_CARDBUS |
585 | if (skt->state & SOCKET_CARDBUS) { | ||
586 | /* We can't be sure the CardBus card is the same | ||
587 | * as the one previously inserted. Therefore, remove | ||
588 | * and re-add... */ | ||
589 | cb_free(skt); | ||
590 | cb_alloc(skt); | ||
591 | return 0; | ||
592 | } | ||
593 | #endif | ||
600 | 594 | ||
595 | send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); | ||
601 | return 0; | 596 | return 0; |
602 | } | 597 | } |
603 | 598 | ||
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index defa44c27b97..87e06395c129 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c | |||
@@ -1252,8 +1252,22 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) | |||
1252 | case CS_EVENT_EJECTION_REQUEST: | 1252 | case CS_EVENT_EJECTION_REQUEST: |
1253 | break; | 1253 | break; |
1254 | 1254 | ||
1255 | case CS_EVENT_PM_SUSPEND: | ||
1256 | case CS_EVENT_PM_RESUME: | 1255 | case CS_EVENT_PM_RESUME: |
1256 | if (verify_cis_cache(skt) != 0) { | ||
1257 | dev_dbg(&skt->dev, "cis mismatch - different card\n"); | ||
1258 | /* first, remove the card */ | ||
1259 | ds_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); | ||
1260 | destroy_cis_cache(skt); | ||
1261 | kfree(skt->fake_cis); | ||
1262 | skt->fake_cis = NULL; | ||
1263 | /* now, add the new card */ | ||
1264 | ds_event(skt, CS_EVENT_CARD_INSERTION, | ||
1265 | CS_EVENT_PRI_LOW); | ||
1266 | } | ||
1267 | handle_event(skt, event); | ||
1268 | break; | ||
1269 | |||
1270 | case CS_EVENT_PM_SUSPEND: | ||
1257 | case CS_EVENT_RESET_PHYSICAL: | 1271 | case CS_EVENT_RESET_PHYSICAL: |
1258 | case CS_EVENT_CARD_RESET: | 1272 | case CS_EVENT_CARD_RESET: |
1259 | default: | 1273 | default: |
diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index cbfba885eb85..b0ebd11130cc 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h | |||
@@ -137,6 +137,7 @@ struct pcmcia_socket { | |||
137 | spinlock_t lock; | 137 | spinlock_t lock; |
138 | socket_state_t socket; | 138 | socket_state_t socket; |
139 | u_int state; | 139 | u_int state; |
140 | u_int suspended_state; /* state before suspend */ | ||
140 | u_short functions; | 141 | u_short functions; |
141 | u_short lock_count; | 142 | u_short lock_count; |
142 | pccard_mem_map cis_mem; | 143 | pccard_mem_map cis_mem; |