diff options
author | Alex Dubov <oakad@yahoo.com> | 2006-10-04 05:15:37 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-04 10:55:14 -0400 |
commit | 4020f2d7f0b0e68b92bec9a3e1f4a54a7a9dc672 (patch) | |
tree | afbe35984c945c5f81b2f29f3c54a144abeca3f0 | |
parent | 856fe98f168e5b80b053979769af2514aab96d6b (diff) |
[PATCH] mmc: driver for TI FlashMedia card reader - source
Driver for TI Flash Media card reader. At present, only MMC/SD cards are
supported.
[akpm@osdl.org: cleanups, build fixes]
Signed-off-by: Alex Dubov <oakad@yahoo.com>
Cc: Daniel Qarras <dqarras@yahoo.com>
Acked-by: Pierre Ossman <drzeus@drzeus.cx>
Cc: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | MAINTAINERS | 5 | ||||
-rw-r--r-- | drivers/misc/tifm_7xx1.c | 437 | ||||
-rw-r--r-- | drivers/misc/tifm_core.c | 272 | ||||
-rw-r--r-- | drivers/mmc/tifm_sd.c | 933 | ||||
-rw-r--r-- | include/linux/tifm.h | 158 |
5 files changed, 1805 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 1c6223d3ce70..77e58585ce55 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -2854,6 +2854,11 @@ M: hlhung3i@gmail.com | |||
2854 | W: http://tcp-lp-mod.sourceforge.net/ | 2854 | W: http://tcp-lp-mod.sourceforge.net/ |
2855 | S: Maintained | 2855 | S: Maintained |
2856 | 2856 | ||
2857 | TI FLASH MEDIA INTERFACE DRIVER | ||
2858 | P: Alex Dubov | ||
2859 | M: oakad@yahoo.com | ||
2860 | S: Maintained | ||
2861 | |||
2857 | TI OMAP RANDOM NUMBER GENERATOR SUPPORT | 2862 | TI OMAP RANDOM NUMBER GENERATOR SUPPORT |
2858 | P: Deepak Saxena | 2863 | P: Deepak Saxena |
2859 | M: dsaxena@plexity.net | 2864 | M: dsaxena@plexity.net |
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c new file mode 100644 index 000000000000..a7ed30446185 --- /dev/null +++ b/drivers/misc/tifm_7xx1.c | |||
@@ -0,0 +1,437 @@ | |||
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 | |||
15 | #define DRIVER_NAME "tifm_7xx1" | ||
16 | #define DRIVER_VERSION "0.6" | ||
17 | |||
18 | static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) | ||
19 | { | ||
20 | int cnt; | ||
21 | unsigned long flags; | ||
22 | |||
23 | spin_lock_irqsave(&fm->lock, flags); | ||
24 | if (!fm->inhibit_new_cards) { | ||
25 | for (cnt = 0; cnt < fm->max_sockets; cnt++) { | ||
26 | if (fm->sockets[cnt] == sock) { | ||
27 | fm->remove_mask |= (1 << cnt); | ||
28 | queue_work(fm->wq, &fm->media_remover); | ||
29 | break; | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | spin_unlock_irqrestore(&fm->lock, flags); | ||
34 | } | ||
35 | |||
36 | static void tifm_7xx1_remove_media(void *adapter) | ||
37 | { | ||
38 | struct tifm_adapter *fm = adapter; | ||
39 | unsigned long flags; | ||
40 | int cnt; | ||
41 | struct tifm_dev *sock; | ||
42 | |||
43 | if (!class_device_get(&fm->cdev)) | ||
44 | return; | ||
45 | spin_lock_irqsave(&fm->lock, flags); | ||
46 | for (cnt = 0; cnt < fm->max_sockets; cnt++) { | ||
47 | if (fm->sockets[cnt] && (fm->remove_mask & (1 << cnt))) { | ||
48 | printk(KERN_INFO DRIVER_NAME | ||
49 | ": demand removing card from socket %d\n", cnt); | ||
50 | sock = fm->sockets[cnt]; | ||
51 | fm->sockets[cnt] = 0; | ||
52 | fm->remove_mask &= ~(1 << cnt); | ||
53 | |||
54 | writel(0x0e00, sock->addr + SOCK_CONTROL); | ||
55 | |||
56 | writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, | ||
57 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
58 | writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, | ||
59 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
60 | |||
61 | spin_unlock_irqrestore(&fm->lock, flags); | ||
62 | device_unregister(&sock->dev); | ||
63 | spin_lock_irqsave(&fm->lock, flags); | ||
64 | } | ||
65 | } | ||
66 | spin_unlock_irqrestore(&fm->lock, flags); | ||
67 | class_device_put(&fm->cdev); | ||
68 | } | ||
69 | |||
70 | static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id, struct pt_regs *regs) | ||
71 | { | ||
72 | struct tifm_adapter *fm = dev_id; | ||
73 | unsigned int irq_status; | ||
74 | unsigned int sock_irq_status, cnt; | ||
75 | |||
76 | spin_lock(&fm->lock); | ||
77 | irq_status = readl(fm->addr + FM_INTERRUPT_STATUS); | ||
78 | if (irq_status == 0 || irq_status == (~0)) { | ||
79 | spin_unlock(&fm->lock); | ||
80 | return IRQ_NONE; | ||
81 | } | ||
82 | |||
83 | if (irq_status & TIFM_IRQ_ENABLE) { | ||
84 | writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
85 | |||
86 | for (cnt = 0; cnt < fm->max_sockets; cnt++) { | ||
87 | sock_irq_status = (irq_status >> cnt) & | ||
88 | (TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK); | ||
89 | |||
90 | if (fm->sockets[cnt]) { | ||
91 | if (sock_irq_status && | ||
92 | fm->sockets[cnt]->signal_irq) | ||
93 | sock_irq_status = fm->sockets[cnt]-> | ||
94 | signal_irq(fm->sockets[cnt], | ||
95 | sock_irq_status); | ||
96 | |||
97 | if (irq_status & (1 << cnt)) | ||
98 | fm->remove_mask |= 1 << cnt; | ||
99 | } else { | ||
100 | if (irq_status & (1 << cnt)) | ||
101 | fm->insert_mask |= 1 << cnt; | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); | ||
106 | |||
107 | if (!fm->inhibit_new_cards) { | ||
108 | if (!fm->remove_mask && !fm->insert_mask) { | ||
109 | writel(TIFM_IRQ_ENABLE, | ||
110 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
111 | } else { | ||
112 | queue_work(fm->wq, &fm->media_remover); | ||
113 | queue_work(fm->wq, &fm->media_inserter); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | spin_unlock(&fm->lock); | ||
118 | return IRQ_HANDLED; | ||
119 | } | ||
120 | |||
121 | static tifm_media_id tifm_7xx1_toggle_sock_power(char *sock_addr, int is_x2) | ||
122 | { | ||
123 | unsigned int s_state; | ||
124 | int cnt; | ||
125 | |||
126 | writel(0x0e00, sock_addr + SOCK_CONTROL); | ||
127 | |||
128 | for (cnt = 0; cnt < 100; cnt++) { | ||
129 | if (!(TIFM_SOCK_STATE_POWERED & | ||
130 | readl(sock_addr + SOCK_PRESENT_STATE))) | ||
131 | break; | ||
132 | msleep(10); | ||
133 | } | ||
134 | |||
135 | s_state = readl(sock_addr + SOCK_PRESENT_STATE); | ||
136 | if (!(TIFM_SOCK_STATE_OCCUPIED & s_state)) | ||
137 | return FM_NULL; | ||
138 | |||
139 | if (is_x2) { | ||
140 | writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL); | ||
141 | } else { | ||
142 | // SmartMedia cards need extra 40 msec | ||
143 | if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1) | ||
144 | msleep(40); | ||
145 | writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED, | ||
146 | sock_addr + SOCK_CONTROL); | ||
147 | msleep(10); | ||
148 | writel((s_state & 0x7) | 0x0c00 | TIFM_CTRL_LED, | ||
149 | sock_addr + SOCK_CONTROL); | ||
150 | } | ||
151 | |||
152 | for (cnt = 0; cnt < 100; cnt++) { | ||
153 | if ((TIFM_SOCK_STATE_POWERED & | ||
154 | readl(sock_addr + SOCK_PRESENT_STATE))) | ||
155 | break; | ||
156 | msleep(10); | ||
157 | } | ||
158 | |||
159 | if (!is_x2) | ||
160 | writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED), | ||
161 | sock_addr + SOCK_CONTROL); | ||
162 | |||
163 | return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7; | ||
164 | } | ||
165 | |||
166 | inline static char *tifm_7xx1_sock_addr(char *base_addr, unsigned int sock_num) | ||
167 | { | ||
168 | return base_addr + ((sock_num + 1) << 10); | ||
169 | } | ||
170 | |||
171 | static void tifm_7xx1_insert_media(void *adapter) | ||
172 | { | ||
173 | struct tifm_adapter *fm = adapter; | ||
174 | unsigned long flags; | ||
175 | tifm_media_id media_id; | ||
176 | char *card_name = "xx"; | ||
177 | int cnt, ok_to_register; | ||
178 | unsigned int insert_mask; | ||
179 | struct tifm_dev *new_sock = 0; | ||
180 | |||
181 | if (!class_device_get(&fm->cdev)) | ||
182 | return; | ||
183 | spin_lock_irqsave(&fm->lock, flags); | ||
184 | insert_mask = fm->insert_mask; | ||
185 | fm->insert_mask = 0; | ||
186 | if (fm->inhibit_new_cards) { | ||
187 | spin_unlock_irqrestore(&fm->lock, flags); | ||
188 | class_device_put(&fm->cdev); | ||
189 | return; | ||
190 | } | ||
191 | spin_unlock_irqrestore(&fm->lock, flags); | ||
192 | |||
193 | for (cnt = 0; cnt < fm->max_sockets; cnt++) { | ||
194 | if (!(insert_mask & (1 << cnt))) | ||
195 | continue; | ||
196 | |||
197 | media_id = tifm_7xx1_toggle_sock_power(tifm_7xx1_sock_addr(fm->addr, cnt), | ||
198 | fm->max_sockets == 2); | ||
199 | if (media_id) { | ||
200 | ok_to_register = 0; | ||
201 | new_sock = tifm_alloc_device(fm, cnt); | ||
202 | if (new_sock) { | ||
203 | new_sock->addr = tifm_7xx1_sock_addr(fm->addr, | ||
204 | cnt); | ||
205 | new_sock->media_id = media_id; | ||
206 | switch (media_id) { | ||
207 | case 1: | ||
208 | card_name = "xd"; | ||
209 | break; | ||
210 | case 2: | ||
211 | card_name = "ms"; | ||
212 | break; | ||
213 | case 3: | ||
214 | card_name = "sd"; | ||
215 | break; | ||
216 | default: | ||
217 | break; | ||
218 | } | ||
219 | snprintf(new_sock->dev.bus_id, BUS_ID_SIZE, | ||
220 | "tifm_%s%u:%u", card_name, fm->id, cnt); | ||
221 | printk(KERN_INFO DRIVER_NAME | ||
222 | ": %s card detected in socket %d\n", | ||
223 | card_name, cnt); | ||
224 | spin_lock_irqsave(&fm->lock, flags); | ||
225 | if (!fm->sockets[cnt]) { | ||
226 | fm->sockets[cnt] = new_sock; | ||
227 | ok_to_register = 1; | ||
228 | } | ||
229 | spin_unlock_irqrestore(&fm->lock, flags); | ||
230 | if (!ok_to_register || | ||
231 | device_register(&new_sock->dev)) { | ||
232 | spin_lock_irqsave(&fm->lock, flags); | ||
233 | fm->sockets[cnt] = 0; | ||
234 | spin_unlock_irqrestore(&fm->lock, | ||
235 | flags); | ||
236 | tifm_free_device(&new_sock->dev); | ||
237 | } | ||
238 | } | ||
239 | } | ||
240 | writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, | ||
241 | fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
242 | writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, | ||
243 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
244 | } | ||
245 | |||
246 | writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
247 | class_device_put(&fm->cdev); | ||
248 | } | ||
249 | |||
250 | static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) | ||
251 | { | ||
252 | struct tifm_adapter *fm = pci_get_drvdata(dev); | ||
253 | unsigned long flags; | ||
254 | |||
255 | spin_lock_irqsave(&fm->lock, flags); | ||
256 | fm->inhibit_new_cards = 1; | ||
257 | fm->remove_mask = 0xf; | ||
258 | fm->insert_mask = 0; | ||
259 | writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
260 | spin_unlock_irqrestore(&fm->lock, flags); | ||
261 | flush_workqueue(fm->wq); | ||
262 | |||
263 | tifm_7xx1_remove_media(fm); | ||
264 | |||
265 | pci_set_power_state(dev, PCI_D3hot); | ||
266 | pci_disable_device(dev); | ||
267 | pci_save_state(dev); | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int tifm_7xx1_resume(struct pci_dev *dev) | ||
272 | { | ||
273 | struct tifm_adapter *fm = pci_get_drvdata(dev); | ||
274 | unsigned long flags; | ||
275 | |||
276 | pci_restore_state(dev); | ||
277 | pci_enable_device(dev); | ||
278 | pci_set_power_state(dev, PCI_D0); | ||
279 | pci_set_master(dev); | ||
280 | |||
281 | spin_lock_irqsave(&fm->lock, flags); | ||
282 | fm->inhibit_new_cards = 0; | ||
283 | writel(TIFM_IRQ_SETALL, fm->addr + FM_INTERRUPT_STATUS); | ||
284 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
285 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SETALLSOCK, | ||
286 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
287 | fm->insert_mask = 0xf; | ||
288 | spin_unlock_irqrestore(&fm->lock, flags); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int tifm_7xx1_probe(struct pci_dev *dev, | ||
293 | const struct pci_device_id *dev_id) | ||
294 | { | ||
295 | struct tifm_adapter *fm; | ||
296 | int pci_dev_busy = 0; | ||
297 | int rc; | ||
298 | |||
299 | rc = pci_set_dma_mask(dev, DMA_32BIT_MASK); | ||
300 | if (rc) | ||
301 | return rc; | ||
302 | |||
303 | rc = pci_enable_device(dev); | ||
304 | if (rc) | ||
305 | return rc; | ||
306 | |||
307 | pci_set_master(dev); | ||
308 | |||
309 | rc = pci_request_regions(dev, DRIVER_NAME); | ||
310 | if (rc) { | ||
311 | pci_dev_busy = 1; | ||
312 | goto err_out; | ||
313 | } | ||
314 | |||
315 | pci_intx(dev, 1); | ||
316 | |||
317 | fm = tifm_alloc_adapter(); | ||
318 | if (!fm) { | ||
319 | rc = -ENOMEM; | ||
320 | goto err_out_int; | ||
321 | } | ||
322 | |||
323 | fm->dev = &dev->dev; | ||
324 | fm->max_sockets = (dev->device == 0x803B) ? 2 : 4; | ||
325 | fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->max_sockets, | ||
326 | GFP_KERNEL); | ||
327 | if (!fm->sockets) | ||
328 | goto err_out_free; | ||
329 | |||
330 | INIT_WORK(&fm->media_inserter, tifm_7xx1_insert_media, fm); | ||
331 | INIT_WORK(&fm->media_remover, tifm_7xx1_remove_media, fm); | ||
332 | fm->eject = tifm_7xx1_eject; | ||
333 | pci_set_drvdata(dev, fm); | ||
334 | |||
335 | fm->addr = ioremap(pci_resource_start(dev, 0), | ||
336 | pci_resource_len(dev, 0)); | ||
337 | if (!fm->addr) | ||
338 | goto err_out_free; | ||
339 | |||
340 | rc = request_irq(dev->irq, tifm_7xx1_isr, SA_SHIRQ, DRIVER_NAME, fm); | ||
341 | if (rc) | ||
342 | goto err_out_unmap; | ||
343 | |||
344 | rc = tifm_add_adapter(fm); | ||
345 | if (rc) | ||
346 | goto err_out_irq; | ||
347 | |||
348 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
349 | writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SETALLSOCK, | ||
350 | fm->addr + FM_SET_INTERRUPT_ENABLE); | ||
351 | |||
352 | fm->insert_mask = 0xf; | ||
353 | |||
354 | return 0; | ||
355 | |||
356 | err_out_irq: | ||
357 | free_irq(dev->irq, fm); | ||
358 | err_out_unmap: | ||
359 | iounmap(fm->addr); | ||
360 | err_out_free: | ||
361 | pci_set_drvdata(dev, NULL); | ||
362 | tifm_free_adapter(fm); | ||
363 | err_out_int: | ||
364 | pci_intx(dev, 0); | ||
365 | pci_release_regions(dev); | ||
366 | err_out: | ||
367 | if (!pci_dev_busy) | ||
368 | pci_disable_device(dev); | ||
369 | return rc; | ||
370 | } | ||
371 | |||
372 | static void tifm_7xx1_remove(struct pci_dev *dev) | ||
373 | { | ||
374 | struct tifm_adapter *fm = pci_get_drvdata(dev); | ||
375 | unsigned long flags; | ||
376 | |||
377 | spin_lock_irqsave(&fm->lock, flags); | ||
378 | fm->inhibit_new_cards = 1; | ||
379 | fm->remove_mask = 0xf; | ||
380 | fm->insert_mask = 0; | ||
381 | writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
382 | spin_unlock_irqrestore(&fm->lock, flags); | ||
383 | |||
384 | flush_workqueue(fm->wq); | ||
385 | |||
386 | tifm_7xx1_remove_media(fm); | ||
387 | |||
388 | writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); | ||
389 | free_irq(dev->irq, fm); | ||
390 | |||
391 | tifm_remove_adapter(fm); | ||
392 | |||
393 | pci_set_drvdata(dev, 0); | ||
394 | |||
395 | iounmap(fm->addr); | ||
396 | pci_intx(dev, 0); | ||
397 | pci_release_regions(dev); | ||
398 | |||
399 | pci_disable_device(dev); | ||
400 | tifm_free_adapter(fm); | ||
401 | } | ||
402 | |||
403 | static struct pci_device_id tifm_7xx1_pci_tbl [] = { | ||
404 | { PCI_VENDOR_ID_TI, 0x8033, PCI_ANY_ID, PCI_ANY_ID, 0, 0, | ||
405 | 0 }, /* xx21 - the one I have */ | ||
406 | { PCI_VENDOR_ID_TI, 0x803B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, | ||
407 | 0 }, /* xx12 - should be also supported */ | ||
408 | { } | ||
409 | }; | ||
410 | |||
411 | static struct pci_driver tifm_7xx1_driver = { | ||
412 | .name = DRIVER_NAME, | ||
413 | .id_table = tifm_7xx1_pci_tbl, | ||
414 | .probe = tifm_7xx1_probe, | ||
415 | .remove = tifm_7xx1_remove, | ||
416 | .suspend = tifm_7xx1_suspend, | ||
417 | .resume = tifm_7xx1_resume, | ||
418 | }; | ||
419 | |||
420 | static int __init tifm_7xx1_init(void) | ||
421 | { | ||
422 | return pci_register_driver(&tifm_7xx1_driver); | ||
423 | } | ||
424 | |||
425 | static void __exit tifm_7xx1_exit(void) | ||
426 | { | ||
427 | pci_unregister_driver(&tifm_7xx1_driver); | ||
428 | } | ||
429 | |||
430 | MODULE_AUTHOR("Alex Dubov"); | ||
431 | MODULE_DESCRIPTION("TI FlashMedia host driver"); | ||
432 | MODULE_LICENSE("GPL"); | ||
433 | MODULE_DEVICE_TABLE(pci, tifm_7xx1_pci_tbl); | ||
434 | MODULE_VERSION(DRIVER_VERSION); | ||
435 | |||
436 | module_init(tifm_7xx1_init); | ||
437 | module_exit(tifm_7xx1_exit); | ||
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c new file mode 100644 index 000000000000..cca5f8522469 --- /dev/null +++ b/drivers/misc/tifm_core.c | |||
@@ -0,0 +1,272 @@ | |||
1 | /* | ||
2 | * tifm_core.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/init.h> | ||
14 | #include <linux/idr.h> | ||
15 | |||
16 | #define DRIVER_NAME "tifm_core" | ||
17 | #define DRIVER_VERSION "0.6" | ||
18 | |||
19 | static DEFINE_IDR(tifm_adapter_idr); | ||
20 | static DEFINE_SPINLOCK(tifm_adapter_lock); | ||
21 | |||
22 | static tifm_media_id *tifm_device_match(tifm_media_id *ids, | ||
23 | struct tifm_dev *dev) | ||
24 | { | ||
25 | while (*ids) { | ||
26 | if (dev->media_id == *ids) | ||
27 | return ids; | ||
28 | ids++; | ||
29 | } | ||
30 | return NULL; | ||
31 | } | ||
32 | |||
33 | static int tifm_match(struct device *dev, struct device_driver *drv) | ||
34 | { | ||
35 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | ||
36 | struct tifm_driver *fm_drv; | ||
37 | |||
38 | fm_drv = container_of(drv, struct tifm_driver, driver); | ||
39 | if (!fm_drv->id_table) | ||
40 | return -EINVAL; | ||
41 | if (tifm_device_match(fm_drv->id_table, fm_dev)) | ||
42 | return 1; | ||
43 | return -ENODEV; | ||
44 | } | ||
45 | |||
46 | static int tifm_uevent(struct device *dev, char **envp, int num_envp, | ||
47 | char *buffer, int buffer_size) | ||
48 | { | ||
49 | struct tifm_dev *fm_dev; | ||
50 | int i = 0; | ||
51 | int length = 0; | ||
52 | const char *card_type_name[] = {"INV", "SM", "MS", "SD"}; | ||
53 | |||
54 | if (!dev || !(fm_dev = container_of(dev, struct tifm_dev, dev))) | ||
55 | return -ENODEV; | ||
56 | if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, | ||
57 | "TIFM_CARD_TYPE=%s", card_type_name[fm_dev->media_id])) | ||
58 | return -ENOMEM; | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static struct bus_type tifm_bus_type = { | ||
64 | .name = "tifm", | ||
65 | .match = tifm_match, | ||
66 | .uevent = tifm_uevent, | ||
67 | }; | ||
68 | |||
69 | static void tifm_free(struct class_device *cdev) | ||
70 | { | ||
71 | struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev); | ||
72 | |||
73 | kfree(fm->sockets); | ||
74 | if (fm->wq) | ||
75 | destroy_workqueue(fm->wq); | ||
76 | kfree(fm); | ||
77 | } | ||
78 | |||
79 | static struct class tifm_adapter_class = { | ||
80 | .name = "tifm_adapter", | ||
81 | .release = tifm_free | ||
82 | }; | ||
83 | |||
84 | struct tifm_adapter *tifm_alloc_adapter(void) | ||
85 | { | ||
86 | struct tifm_adapter *fm; | ||
87 | |||
88 | fm = kzalloc(sizeof(struct tifm_adapter), GFP_KERNEL); | ||
89 | if (fm) { | ||
90 | fm->cdev.class = &tifm_adapter_class; | ||
91 | spin_lock_init(&fm->lock); | ||
92 | class_device_initialize(&fm->cdev); | ||
93 | } | ||
94 | return fm; | ||
95 | } | ||
96 | EXPORT_SYMBOL(tifm_alloc_adapter); | ||
97 | |||
98 | void tifm_free_adapter(struct tifm_adapter *fm) | ||
99 | { | ||
100 | class_device_put(&fm->cdev); | ||
101 | } | ||
102 | EXPORT_SYMBOL(tifm_free_adapter); | ||
103 | |||
104 | int tifm_add_adapter(struct tifm_adapter *fm) | ||
105 | { | ||
106 | int rc; | ||
107 | |||
108 | if (!idr_pre_get(&tifm_adapter_idr, GFP_KERNEL)) | ||
109 | return -ENOMEM; | ||
110 | |||
111 | spin_lock(&tifm_adapter_lock); | ||
112 | rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id); | ||
113 | spin_unlock(&tifm_adapter_lock); | ||
114 | if (!rc) { | ||
115 | snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); | ||
116 | strncpy(fm->wq_name, fm->cdev.class_id, KOBJ_NAME_LEN); | ||
117 | |||
118 | fm->wq = create_singlethread_workqueue(fm->wq_name); | ||
119 | if (fm->wq) | ||
120 | return class_device_add(&fm->cdev); | ||
121 | |||
122 | spin_lock(&tifm_adapter_lock); | ||
123 | idr_remove(&tifm_adapter_idr, fm->id); | ||
124 | spin_unlock(&tifm_adapter_lock); | ||
125 | rc = -ENOMEM; | ||
126 | } | ||
127 | return rc; | ||
128 | } | ||
129 | EXPORT_SYMBOL(tifm_add_adapter); | ||
130 | |||
131 | void tifm_remove_adapter(struct tifm_adapter *fm) | ||
132 | { | ||
133 | class_device_del(&fm->cdev); | ||
134 | |||
135 | spin_lock(&tifm_adapter_lock); | ||
136 | idr_remove(&tifm_adapter_idr, fm->id); | ||
137 | spin_unlock(&tifm_adapter_lock); | ||
138 | } | ||
139 | EXPORT_SYMBOL(tifm_remove_adapter); | ||
140 | |||
141 | void tifm_free_device(struct device *dev) | ||
142 | { | ||
143 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | ||
144 | if (fm_dev->wq) | ||
145 | destroy_workqueue(fm_dev->wq); | ||
146 | kfree(fm_dev); | ||
147 | } | ||
148 | EXPORT_SYMBOL(tifm_free_device); | ||
149 | |||
150 | struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id) | ||
151 | { | ||
152 | struct tifm_dev *dev = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); | ||
153 | |||
154 | if (dev) { | ||
155 | spin_lock_init(&dev->lock); | ||
156 | snprintf(dev->wq_name, KOBJ_NAME_LEN, "tifm%u:%u", fm->id, id); | ||
157 | dev->wq = create_singlethread_workqueue(dev->wq_name); | ||
158 | if (!dev->wq) { | ||
159 | kfree(dev); | ||
160 | return 0; | ||
161 | } | ||
162 | dev->dev.parent = fm->dev; | ||
163 | dev->dev.bus = &tifm_bus_type; | ||
164 | dev->dev.release = tifm_free_device; | ||
165 | } | ||
166 | return dev; | ||
167 | } | ||
168 | EXPORT_SYMBOL(tifm_alloc_device); | ||
169 | |||
170 | void tifm_eject(struct tifm_dev *sock) | ||
171 | { | ||
172 | struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent); | ||
173 | fm->eject(fm, sock); | ||
174 | } | ||
175 | EXPORT_SYMBOL(tifm_eject); | ||
176 | |||
177 | int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, | ||
178 | int direction) | ||
179 | { | ||
180 | return pci_map_sg(to_pci_dev(sock->dev.parent), sg, nents, direction); | ||
181 | } | ||
182 | EXPORT_SYMBOL(tifm_map_sg); | ||
183 | |||
184 | void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, | ||
185 | int direction) | ||
186 | { | ||
187 | pci_unmap_sg(to_pci_dev(sock->dev.parent), sg, nents, direction); | ||
188 | } | ||
189 | EXPORT_SYMBOL(tifm_unmap_sg); | ||
190 | |||
191 | static int tifm_device_probe(struct device *dev) | ||
192 | { | ||
193 | struct tifm_driver *drv; | ||
194 | struct tifm_dev *fm_dev; | ||
195 | int rc = 0; | ||
196 | const tifm_media_id *id; | ||
197 | |||
198 | drv = container_of(dev->driver, struct tifm_driver, driver); | ||
199 | fm_dev = container_of(dev, struct tifm_dev, dev); | ||
200 | get_device(dev); | ||
201 | if (!fm_dev->drv && drv->probe && drv->id_table) { | ||
202 | rc = -ENODEV; | ||
203 | id = tifm_device_match(drv->id_table, fm_dev); | ||
204 | if (id) | ||
205 | rc = drv->probe(fm_dev); | ||
206 | if (rc >= 0) { | ||
207 | rc = 0; | ||
208 | fm_dev->drv = drv; | ||
209 | } | ||
210 | } | ||
211 | if (rc) | ||
212 | put_device(dev); | ||
213 | return rc; | ||
214 | } | ||
215 | |||
216 | static int tifm_device_remove(struct device *dev) | ||
217 | { | ||
218 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | ||
219 | struct tifm_driver *drv = fm_dev->drv; | ||
220 | |||
221 | if (drv) { | ||
222 | if (drv->remove) drv->remove(fm_dev); | ||
223 | fm_dev->drv = 0; | ||
224 | } | ||
225 | |||
226 | put_device(dev); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | int tifm_register_driver(struct tifm_driver *drv) | ||
231 | { | ||
232 | drv->driver.bus = &tifm_bus_type; | ||
233 | drv->driver.probe = tifm_device_probe; | ||
234 | drv->driver.remove = tifm_device_remove; | ||
235 | |||
236 | return driver_register(&drv->driver); | ||
237 | } | ||
238 | EXPORT_SYMBOL(tifm_register_driver); | ||
239 | |||
240 | void tifm_unregister_driver(struct tifm_driver *drv) | ||
241 | { | ||
242 | driver_unregister(&drv->driver); | ||
243 | } | ||
244 | EXPORT_SYMBOL(tifm_unregister_driver); | ||
245 | |||
246 | static int __init tifm_init(void) | ||
247 | { | ||
248 | int rc = bus_register(&tifm_bus_type); | ||
249 | |||
250 | if (!rc) { | ||
251 | rc = class_register(&tifm_adapter_class); | ||
252 | if (rc) | ||
253 | bus_unregister(&tifm_bus_type); | ||
254 | } | ||
255 | |||
256 | return rc; | ||
257 | } | ||
258 | |||
259 | static void __exit tifm_exit(void) | ||
260 | { | ||
261 | class_unregister(&tifm_adapter_class); | ||
262 | bus_unregister(&tifm_bus_type); | ||
263 | } | ||
264 | |||
265 | subsys_initcall(tifm_init); | ||
266 | module_exit(tifm_exit); | ||
267 | |||
268 | MODULE_LICENSE("GPL"); | ||
269 | MODULE_AUTHOR("Alex Dubov"); | ||
270 | MODULE_DESCRIPTION("TI FlashMedia core driver"); | ||
271 | MODULE_LICENSE("GPL"); | ||
272 | MODULE_VERSION(DRIVER_VERSION); | ||
diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c new file mode 100644 index 000000000000..6d23dc08d169 --- /dev/null +++ b/drivers/mmc/tifm_sd.c | |||
@@ -0,0 +1,933 @@ | |||
1 | /* | ||
2 | * tifm_sd.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 | |||
13 | #include <linux/tifm.h> | ||
14 | #include <linux/mmc/protocol.h> | ||
15 | #include <linux/mmc/host.h> | ||
16 | #include <linux/highmem.h> | ||
17 | |||
18 | #define DRIVER_NAME "tifm_sd" | ||
19 | #define DRIVER_VERSION "0.6" | ||
20 | |||
21 | static int no_dma = 0; | ||
22 | static int fixed_timeout = 0; | ||
23 | module_param(no_dma, bool, 0644); | ||
24 | module_param(fixed_timeout, bool, 0644); | ||
25 | |||
26 | /* Constants here are mostly from OMAP5912 datasheet */ | ||
27 | #define TIFM_MMCSD_RESET 0x0002 | ||
28 | #define TIFM_MMCSD_CLKMASK 0x03ff | ||
29 | #define TIFM_MMCSD_POWER 0x0800 | ||
30 | #define TIFM_MMCSD_4BBUS 0x8000 | ||
31 | #define TIFM_MMCSD_RXDE 0x8000 /* rx dma enable */ | ||
32 | #define TIFM_MMCSD_TXDE 0x0080 /* tx dma enable */ | ||
33 | #define TIFM_MMCSD_BUFINT 0x0c00 /* set bits: AE, AF */ | ||
34 | #define TIFM_MMCSD_DPE 0x0020 /* data timeout counted in kilocycles */ | ||
35 | #define TIFM_MMCSD_INAB 0x0080 /* abort / initialize command */ | ||
36 | #define TIFM_MMCSD_READ 0x8000 | ||
37 | |||
38 | #define TIFM_MMCSD_DATAMASK 0x001d /* set bits: EOFB, BRS, CB, EOC */ | ||
39 | #define TIFM_MMCSD_ERRMASK 0x41e0 /* set bits: CERR, CCRC, CTO, DCRC, DTO */ | ||
40 | #define TIFM_MMCSD_EOC 0x0001 /* end of command phase */ | ||
41 | #define TIFM_MMCSD_CB 0x0004 /* card enter busy state */ | ||
42 | #define TIFM_MMCSD_BRS 0x0008 /* block received/sent */ | ||
43 | #define TIFM_MMCSD_EOFB 0x0010 /* card exit busy state */ | ||
44 | #define TIFM_MMCSD_DTO 0x0020 /* data time-out */ | ||
45 | #define TIFM_MMCSD_DCRC 0x0040 /* data crc error */ | ||
46 | #define TIFM_MMCSD_CTO 0x0080 /* command time-out */ | ||
47 | #define TIFM_MMCSD_CCRC 0x0100 /* command crc error */ | ||
48 | #define TIFM_MMCSD_AF 0x0400 /* fifo almost full */ | ||
49 | #define TIFM_MMCSD_AE 0x0800 /* fifo almost empty */ | ||
50 | #define TIFM_MMCSD_CERR 0x4000 /* card status error */ | ||
51 | |||
52 | #define TIFM_MMCSD_FIFO_SIZE 0x0020 | ||
53 | |||
54 | #define TIFM_MMCSD_RSP_R0 0x0000 | ||
55 | #define TIFM_MMCSD_RSP_R1 0x0100 | ||
56 | #define TIFM_MMCSD_RSP_R2 0x0200 | ||
57 | #define TIFM_MMCSD_RSP_R3 0x0300 | ||
58 | #define TIFM_MMCSD_RSP_R4 0x0400 | ||
59 | #define TIFM_MMCSD_RSP_R5 0x0500 | ||
60 | #define TIFM_MMCSD_RSP_R6 0x0600 | ||
61 | |||
62 | #define TIFM_MMCSD_RSP_BUSY 0x0800 | ||
63 | |||
64 | #define TIFM_MMCSD_CMD_BC 0x0000 | ||
65 | #define TIFM_MMCSD_CMD_BCR 0x1000 | ||
66 | #define TIFM_MMCSD_CMD_AC 0x2000 | ||
67 | #define TIFM_MMCSD_CMD_ADTC 0x3000 | ||
68 | |||
69 | typedef enum { | ||
70 | IDLE = 0, | ||
71 | CMD, /* main command ended */ | ||
72 | BRS, /* block transfer finished */ | ||
73 | SCMD, /* stop command ended */ | ||
74 | CARD, /* card left busy state */ | ||
75 | FIFO, /* FIFO operation completed (uncertain) */ | ||
76 | READY | ||
77 | } card_state_t; | ||
78 | |||
79 | enum { | ||
80 | FIFO_RDY = 0x0001, /* hardware dependent value */ | ||
81 | HOST_REG = 0x0002, | ||
82 | EJECT = 0x0004, | ||
83 | EJECT_DONE = 0x0008, | ||
84 | CARD_BUSY = 0x0010, | ||
85 | OPENDRAIN = 0x0040, /* hardware dependent value */ | ||
86 | CARD_EVENT = 0x0100, /* hardware dependent value */ | ||
87 | CARD_RO = 0x0200, /* hardware dependent value */ | ||
88 | FIFO_EVENT = 0x10000 }; /* hardware dependent value */ | ||
89 | |||
90 | struct tifm_sd { | ||
91 | struct tifm_dev *dev; | ||
92 | |||
93 | unsigned int flags; | ||
94 | card_state_t state; | ||
95 | unsigned int clk_freq; | ||
96 | unsigned int clk_div; | ||
97 | unsigned long timeout_jiffies; // software timeout - 2 sec | ||
98 | |||
99 | struct mmc_request *req; | ||
100 | struct work_struct cmd_handler; | ||
101 | struct work_struct abort_handler; | ||
102 | wait_queue_head_t can_eject; | ||
103 | |||
104 | size_t written_blocks; | ||
105 | char *buffer; | ||
106 | size_t buffer_size; | ||
107 | size_t buffer_pos; | ||
108 | |||
109 | }; | ||
110 | |||
111 | static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, | ||
112 | unsigned int host_status) | ||
113 | { | ||
114 | struct mmc_command *cmd = host->req->cmd; | ||
115 | unsigned int t_val = 0, cnt = 0; | ||
116 | |||
117 | if (host_status & TIFM_MMCSD_BRS) { | ||
118 | /* in non-dma rx mode BRS fires when fifo is still not empty */ | ||
119 | if (host->buffer && (cmd->data->flags & MMC_DATA_READ)) { | ||
120 | while (host->buffer_size > host->buffer_pos) { | ||
121 | t_val = readl(sock->addr + SOCK_MMCSD_DATA); | ||
122 | host->buffer[host->buffer_pos++] = t_val & 0xff; | ||
123 | host->buffer[host->buffer_pos++] = | ||
124 | (t_val >> 8) & 0xff; | ||
125 | } | ||
126 | } | ||
127 | return 1; | ||
128 | } else if (host->buffer) { | ||
129 | if ((cmd->data->flags & MMC_DATA_READ) && | ||
130 | (host_status & TIFM_MMCSD_AF)) { | ||
131 | for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { | ||
132 | t_val = readl(sock->addr + SOCK_MMCSD_DATA); | ||
133 | if (host->buffer_size > host->buffer_pos) { | ||
134 | host->buffer[host->buffer_pos++] = | ||
135 | t_val & 0xff; | ||
136 | host->buffer[host->buffer_pos++] = | ||
137 | (t_val >> 8) & 0xff; | ||
138 | } | ||
139 | } | ||
140 | } else if ((cmd->data->flags & MMC_DATA_WRITE) | ||
141 | && (host_status & TIFM_MMCSD_AE)) { | ||
142 | for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { | ||
143 | if (host->buffer_size > host->buffer_pos) { | ||
144 | t_val = host->buffer[host->buffer_pos++] & 0x00ff; | ||
145 | t_val |= ((host->buffer[host->buffer_pos++]) << 8) | ||
146 | & 0xff00; | ||
147 | writel(t_val, | ||
148 | sock->addr + SOCK_MMCSD_DATA); | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | } | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static unsigned int tifm_sd_op_flags(struct mmc_command *cmd) | ||
157 | { | ||
158 | unsigned int rc = 0; | ||
159 | |||
160 | switch (mmc_resp_type(cmd)) { | ||
161 | case MMC_RSP_NONE: | ||
162 | rc |= TIFM_MMCSD_RSP_R0; | ||
163 | break; | ||
164 | case MMC_RSP_R1B: | ||
165 | rc |= TIFM_MMCSD_RSP_BUSY; // deliberate fall-through | ||
166 | case MMC_RSP_R1: | ||
167 | rc |= TIFM_MMCSD_RSP_R1; | ||
168 | break; | ||
169 | case MMC_RSP_R2: | ||
170 | rc |= TIFM_MMCSD_RSP_R2; | ||
171 | break; | ||
172 | case MMC_RSP_R3: | ||
173 | rc |= TIFM_MMCSD_RSP_R3; | ||
174 | break; | ||
175 | case MMC_RSP_R6: | ||
176 | rc |= TIFM_MMCSD_RSP_R6; | ||
177 | break; | ||
178 | default: | ||
179 | BUG(); | ||
180 | } | ||
181 | |||
182 | switch (mmc_cmd_type(cmd)) { | ||
183 | case MMC_CMD_BC: | ||
184 | rc |= TIFM_MMCSD_CMD_BC; | ||
185 | break; | ||
186 | case MMC_CMD_BCR: | ||
187 | rc |= TIFM_MMCSD_CMD_BCR; | ||
188 | break; | ||
189 | case MMC_CMD_AC: | ||
190 | rc |= TIFM_MMCSD_CMD_AC; | ||
191 | break; | ||
192 | case MMC_CMD_ADTC: | ||
193 | rc |= TIFM_MMCSD_CMD_ADTC; | ||
194 | break; | ||
195 | default: | ||
196 | BUG(); | ||
197 | } | ||
198 | return rc; | ||
199 | } | ||
200 | |||
201 | static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd) | ||
202 | { | ||
203 | struct tifm_dev *sock = host->dev; | ||
204 | unsigned int cmd_mask = tifm_sd_op_flags(cmd) | | ||
205 | (host->flags & OPENDRAIN); | ||
206 | |||
207 | if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) | ||
208 | cmd_mask |= TIFM_MMCSD_READ; | ||
209 | |||
210 | dev_dbg(&sock->dev, "executing opcode 0x%x, arg: 0x%x, mask: 0x%x\n", | ||
211 | cmd->opcode, cmd->arg, cmd_mask); | ||
212 | |||
213 | writel((cmd->arg >> 16) & 0xffff, sock->addr + SOCK_MMCSD_ARG_HIGH); | ||
214 | writel(cmd->arg & 0xffff, sock->addr + SOCK_MMCSD_ARG_LOW); | ||
215 | writel(cmd->opcode | cmd_mask, sock->addr + SOCK_MMCSD_COMMAND); | ||
216 | } | ||
217 | |||
218 | static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock) | ||
219 | { | ||
220 | cmd->resp[0] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x1c) << 16) | ||
221 | | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x18); | ||
222 | cmd->resp[1] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x14) << 16) | ||
223 | | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x10); | ||
224 | cmd->resp[2] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x0c) << 16) | ||
225 | | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x08); | ||
226 | cmd->resp[3] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x04) << 16) | ||
227 | | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00); | ||
228 | } | ||
229 | |||
230 | static void tifm_sd_process_cmd(struct tifm_dev *sock, struct tifm_sd *host, | ||
231 | unsigned int host_status) | ||
232 | { | ||
233 | struct mmc_command *cmd = host->req->cmd; | ||
234 | |||
235 | change_state: | ||
236 | switch (host->state) { | ||
237 | case IDLE: | ||
238 | return; | ||
239 | case CMD: | ||
240 | if (host_status & TIFM_MMCSD_EOC) { | ||
241 | tifm_sd_fetch_resp(cmd, sock); | ||
242 | if (cmd->data) { | ||
243 | host->state = BRS; | ||
244 | } else | ||
245 | host->state = READY; | ||
246 | goto change_state; | ||
247 | } | ||
248 | break; | ||
249 | case BRS: | ||
250 | if (tifm_sd_transfer_data(sock, host, host_status)) { | ||
251 | if (!host->req->stop) { | ||
252 | if (cmd->data->flags & MMC_DATA_WRITE) { | ||
253 | host->state = CARD; | ||
254 | } else { | ||
255 | host->state = | ||
256 | host->buffer ? READY : FIFO; | ||
257 | } | ||
258 | goto change_state; | ||
259 | } | ||
260 | tifm_sd_exec(host, host->req->stop); | ||
261 | host->state = SCMD; | ||
262 | } | ||
263 | break; | ||
264 | case SCMD: | ||
265 | if (host_status & TIFM_MMCSD_EOC) { | ||
266 | tifm_sd_fetch_resp(host->req->stop, sock); | ||
267 | if (cmd->error) { | ||
268 | host->state = READY; | ||
269 | } else if (cmd->data->flags & MMC_DATA_WRITE) { | ||
270 | host->state = CARD; | ||
271 | } else { | ||
272 | host->state = host->buffer ? READY : FIFO; | ||
273 | } | ||
274 | goto change_state; | ||
275 | } | ||
276 | break; | ||
277 | case CARD: | ||
278 | if (!(host->flags & CARD_BUSY) | ||
279 | && (host->written_blocks == cmd->data->blocks)) { | ||
280 | host->state = host->buffer ? READY : FIFO; | ||
281 | goto change_state; | ||
282 | } | ||
283 | break; | ||
284 | case FIFO: | ||
285 | if (host->flags & FIFO_RDY) { | ||
286 | host->state = READY; | ||
287 | host->flags &= ~FIFO_RDY; | ||
288 | goto change_state; | ||
289 | } | ||
290 | break; | ||
291 | case READY: | ||
292 | queue_work(sock->wq, &host->cmd_handler); | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | queue_delayed_work(sock->wq, &host->abort_handler, | ||
297 | host->timeout_jiffies); | ||
298 | } | ||
299 | |||
300 | /* Called from interrupt handler */ | ||
301 | static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, | ||
302 | unsigned int sock_irq_status) | ||
303 | { | ||
304 | struct tifm_sd *host; | ||
305 | unsigned int host_status = 0, fifo_status = 0; | ||
306 | int error_code = 0; | ||
307 | |||
308 | spin_lock(&sock->lock); | ||
309 | host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); | ||
310 | cancel_delayed_work(&host->abort_handler); | ||
311 | |||
312 | if (sock_irq_status & FIFO_EVENT) { | ||
313 | fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); | ||
314 | writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); | ||
315 | |||
316 | host->flags |= fifo_status & FIFO_RDY; | ||
317 | } | ||
318 | |||
319 | if (sock_irq_status & CARD_EVENT) { | ||
320 | host_status = readl(sock->addr + SOCK_MMCSD_STATUS); | ||
321 | writel(host_status, sock->addr + SOCK_MMCSD_STATUS); | ||
322 | |||
323 | if (!(host->flags & HOST_REG)) | ||
324 | queue_work(sock->wq, &host->cmd_handler); | ||
325 | if (!host->req) | ||
326 | goto done; | ||
327 | |||
328 | if (host_status & TIFM_MMCSD_ERRMASK) { | ||
329 | if (host_status & TIFM_MMCSD_CERR) | ||
330 | error_code = MMC_ERR_FAILED; | ||
331 | else if (host_status & | ||
332 | (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO)) | ||
333 | error_code = MMC_ERR_TIMEOUT; | ||
334 | else if (host_status & | ||
335 | (TIFM_MMCSD_CCRC | TIFM_MMCSD_DCRC)) | ||
336 | error_code = MMC_ERR_BADCRC; | ||
337 | |||
338 | writel(TIFM_FIFO_INT_SETALL, | ||
339 | sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); | ||
340 | writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); | ||
341 | |||
342 | if (host->req->stop) { | ||
343 | if (host->state == SCMD) { | ||
344 | host->req->stop->error = error_code; | ||
345 | } else if(host->state == BRS) { | ||
346 | host->req->cmd->error = error_code; | ||
347 | tifm_sd_exec(host, host->req->stop); | ||
348 | queue_delayed_work(sock->wq, | ||
349 | &host->abort_handler, | ||
350 | host->timeout_jiffies); | ||
351 | host->state = SCMD; | ||
352 | goto done; | ||
353 | } else { | ||
354 | host->req->cmd->error = error_code; | ||
355 | } | ||
356 | } else { | ||
357 | host->req->cmd->error = error_code; | ||
358 | } | ||
359 | host->state = READY; | ||
360 | } | ||
361 | |||
362 | if (host_status & TIFM_MMCSD_CB) | ||
363 | host->flags |= CARD_BUSY; | ||
364 | if ((host_status & TIFM_MMCSD_EOFB) && | ||
365 | (host->flags & CARD_BUSY)) { | ||
366 | host->written_blocks++; | ||
367 | host->flags &= ~CARD_BUSY; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | if (host->req) | ||
372 | tifm_sd_process_cmd(sock, host, host_status); | ||
373 | done: | ||
374 | dev_dbg(&sock->dev, "host_status %x, fifo_status %x\n", | ||
375 | host_status, fifo_status); | ||
376 | spin_unlock(&sock->lock); | ||
377 | return sock_irq_status; | ||
378 | } | ||
379 | |||
380 | static void tifm_sd_prepare_data(struct tifm_sd *card, struct mmc_command *cmd) | ||
381 | { | ||
382 | struct tifm_dev *sock = card->dev; | ||
383 | unsigned int dest_cnt; | ||
384 | |||
385 | /* DMA style IO */ | ||
386 | |||
387 | writel(TIFM_FIFO_INT_SETALL, | ||
388 | sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); | ||
389 | writel(long_log2(cmd->data->blksz) - 2, | ||
390 | sock->addr + SOCK_FIFO_PAGE_SIZE); | ||
391 | writel(TIFM_FIFO_ENABLE, sock->addr + SOCK_FIFO_CONTROL); | ||
392 | writel(TIFM_FIFO_INTMASK, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); | ||
393 | |||
394 | dest_cnt = (cmd->data->blocks) << 8; | ||
395 | |||
396 | writel(sg_dma_address(cmd->data->sg), sock->addr + SOCK_DMA_ADDRESS); | ||
397 | |||
398 | writel(cmd->data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); | ||
399 | writel(cmd->data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); | ||
400 | |||
401 | if (cmd->data->flags & MMC_DATA_WRITE) { | ||
402 | writel(TIFM_MMCSD_TXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); | ||
403 | writel(dest_cnt | TIFM_DMA_TX | TIFM_DMA_EN, | ||
404 | sock->addr + SOCK_DMA_CONTROL); | ||
405 | } else { | ||
406 | writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); | ||
407 | writel(dest_cnt | TIFM_DMA_EN, sock->addr + SOCK_DMA_CONTROL); | ||
408 | } | ||
409 | } | ||
410 | |||
411 | static void tifm_sd_set_data_timeout(struct tifm_sd *host, | ||
412 | struct mmc_data *data) | ||
413 | { | ||
414 | struct tifm_dev *sock = host->dev; | ||
415 | unsigned int data_timeout = data->timeout_clks; | ||
416 | |||
417 | if (fixed_timeout) | ||
418 | return; | ||
419 | |||
420 | data_timeout += data->timeout_ns / | ||
421 | ((1000000000 / host->clk_freq) * host->clk_div); | ||
422 | data_timeout *= 10; // call it fudge factor for now | ||
423 | |||
424 | if (data_timeout < 0xffff) { | ||
425 | writel((~TIFM_MMCSD_DPE) & | ||
426 | readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), | ||
427 | sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); | ||
428 | writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); | ||
429 | } else { | ||
430 | writel(TIFM_MMCSD_DPE | | ||
431 | readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), | ||
432 | sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); | ||
433 | data_timeout = (data_timeout >> 10) + 1; | ||
434 | if(data_timeout > 0xffff) | ||
435 | data_timeout = 0; /* set to unlimited */ | ||
436 | writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) | ||
441 | { | ||
442 | struct tifm_sd *host = mmc_priv(mmc); | ||
443 | struct tifm_dev *sock = host->dev; | ||
444 | unsigned long flags; | ||
445 | int sg_count = 0; | ||
446 | struct mmc_data *r_data = mrq->cmd->data; | ||
447 | |||
448 | spin_lock_irqsave(&sock->lock, flags); | ||
449 | if (host->flags & EJECT) { | ||
450 | spin_unlock_irqrestore(&sock->lock, flags); | ||
451 | goto err_out; | ||
452 | } | ||
453 | |||
454 | if (host->req) { | ||
455 | printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n"); | ||
456 | spin_unlock_irqrestore(&sock->lock, flags); | ||
457 | goto err_out; | ||
458 | } | ||
459 | |||
460 | if (r_data) { | ||
461 | tifm_sd_set_data_timeout(host, r_data); | ||
462 | |||
463 | sg_count = tifm_map_sg(sock, r_data->sg, r_data->sg_len, | ||
464 | mrq->cmd->flags & MMC_DATA_WRITE | ||
465 | ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); | ||
466 | if (sg_count != 1) { | ||
467 | printk(KERN_ERR DRIVER_NAME | ||
468 | ": scatterlist map failed\n"); | ||
469 | spin_unlock_irqrestore(&sock->lock, flags); | ||
470 | goto err_out; | ||
471 | } | ||
472 | |||
473 | host->written_blocks = 0; | ||
474 | host->flags &= ~CARD_BUSY; | ||
475 | tifm_sd_prepare_data(host, mrq->cmd); | ||
476 | } | ||
477 | |||
478 | host->req = mrq; | ||
479 | host->state = CMD; | ||
480 | queue_delayed_work(sock->wq, &host->abort_handler, | ||
481 | host->timeout_jiffies); | ||
482 | writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), | ||
483 | sock->addr + SOCK_CONTROL); | ||
484 | tifm_sd_exec(host, mrq->cmd); | ||
485 | spin_unlock_irqrestore(&sock->lock, flags); | ||
486 | return; | ||
487 | |||
488 | err_out: | ||
489 | if (sg_count > 0) | ||
490 | tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, | ||
491 | (r_data->flags & MMC_DATA_WRITE) | ||
492 | ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); | ||
493 | |||
494 | mrq->cmd->error = MMC_ERR_TIMEOUT; | ||
495 | mmc_request_done(mmc, mrq); | ||
496 | } | ||
497 | |||
498 | static void tifm_sd_end_cmd(void *data) | ||
499 | { | ||
500 | struct tifm_sd *host = data; | ||
501 | struct tifm_dev *sock = host->dev; | ||
502 | struct mmc_host *mmc = tifm_get_drvdata(sock); | ||
503 | struct mmc_request *mrq; | ||
504 | struct mmc_data *r_data = 0; | ||
505 | unsigned long flags; | ||
506 | |||
507 | spin_lock_irqsave(&sock->lock, flags); | ||
508 | |||
509 | mrq = host->req; | ||
510 | host->req = 0; | ||
511 | host->state = IDLE; | ||
512 | |||
513 | if (!mrq) { | ||
514 | printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); | ||
515 | spin_unlock_irqrestore(&sock->lock, flags); | ||
516 | return; | ||
517 | } | ||
518 | |||
519 | r_data = mrq->cmd->data; | ||
520 | if (r_data) { | ||
521 | if (r_data->flags & MMC_DATA_WRITE) { | ||
522 | r_data->bytes_xfered = host->written_blocks * | ||
523 | r_data->blksz; | ||
524 | } else { | ||
525 | r_data->bytes_xfered = r_data->blocks - | ||
526 | readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; | ||
527 | r_data->bytes_xfered *= r_data->blksz; | ||
528 | r_data->bytes_xfered += r_data->blksz - | ||
529 | readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; | ||
530 | } | ||
531 | tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, | ||
532 | (r_data->flags & MMC_DATA_WRITE) | ||
533 | ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); | ||
534 | } | ||
535 | |||
536 | writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), | ||
537 | sock->addr + SOCK_CONTROL); | ||
538 | |||
539 | spin_unlock_irqrestore(&sock->lock, flags); | ||
540 | mmc_request_done(mmc, mrq); | ||
541 | } | ||
542 | |||
543 | static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) | ||
544 | { | ||
545 | struct tifm_sd *host = mmc_priv(mmc); | ||
546 | struct tifm_dev *sock = host->dev; | ||
547 | unsigned long flags; | ||
548 | struct mmc_data *r_data = mrq->cmd->data; | ||
549 | char *t_buffer = 0; | ||
550 | |||
551 | if (r_data) { | ||
552 | t_buffer = kmap(r_data->sg->page); | ||
553 | if (!t_buffer) { | ||
554 | printk(KERN_ERR DRIVER_NAME ": kmap failed\n"); | ||
555 | goto err_out; | ||
556 | } | ||
557 | } | ||
558 | |||
559 | spin_lock_irqsave(&sock->lock, flags); | ||
560 | if (host->flags & EJECT) { | ||
561 | spin_unlock_irqrestore(&sock->lock, flags); | ||
562 | goto err_out; | ||
563 | } | ||
564 | |||
565 | if (host->req) { | ||
566 | printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n"); | ||
567 | spin_unlock_irqrestore(&sock->lock, flags); | ||
568 | goto err_out; | ||
569 | } | ||
570 | |||
571 | if (r_data) { | ||
572 | tifm_sd_set_data_timeout(host, r_data); | ||
573 | |||
574 | host->buffer = t_buffer + r_data->sg->offset; | ||
575 | host->buffer_size = mrq->cmd->data->blocks * | ||
576 | mrq->cmd->data->blksz; | ||
577 | |||
578 | writel(TIFM_MMCSD_BUFINT | | ||
579 | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), | ||
580 | sock->addr + SOCK_MMCSD_INT_ENABLE); | ||
581 | writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) | | ||
582 | (TIFM_MMCSD_FIFO_SIZE - 1), | ||
583 | sock->addr + SOCK_MMCSD_BUFFER_CONFIG); | ||
584 | |||
585 | host->written_blocks = 0; | ||
586 | host->flags &= ~CARD_BUSY; | ||
587 | host->buffer_pos = 0; | ||
588 | writel(r_data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); | ||
589 | writel(r_data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); | ||
590 | } | ||
591 | |||
592 | host->req = mrq; | ||
593 | host->state = CMD; | ||
594 | queue_delayed_work(sock->wq, &host->abort_handler, | ||
595 | host->timeout_jiffies); | ||
596 | writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), | ||
597 | sock->addr + SOCK_CONTROL); | ||
598 | tifm_sd_exec(host, mrq->cmd); | ||
599 | spin_unlock_irqrestore(&sock->lock, flags); | ||
600 | return; | ||
601 | |||
602 | err_out: | ||
603 | if (t_buffer) | ||
604 | kunmap(r_data->sg->page); | ||
605 | |||
606 | mrq->cmd->error = MMC_ERR_TIMEOUT; | ||
607 | mmc_request_done(mmc, mrq); | ||
608 | } | ||
609 | |||
610 | static void tifm_sd_end_cmd_nodma(void *data) | ||
611 | { | ||
612 | struct tifm_sd *host = (struct tifm_sd*)data; | ||
613 | struct tifm_dev *sock = host->dev; | ||
614 | struct mmc_host *mmc = tifm_get_drvdata(sock); | ||
615 | struct mmc_request *mrq; | ||
616 | struct mmc_data *r_data = 0; | ||
617 | unsigned long flags; | ||
618 | |||
619 | spin_lock_irqsave(&sock->lock, flags); | ||
620 | |||
621 | mrq = host->req; | ||
622 | host->req = 0; | ||
623 | host->state = IDLE; | ||
624 | |||
625 | if (!mrq) { | ||
626 | printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); | ||
627 | spin_unlock_irqrestore(&sock->lock, flags); | ||
628 | return; | ||
629 | } | ||
630 | |||
631 | r_data = mrq->cmd->data; | ||
632 | if (r_data) { | ||
633 | writel((~TIFM_MMCSD_BUFINT) & | ||
634 | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), | ||
635 | sock->addr + SOCK_MMCSD_INT_ENABLE); | ||
636 | |||
637 | if (r_data->flags & MMC_DATA_WRITE) { | ||
638 | r_data->bytes_xfered = host->written_blocks * | ||
639 | r_data->blksz; | ||
640 | } else { | ||
641 | r_data->bytes_xfered = r_data->blocks - | ||
642 | readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; | ||
643 | r_data->bytes_xfered *= r_data->blksz; | ||
644 | r_data->bytes_xfered += r_data->blksz - | ||
645 | readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; | ||
646 | } | ||
647 | host->buffer = 0; | ||
648 | host->buffer_pos = 0; | ||
649 | host->buffer_size = 0; | ||
650 | } | ||
651 | |||
652 | writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), | ||
653 | sock->addr + SOCK_CONTROL); | ||
654 | |||
655 | spin_unlock_irqrestore(&sock->lock, flags); | ||
656 | |||
657 | if (r_data) | ||
658 | kunmap(r_data->sg->page); | ||
659 | |||
660 | mmc_request_done(mmc, mrq); | ||
661 | } | ||
662 | |||
663 | static void tifm_sd_abort(void *data) | ||
664 | { | ||
665 | printk(KERN_ERR DRIVER_NAME | ||
666 | ": card failed to respond for a long period of time"); | ||
667 | tifm_eject(((struct tifm_sd*)data)->dev); | ||
668 | } | ||
669 | |||
670 | static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) | ||
671 | { | ||
672 | struct tifm_sd *host = mmc_priv(mmc); | ||
673 | struct tifm_dev *sock = host->dev; | ||
674 | unsigned int clk_div1, clk_div2; | ||
675 | unsigned long flags; | ||
676 | |||
677 | spin_lock_irqsave(&sock->lock, flags); | ||
678 | |||
679 | dev_dbg(&sock->dev, "Setting bus width %d, power %d\n", ios->bus_width, | ||
680 | ios->power_mode); | ||
681 | if (ios->bus_width == MMC_BUS_WIDTH_4) { | ||
682 | writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG), | ||
683 | sock->addr + SOCK_MMCSD_CONFIG); | ||
684 | } else { | ||
685 | writel((~TIFM_MMCSD_4BBUS) & | ||
686 | readl(sock->addr + SOCK_MMCSD_CONFIG), | ||
687 | sock->addr + SOCK_MMCSD_CONFIG); | ||
688 | } | ||
689 | |||
690 | if (ios->clock) { | ||
691 | clk_div1 = 20000000 / ios->clock; | ||
692 | if (!clk_div1) | ||
693 | clk_div1 = 1; | ||
694 | |||
695 | clk_div2 = 24000000 / ios->clock; | ||
696 | if (!clk_div2) | ||
697 | clk_div2 = 1; | ||
698 | |||
699 | if ((20000000 / clk_div1) > ios->clock) | ||
700 | clk_div1++; | ||
701 | if ((24000000 / clk_div2) > ios->clock) | ||
702 | clk_div2++; | ||
703 | if ((20000000 / clk_div1) > (24000000 / clk_div2)) { | ||
704 | host->clk_freq = 20000000; | ||
705 | host->clk_div = clk_div1; | ||
706 | writel((~TIFM_CTRL_FAST_CLK) & | ||
707 | readl(sock->addr + SOCK_CONTROL), | ||
708 | sock->addr + SOCK_CONTROL); | ||
709 | } else { | ||
710 | host->clk_freq = 24000000; | ||
711 | host->clk_div = clk_div2; | ||
712 | writel(TIFM_CTRL_FAST_CLK | | ||
713 | readl(sock->addr + SOCK_CONTROL), | ||
714 | sock->addr + SOCK_CONTROL); | ||
715 | } | ||
716 | } else { | ||
717 | host->clk_div = 0; | ||
718 | } | ||
719 | host->clk_div &= TIFM_MMCSD_CLKMASK; | ||
720 | writel(host->clk_div | ((~TIFM_MMCSD_CLKMASK) & | ||
721 | readl(sock->addr + SOCK_MMCSD_CONFIG)), | ||
722 | sock->addr + SOCK_MMCSD_CONFIG); | ||
723 | |||
724 | if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) | ||
725 | host->flags |= OPENDRAIN; | ||
726 | else | ||
727 | host->flags &= ~OPENDRAIN; | ||
728 | |||
729 | /* chip_select : maybe later */ | ||
730 | //vdd | ||
731 | //power is set before probe / after remove | ||
732 | //I believe, power_off when already marked for eject is sufficient to | ||
733 | // allow removal. | ||
734 | if ((host->flags & EJECT) && ios->power_mode == MMC_POWER_OFF) { | ||
735 | host->flags |= EJECT_DONE; | ||
736 | wake_up_all(&host->can_eject); | ||
737 | } | ||
738 | |||
739 | spin_unlock_irqrestore(&sock->lock, flags); | ||
740 | } | ||
741 | |||
742 | static int tifm_sd_ro(struct mmc_host *mmc) | ||
743 | { | ||
744 | int rc; | ||
745 | struct tifm_sd *host = mmc_priv(mmc); | ||
746 | struct tifm_dev *sock = host->dev; | ||
747 | unsigned long flags; | ||
748 | |||
749 | spin_lock_irqsave(&sock->lock, flags); | ||
750 | |||
751 | host->flags |= (CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE)); | ||
752 | rc = (host->flags & CARD_RO) ? 1 : 0; | ||
753 | |||
754 | spin_unlock_irqrestore(&sock->lock, flags); | ||
755 | return rc; | ||
756 | } | ||
757 | |||
758 | static struct mmc_host_ops tifm_sd_ops = { | ||
759 | .request = tifm_sd_request, | ||
760 | .set_ios = tifm_sd_ios, | ||
761 | .get_ro = tifm_sd_ro | ||
762 | }; | ||
763 | |||
764 | static void tifm_sd_register_host(void *data) | ||
765 | { | ||
766 | struct tifm_sd *host = (struct tifm_sd*)data; | ||
767 | struct tifm_dev *sock = host->dev; | ||
768 | struct mmc_host *mmc = tifm_get_drvdata(sock); | ||
769 | unsigned long flags; | ||
770 | |||
771 | spin_lock_irqsave(&sock->lock, flags); | ||
772 | host->flags |= HOST_REG; | ||
773 | PREPARE_WORK(&host->cmd_handler, | ||
774 | no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd, | ||
775 | data); | ||
776 | spin_unlock_irqrestore(&sock->lock, flags); | ||
777 | dev_dbg(&sock->dev, "adding host\n"); | ||
778 | mmc_add_host(mmc); | ||
779 | } | ||
780 | |||
781 | static int tifm_sd_probe(struct tifm_dev *sock) | ||
782 | { | ||
783 | struct mmc_host *mmc; | ||
784 | struct tifm_sd *host; | ||
785 | int rc = -EIO; | ||
786 | |||
787 | if (!(TIFM_SOCK_STATE_OCCUPIED & | ||
788 | readl(sock->addr + SOCK_PRESENT_STATE))) { | ||
789 | printk(KERN_WARNING DRIVER_NAME ": card gone, unexpectedly\n"); | ||
790 | return rc; | ||
791 | } | ||
792 | |||
793 | mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev); | ||
794 | if (!mmc) | ||
795 | return -ENOMEM; | ||
796 | |||
797 | host = mmc_priv(mmc); | ||
798 | host->dev = sock; | ||
799 | host->clk_div = 61; | ||
800 | init_waitqueue_head(&host->can_eject); | ||
801 | INIT_WORK(&host->cmd_handler, tifm_sd_register_host, host); | ||
802 | INIT_WORK(&host->abort_handler, tifm_sd_abort, host); | ||
803 | |||
804 | tifm_set_drvdata(sock, mmc); | ||
805 | sock->signal_irq = tifm_sd_signal_irq; | ||
806 | |||
807 | host->clk_freq = 20000000; | ||
808 | host->timeout_jiffies = msecs_to_jiffies(1000); | ||
809 | |||
810 | tifm_sd_ops.request = no_dma ? tifm_sd_request_nodma : tifm_sd_request; | ||
811 | mmc->ops = &tifm_sd_ops; | ||
812 | mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; | ||
813 | mmc->caps = MMC_CAP_4_BIT_DATA; | ||
814 | mmc->f_min = 20000000 / 60; | ||
815 | mmc->f_max = 24000000; | ||
816 | mmc->max_hw_segs = 1; | ||
817 | mmc->max_phys_segs = 1; | ||
818 | mmc->max_sectors = 127; | ||
819 | mmc->max_seg_size = mmc->max_sectors << 11; //2k maximum hw block length | ||
820 | |||
821 | writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); | ||
822 | writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL); | ||
823 | writel(host->clk_div | TIFM_MMCSD_POWER, | ||
824 | sock->addr + SOCK_MMCSD_CONFIG); | ||
825 | |||
826 | for (rc = 0; rc < 50; rc++) { | ||
827 | /* Wait for reset ack */ | ||
828 | if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { | ||
829 | rc = 0; | ||
830 | break; | ||
831 | } | ||
832 | msleep(10); | ||
833 | } | ||
834 | |||
835 | if (rc) { | ||
836 | printk(KERN_ERR DRIVER_NAME | ||
837 | ": card not ready - probe failed\n"); | ||
838 | mmc_free_host(mmc); | ||
839 | return -ENODEV; | ||
840 | } | ||
841 | |||
842 | writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS); | ||
843 | writel(host->clk_div | TIFM_MMCSD_POWER, | ||
844 | sock->addr + SOCK_MMCSD_CONFIG); | ||
845 | writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); | ||
846 | writel(TIFM_MMCSD_DATAMASK | TIFM_MMCSD_ERRMASK, | ||
847 | sock->addr + SOCK_MMCSD_INT_ENABLE); | ||
848 | |||
849 | writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); // command timeout 64 clocks for now | ||
850 | writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); | ||
851 | writel(host->clk_div | TIFM_MMCSD_POWER, | ||
852 | sock->addr + SOCK_MMCSD_CONFIG); | ||
853 | |||
854 | queue_delayed_work(sock->wq, &host->abort_handler, | ||
855 | host->timeout_jiffies); | ||
856 | |||
857 | return 0; | ||
858 | } | ||
859 | |||
860 | static int tifm_sd_host_is_down(struct tifm_dev *sock) | ||
861 | { | ||
862 | struct mmc_host *mmc = tifm_get_drvdata(sock); | ||
863 | struct tifm_sd *host = mmc_priv(mmc); | ||
864 | unsigned long flags; | ||
865 | int rc = 0; | ||
866 | |||
867 | spin_lock_irqsave(&sock->lock, flags); | ||
868 | rc = (host->flags & EJECT_DONE); | ||
869 | spin_unlock_irqrestore(&sock->lock, flags); | ||
870 | return rc; | ||
871 | } | ||
872 | |||
873 | static void tifm_sd_remove(struct tifm_dev *sock) | ||
874 | { | ||
875 | struct mmc_host *mmc = tifm_get_drvdata(sock); | ||
876 | struct tifm_sd *host = mmc_priv(mmc); | ||
877 | unsigned long flags; | ||
878 | |||
879 | spin_lock_irqsave(&sock->lock, flags); | ||
880 | host->flags |= EJECT; | ||
881 | if (host->req) | ||
882 | queue_work(sock->wq, &host->cmd_handler); | ||
883 | spin_unlock_irqrestore(&sock->lock, flags); | ||
884 | wait_event_timeout(host->can_eject, tifm_sd_host_is_down(sock), | ||
885 | host->timeout_jiffies); | ||
886 | |||
887 | if (host->flags & HOST_REG) | ||
888 | mmc_remove_host(mmc); | ||
889 | |||
890 | /* The meaning of the bit majority in this constant is unknown. */ | ||
891 | writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), | ||
892 | sock->addr + SOCK_CONTROL); | ||
893 | writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); | ||
894 | writel(TIFM_FIFO_INT_SETALL, | ||
895 | sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); | ||
896 | writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); | ||
897 | |||
898 | tifm_set_drvdata(sock, 0); | ||
899 | mmc_free_host(mmc); | ||
900 | } | ||
901 | |||
902 | static tifm_media_id tifm_sd_id_tbl[] = { | ||
903 | FM_SD, 0 | ||
904 | }; | ||
905 | |||
906 | static struct tifm_driver tifm_sd_driver = { | ||
907 | .driver = { | ||
908 | .name = DRIVER_NAME, | ||
909 | .owner = THIS_MODULE | ||
910 | }, | ||
911 | .id_table = tifm_sd_id_tbl, | ||
912 | .probe = tifm_sd_probe, | ||
913 | .remove = tifm_sd_remove | ||
914 | }; | ||
915 | |||
916 | static int __init tifm_sd_init(void) | ||
917 | { | ||
918 | return tifm_register_driver(&tifm_sd_driver); | ||
919 | } | ||
920 | |||
921 | static void __exit tifm_sd_exit(void) | ||
922 | { | ||
923 | tifm_unregister_driver(&tifm_sd_driver); | ||
924 | } | ||
925 | |||
926 | MODULE_AUTHOR("Alex Dubov"); | ||
927 | MODULE_DESCRIPTION("TI FlashMedia SD driver"); | ||
928 | MODULE_LICENSE("GPL"); | ||
929 | MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl); | ||
930 | MODULE_VERSION(DRIVER_VERSION); | ||
931 | |||
932 | module_init(tifm_sd_init); | ||
933 | module_exit(tifm_sd_exit); | ||
diff --git a/include/linux/tifm.h b/include/linux/tifm.h new file mode 100644 index 000000000000..203dd5e11ecb --- /dev/null +++ b/include/linux/tifm.h | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * tifm.h - 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 | #ifndef _TIFM_H | ||
13 | #define _TIFM_H | ||
14 | |||
15 | #include <linux/spinlock.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/wait.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/pci.h> | ||
20 | |||
21 | /* Host registers (relative to pci base address): */ | ||
22 | enum { | ||
23 | FM_SET_INTERRUPT_ENABLE = 0x008, | ||
24 | FM_CLEAR_INTERRUPT_ENABLE = 0x00c, | ||
25 | FM_INTERRUPT_STATUS = 0x014 }; | ||
26 | |||
27 | /* Socket registers (relative to socket base address): */ | ||
28 | enum { | ||
29 | SOCK_CONTROL = 0x004, | ||
30 | SOCK_PRESENT_STATE = 0x008, | ||
31 | SOCK_DMA_ADDRESS = 0x00c, | ||
32 | SOCK_DMA_CONTROL = 0x010, | ||
33 | SOCK_DMA_FIFO_INT_ENABLE_SET = 0x014, | ||
34 | SOCK_DMA_FIFO_INT_ENABLE_CLEAR = 0x018, | ||
35 | SOCK_DMA_FIFO_STATUS = 0x020, | ||
36 | SOCK_FIFO_CONTROL = 0x024, | ||
37 | SOCK_FIFO_PAGE_SIZE = 0x028, | ||
38 | SOCK_MMCSD_COMMAND = 0x104, | ||
39 | SOCK_MMCSD_ARG_LOW = 0x108, | ||
40 | SOCK_MMCSD_ARG_HIGH = 0x10c, | ||
41 | SOCK_MMCSD_CONFIG = 0x110, | ||
42 | SOCK_MMCSD_STATUS = 0x114, | ||
43 | SOCK_MMCSD_INT_ENABLE = 0x118, | ||
44 | SOCK_MMCSD_COMMAND_TO = 0x11c, | ||
45 | SOCK_MMCSD_DATA_TO = 0x120, | ||
46 | SOCK_MMCSD_DATA = 0x124, | ||
47 | SOCK_MMCSD_BLOCK_LEN = 0x128, | ||
48 | SOCK_MMCSD_NUM_BLOCKS = 0x12c, | ||
49 | SOCK_MMCSD_BUFFER_CONFIG = 0x130, | ||
50 | SOCK_MMCSD_SPI_CONFIG = 0x134, | ||
51 | SOCK_MMCSD_SDIO_MODE_CONFIG = 0x138, | ||
52 | SOCK_MMCSD_RESPONSE = 0x144, | ||
53 | SOCK_MMCSD_SDIO_SR = 0x164, | ||
54 | SOCK_MMCSD_SYSTEM_CONTROL = 0x168, | ||
55 | SOCK_MMCSD_SYSTEM_STATUS = 0x16c, | ||
56 | SOCK_MS_COMMAND = 0x184, | ||
57 | SOCK_MS_DATA = 0x188, | ||
58 | SOCK_MS_STATUS = 0x18c, | ||
59 | SOCK_MS_SYSTEM = 0x190, | ||
60 | SOCK_FIFO_ACCESS = 0x200 }; | ||
61 | |||
62 | |||
63 | #define TIFM_IRQ_ENABLE 0x80000000 | ||
64 | #define TIFM_IRQ_SOCKMASK 0x00000001 | ||
65 | #define TIFM_IRQ_CARDMASK 0x00000100 | ||
66 | #define TIFM_IRQ_FIFOMASK 0x00010000 | ||
67 | #define TIFM_IRQ_SETALL 0xffffffff | ||
68 | #define TIFM_IRQ_SETALLSOCK 0x0000000f | ||
69 | |||
70 | #define TIFM_CTRL_LED 0x00000040 | ||
71 | #define TIFM_CTRL_FAST_CLK 0x00000100 | ||
72 | |||
73 | #define TIFM_SOCK_STATE_OCCUPIED 0x00000008 | ||
74 | #define TIFM_SOCK_STATE_POWERED 0x00000080 | ||
75 | |||
76 | #define TIFM_FIFO_ENABLE 0x00000001 /* Meaning of this constant is unverified */ | ||
77 | #define TIFM_FIFO_INT_SETALL 0x0000ffff | ||
78 | #define TIFM_FIFO_INTMASK 0x00000005 /* Meaning of this constant is unverified */ | ||
79 | |||
80 | #define TIFM_DMA_RESET 0x00000002 /* Meaning of this constant is unverified */ | ||
81 | #define TIFM_DMA_TX 0x00008000 /* Meaning of this constant is unverified */ | ||
82 | #define TIFM_DMA_EN 0x00000001 /* Meaning of this constant is unverified */ | ||
83 | |||
84 | typedef enum {FM_NULL = 0, FM_XD = 0x01, FM_MS = 0x02, FM_SD = 0x03} tifm_media_id; | ||
85 | |||
86 | struct tifm_driver; | ||
87 | struct tifm_dev { | ||
88 | char __iomem *addr; | ||
89 | spinlock_t lock; | ||
90 | tifm_media_id media_id; | ||
91 | char wq_name[KOBJ_NAME_LEN]; | ||
92 | struct workqueue_struct *wq; | ||
93 | |||
94 | unsigned int (*signal_irq)(struct tifm_dev *sock, | ||
95 | unsigned int sock_irq_status); | ||
96 | |||
97 | struct tifm_driver *drv; | ||
98 | struct device dev; | ||
99 | }; | ||
100 | |||
101 | struct tifm_driver { | ||
102 | tifm_media_id *id_table; | ||
103 | int (*probe)(struct tifm_dev *dev); | ||
104 | void (*remove)(struct tifm_dev *dev); | ||
105 | |||
106 | struct device_driver driver; | ||
107 | }; | ||
108 | |||
109 | struct tifm_adapter { | ||
110 | char __iomem *addr; | ||
111 | unsigned int irq_status; | ||
112 | unsigned int insert_mask; | ||
113 | unsigned int remove_mask; | ||
114 | spinlock_t lock; | ||
115 | unsigned int id; | ||
116 | unsigned int max_sockets; | ||
117 | char wq_name[KOBJ_NAME_LEN]; | ||
118 | unsigned int inhibit_new_cards; | ||
119 | struct workqueue_struct *wq; | ||
120 | struct work_struct media_inserter; | ||
121 | struct work_struct media_remover; | ||
122 | struct tifm_dev **sockets; | ||
123 | struct class_device cdev; | ||
124 | struct device *dev; | ||
125 | |||
126 | void (*eject)(struct tifm_adapter *fm, struct tifm_dev *sock); | ||
127 | }; | ||
128 | |||
129 | struct tifm_adapter *tifm_alloc_adapter(void); | ||
130 | void tifm_free_device(struct device *dev); | ||
131 | void tifm_free_adapter(struct tifm_adapter *fm); | ||
132 | int tifm_add_adapter(struct tifm_adapter *fm); | ||
133 | void tifm_remove_adapter(struct tifm_adapter *fm); | ||
134 | struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id); | ||
135 | int tifm_register_driver(struct tifm_driver *drv); | ||
136 | void tifm_unregister_driver(struct tifm_driver *drv); | ||
137 | void tifm_eject(struct tifm_dev *sock); | ||
138 | int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, | ||
139 | int direction); | ||
140 | void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, | ||
141 | int direction); | ||
142 | |||
143 | |||
144 | static inline void *tifm_get_drvdata(struct tifm_dev *dev) | ||
145 | { | ||
146 | return dev_get_drvdata(&dev->dev); | ||
147 | } | ||
148 | |||
149 | static inline void tifm_set_drvdata(struct tifm_dev *dev, void *data) | ||
150 | { | ||
151 | dev_set_drvdata(&dev->dev, data); | ||
152 | } | ||
153 | |||
154 | struct tifm_device_id { | ||
155 | tifm_media_id media_id; | ||
156 | }; | ||
157 | |||
158 | #endif | ||