diff options
author | Alex Dubov <oakad@yahoo.com> | 2006-12-17 22:20:06 -0500 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2007-02-04 14:54:09 -0500 |
commit | 7146f0d3bd2bcd0100a5db54f4ba9edc1042fe01 (patch) | |
tree | bb8dd99b153d6aa8fe565be7256a586b0abd2977 /drivers/misc/tifm_7xx1.c | |
parent | 6412d927313f08808d61b7efba8da43717d4e8d2 (diff) |
tifm_7xx1: switch from workqueue to kthread
As there's only one work item (media_switcher) to handle and it's effectively
serialized with itself, I found it more convenient to use kthread instead of
workqueue. This also allows for a working implementation of suspend/resume,
which were totally broken in the past version.
Signed-off-by: Alex Dubov <oakad@yahoo.com>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/misc/tifm_7xx1.c')
-rw-r--r-- | drivers/misc/tifm_7xx1.c | 151 |
1 files changed, 89 insertions, 62 deletions
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 5ab81dd37857..d3e8ff46c237 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c | |||
@@ -11,6 +11,7 @@ | |||
11 | 11 | ||
12 | #include <linux/tifm.h> | 12 | #include <linux/tifm.h> |
13 | #include <linux/dma-mapping.h> | 13 | #include <linux/dma-mapping.h> |
14 | #include <linux/freezer.h> | ||
14 | 15 | ||
15 | #define DRIVER_NAME "tifm_7xx1" | 16 | #define DRIVER_NAME "tifm_7xx1" |
16 | #define DRIVER_VERSION "0.7" | 17 | #define DRIVER_VERSION "0.7" |
@@ -20,10 +21,8 @@ static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) | |||
20 | unsigned long flags; | 21 | unsigned long flags; |
21 | 22 | ||
22 | spin_lock_irqsave(&fm->lock, flags); | 23 | spin_lock_irqsave(&fm->lock, flags); |
23 | if (!fm->inhibit_new_cards) { | 24 | fm->socket_change_set |= 1 << sock->socket_id; |
24 | fm->socket_change_set |= 1 << sock->socket_id; | 25 | wake_up_all(&fm->change_set_notify); |
25 | queue_work(fm->wq, &fm->media_switcher); | ||
26 | } | ||
27 | spin_unlock_irqrestore(&fm->lock, flags); | 26 | spin_unlock_irqrestore(&fm->lock, flags); |
28 | } | 27 | } |
29 | 28 | ||
@@ -59,14 +58,10 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) | |||
59 | } | 58 | } |
60 | writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); | 59 | writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); |
61 | 60 | ||
62 | if (!fm->inhibit_new_cards) { | 61 | if (!fm->socket_change_set) |
63 | if (!fm->socket_change_set) { | 62 | writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); |
64 | writel(TIFM_IRQ_ENABLE, | 63 | else |
65 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 64 | wake_up_all(&fm->change_set_notify); |
66 | } else { | ||
67 | queue_work(fm->wq, &fm->media_switcher); | ||
68 | } | ||
69 | } | ||
70 | 65 | ||
71 | spin_unlock(&fm->lock); | 66 | spin_unlock(&fm->lock); |
72 | return IRQ_HANDLED; | 67 | return IRQ_HANDLED; |
@@ -123,21 +118,22 @@ tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num) | |||
123 | return base_addr + ((sock_num + 1) << 10); | 118 | return base_addr + ((sock_num + 1) << 10); |
124 | } | 119 | } |
125 | 120 | ||
126 | static void tifm_7xx1_switch_media(struct work_struct *work) | 121 | static int tifm_7xx1_switch_media(void *data) |
127 | { | 122 | { |
128 | struct tifm_adapter *fm = | 123 | struct tifm_adapter *fm = data; |
129 | container_of(work, struct tifm_adapter, media_switcher); | ||
130 | unsigned long flags; | 124 | unsigned long flags; |
131 | tifm_media_id media_id; | 125 | tifm_media_id media_id; |
132 | char *card_name = "xx"; | 126 | char *card_name = "xx"; |
133 | int cnt; | 127 | int cnt, rc; |
134 | struct tifm_dev *sock; | 128 | struct tifm_dev *sock; |
135 | unsigned int socket_change_set; | 129 | unsigned int socket_change_set; |
136 | 130 | ||
137 | if (!class_device_get(&fm->cdev)) | ||
138 | return; | ||
139 | |||
140 | while (1) { | 131 | while (1) { |
132 | rc = wait_event_interruptible(fm->change_set_notify, | ||
133 | fm->socket_change_set); | ||
134 | if (rc == -ERESTARTSYS) | ||
135 | try_to_freeze(); | ||
136 | |||
141 | spin_lock_irqsave(&fm->lock, flags); | 137 | spin_lock_irqsave(&fm->lock, flags); |
142 | socket_change_set = fm->socket_change_set; | 138 | socket_change_set = fm->socket_change_set; |
143 | fm->socket_change_set = 0; | 139 | fm->socket_change_set = 0; |
@@ -145,12 +141,12 @@ static void tifm_7xx1_switch_media(struct work_struct *work) | |||
145 | dev_dbg(fm->dev, "checking media set %x\n", | 141 | dev_dbg(fm->dev, "checking media set %x\n", |
146 | socket_change_set); | 142 | socket_change_set); |
147 | 143 | ||
148 | if (fm->inhibit_new_cards) | 144 | if (kthread_should_stop()) |
149 | socket_change_set = (1 << fm->num_sockets) - 1; | 145 | socket_change_set = (1 << fm->num_sockets) - 1; |
150 | spin_unlock_irqrestore(&fm->lock, flags); | 146 | spin_unlock_irqrestore(&fm->lock, flags); |
151 | 147 | ||
152 | if (!socket_change_set) | 148 | if (!socket_change_set) |
153 | break; | 149 | continue; |
154 | 150 | ||
155 | spin_lock_irqsave(&fm->lock, flags); | 151 | spin_lock_irqsave(&fm->lock, flags); |
156 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { | 152 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { |
@@ -169,7 +165,7 @@ static void tifm_7xx1_switch_media(struct work_struct *work) | |||
169 | tifm_7xx1_sock_addr(fm->addr, cnt) | 165 | tifm_7xx1_sock_addr(fm->addr, cnt) |
170 | + SOCK_CONTROL); | 166 | + SOCK_CONTROL); |
171 | } | 167 | } |
172 | if (fm->inhibit_new_cards) | 168 | if (kthread_should_stop()) |
173 | continue; | 169 | continue; |
174 | 170 | ||
175 | spin_unlock_irqrestore(&fm->lock, flags); | 171 | spin_unlock_irqrestore(&fm->lock, flags); |
@@ -218,7 +214,7 @@ static void tifm_7xx1_switch_media(struct work_struct *work) | |||
218 | } | 214 | } |
219 | } | 215 | } |
220 | 216 | ||
221 | if (!fm->inhibit_new_cards) { | 217 | if (!kthread_should_stop()) { |
222 | writel(TIFM_IRQ_FIFOMASK(socket_change_set) | 218 | writel(TIFM_IRQ_FIFOMASK(socket_change_set) |
223 | | TIFM_IRQ_CARDMASK(socket_change_set), | 219 | | TIFM_IRQ_CARDMASK(socket_change_set), |
224 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | 220 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); |
@@ -228,7 +224,6 @@ static void tifm_7xx1_switch_media(struct work_struct *work) | |||
228 | writel(TIFM_IRQ_ENABLE, | 224 | writel(TIFM_IRQ_ENABLE, |
229 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 225 | fm->addr + FM_SET_INTERRUPT_ENABLE); |
230 | spin_unlock_irqrestore(&fm->lock, flags); | 226 | spin_unlock_irqrestore(&fm->lock, flags); |
231 | break; | ||
232 | } else { | 227 | } else { |
233 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { | 228 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { |
234 | if (fm->sockets[cnt]) | 229 | if (fm->sockets[cnt]) |
@@ -236,56 +231,93 @@ static void tifm_7xx1_switch_media(struct work_struct *work) | |||
236 | } | 231 | } |
237 | if (!fm->socket_change_set) { | 232 | if (!fm->socket_change_set) { |
238 | spin_unlock_irqrestore(&fm->lock, flags); | 233 | spin_unlock_irqrestore(&fm->lock, flags); |
239 | break; | 234 | return 0; |
240 | } else { | 235 | } else { |
241 | spin_unlock_irqrestore(&fm->lock, flags); | 236 | spin_unlock_irqrestore(&fm->lock, flags); |
242 | } | 237 | } |
243 | } | 238 | } |
244 | } | 239 | } |
245 | class_device_put(&fm->cdev); | 240 | return 0; |
246 | } | 241 | } |
247 | 242 | ||
243 | #ifdef CONFIG_PM | ||
244 | |||
248 | static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) | 245 | static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) |
249 | { | 246 | { |
250 | struct tifm_adapter *fm = pci_get_drvdata(dev); | 247 | dev_dbg(&dev->dev, "suspending host\n"); |
251 | unsigned long flags; | ||
252 | 248 | ||
253 | spin_lock_irqsave(&fm->lock, flags); | 249 | pci_save_state(dev); |
254 | fm->inhibit_new_cards = 1; | 250 | pci_enable_wake(dev, pci_choose_state(dev, state), 0); |
255 | fm->socket_change_set = 0xf; | 251 | pci_disable_device(dev); |
256 | writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | 252 | pci_set_power_state(dev, pci_choose_state(dev, state)); |
257 | spin_unlock_irqrestore(&fm->lock, flags); | ||
258 | flush_workqueue(fm->wq); | ||
259 | |||
260 | tifm_7xx1_switch_media(&fm->media_switcher); | ||
261 | |||
262 | pci_set_power_state(dev, PCI_D3hot); | ||
263 | pci_disable_device(dev); | ||
264 | pci_save_state(dev); | ||
265 | return 0; | 253 | return 0; |
266 | } | 254 | } |
267 | 255 | ||
268 | static int tifm_7xx1_resume(struct pci_dev *dev) | 256 | static int tifm_7xx1_resume(struct pci_dev *dev) |
269 | { | 257 | { |
270 | struct tifm_adapter *fm = pci_get_drvdata(dev); | 258 | struct tifm_adapter *fm = pci_get_drvdata(dev); |
259 | int cnt, rc; | ||
271 | unsigned long flags; | 260 | unsigned long flags; |
261 | tifm_media_id new_ids[fm->num_sockets]; | ||
272 | 262 | ||
263 | pci_set_power_state(dev, PCI_D0); | ||
273 | pci_restore_state(dev); | 264 | pci_restore_state(dev); |
274 | pci_enable_device(dev); | 265 | rc = pci_enable_device(dev); |
275 | pci_set_power_state(dev, PCI_D0); | 266 | if (rc) |
276 | pci_set_master(dev); | 267 | return rc; |
268 | pci_set_master(dev); | ||
269 | |||
270 | dev_dbg(&dev->dev, "resuming host\n"); | ||
277 | 271 | ||
272 | for (cnt = 0; cnt < fm->num_sockets; cnt++) | ||
273 | new_ids[cnt] = tifm_7xx1_toggle_sock_power( | ||
274 | tifm_7xx1_sock_addr(fm->addr, cnt), | ||
275 | fm->num_sockets == 2); | ||
278 | spin_lock_irqsave(&fm->lock, flags); | 276 | spin_lock_irqsave(&fm->lock, flags); |
279 | fm->inhibit_new_cards = 0; | 277 | fm->socket_change_set = 0; |
280 | writel(TIFM_IRQ_SETALL, fm->addr + FM_INTERRUPT_STATUS); | 278 | for (cnt = 0; cnt < fm->num_sockets; cnt++) { |
281 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | 279 | if (fm->sockets[cnt]) { |
280 | if (fm->sockets[cnt]->media_id == new_ids[cnt]) | ||
281 | fm->socket_change_set |= 1 << cnt; | ||
282 | |||
283 | fm->sockets[cnt]->media_id = new_ids[cnt]; | ||
284 | } | ||
285 | } | ||
286 | |||
282 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), | 287 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), |
283 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 288 | fm->addr + FM_SET_INTERRUPT_ENABLE); |
284 | fm->socket_change_set = 0xf; | 289 | if (!fm->socket_change_set) { |
290 | spin_unlock_irqrestore(&fm->lock, flags); | ||
291 | return 0; | ||
292 | } else { | ||
293 | fm->socket_change_set = 0; | ||
294 | spin_unlock_irqrestore(&fm->lock, flags); | ||
295 | } | ||
296 | |||
297 | wait_event_timeout(fm->change_set_notify, fm->socket_change_set, HZ); | ||
298 | |||
299 | spin_lock_irqsave(&fm->lock, flags); | ||
300 | writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) | ||
301 | | TIFM_IRQ_CARDMASK(fm->socket_change_set), | ||
302 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
303 | writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) | ||
304 | | TIFM_IRQ_CARDMASK(fm->socket_change_set), | ||
305 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
306 | writel(TIFM_IRQ_ENABLE, | ||
307 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
308 | fm->socket_change_set = 0; | ||
309 | |||
285 | spin_unlock_irqrestore(&fm->lock, flags); | 310 | spin_unlock_irqrestore(&fm->lock, flags); |
286 | return 0; | 311 | return 0; |
287 | } | 312 | } |
288 | 313 | ||
314 | #else | ||
315 | |||
316 | #define tifm_7xx1_suspend NULL | ||
317 | #define tifm_7xx1_resume NULL | ||
318 | |||
319 | #endif /* CONFIG_PM */ | ||
320 | |||
289 | static int tifm_7xx1_probe(struct pci_dev *dev, | 321 | static int tifm_7xx1_probe(struct pci_dev *dev, |
290 | const struct pci_device_id *dev_id) | 322 | const struct pci_device_id *dev_id) |
291 | { | 323 | { |
@@ -324,7 +356,6 @@ static int tifm_7xx1_probe(struct pci_dev *dev, | |||
324 | if (!fm->sockets) | 356 | if (!fm->sockets) |
325 | goto err_out_free; | 357 | goto err_out_free; |
326 | 358 | ||
327 | INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media); | ||
328 | fm->eject = tifm_7xx1_eject; | 359 | fm->eject = tifm_7xx1_eject; |
329 | pci_set_drvdata(dev, fm); | 360 | pci_set_drvdata(dev, fm); |
330 | 361 | ||
@@ -337,16 +368,15 @@ static int tifm_7xx1_probe(struct pci_dev *dev, | |||
337 | if (rc) | 368 | if (rc) |
338 | goto err_out_unmap; | 369 | goto err_out_unmap; |
339 | 370 | ||
340 | rc = tifm_add_adapter(fm); | 371 | init_waitqueue_head(&fm->change_set_notify); |
372 | rc = tifm_add_adapter(fm, tifm_7xx1_switch_media); | ||
341 | if (rc) | 373 | if (rc) |
342 | goto err_out_irq; | 374 | goto err_out_irq; |
343 | 375 | ||
344 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | 376 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); |
345 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), | 377 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), |
346 | fm->addr + FM_SET_INTERRUPT_ENABLE); | 378 | fm->addr + FM_SET_INTERRUPT_ENABLE); |
347 | 379 | wake_up_process(fm->media_switcher); | |
348 | fm->socket_change_set = 0xf; | ||
349 | |||
350 | return 0; | 380 | return 0; |
351 | 381 | ||
352 | err_out_irq: | 382 | err_out_irq: |
@@ -370,18 +400,15 @@ static void tifm_7xx1_remove(struct pci_dev *dev) | |||
370 | struct tifm_adapter *fm = pci_get_drvdata(dev); | 400 | struct tifm_adapter *fm = pci_get_drvdata(dev); |
371 | unsigned long flags; | 401 | unsigned long flags; |
372 | 402 | ||
403 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
404 | mmiowb(); | ||
405 | free_irq(dev->irq, fm); | ||
406 | |||
373 | spin_lock_irqsave(&fm->lock, flags); | 407 | spin_lock_irqsave(&fm->lock, flags); |
374 | fm->inhibit_new_cards = 1; | 408 | fm->socket_change_set = (1 << fm->num_sockets) - 1; |
375 | fm->socket_change_set = 0xf; | ||
376 | writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
377 | spin_unlock_irqrestore(&fm->lock, flags); | 409 | spin_unlock_irqrestore(&fm->lock, flags); |
378 | 410 | ||
379 | flush_workqueue(fm->wq); | 411 | kthread_stop(fm->media_switcher); |
380 | |||
381 | tifm_7xx1_switch_media(&fm->media_switcher); | ||
382 | |||
383 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
384 | free_irq(dev->irq, fm); | ||
385 | 412 | ||
386 | tifm_remove_adapter(fm); | 413 | tifm_remove_adapter(fm); |
387 | 414 | ||