diff options
author | Alex Dubov <oakad@yahoo.com> | 2007-04-12 02:59:19 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2007-05-01 07:04:13 -0400 |
commit | 88de1b2fed2bbe9eb1b7310195be84cf143efb4f (patch) | |
tree | 4f52fe092254b8992a19797860edc8889c08a1f5 /drivers | |
parent | 2428a8fe2261e901e058d9ea8b6ed7e1b4268b79 (diff) |
tifm_7xx1: fix adapter resume function
Fixes to the adapter resume function to correctly handle all possible cases:
1. Card is removed during suspend
2. Card is inserted during suspend into previously empty socket
3. Card is replaced during suspend by same or different media type card.
Signed-off-by: Alex Dubov <oakad@yahoo.com>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/misc/tifm_7xx1.c | 58 |
1 files changed, 31 insertions, 27 deletions
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 356386904a5d..eafa5575f312 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c | |||
@@ -220,7 +220,8 @@ static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) | |||
220 | static int tifm_7xx1_resume(struct pci_dev *dev) | 220 | static int tifm_7xx1_resume(struct pci_dev *dev) |
221 | { | 221 | { |
222 | struct tifm_adapter *fm = pci_get_drvdata(dev); | 222 | struct tifm_adapter *fm = pci_get_drvdata(dev); |
223 | int cnt, rc; | 223 | int rc; |
224 | unsigned int good_sockets = 0, bad_sockets = 0; | ||
224 | unsigned long flags; | 225 | unsigned long flags; |
225 | unsigned char new_ids[fm->num_sockets]; | 226 | unsigned char new_ids[fm->num_sockets]; |
226 | DECLARE_COMPLETION_ONSTACK(finish_resume); | 227 | DECLARE_COMPLETION_ONSTACK(finish_resume); |
@@ -234,46 +235,49 @@ static int tifm_7xx1_resume(struct pci_dev *dev) | |||
234 | 235 | ||
235 | dev_dbg(&dev->dev, "resuming host\n"); | 236 | dev_dbg(&dev->dev, "resuming host\n"); |
236 | 237 | ||
237 | for (cnt = 0; cnt < fm->num_sockets; cnt++) | 238 | for (rc = 0; rc < fm->num_sockets; rc++) |
238 | new_ids[cnt] = tifm_7xx1_toggle_sock_power( | 239 | new_ids[rc] = tifm_7xx1_toggle_sock_power( |
239 | tifm_7xx1_sock_addr(fm->addr, cnt)); | 240 | tifm_7xx1_sock_addr(fm->addr, rc)); |
240 | spin_lock_irqsave(&fm->lock, flags); | 241 | spin_lock_irqsave(&fm->lock, flags); |
241 | fm->socket_change_set = 0; | 242 | for (rc = 0; rc < fm->num_sockets; rc++) { |
242 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { | 243 | if (fm->sockets[rc]) { |
243 | if (fm->sockets[cnt]) { | 244 | if (fm->sockets[rc]->type == new_ids[rc]) |
244 | if (fm->sockets[cnt]->type == new_ids[cnt]) | 245 | good_sockets |= 1 << rc; |
245 | fm->socket_change_set |= 1 << cnt; | 246 | else |
246 | 247 | bad_sockets |= 1 << rc; | |
247 | fm->sockets[cnt]->type = new_ids[cnt]; | ||
248 | } | 248 | } |
249 | } | 249 | } |
250 | 250 | ||
251 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), | 251 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), |
252 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 252 | fm->addr + FM_SET_INTERRUPT_ENABLE); |
253 | if (!fm->socket_change_set) { | 253 | dev_dbg(&dev->dev, "change sets on resume: good %x, bad %x\n", |
254 | spin_unlock_irqrestore(&fm->lock, flags); | 254 | good_sockets, bad_sockets); |
255 | return 0; | 255 | |
256 | } else { | 256 | fm->socket_change_set = 0; |
257 | fm->socket_change_set = 0; | 257 | if (good_sockets) { |
258 | fm->finish_me = &finish_resume; | 258 | fm->finish_me = &finish_resume; |
259 | spin_unlock_irqrestore(&fm->lock, flags); | 259 | spin_unlock_irqrestore(&fm->lock, flags); |
260 | rc = wait_for_completion_timeout(&finish_resume, HZ); | ||
261 | dev_dbg(&dev->dev, "wait returned %d\n", rc); | ||
262 | writel(TIFM_IRQ_FIFOMASK(good_sockets) | ||
263 | | TIFM_IRQ_CARDMASK(good_sockets), | ||
264 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
265 | writel(TIFM_IRQ_FIFOMASK(good_sockets) | ||
266 | | TIFM_IRQ_CARDMASK(good_sockets), | ||
267 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
268 | spin_lock_irqsave(&fm->lock, flags); | ||
269 | fm->finish_me = NULL; | ||
270 | fm->socket_change_set ^= good_sockets & fm->socket_change_set; | ||
260 | } | 271 | } |
261 | 272 | ||
262 | wait_for_completion_timeout(&finish_resume, HZ); | 273 | fm->socket_change_set |= bad_sockets; |
274 | if (fm->socket_change_set) | ||
275 | tifm_queue_work(&fm->media_switcher); | ||
263 | 276 | ||
264 | spin_lock_irqsave(&fm->lock, flags); | 277 | spin_unlock_irqrestore(&fm->lock, flags); |
265 | fm->finish_me = NULL; | ||
266 | writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) | ||
267 | | TIFM_IRQ_CARDMASK(fm->socket_change_set), | ||
268 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
269 | writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) | ||
270 | | TIFM_IRQ_CARDMASK(fm->socket_change_set), | ||
271 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
272 | writel(TIFM_IRQ_ENABLE, | 278 | writel(TIFM_IRQ_ENABLE, |
273 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 279 | fm->addr + FM_SET_INTERRUPT_ENABLE); |
274 | fm->socket_change_set = 0; | ||
275 | 280 | ||
276 | spin_unlock_irqrestore(&fm->lock, flags); | ||
277 | return 0; | 281 | return 0; |
278 | } | 282 | } |
279 | 283 | ||