aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/tifm_7xx1.c
diff options
context:
space:
mode:
authorAlex Dubov <oakad@yahoo.com>2006-12-17 22:20:06 -0500
committerPierre Ossman <drzeus@drzeus.cx>2007-02-04 14:54:09 -0500
commit7146f0d3bd2bcd0100a5db54f4ba9edc1042fe01 (patch)
treebb8dd99b153d6aa8fe565be7256a586b0abd2977 /drivers/misc/tifm_7xx1.c
parent6412d927313f08808d61b7efba8da43717d4e8d2 (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.c151
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
126static void tifm_7xx1_switch_media(struct work_struct *work) 121static 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
248static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) 245static 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
268static int tifm_7xx1_resume(struct pci_dev *dev) 256static 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
289static int tifm_7xx1_probe(struct pci_dev *dev, 321static 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
352err_out_irq: 382err_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