Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[linux-2.6] / drivers / misc / tifm_7xx1.c
1 /*
2  *  tifm_7xx1.c - TI FlashMedia driver
3  *
4  *  Copyright (C) 2006 Alex Dubov <oakad@yahoo.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  */
11
12 #include <linux/tifm.h>
13 #include <linux/dma-mapping.h>
14 #include <linux/freezer.h>
15
16 #define DRIVER_NAME "tifm_7xx1"
17 #define DRIVER_VERSION "0.7"
18
19 static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock)
20 {
21         unsigned long flags;
22
23         spin_lock_irqsave(&fm->lock, flags);
24         fm->socket_change_set |= 1 << sock->socket_id;
25         wake_up_all(&fm->change_set_notify);
26         spin_unlock_irqrestore(&fm->lock, flags);
27 }
28
29 static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id)
30 {
31         struct tifm_adapter *fm = dev_id;
32         struct tifm_dev *sock;
33         unsigned int irq_status;
34         unsigned int sock_irq_status, cnt;
35
36         spin_lock(&fm->lock);
37         irq_status = readl(fm->addr + FM_INTERRUPT_STATUS);
38         if (irq_status == 0 || irq_status == (~0)) {
39                 spin_unlock(&fm->lock);
40                 return IRQ_NONE;
41         }
42
43         if (irq_status & TIFM_IRQ_ENABLE) {
44                 writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
45
46                 for (cnt = 0; cnt < fm->num_sockets; cnt++) {
47                         sock = fm->sockets[cnt];
48                         sock_irq_status = (irq_status >> cnt)
49                                           & (TIFM_IRQ_FIFOMASK(1)
50                                              | TIFM_IRQ_CARDMASK(1));
51
52                         if (sock && sock_irq_status)
53                                 sock->signal_irq(sock, sock_irq_status);
54                 }
55
56                 fm->socket_change_set |= irq_status
57                                          & ((1 << fm->num_sockets) - 1);
58         }
59         writel(irq_status, fm->addr + FM_INTERRUPT_STATUS);
60
61         if (!fm->socket_change_set)
62                 writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE);
63         else
64                 wake_up_all(&fm->change_set_notify);
65
66         spin_unlock(&fm->lock);
67         return IRQ_HANDLED;
68 }
69
70 static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr,
71                                                  int is_x2)
72 {
73         unsigned int s_state;
74         int cnt;
75
76         writel(0x0e00, sock_addr + SOCK_CONTROL);
77
78         for (cnt = 0; cnt < 100; cnt++) {
79                 if (!(TIFM_SOCK_STATE_POWERED
80                       & readl(sock_addr + SOCK_PRESENT_STATE)))
81                         break;
82                 msleep(10);
83         }
84
85         s_state = readl(sock_addr + SOCK_PRESENT_STATE);
86         if (!(TIFM_SOCK_STATE_OCCUPIED & s_state))
87                 return FM_NULL;
88
89         if (is_x2) {
90                 writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL);
91         } else {
92                 // SmartMedia cards need extra 40 msec
93                 if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1)
94                         msleep(40);
95                 writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED,
96                        sock_addr + SOCK_CONTROL);
97                 msleep(10);
98                 writel((s_state & 0x7) | 0x0c00 | TIFM_CTRL_LED,
99                         sock_addr + SOCK_CONTROL);
100         }
101
102         for (cnt = 0; cnt < 100; cnt++) {
103                 if ((TIFM_SOCK_STATE_POWERED
104                      & readl(sock_addr + SOCK_PRESENT_STATE)))
105                         break;
106                 msleep(10);
107         }
108
109         if (!is_x2)
110                 writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED),
111                        sock_addr + SOCK_CONTROL);
112
113         return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7;
114 }
115
116 inline static char __iomem *
117 tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num)
118 {
119         return base_addr + ((sock_num + 1) << 10);
120 }
121
122 static int tifm_7xx1_switch_media(void *data)
123 {
124         struct tifm_adapter *fm = data;
125         unsigned long flags;
126         tifm_media_id media_id;
127         char *card_name = "xx";
128         int cnt, rc;
129         struct tifm_dev *sock;
130         unsigned int socket_change_set;
131
132         while (1) {
133                 rc = wait_event_interruptible(fm->change_set_notify,
134                                               fm->socket_change_set);
135                 if (rc == -ERESTARTSYS)
136                         try_to_freeze();
137
138                 spin_lock_irqsave(&fm->lock, flags);
139                 socket_change_set = fm->socket_change_set;
140                 fm->socket_change_set = 0;
141
142                 dev_dbg(fm->dev, "checking media set %x\n",
143                         socket_change_set);
144
145                 if (kthread_should_stop())
146                         socket_change_set = (1 << fm->num_sockets) - 1;
147                 spin_unlock_irqrestore(&fm->lock, flags);
148
149                 if (!socket_change_set)
150                         continue;
151
152                 spin_lock_irqsave(&fm->lock, flags);
153                 for (cnt = 0; cnt < fm->num_sockets; cnt++) {
154                         if (!(socket_change_set & (1 << cnt)))
155                                 continue;
156                         sock = fm->sockets[cnt];
157                         if (sock) {
158                                 printk(KERN_INFO DRIVER_NAME
159                                        ": demand removing card from socket %d\n",
160                                        cnt);
161                                 fm->sockets[cnt] = NULL;
162                                 spin_unlock_irqrestore(&fm->lock, flags);
163                                 device_unregister(&sock->dev);
164                                 spin_lock_irqsave(&fm->lock, flags);
165                                 writel(0x0e00,
166                                        tifm_7xx1_sock_addr(fm->addr, cnt)
167                                        + SOCK_CONTROL);
168                         }
169                         if (kthread_should_stop())
170                                 continue;
171
172                         spin_unlock_irqrestore(&fm->lock, flags);
173                         media_id = tifm_7xx1_toggle_sock_power(
174                                         tifm_7xx1_sock_addr(fm->addr, cnt),
175                                         fm->num_sockets == 2);
176                         if (media_id) {
177                                 sock = tifm_alloc_device(fm);
178                                 if (sock) {
179                                         sock->addr = tifm_7xx1_sock_addr(fm->addr,
180                                                                          cnt);
181                                         sock->media_id = media_id;
182                                         sock->socket_id = cnt;
183                                         switch (media_id) {
184                                         case 1:
185                                                 card_name = "xd";
186                                                 break;
187                                         case 2:
188                                                 card_name = "ms";
189                                                 break;
190                                         case 3:
191                                                 card_name = "sd";
192                                                 break;
193                                         default:
194                                                 tifm_free_device(&sock->dev);
195                                                 spin_lock_irqsave(&fm->lock, flags);
196                                                 continue;
197                                         }
198                                         snprintf(sock->dev.bus_id, BUS_ID_SIZE,
199                                                  "tifm_%s%u:%u", card_name,
200                                                  fm->id, cnt);
201                                         printk(KERN_INFO DRIVER_NAME
202                                                ": %s card detected in socket %d\n",
203                                                card_name, cnt);
204                                         if (!device_register(&sock->dev)) {
205                                                 spin_lock_irqsave(&fm->lock, flags);
206                                                 if (!fm->sockets[cnt]) {
207                                                         fm->sockets[cnt] = sock;
208                                                         sock = NULL;
209                                                 }
210                                                 spin_unlock_irqrestore(&fm->lock, flags);
211                                         }
212                                         if (sock)
213                                                 tifm_free_device(&sock->dev);
214                                 }
215                                 spin_lock_irqsave(&fm->lock, flags);
216                         }
217                 }
218
219                 if (!kthread_should_stop()) {
220                         writel(TIFM_IRQ_FIFOMASK(socket_change_set)
221                                | TIFM_IRQ_CARDMASK(socket_change_set),
222                                fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
223                         writel(TIFM_IRQ_FIFOMASK(socket_change_set)
224                                | TIFM_IRQ_CARDMASK(socket_change_set),
225                                fm->addr + FM_SET_INTERRUPT_ENABLE);
226                         writel(TIFM_IRQ_ENABLE,
227                                fm->addr + FM_SET_INTERRUPT_ENABLE);
228                         spin_unlock_irqrestore(&fm->lock, flags);
229                 } else {
230                         for (cnt = 0; cnt < fm->num_sockets; cnt++) {
231                                 if (fm->sockets[cnt])
232                                         fm->socket_change_set |= 1 << cnt;
233                         }
234                         if (!fm->socket_change_set) {
235                                 spin_unlock_irqrestore(&fm->lock, flags);
236                                 return 0;
237                         } else {
238                                 spin_unlock_irqrestore(&fm->lock, flags);
239                         }
240                 }
241         }
242         return 0;
243 }
244
245 #ifdef CONFIG_PM
246
247 static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state)
248 {
249         dev_dbg(&dev->dev, "suspending host\n");
250
251         pci_save_state(dev);
252         pci_enable_wake(dev, pci_choose_state(dev, state), 0);
253         pci_disable_device(dev);
254         pci_set_power_state(dev, pci_choose_state(dev, state));
255         return 0;
256 }
257
258 static int tifm_7xx1_resume(struct pci_dev *dev)
259 {
260         struct tifm_adapter *fm = pci_get_drvdata(dev);
261         int cnt, rc;
262         unsigned long flags;
263         tifm_media_id new_ids[fm->num_sockets];
264
265         pci_set_power_state(dev, PCI_D0);
266         pci_restore_state(dev);
267         rc = pci_enable_device(dev);
268         if (rc)
269                 return rc;
270         pci_set_master(dev);
271
272         dev_dbg(&dev->dev, "resuming host\n");
273
274         for (cnt = 0; cnt < fm->num_sockets; cnt++)
275                 new_ids[cnt] = tifm_7xx1_toggle_sock_power(
276                                         tifm_7xx1_sock_addr(fm->addr, cnt),
277                                         fm->num_sockets == 2);
278         spin_lock_irqsave(&fm->lock, flags);
279         fm->socket_change_set = 0;
280         for (cnt = 0; cnt < fm->num_sockets; cnt++) {
281                 if (fm->sockets[cnt]) {
282                         if (fm->sockets[cnt]->media_id == new_ids[cnt])
283                                 fm->socket_change_set |= 1 << cnt;
284
285                         fm->sockets[cnt]->media_id = new_ids[cnt];
286                 }
287         }
288
289         writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
290                fm->addr + FM_SET_INTERRUPT_ENABLE);
291         if (!fm->socket_change_set) {
292                 spin_unlock_irqrestore(&fm->lock, flags);
293                 return 0;
294         } else {
295                 fm->socket_change_set = 0;
296                 spin_unlock_irqrestore(&fm->lock, flags);
297         }
298
299         wait_event_timeout(fm->change_set_notify, fm->socket_change_set, HZ);
300
301         spin_lock_irqsave(&fm->lock, flags);
302         writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
303                | TIFM_IRQ_CARDMASK(fm->socket_change_set),
304                fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
305         writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
306                | TIFM_IRQ_CARDMASK(fm->socket_change_set),
307                fm->addr + FM_SET_INTERRUPT_ENABLE);
308         writel(TIFM_IRQ_ENABLE,
309                fm->addr + FM_SET_INTERRUPT_ENABLE);
310         fm->socket_change_set = 0;
311
312         spin_unlock_irqrestore(&fm->lock, flags);
313         return 0;
314 }
315
316 #else
317
318 #define tifm_7xx1_suspend NULL
319 #define tifm_7xx1_resume NULL
320
321 #endif /* CONFIG_PM */
322
323 static int tifm_7xx1_probe(struct pci_dev *dev,
324                            const struct pci_device_id *dev_id)
325 {
326         struct tifm_adapter *fm;
327         int pci_dev_busy = 0;
328         int rc;
329
330         rc = pci_set_dma_mask(dev, DMA_32BIT_MASK);
331         if (rc)
332                 return rc;
333
334         rc = pci_enable_device(dev);
335         if (rc)
336                 return rc;
337
338         pci_set_master(dev);
339
340         rc = pci_request_regions(dev, DRIVER_NAME);
341         if (rc) {
342                 pci_dev_busy = 1;
343                 goto err_out;
344         }
345
346         pci_intx(dev, 1);
347
348         fm = tifm_alloc_adapter();
349         if (!fm) {
350                 rc = -ENOMEM;
351                 goto err_out_int;
352         }
353
354         fm->dev = &dev->dev;
355         fm->num_sockets = (dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM)
356                           ? 4 : 2;
357         fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->num_sockets,
358                               GFP_KERNEL);
359         if (!fm->sockets)
360                 goto err_out_free;
361
362         fm->eject = tifm_7xx1_eject;
363         pci_set_drvdata(dev, fm);
364
365         fm->addr = ioremap(pci_resource_start(dev, 0),
366                            pci_resource_len(dev, 0));
367         if (!fm->addr)
368                 goto err_out_free;
369
370         rc = request_irq(dev->irq, tifm_7xx1_isr, SA_SHIRQ, DRIVER_NAME, fm);
371         if (rc)
372                 goto err_out_unmap;
373
374         init_waitqueue_head(&fm->change_set_notify);
375         rc = tifm_add_adapter(fm, tifm_7xx1_switch_media);
376         if (rc)
377                 goto err_out_irq;
378
379         writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
380         writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
381                fm->addr + FM_SET_INTERRUPT_ENABLE);
382         wake_up_process(fm->media_switcher);
383         return 0;
384
385 err_out_irq:
386         free_irq(dev->irq, fm);
387 err_out_unmap:
388         iounmap(fm->addr);
389 err_out_free:
390         pci_set_drvdata(dev, NULL);
391         tifm_free_adapter(fm);
392 err_out_int:
393         pci_intx(dev, 0);
394         pci_release_regions(dev);
395 err_out:
396         if (!pci_dev_busy)
397                 pci_disable_device(dev);
398         return rc;
399 }
400
401 static void tifm_7xx1_remove(struct pci_dev *dev)
402 {
403         struct tifm_adapter *fm = pci_get_drvdata(dev);
404         unsigned long flags;
405
406         writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
407         mmiowb();
408         free_irq(dev->irq, fm);
409
410         spin_lock_irqsave(&fm->lock, flags);
411         fm->socket_change_set = (1 << fm->num_sockets) - 1;
412         spin_unlock_irqrestore(&fm->lock, flags);
413
414         kthread_stop(fm->media_switcher);
415
416         tifm_remove_adapter(fm);
417
418         pci_set_drvdata(dev, NULL);
419
420         iounmap(fm->addr);
421         pci_intx(dev, 0);
422         pci_release_regions(dev);
423
424         pci_disable_device(dev);
425         tifm_free_adapter(fm);
426 }
427
428 static struct pci_device_id tifm_7xx1_pci_tbl [] = {
429         { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX21_XX11_FM, PCI_ANY_ID,
430           PCI_ANY_ID, 0, 0, 0 }, /* xx21 - the one I have */
431         { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX12_FM, PCI_ANY_ID,
432           PCI_ANY_ID, 0, 0, 0 },
433         { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX20_FM, PCI_ANY_ID,
434           PCI_ANY_ID, 0, 0, 0 },
435         { }
436 };
437
438 static struct pci_driver tifm_7xx1_driver = {
439         .name = DRIVER_NAME,
440         .id_table = tifm_7xx1_pci_tbl,
441         .probe = tifm_7xx1_probe,
442         .remove = tifm_7xx1_remove,
443         .suspend = tifm_7xx1_suspend,
444         .resume = tifm_7xx1_resume,
445 };
446
447 static int __init tifm_7xx1_init(void)
448 {
449         return pci_register_driver(&tifm_7xx1_driver);
450 }
451
452 static void __exit tifm_7xx1_exit(void)
453 {
454         pci_unregister_driver(&tifm_7xx1_driver);
455 }
456
457 MODULE_AUTHOR("Alex Dubov");
458 MODULE_DESCRIPTION("TI FlashMedia host driver");
459 MODULE_LICENSE("GPL");
460 MODULE_DEVICE_TABLE(pci, tifm_7xx1_pci_tbl);
461 MODULE_VERSION(DRIVER_VERSION);
462
463 module_init(tifm_7xx1_init);
464 module_exit(tifm_7xx1_exit);