aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDominik Brodowski <linux@dominikbrodowski.net>2010-01-02 08:14:23 -0500
committerDominik Brodowski <linux@dominikbrodowski.net>2010-01-17 12:30:12 -0500
commit88b060d6c03fcb9e4d2018b4349954c4242a5c7f (patch)
tree34954c1b7f1d4d978cae56ee40a9ab57829dccf2
parentf131ddc4bd1713385c70606555d4d63bed5ec3fd (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.c1
-rw-r--r--drivers/pcmcia/cs.c65
-rw-r--r--drivers/pcmcia/ds.c16
-rw-r--r--include/pcmcia/ss.h1
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}
380EXPORT_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
349static 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
356static int socket_reset(struct pcmcia_socket *skt) 349static 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
567static int socket_late_resume(struct pcmcia_socket *skt) 563static 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;