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