diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pcmcia/tcic.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/pcmcia/tcic.c')
-rw-r--r-- | drivers/pcmcia/tcic.c | 903 |
1 files changed, 903 insertions, 0 deletions
diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c new file mode 100644 index 000000000000..aacbbb5f055d --- /dev/null +++ b/drivers/pcmcia/tcic.c | |||
@@ -0,0 +1,903 @@ | |||
1 | /*====================================================================== | ||
2 | |||
3 | Device driver for Databook TCIC-2 PCMCIA controller | ||
4 | |||
5 | tcic.c 1.111 2000/02/15 04:13:12 | ||
6 | |||
7 | The contents of this file are subject to the Mozilla Public | ||
8 | License Version 1.1 (the "License"); you may not use this file | ||
9 | except in compliance with the License. You may obtain a copy of | ||
10 | the License at http://www.mozilla.org/MPL/ | ||
11 | |||
12 | Software distributed under the License is distributed on an "AS | ||
13 | IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
14 | implied. See the License for the specific language governing | ||
15 | rights and limitations under the License. | ||
16 | |||
17 | The initial developer of the original code is David A. Hinds | ||
18 | <dahinds@users.sourceforge.net>. Portions created by David A. Hinds | ||
19 | are Copyright (C) 1999 David A. Hinds. All Rights Reserved. | ||
20 | |||
21 | Alternatively, the contents of this file may be used under the | ||
22 | terms of the GNU General Public License version 2 (the "GPL"), in which | ||
23 | case the provisions of the GPL are applicable instead of the | ||
24 | above. If you wish to allow the use of your version of this file | ||
25 | only under the terms of the GPL and not to allow others to use | ||
26 | your version of this file under the MPL, indicate your decision | ||
27 | by deleting the provisions above and replace them with the notice | ||
28 | and other provisions required by the GPL. If you do not delete | ||
29 | the provisions above, a recipient may use your version of this | ||
30 | file under either the MPL or the GPL. | ||
31 | |||
32 | ======================================================================*/ | ||
33 | |||
34 | #include <linux/module.h> | ||
35 | #include <linux/moduleparam.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/types.h> | ||
38 | #include <linux/fcntl.h> | ||
39 | #include <linux/string.h> | ||
40 | #include <linux/errno.h> | ||
41 | #include <linux/interrupt.h> | ||
42 | #include <linux/slab.h> | ||
43 | #include <linux/timer.h> | ||
44 | #include <linux/ioport.h> | ||
45 | #include <linux/delay.h> | ||
46 | #include <linux/workqueue.h> | ||
47 | #include <linux/device.h> | ||
48 | #include <linux/bitops.h> | ||
49 | |||
50 | #include <asm/io.h> | ||
51 | #include <asm/system.h> | ||
52 | |||
53 | #include <pcmcia/version.h> | ||
54 | #include <pcmcia/cs_types.h> | ||
55 | #include <pcmcia/cs.h> | ||
56 | #include <pcmcia/ss.h> | ||
57 | #include "tcic.h" | ||
58 | |||
59 | #ifdef DEBUG | ||
60 | static int pc_debug; | ||
61 | |||
62 | module_param(pc_debug, int, 0644); | ||
63 | static const char version[] = | ||
64 | "tcic.c 1.111 2000/02/15 04:13:12 (David Hinds)"; | ||
65 | |||
66 | #define debug(lvl, fmt, arg...) do { \ | ||
67 | if (pc_debug > (lvl)) \ | ||
68 | printk(KERN_DEBUG "tcic: " fmt , ## arg); \ | ||
69 | } while (0) | ||
70 | #else | ||
71 | #define debug(lvl, fmt, arg...) do { } while (0) | ||
72 | #endif | ||
73 | |||
74 | MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); | ||
75 | MODULE_DESCRIPTION("Databook TCIC-2 PCMCIA socket driver"); | ||
76 | MODULE_LICENSE("Dual MPL/GPL"); | ||
77 | |||
78 | /*====================================================================*/ | ||
79 | |||
80 | /* Parameters that can be set with 'insmod' */ | ||
81 | |||
82 | /* The base port address of the TCIC-2 chip */ | ||
83 | static unsigned long tcic_base = TCIC_BASE; | ||
84 | |||
85 | /* Specify a socket number to ignore */ | ||
86 | static int ignore = -1; | ||
87 | |||
88 | /* Probe for safe interrupts? */ | ||
89 | static int do_scan = 1; | ||
90 | |||
91 | /* Bit map of interrupts to choose from */ | ||
92 | static u_int irq_mask = 0xffff; | ||
93 | static int irq_list[16]; | ||
94 | static int irq_list_count; | ||
95 | |||
96 | /* The card status change interrupt -- 0 means autoselect */ | ||
97 | static int cs_irq; | ||
98 | |||
99 | /* Poll status interval -- 0 means default to interrupt */ | ||
100 | static int poll_interval; | ||
101 | |||
102 | /* Delay for card status double-checking */ | ||
103 | static int poll_quick = HZ/20; | ||
104 | |||
105 | /* CCLK external clock time, in nanoseconds. 70 ns = 14.31818 MHz */ | ||
106 | static int cycle_time = 70; | ||
107 | |||
108 | module_param(tcic_base, ulong, 0444); | ||
109 | module_param(ignore, int, 0444); | ||
110 | module_param(do_scan, int, 0444); | ||
111 | module_param(irq_mask, int, 0444); | ||
112 | module_param_array(irq_list, int, &irq_list_count, 0444); | ||
113 | module_param(cs_irq, int, 0444); | ||
114 | module_param(poll_interval, int, 0444); | ||
115 | module_param(poll_quick, int, 0444); | ||
116 | module_param(cycle_time, int, 0444); | ||
117 | |||
118 | /*====================================================================*/ | ||
119 | |||
120 | static irqreturn_t tcic_interrupt(int irq, void *dev, struct pt_regs *regs); | ||
121 | static void tcic_timer(u_long data); | ||
122 | static struct pccard_operations tcic_operations; | ||
123 | |||
124 | struct tcic_socket { | ||
125 | u_short psock; | ||
126 | u_char last_sstat; | ||
127 | u_char id; | ||
128 | struct pcmcia_socket socket; | ||
129 | }; | ||
130 | |||
131 | static struct timer_list poll_timer; | ||
132 | static int tcic_timer_pending; | ||
133 | |||
134 | static int sockets; | ||
135 | static struct tcic_socket socket_table[2]; | ||
136 | |||
137 | /*====================================================================*/ | ||
138 | |||
139 | /* Trick when selecting interrupts: the TCIC sktirq pin is supposed | ||
140 | to map to irq 11, but is coded as 0 or 1 in the irq registers. */ | ||
141 | #define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15) | ||
142 | |||
143 | #ifdef DEBUG_X | ||
144 | static u_char tcic_getb(u_char reg) | ||
145 | { | ||
146 | u_char val = inb(tcic_base+reg); | ||
147 | printk(KERN_DEBUG "tcic_getb(%#lx) = %#x\n", tcic_base+reg, val); | ||
148 | return val; | ||
149 | } | ||
150 | |||
151 | static u_short tcic_getw(u_char reg) | ||
152 | { | ||
153 | u_short val = inw(tcic_base+reg); | ||
154 | printk(KERN_DEBUG "tcic_getw(%#lx) = %#x\n", tcic_base+reg, val); | ||
155 | return val; | ||
156 | } | ||
157 | |||
158 | static void tcic_setb(u_char reg, u_char data) | ||
159 | { | ||
160 | printk(KERN_DEBUG "tcic_setb(%#lx, %#x)\n", tcic_base+reg, data); | ||
161 | outb(data, tcic_base+reg); | ||
162 | } | ||
163 | |||
164 | static void tcic_setw(u_char reg, u_short data) | ||
165 | { | ||
166 | printk(KERN_DEBUG "tcic_setw(%#lx, %#x)\n", tcic_base+reg, data); | ||
167 | outw(data, tcic_base+reg); | ||
168 | } | ||
169 | #else | ||
170 | #define tcic_getb(reg) inb(tcic_base+reg) | ||
171 | #define tcic_getw(reg) inw(tcic_base+reg) | ||
172 | #define tcic_setb(reg, data) outb(data, tcic_base+reg) | ||
173 | #define tcic_setw(reg, data) outw(data, tcic_base+reg) | ||
174 | #endif | ||
175 | |||
176 | static void tcic_setl(u_char reg, u_int data) | ||
177 | { | ||
178 | #ifdef DEBUG_X | ||
179 | printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data); | ||
180 | #endif | ||
181 | outw(data & 0xffff, tcic_base+reg); | ||
182 | outw(data >> 16, tcic_base+reg+2); | ||
183 | } | ||
184 | |||
185 | static u_char tcic_aux_getb(u_short reg) | ||
186 | { | ||
187 | u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; | ||
188 | tcic_setb(TCIC_MODE, mode); | ||
189 | return tcic_getb(TCIC_AUX); | ||
190 | } | ||
191 | |||
192 | static void tcic_aux_setb(u_short reg, u_char data) | ||
193 | { | ||
194 | u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; | ||
195 | tcic_setb(TCIC_MODE, mode); | ||
196 | tcic_setb(TCIC_AUX, data); | ||
197 | } | ||
198 | |||
199 | static u_short tcic_aux_getw(u_short reg) | ||
200 | { | ||
201 | u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; | ||
202 | tcic_setb(TCIC_MODE, mode); | ||
203 | return tcic_getw(TCIC_AUX); | ||
204 | } | ||
205 | |||
206 | static void tcic_aux_setw(u_short reg, u_short data) | ||
207 | { | ||
208 | u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; | ||
209 | tcic_setb(TCIC_MODE, mode); | ||
210 | tcic_setw(TCIC_AUX, data); | ||
211 | } | ||
212 | |||
213 | /*====================================================================*/ | ||
214 | |||
215 | /* Time conversion functions */ | ||
216 | |||
217 | static int to_cycles(int ns) | ||
218 | { | ||
219 | if (ns < 14) | ||
220 | return 0; | ||
221 | else | ||
222 | return 2*(ns-14)/cycle_time; | ||
223 | } | ||
224 | |||
225 | /*====================================================================*/ | ||
226 | |||
227 | static volatile u_int irq_hits; | ||
228 | |||
229 | static irqreturn_t __init tcic_irq_count(int irq, void *dev, struct pt_regs *regs) | ||
230 | { | ||
231 | irq_hits++; | ||
232 | return IRQ_HANDLED; | ||
233 | } | ||
234 | |||
235 | static u_int __init try_irq(int irq) | ||
236 | { | ||
237 | u_short cfg; | ||
238 | |||
239 | irq_hits = 0; | ||
240 | if (request_irq(irq, tcic_irq_count, 0, "irq scan", tcic_irq_count) != 0) | ||
241 | return -1; | ||
242 | mdelay(10); | ||
243 | if (irq_hits) { | ||
244 | free_irq(irq, tcic_irq_count); | ||
245 | return -1; | ||
246 | } | ||
247 | |||
248 | /* Generate one interrupt */ | ||
249 | cfg = TCIC_SYSCFG_AUTOBUSY | 0x0a00; | ||
250 | tcic_aux_setw(TCIC_AUX_SYSCFG, cfg | TCIC_IRQ(irq)); | ||
251 | tcic_setb(TCIC_IENA, TCIC_IENA_ERR | TCIC_IENA_CFG_HIGH); | ||
252 | tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM); | ||
253 | |||
254 | udelay(1000); | ||
255 | free_irq(irq, tcic_irq_count); | ||
256 | |||
257 | /* Turn off interrupts */ | ||
258 | tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF); | ||
259 | while (tcic_getb(TCIC_ICSR)) | ||
260 | tcic_setb(TCIC_ICSR, TCIC_ICSR_JAM); | ||
261 | tcic_aux_setw(TCIC_AUX_SYSCFG, cfg); | ||
262 | |||
263 | return (irq_hits != 1); | ||
264 | } | ||
265 | |||
266 | static u_int __init irq_scan(u_int mask0) | ||
267 | { | ||
268 | u_int mask1; | ||
269 | int i; | ||
270 | |||
271 | #ifdef __alpha__ | ||
272 | #define PIC 0x4d0 | ||
273 | /* Don't probe level-triggered interrupts -- reserved for PCI */ | ||
274 | int level_mask = inb_p(PIC) | (inb_p(PIC+1) << 8); | ||
275 | if (level_mask) | ||
276 | mask0 &= ~level_mask; | ||
277 | #endif | ||
278 | |||
279 | mask1 = 0; | ||
280 | if (do_scan) { | ||
281 | for (i = 0; i < 16; i++) | ||
282 | if ((mask0 & (1 << i)) && (try_irq(i) == 0)) | ||
283 | mask1 |= (1 << i); | ||
284 | for (i = 0; i < 16; i++) | ||
285 | if ((mask1 & (1 << i)) && (try_irq(i) != 0)) { | ||
286 | mask1 ^= (1 << i); | ||
287 | } | ||
288 | } | ||
289 | |||
290 | if (mask1) { | ||
291 | printk("scanned"); | ||
292 | } else { | ||
293 | /* Fallback: just find interrupts that aren't in use */ | ||
294 | for (i = 0; i < 16; i++) | ||
295 | if ((mask0 & (1 << i)) && | ||
296 | (request_irq(i, tcic_irq_count, 0, "x", tcic_irq_count) == 0)) { | ||
297 | mask1 |= (1 << i); | ||
298 | free_irq(i, tcic_irq_count); | ||
299 | } | ||
300 | printk("default"); | ||
301 | } | ||
302 | |||
303 | printk(") = "); | ||
304 | for (i = 0; i < 16; i++) | ||
305 | if (mask1 & (1<<i)) | ||
306 | printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i); | ||
307 | printk(" "); | ||
308 | |||
309 | return mask1; | ||
310 | } | ||
311 | |||
312 | /*====================================================================== | ||
313 | |||
314 | See if a card is present, powered up, in IO mode, and already | ||
315 | bound to a (non-PCMCIA) Linux driver. | ||
316 | |||
317 | We make an exception for cards that look like serial devices. | ||
318 | |||
319 | ======================================================================*/ | ||
320 | |||
321 | static int __init is_active(int s) | ||
322 | { | ||
323 | u_short scf1, ioctl, base, num; | ||
324 | u_char pwr, sstat; | ||
325 | u_int addr; | ||
326 | |||
327 | tcic_setl(TCIC_ADDR, (s << TCIC_ADDR_SS_SHFT) | ||
328 | | TCIC_ADDR_INDREG | TCIC_SCF1(s)); | ||
329 | scf1 = tcic_getw(TCIC_DATA); | ||
330 | pwr = tcic_getb(TCIC_PWR); | ||
331 | sstat = tcic_getb(TCIC_SSTAT); | ||
332 | addr = TCIC_IWIN(s, 0); | ||
333 | tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X); | ||
334 | base = tcic_getw(TCIC_DATA); | ||
335 | tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X); | ||
336 | ioctl = tcic_getw(TCIC_DATA); | ||
337 | |||
338 | if (ioctl & TCIC_ICTL_TINY) | ||
339 | num = 1; | ||
340 | else { | ||
341 | num = (base ^ (base-1)); | ||
342 | base = base & (base-1); | ||
343 | } | ||
344 | |||
345 | if ((sstat & TCIC_SSTAT_CD) && (pwr & TCIC_PWR_VCC(s)) && | ||
346 | (scf1 & TCIC_SCF1_IOSTS) && (ioctl & TCIC_ICTL_ENA) && | ||
347 | ((base & 0xfeef) != 0x02e8)) { | ||
348 | struct resource *res = request_region(base, num, "tcic-2"); | ||
349 | if (!res) /* region is busy */ | ||
350 | return 1; | ||
351 | release_region(base, num); | ||
352 | } | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | /*====================================================================== | ||
358 | |||
359 | This returns the revision code for the specified socket. | ||
360 | |||
361 | ======================================================================*/ | ||
362 | |||
363 | static int __init get_tcic_id(void) | ||
364 | { | ||
365 | u_short id; | ||
366 | |||
367 | tcic_aux_setw(TCIC_AUX_TEST, TCIC_TEST_DIAG); | ||
368 | id = tcic_aux_getw(TCIC_AUX_ILOCK); | ||
369 | id = (id & TCIC_ILOCKTEST_ID_MASK) >> TCIC_ILOCKTEST_ID_SH; | ||
370 | tcic_aux_setw(TCIC_AUX_TEST, 0); | ||
371 | return id; | ||
372 | } | ||
373 | |||
374 | /*====================================================================*/ | ||
375 | |||
376 | static int tcic_drv_suspend(struct device *dev, pm_message_t state, u32 level) | ||
377 | { | ||
378 | int ret = 0; | ||
379 | if (level == SUSPEND_SAVE_STATE) | ||
380 | ret = pcmcia_socket_dev_suspend(dev, state); | ||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | static int tcic_drv_resume(struct device *dev, u32 level) | ||
385 | { | ||
386 | int ret = 0; | ||
387 | if (level == RESUME_RESTORE_STATE) | ||
388 | ret = pcmcia_socket_dev_resume(dev); | ||
389 | return ret; | ||
390 | } | ||
391 | |||
392 | static struct device_driver tcic_driver = { | ||
393 | .name = "tcic-pcmcia", | ||
394 | .bus = &platform_bus_type, | ||
395 | .suspend = tcic_drv_suspend, | ||
396 | .resume = tcic_drv_resume, | ||
397 | }; | ||
398 | |||
399 | static struct platform_device tcic_device = { | ||
400 | .name = "tcic-pcmcia", | ||
401 | .id = 0, | ||
402 | }; | ||
403 | |||
404 | |||
405 | static int __init init_tcic(void) | ||
406 | { | ||
407 | int i, sock, ret = 0; | ||
408 | u_int mask, scan; | ||
409 | |||
410 | if (driver_register(&tcic_driver)) | ||
411 | return -1; | ||
412 | |||
413 | printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: "); | ||
414 | sock = 0; | ||
415 | |||
416 | if (!request_region(tcic_base, 16, "tcic-2")) { | ||
417 | printk("could not allocate ports,\n "); | ||
418 | driver_unregister(&tcic_driver); | ||
419 | return -ENODEV; | ||
420 | } | ||
421 | else { | ||
422 | tcic_setw(TCIC_ADDR, 0); | ||
423 | if (tcic_getw(TCIC_ADDR) == 0) { | ||
424 | tcic_setw(TCIC_ADDR, 0xc3a5); | ||
425 | if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2; | ||
426 | } | ||
427 | if (sock == 0) { | ||
428 | /* See if resetting the controller does any good */ | ||
429 | tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET); | ||
430 | tcic_setb(TCIC_SCTRL, 0); | ||
431 | tcic_setw(TCIC_ADDR, 0); | ||
432 | if (tcic_getw(TCIC_ADDR) == 0) { | ||
433 | tcic_setw(TCIC_ADDR, 0xc3a5); | ||
434 | if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2; | ||
435 | } | ||
436 | } | ||
437 | } | ||
438 | if (sock == 0) { | ||
439 | printk("not found.\n"); | ||
440 | release_region(tcic_base, 16); | ||
441 | driver_unregister(&tcic_driver); | ||
442 | return -ENODEV; | ||
443 | } | ||
444 | |||
445 | sockets = 0; | ||
446 | for (i = 0; i < sock; i++) { | ||
447 | if ((i == ignore) || is_active(i)) continue; | ||
448 | socket_table[sockets].psock = i; | ||
449 | socket_table[sockets].id = get_tcic_id(); | ||
450 | |||
451 | socket_table[sockets].socket.owner = THIS_MODULE; | ||
452 | /* only 16-bit cards, memory windows must be size-aligned */ | ||
453 | /* No PCI or CardBus support */ | ||
454 | socket_table[sockets].socket.features = SS_CAP_PCCARD | SS_CAP_MEM_ALIGN; | ||
455 | /* irq 14, 11, 10, 7, 6, 5, 4, 3 */ | ||
456 | socket_table[sockets].socket.irq_mask = 0x4cf8; | ||
457 | /* 4K minimum window size */ | ||
458 | socket_table[sockets].socket.map_size = 0x1000; | ||
459 | sockets++; | ||
460 | } | ||
461 | |||
462 | switch (socket_table[0].id) { | ||
463 | case TCIC_ID_DB86082: | ||
464 | printk("DB86082"); break; | ||
465 | case TCIC_ID_DB86082A: | ||
466 | printk("DB86082A"); break; | ||
467 | case TCIC_ID_DB86084: | ||
468 | printk("DB86084"); break; | ||
469 | case TCIC_ID_DB86084A: | ||
470 | printk("DB86084A"); break; | ||
471 | case TCIC_ID_DB86072: | ||
472 | printk("DB86072"); break; | ||
473 | case TCIC_ID_DB86184: | ||
474 | printk("DB86184"); break; | ||
475 | case TCIC_ID_DB86082B: | ||
476 | printk("DB86082B"); break; | ||
477 | default: | ||
478 | printk("Unknown ID 0x%02x", socket_table[0].id); | ||
479 | } | ||
480 | |||
481 | /* Set up polling */ | ||
482 | poll_timer.function = &tcic_timer; | ||
483 | poll_timer.data = 0; | ||
484 | init_timer(&poll_timer); | ||
485 | |||
486 | /* Build interrupt mask */ | ||
487 | printk(", %d sockets\n" KERN_INFO " irq list (", sockets); | ||
488 | if (irq_list_count == 0) | ||
489 | mask = irq_mask; | ||
490 | else | ||
491 | for (i = mask = 0; i < irq_list_count; i++) | ||
492 | mask |= (1<<irq_list[i]); | ||
493 | |||
494 | /* irq 14, 11, 10, 7, 6, 5, 4, 3 */ | ||
495 | mask &= 0x4cf8; | ||
496 | /* Scan interrupts */ | ||
497 | mask = irq_scan(mask); | ||
498 | for (i=0;i<sockets;i++) | ||
499 | socket_table[i].socket.irq_mask = mask; | ||
500 | |||
501 | /* Check for only two interrupts available */ | ||
502 | scan = (mask & (mask-1)); | ||
503 | if (((scan & (scan-1)) == 0) && (poll_interval == 0)) | ||
504 | poll_interval = HZ; | ||
505 | |||
506 | if (poll_interval == 0) { | ||
507 | /* Avoid irq 12 unless it is explicitly requested */ | ||
508 | u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12)); | ||
509 | for (i = 15; i > 0; i--) | ||
510 | if ((cs_mask & (1 << i)) && | ||
511 | (request_irq(i, tcic_interrupt, 0, "tcic", | ||
512 | tcic_interrupt) == 0)) | ||
513 | break; | ||
514 | cs_irq = i; | ||
515 | if (cs_irq == 0) poll_interval = HZ; | ||
516 | } | ||
517 | |||
518 | if (socket_table[0].socket.irq_mask & (1 << 11)) | ||
519 | printk("sktirq is irq 11, "); | ||
520 | if (cs_irq != 0) | ||
521 | printk("status change on irq %d\n", cs_irq); | ||
522 | else | ||
523 | printk("polled status, interval = %d ms\n", | ||
524 | poll_interval * 1000 / HZ); | ||
525 | |||
526 | for (i = 0; i < sockets; i++) { | ||
527 | tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT); | ||
528 | socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT); | ||
529 | } | ||
530 | |||
531 | /* jump start interrupt handler, if needed */ | ||
532 | tcic_interrupt(0, NULL, NULL); | ||
533 | |||
534 | platform_device_register(&tcic_device); | ||
535 | |||
536 | for (i = 0; i < sockets; i++) { | ||
537 | socket_table[i].socket.ops = &tcic_operations; | ||
538 | socket_table[i].socket.resource_ops = &pccard_nonstatic_ops; | ||
539 | socket_table[i].socket.dev.dev = &tcic_device.dev; | ||
540 | ret = pcmcia_register_socket(&socket_table[i].socket); | ||
541 | if (ret && i) | ||
542 | pcmcia_unregister_socket(&socket_table[0].socket); | ||
543 | } | ||
544 | |||
545 | return ret; | ||
546 | |||
547 | return 0; | ||
548 | |||
549 | } /* init_tcic */ | ||
550 | |||
551 | /*====================================================================*/ | ||
552 | |||
553 | static void __exit exit_tcic(void) | ||
554 | { | ||
555 | int i; | ||
556 | |||
557 | del_timer_sync(&poll_timer); | ||
558 | if (cs_irq != 0) { | ||
559 | tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00); | ||
560 | free_irq(cs_irq, tcic_interrupt); | ||
561 | } | ||
562 | release_region(tcic_base, 16); | ||
563 | |||
564 | for (i = 0; i < sockets; i++) { | ||
565 | pcmcia_unregister_socket(&socket_table[i].socket); | ||
566 | } | ||
567 | |||
568 | platform_device_unregister(&tcic_device); | ||
569 | driver_unregister(&tcic_driver); | ||
570 | } /* exit_tcic */ | ||
571 | |||
572 | /*====================================================================*/ | ||
573 | |||
574 | static irqreturn_t tcic_interrupt(int irq, void *dev, struct pt_regs *regs) | ||
575 | { | ||
576 | int i, quick = 0; | ||
577 | u_char latch, sstat; | ||
578 | u_short psock; | ||
579 | u_int events; | ||
580 | static volatile int active = 0; | ||
581 | |||
582 | if (active) { | ||
583 | printk(KERN_NOTICE "tcic: reentered interrupt handler!\n"); | ||
584 | return IRQ_NONE; | ||
585 | } else | ||
586 | active = 1; | ||
587 | |||
588 | debug(2, "tcic_interrupt()\n"); | ||
589 | |||
590 | for (i = 0; i < sockets; i++) { | ||
591 | psock = socket_table[i].psock; | ||
592 | tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT) | ||
593 | | TCIC_ADDR_INDREG | TCIC_SCF1(psock)); | ||
594 | sstat = tcic_getb(TCIC_SSTAT); | ||
595 | latch = sstat ^ socket_table[psock].last_sstat; | ||
596 | socket_table[i].last_sstat = sstat; | ||
597 | if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) { | ||
598 | tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR); | ||
599 | quick = 1; | ||
600 | } | ||
601 | if (latch == 0) | ||
602 | continue; | ||
603 | events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0; | ||
604 | events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0; | ||
605 | if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) { | ||
606 | events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0; | ||
607 | } else { | ||
608 | events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0; | ||
609 | events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0; | ||
610 | events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0; | ||
611 | } | ||
612 | if (events) { | ||
613 | pcmcia_parse_events(&socket_table[i].socket, events); | ||
614 | } | ||
615 | } | ||
616 | |||
617 | /* Schedule next poll, if needed */ | ||
618 | if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) { | ||
619 | poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval); | ||
620 | add_timer(&poll_timer); | ||
621 | tcic_timer_pending = 1; | ||
622 | } | ||
623 | active = 0; | ||
624 | |||
625 | debug(2, "interrupt done\n"); | ||
626 | return IRQ_HANDLED; | ||
627 | } /* tcic_interrupt */ | ||
628 | |||
629 | static void tcic_timer(u_long data) | ||
630 | { | ||
631 | debug(2, "tcic_timer()\n"); | ||
632 | tcic_timer_pending = 0; | ||
633 | tcic_interrupt(0, NULL, NULL); | ||
634 | } /* tcic_timer */ | ||
635 | |||
636 | /*====================================================================*/ | ||
637 | |||
638 | static int tcic_get_status(struct pcmcia_socket *sock, u_int *value) | ||
639 | { | ||
640 | u_short psock = container_of(sock, struct tcic_socket, socket)->psock; | ||
641 | u_char reg; | ||
642 | |||
643 | tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT) | ||
644 | | TCIC_ADDR_INDREG | TCIC_SCF1(psock)); | ||
645 | reg = tcic_getb(TCIC_SSTAT); | ||
646 | *value = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0; | ||
647 | *value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0; | ||
648 | if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) { | ||
649 | *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0; | ||
650 | } else { | ||
651 | *value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0; | ||
652 | *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0; | ||
653 | *value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0; | ||
654 | } | ||
655 | reg = tcic_getb(TCIC_PWR); | ||
656 | if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock))) | ||
657 | *value |= SS_POWERON; | ||
658 | debug(1, "GetStatus(%d) = %#2.2x\n", psock, *value); | ||
659 | return 0; | ||
660 | } /* tcic_get_status */ | ||
661 | |||
662 | /*====================================================================*/ | ||
663 | |||
664 | static int tcic_get_socket(struct pcmcia_socket *sock, socket_state_t *state) | ||
665 | { | ||
666 | u_short psock = container_of(sock, struct tcic_socket, socket)->psock; | ||
667 | u_char reg; | ||
668 | u_short scf1, scf2; | ||
669 | |||
670 | tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT) | ||
671 | | TCIC_ADDR_INDREG | TCIC_SCF1(psock)); | ||
672 | scf1 = tcic_getw(TCIC_DATA); | ||
673 | state->flags = (scf1 & TCIC_SCF1_IOSTS) ? SS_IOCARD : 0; | ||
674 | state->flags |= (scf1 & TCIC_SCF1_DMA_MASK) ? SS_DMA_MODE : 0; | ||
675 | state->flags |= (scf1 & TCIC_SCF1_SPKR) ? SS_SPKR_ENA : 0; | ||
676 | if (tcic_getb(TCIC_SCTRL) & TCIC_SCTRL_ENA) | ||
677 | state->flags |= SS_OUTPUT_ENA; | ||
678 | state->io_irq = scf1 & TCIC_SCF1_IRQ_MASK; | ||
679 | if (state->io_irq == 1) state->io_irq = 11; | ||
680 | |||
681 | reg = tcic_getb(TCIC_PWR); | ||
682 | state->Vcc = state->Vpp = 0; | ||
683 | if (reg & TCIC_PWR_VCC(psock)) { | ||
684 | if (reg & TCIC_PWR_VPP(psock)) | ||
685 | state->Vcc = 50; | ||
686 | else | ||
687 | state->Vcc = state->Vpp = 50; | ||
688 | } else { | ||
689 | if (reg & TCIC_PWR_VPP(psock)) { | ||
690 | state->Vcc = 50; | ||
691 | state->Vpp = 120; | ||
692 | } | ||
693 | } | ||
694 | reg = tcic_aux_getb(TCIC_AUX_ILOCK); | ||
695 | state->flags |= (reg & TCIC_ILOCK_CRESET) ? SS_RESET : 0; | ||
696 | |||
697 | /* Card status change interrupt mask */ | ||
698 | tcic_setw(TCIC_ADDR, TCIC_SCF2(psock)); | ||
699 | scf2 = tcic_getw(TCIC_DATA); | ||
700 | state->csc_mask = (scf2 & TCIC_SCF2_MCD) ? 0 : SS_DETECT; | ||
701 | if (state->flags & SS_IOCARD) { | ||
702 | state->csc_mask |= (scf2 & TCIC_SCF2_MLBAT1) ? 0 : SS_STSCHG; | ||
703 | } else { | ||
704 | state->csc_mask |= (scf2 & TCIC_SCF2_MLBAT1) ? 0 : SS_BATDEAD; | ||
705 | state->csc_mask |= (scf2 & TCIC_SCF2_MLBAT2) ? 0 : SS_BATWARN; | ||
706 | state->csc_mask |= (scf2 & TCIC_SCF2_MRDY) ? 0 : SS_READY; | ||
707 | } | ||
708 | |||
709 | debug(1, "GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, " | ||
710 | "io_irq %d, csc_mask %#2.2x\n", psock, state->flags, | ||
711 | state->Vcc, state->Vpp, state->io_irq, state->csc_mask); | ||
712 | return 0; | ||
713 | } /* tcic_get_socket */ | ||
714 | |||
715 | /*====================================================================*/ | ||
716 | |||
717 | static int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state) | ||
718 | { | ||
719 | u_short psock = container_of(sock, struct tcic_socket, socket)->psock; | ||
720 | u_char reg; | ||
721 | u_short scf1, scf2; | ||
722 | |||
723 | debug(1, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " | ||
724 | "io_irq %d, csc_mask %#2.2x)\n", psock, state->flags, | ||
725 | state->Vcc, state->Vpp, state->io_irq, state->csc_mask); | ||
726 | tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG); | ||
727 | |||
728 | reg = tcic_getb(TCIC_PWR); | ||
729 | reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock)); | ||
730 | |||
731 | if (state->Vcc == 50) { | ||
732 | switch (state->Vpp) { | ||
733 | case 0: reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break; | ||
734 | case 50: reg |= TCIC_PWR_VCC(psock); break; | ||
735 | case 120: reg |= TCIC_PWR_VPP(psock); break; | ||
736 | default: return -EINVAL; | ||
737 | } | ||
738 | } else if (state->Vcc != 0) | ||
739 | return -EINVAL; | ||
740 | |||
741 | if (reg != tcic_getb(TCIC_PWR)) | ||
742 | tcic_setb(TCIC_PWR, reg); | ||
743 | |||
744 | reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT; | ||
745 | if (state->flags & SS_OUTPUT_ENA) { | ||
746 | tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA); | ||
747 | reg |= TCIC_ILOCK_CRESENA; | ||
748 | } else | ||
749 | tcic_setb(TCIC_SCTRL, 0); | ||
750 | if (state->flags & SS_RESET) | ||
751 | reg |= TCIC_ILOCK_CRESET; | ||
752 | tcic_aux_setb(TCIC_AUX_ILOCK, reg); | ||
753 | |||
754 | tcic_setw(TCIC_ADDR, TCIC_SCF1(psock)); | ||
755 | scf1 = TCIC_SCF1_FINPACK; | ||
756 | scf1 |= TCIC_IRQ(state->io_irq); | ||
757 | if (state->flags & SS_IOCARD) { | ||
758 | scf1 |= TCIC_SCF1_IOSTS; | ||
759 | if (state->flags & SS_SPKR_ENA) | ||
760 | scf1 |= TCIC_SCF1_SPKR; | ||
761 | if (state->flags & SS_DMA_MODE) | ||
762 | scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT; | ||
763 | } | ||
764 | tcic_setw(TCIC_DATA, scf1); | ||
765 | |||
766 | /* Some general setup stuff, and configure status interrupt */ | ||
767 | reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250); | ||
768 | tcic_aux_setb(TCIC_AUX_WCTL, reg); | ||
769 | tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00| | ||
770 | TCIC_IRQ(cs_irq)); | ||
771 | |||
772 | /* Card status change interrupt mask */ | ||
773 | tcic_setw(TCIC_ADDR, TCIC_SCF2(psock)); | ||
774 | scf2 = TCIC_SCF2_MALL; | ||
775 | if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD; | ||
776 | if (state->flags & SS_IOCARD) { | ||
777 | if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1; | ||
778 | } else { | ||
779 | if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1; | ||
780 | if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2; | ||
781 | if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY; | ||
782 | } | ||
783 | tcic_setw(TCIC_DATA, scf2); | ||
784 | /* For the ISA bus, the irq should be active-high totem-pole */ | ||
785 | tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH); | ||
786 | |||
787 | return 0; | ||
788 | } /* tcic_set_socket */ | ||
789 | |||
790 | /*====================================================================*/ | ||
791 | |||
792 | static int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) | ||
793 | { | ||
794 | u_short psock = container_of(sock, struct tcic_socket, socket)->psock; | ||
795 | u_int addr; | ||
796 | u_short base, len, ioctl; | ||
797 | |||
798 | debug(1, "SetIOMap(%d, %d, %#2.2x, %d ns, " | ||
799 | "%#lx-%#lx)\n", psock, io->map, io->flags, | ||
800 | io->speed, io->start, io->stop); | ||
801 | if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) || | ||
802 | (io->stop < io->start)) return -EINVAL; | ||
803 | tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT)); | ||
804 | addr = TCIC_IWIN(psock, io->map); | ||
805 | |||
806 | base = io->start; len = io->stop - io->start; | ||
807 | /* Check to see that len+1 is power of two, etc */ | ||
808 | if ((len & (len+1)) || (base & len)) return -EINVAL; | ||
809 | base |= (len+1)>>1; | ||
810 | tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X); | ||
811 | tcic_setw(TCIC_DATA, base); | ||
812 | |||
813 | ioctl = (psock << TCIC_ICTL_SS_SHFT); | ||
814 | ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0; | ||
815 | ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0; | ||
816 | ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK; | ||
817 | if (!(io->flags & MAP_AUTOSZ)) { | ||
818 | ioctl |= TCIC_ICTL_QUIET; | ||
819 | ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8; | ||
820 | } | ||
821 | tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X); | ||
822 | tcic_setw(TCIC_DATA, ioctl); | ||
823 | |||
824 | return 0; | ||
825 | } /* tcic_set_io_map */ | ||
826 | |||
827 | /*====================================================================*/ | ||
828 | |||
829 | static int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem) | ||
830 | { | ||
831 | u_short psock = container_of(sock, struct tcic_socket, socket)->psock; | ||
832 | u_short addr, ctl; | ||
833 | u_long base, len, mmap; | ||
834 | |||
835 | debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, " | ||
836 | "%#lx-%#lx, %#x)\n", psock, mem->map, mem->flags, | ||
837 | mem->speed, mem->res->start, mem->res->end, mem->card_start); | ||
838 | if ((mem->map > 3) || (mem->card_start > 0x3ffffff) || | ||
839 | (mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) || | ||
840 | (mem->res->start > mem->res->end) || (mem->speed > 1000)) | ||
841 | return -EINVAL; | ||
842 | tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT)); | ||
843 | addr = TCIC_MWIN(psock, mem->map); | ||
844 | |||
845 | base = mem->res->start; len = mem->res->end - mem->res->start; | ||
846 | if ((len & (len+1)) || (base & len)) return -EINVAL; | ||
847 | if (len == 0x0fff) | ||
848 | base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT; | ||
849 | else | ||
850 | base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT; | ||
851 | tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X); | ||
852 | tcic_setw(TCIC_DATA, base); | ||
853 | |||
854 | mmap = mem->card_start - mem->res->start; | ||
855 | mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK; | ||
856 | if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG; | ||
857 | tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X); | ||
858 | tcic_setw(TCIC_DATA, mmap); | ||
859 | |||
860 | ctl = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT); | ||
861 | ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK; | ||
862 | ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8; | ||
863 | ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0; | ||
864 | ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0; | ||
865 | tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X); | ||
866 | tcic_setw(TCIC_DATA, ctl); | ||
867 | |||
868 | return 0; | ||
869 | } /* tcic_set_mem_map */ | ||
870 | |||
871 | /*====================================================================*/ | ||
872 | |||
873 | static int tcic_init(struct pcmcia_socket *s) | ||
874 | { | ||
875 | int i; | ||
876 | struct resource res = { .start = 0, .end = 0x1000 }; | ||
877 | pccard_io_map io = { 0, 0, 0, 0, 1 }; | ||
878 | pccard_mem_map mem = { .res = &res, }; | ||
879 | |||
880 | for (i = 0; i < 2; i++) { | ||
881 | io.map = i; | ||
882 | tcic_set_io_map(s, &io); | ||
883 | } | ||
884 | for (i = 0; i < 5; i++) { | ||
885 | mem.map = i; | ||
886 | tcic_set_mem_map(s, &mem); | ||
887 | } | ||
888 | return 0; | ||
889 | } | ||
890 | |||
891 | static struct pccard_operations tcic_operations = { | ||
892 | .init = tcic_init, | ||
893 | .get_status = tcic_get_status, | ||
894 | .get_socket = tcic_get_socket, | ||
895 | .set_socket = tcic_set_socket, | ||
896 | .set_io_map = tcic_set_io_map, | ||
897 | .set_mem_map = tcic_set_mem_map, | ||
898 | }; | ||
899 | |||
900 | /*====================================================================*/ | ||
901 | |||
902 | module_init(init_tcic); | ||
903 | module_exit(exit_tcic); | ||