blob: be0d841c7ebd9d9e2bb1407a4165ee92623105ec [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*======================================================================
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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/timer.h>
43#include <linux/ioport.h>
44#include <linux/delay.h>
45#include <linux/workqueue.h>
Russell Kingd052d1b2005-10-29 19:07:23 +010046#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#include <linux/bitops.h>
48
49#include <asm/io.h>
50#include <asm/system.h>
51
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#include <pcmcia/cs.h>
53#include <pcmcia/ss.h>
54#include "tcic.h"
55
Linus Torvalds1da177e2005-04-16 15:20:36 -070056MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
57MODULE_DESCRIPTION("Databook TCIC-2 PCMCIA socket driver");
58MODULE_LICENSE("Dual MPL/GPL");
59
60/*====================================================================*/
61
62/* Parameters that can be set with 'insmod' */
63
64/* The base port address of the TCIC-2 chip */
65static unsigned long tcic_base = TCIC_BASE;
66
67/* Specify a socket number to ignore */
68static int ignore = -1;
69
70/* Probe for safe interrupts? */
71static int do_scan = 1;
72
73/* Bit map of interrupts to choose from */
74static u_int irq_mask = 0xffff;
75static int irq_list[16];
Al Viro64a6f952007-10-14 19:35:30 +010076static unsigned int irq_list_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78/* The card status change interrupt -- 0 means autoselect */
79static int cs_irq;
80
81/* Poll status interval -- 0 means default to interrupt */
82static int poll_interval;
83
84/* Delay for card status double-checking */
85static int poll_quick = HZ/20;
86
87/* CCLK external clock time, in nanoseconds. 70 ns = 14.31818 MHz */
88static int cycle_time = 70;
89
90module_param(tcic_base, ulong, 0444);
91module_param(ignore, int, 0444);
92module_param(do_scan, int, 0444);
93module_param(irq_mask, int, 0444);
94module_param_array(irq_list, int, &irq_list_count, 0444);
95module_param(cs_irq, int, 0444);
96module_param(poll_interval, int, 0444);
97module_param(poll_quick, int, 0444);
98module_param(cycle_time, int, 0444);
99
100/*====================================================================*/
101
David Howells7d12e782006-10-05 14:55:46 +0100102static irqreturn_t tcic_interrupt(int irq, void *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103static void tcic_timer(u_long data);
104static struct pccard_operations tcic_operations;
105
106struct tcic_socket {
107 u_short psock;
108 u_char last_sstat;
109 u_char id;
110 struct pcmcia_socket socket;
111};
112
113static struct timer_list poll_timer;
114static int tcic_timer_pending;
115
116static int sockets;
117static struct tcic_socket socket_table[2];
118
119/*====================================================================*/
120
121/* Trick when selecting interrupts: the TCIC sktirq pin is supposed
122 to map to irq 11, but is coded as 0 or 1 in the irq registers. */
123#define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15)
124
125#ifdef DEBUG_X
126static u_char tcic_getb(u_char reg)
127{
128 u_char val = inb(tcic_base+reg);
129 printk(KERN_DEBUG "tcic_getb(%#lx) = %#x\n", tcic_base+reg, val);
130 return val;
131}
132
133static u_short tcic_getw(u_char reg)
134{
135 u_short val = inw(tcic_base+reg);
136 printk(KERN_DEBUG "tcic_getw(%#lx) = %#x\n", tcic_base+reg, val);
137 return val;
138}
139
140static void tcic_setb(u_char reg, u_char data)
141{
142 printk(KERN_DEBUG "tcic_setb(%#lx, %#x)\n", tcic_base+reg, data);
143 outb(data, tcic_base+reg);
144}
145
146static void tcic_setw(u_char reg, u_short data)
147{
148 printk(KERN_DEBUG "tcic_setw(%#lx, %#x)\n", tcic_base+reg, data);
149 outw(data, tcic_base+reg);
150}
151#else
152#define tcic_getb(reg) inb(tcic_base+reg)
153#define tcic_getw(reg) inw(tcic_base+reg)
154#define tcic_setb(reg, data) outb(data, tcic_base+reg)
155#define tcic_setw(reg, data) outw(data, tcic_base+reg)
156#endif
157
158static void tcic_setl(u_char reg, u_int data)
159{
160#ifdef DEBUG_X
161 printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data);
162#endif
163 outw(data & 0xffff, tcic_base+reg);
164 outw(data >> 16, tcic_base+reg+2);
165}
166
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167static void tcic_aux_setb(u_short reg, u_char data)
168{
169 u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
170 tcic_setb(TCIC_MODE, mode);
171 tcic_setb(TCIC_AUX, data);
172}
173
174static u_short tcic_aux_getw(u_short reg)
175{
176 u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
177 tcic_setb(TCIC_MODE, mode);
178 return tcic_getw(TCIC_AUX);
179}
180
181static void tcic_aux_setw(u_short reg, u_short data)
182{
183 u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
184 tcic_setb(TCIC_MODE, mode);
185 tcic_setw(TCIC_AUX, data);
186}
187
188/*====================================================================*/
189
190/* Time conversion functions */
191
192static int to_cycles(int ns)
193{
194 if (ns < 14)
195 return 0;
196 else
197 return 2*(ns-14)/cycle_time;
198}
199
200/*====================================================================*/
201
202static volatile u_int irq_hits;
203
David Howells7d12e782006-10-05 14:55:46 +0100204static irqreturn_t __init tcic_irq_count(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205{
206 irq_hits++;
207 return IRQ_HANDLED;
208}
209
210static u_int __init try_irq(int irq)
211{
212 u_short cfg;
213
214 irq_hits = 0;
215 if (request_irq(irq, tcic_irq_count, 0, "irq scan", tcic_irq_count) != 0)
216 return -1;
217 mdelay(10);
218 if (irq_hits) {
219 free_irq(irq, tcic_irq_count);
220 return -1;
221 }
222
223 /* Generate one interrupt */
224 cfg = TCIC_SYSCFG_AUTOBUSY | 0x0a00;
225 tcic_aux_setw(TCIC_AUX_SYSCFG, cfg | TCIC_IRQ(irq));
226 tcic_setb(TCIC_IENA, TCIC_IENA_ERR | TCIC_IENA_CFG_HIGH);
227 tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM);
228
229 udelay(1000);
230 free_irq(irq, tcic_irq_count);
231
232 /* Turn off interrupts */
233 tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF);
234 while (tcic_getb(TCIC_ICSR))
235 tcic_setb(TCIC_ICSR, TCIC_ICSR_JAM);
236 tcic_aux_setw(TCIC_AUX_SYSCFG, cfg);
237
238 return (irq_hits != 1);
239}
240
241static u_int __init irq_scan(u_int mask0)
242{
243 u_int mask1;
244 int i;
245
246#ifdef __alpha__
247#define PIC 0x4d0
248 /* Don't probe level-triggered interrupts -- reserved for PCI */
249 int level_mask = inb_p(PIC) | (inb_p(PIC+1) << 8);
250 if (level_mask)
251 mask0 &= ~level_mask;
252#endif
253
254 mask1 = 0;
255 if (do_scan) {
256 for (i = 0; i < 16; i++)
257 if ((mask0 & (1 << i)) && (try_irq(i) == 0))
258 mask1 |= (1 << i);
259 for (i = 0; i < 16; i++)
260 if ((mask1 & (1 << i)) && (try_irq(i) != 0)) {
261 mask1 ^= (1 << i);
262 }
263 }
264
265 if (mask1) {
266 printk("scanned");
267 } else {
268 /* Fallback: just find interrupts that aren't in use */
269 for (i = 0; i < 16; i++)
270 if ((mask0 & (1 << i)) &&
271 (request_irq(i, tcic_irq_count, 0, "x", tcic_irq_count) == 0)) {
272 mask1 |= (1 << i);
273 free_irq(i, tcic_irq_count);
274 }
275 printk("default");
276 }
277
278 printk(") = ");
279 for (i = 0; i < 16; i++)
280 if (mask1 & (1<<i))
281 printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
282 printk(" ");
283
284 return mask1;
285}
286
287/*======================================================================
288
289 See if a card is present, powered up, in IO mode, and already
290 bound to a (non-PCMCIA) Linux driver.
291
292 We make an exception for cards that look like serial devices.
293
294======================================================================*/
295
296static int __init is_active(int s)
297{
298 u_short scf1, ioctl, base, num;
299 u_char pwr, sstat;
300 u_int addr;
301
302 tcic_setl(TCIC_ADDR, (s << TCIC_ADDR_SS_SHFT)
303 | TCIC_ADDR_INDREG | TCIC_SCF1(s));
304 scf1 = tcic_getw(TCIC_DATA);
305 pwr = tcic_getb(TCIC_PWR);
306 sstat = tcic_getb(TCIC_SSTAT);
307 addr = TCIC_IWIN(s, 0);
308 tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
309 base = tcic_getw(TCIC_DATA);
310 tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
311 ioctl = tcic_getw(TCIC_DATA);
312
313 if (ioctl & TCIC_ICTL_TINY)
314 num = 1;
315 else {
316 num = (base ^ (base-1));
317 base = base & (base-1);
318 }
319
320 if ((sstat & TCIC_SSTAT_CD) && (pwr & TCIC_PWR_VCC(s)) &&
321 (scf1 & TCIC_SCF1_IOSTS) && (ioctl & TCIC_ICTL_ENA) &&
322 ((base & 0xfeef) != 0x02e8)) {
323 struct resource *res = request_region(base, num, "tcic-2");
324 if (!res) /* region is busy */
325 return 1;
326 release_region(base, num);
327 }
328
329 return 0;
330}
331
332/*======================================================================
333
334 This returns the revision code for the specified socket.
335
336======================================================================*/
337
338static int __init get_tcic_id(void)
339{
340 u_short id;
341
342 tcic_aux_setw(TCIC_AUX_TEST, TCIC_TEST_DIAG);
343 id = tcic_aux_getw(TCIC_AUX_ILOCK);
344 id = (id & TCIC_ILOCKTEST_ID_MASK) >> TCIC_ILOCKTEST_ID_SH;
345 tcic_aux_setw(TCIC_AUX_TEST, 0);
346 return id;
347}
348
349/*====================================================================*/
350
Ming Lei7a192ec2009-02-06 23:40:12 +0800351static struct platform_driver tcic_driver = {
352 .driver = {
353 .name = "tcic-pcmcia",
354 .owner = THIS_MODULE,
355 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356};
357
358static struct platform_device tcic_device = {
359 .name = "tcic-pcmcia",
360 .id = 0,
361};
362
363
364static int __init init_tcic(void)
365{
366 int i, sock, ret = 0;
367 u_int mask, scan;
368
Ming Lei7a192ec2009-02-06 23:40:12 +0800369 if (platform_driver_register(&tcic_driver))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 return -1;
371
372 printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: ");
373 sock = 0;
374
375 if (!request_region(tcic_base, 16, "tcic-2")) {
376 printk("could not allocate ports,\n ");
Ming Lei7a192ec2009-02-06 23:40:12 +0800377 platform_driver_unregister(&tcic_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 return -ENODEV;
379 }
380 else {
381 tcic_setw(TCIC_ADDR, 0);
382 if (tcic_getw(TCIC_ADDR) == 0) {
383 tcic_setw(TCIC_ADDR, 0xc3a5);
384 if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
385 }
386 if (sock == 0) {
387 /* See if resetting the controller does any good */
388 tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET);
389 tcic_setb(TCIC_SCTRL, 0);
390 tcic_setw(TCIC_ADDR, 0);
391 if (tcic_getw(TCIC_ADDR) == 0) {
392 tcic_setw(TCIC_ADDR, 0xc3a5);
393 if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
394 }
395 }
396 }
397 if (sock == 0) {
398 printk("not found.\n");
399 release_region(tcic_base, 16);
Ming Lei7a192ec2009-02-06 23:40:12 +0800400 platform_driver_unregister(&tcic_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 return -ENODEV;
402 }
403
404 sockets = 0;
405 for (i = 0; i < sock; i++) {
406 if ((i == ignore) || is_active(i)) continue;
407 socket_table[sockets].psock = i;
408 socket_table[sockets].id = get_tcic_id();
409
410 socket_table[sockets].socket.owner = THIS_MODULE;
411 /* only 16-bit cards, memory windows must be size-aligned */
412 /* No PCI or CardBus support */
413 socket_table[sockets].socket.features = SS_CAP_PCCARD | SS_CAP_MEM_ALIGN;
414 /* irq 14, 11, 10, 7, 6, 5, 4, 3 */
415 socket_table[sockets].socket.irq_mask = 0x4cf8;
416 /* 4K minimum window size */
417 socket_table[sockets].socket.map_size = 0x1000;
418 sockets++;
419 }
420
421 switch (socket_table[0].id) {
422 case TCIC_ID_DB86082:
423 printk("DB86082"); break;
424 case TCIC_ID_DB86082A:
425 printk("DB86082A"); break;
426 case TCIC_ID_DB86084:
427 printk("DB86084"); break;
428 case TCIC_ID_DB86084A:
429 printk("DB86084A"); break;
430 case TCIC_ID_DB86072:
431 printk("DB86072"); break;
432 case TCIC_ID_DB86184:
433 printk("DB86184"); break;
434 case TCIC_ID_DB86082B:
435 printk("DB86082B"); break;
436 default:
437 printk("Unknown ID 0x%02x", socket_table[0].id);
438 }
439
440 /* Set up polling */
441 poll_timer.function = &tcic_timer;
442 poll_timer.data = 0;
443 init_timer(&poll_timer);
444
445 /* Build interrupt mask */
Joe Perchesad361c92009-07-06 13:05:40 -0700446 printk(KERN_CONT ", %d sockets\n", sockets);
447 printk(KERN_INFO " irq list (");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 if (irq_list_count == 0)
449 mask = irq_mask;
450 else
451 for (i = mask = 0; i < irq_list_count; i++)
452 mask |= (1<<irq_list[i]);
453
454 /* irq 14, 11, 10, 7, 6, 5, 4, 3 */
455 mask &= 0x4cf8;
456 /* Scan interrupts */
457 mask = irq_scan(mask);
458 for (i=0;i<sockets;i++)
459 socket_table[i].socket.irq_mask = mask;
460
461 /* Check for only two interrupts available */
462 scan = (mask & (mask-1));
463 if (((scan & (scan-1)) == 0) && (poll_interval == 0))
464 poll_interval = HZ;
465
466 if (poll_interval == 0) {
467 /* Avoid irq 12 unless it is explicitly requested */
468 u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
469 for (i = 15; i > 0; i--)
470 if ((cs_mask & (1 << i)) &&
471 (request_irq(i, tcic_interrupt, 0, "tcic",
472 tcic_interrupt) == 0))
473 break;
474 cs_irq = i;
475 if (cs_irq == 0) poll_interval = HZ;
476 }
477
478 if (socket_table[0].socket.irq_mask & (1 << 11))
479 printk("sktirq is irq 11, ");
480 if (cs_irq != 0)
481 printk("status change on irq %d\n", cs_irq);
482 else
483 printk("polled status, interval = %d ms\n",
484 poll_interval * 1000 / HZ);
485
486 for (i = 0; i < sockets; i++) {
487 tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT);
488 socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT);
489 }
490
491 /* jump start interrupt handler, if needed */
David Howells7d12e782006-10-05 14:55:46 +0100492 tcic_interrupt(0, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
494 platform_device_register(&tcic_device);
495
496 for (i = 0; i < sockets; i++) {
497 socket_table[i].socket.ops = &tcic_operations;
498 socket_table[i].socket.resource_ops = &pccard_nonstatic_ops;
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200499 socket_table[i].socket.dev.parent = &tcic_device.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 ret = pcmcia_register_socket(&socket_table[i].socket);
501 if (ret && i)
502 pcmcia_unregister_socket(&socket_table[0].socket);
503 }
504
505 return ret;
506
507 return 0;
508
509} /* init_tcic */
510
511/*====================================================================*/
512
513static void __exit exit_tcic(void)
514{
515 int i;
516
517 del_timer_sync(&poll_timer);
518 if (cs_irq != 0) {
519 tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00);
520 free_irq(cs_irq, tcic_interrupt);
521 }
522 release_region(tcic_base, 16);
523
524 for (i = 0; i < sockets; i++) {
525 pcmcia_unregister_socket(&socket_table[i].socket);
526 }
527
528 platform_device_unregister(&tcic_device);
Ming Lei7a192ec2009-02-06 23:40:12 +0800529 platform_driver_unregister(&tcic_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530} /* exit_tcic */
531
532/*====================================================================*/
533
David Howells7d12e782006-10-05 14:55:46 +0100534static irqreturn_t tcic_interrupt(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{
536 int i, quick = 0;
537 u_char latch, sstat;
538 u_short psock;
539 u_int events;
540 static volatile int active = 0;
541
542 if (active) {
543 printk(KERN_NOTICE "tcic: reentered interrupt handler!\n");
544 return IRQ_NONE;
545 } else
546 active = 1;
547
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200548 pr_debug("tcic_interrupt()\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
550 for (i = 0; i < sockets; i++) {
551 psock = socket_table[i].psock;
552 tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
553 | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
554 sstat = tcic_getb(TCIC_SSTAT);
555 latch = sstat ^ socket_table[psock].last_sstat;
556 socket_table[i].last_sstat = sstat;
557 if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) {
558 tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR);
559 quick = 1;
560 }
561 if (latch == 0)
562 continue;
563 events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0;
564 events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
565 if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
566 events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
567 } else {
568 events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0;
569 events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
570 events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
571 }
572 if (events) {
573 pcmcia_parse_events(&socket_table[i].socket, events);
574 }
575 }
576
577 /* Schedule next poll, if needed */
578 if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) {
579 poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval);
580 add_timer(&poll_timer);
581 tcic_timer_pending = 1;
582 }
583 active = 0;
584
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200585 pr_debug("interrupt done\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 return IRQ_HANDLED;
587} /* tcic_interrupt */
588
589static void tcic_timer(u_long data)
590{
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200591 pr_debug("tcic_timer()\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 tcic_timer_pending = 0;
David Howells7d12e782006-10-05 14:55:46 +0100593 tcic_interrupt(0, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594} /* tcic_timer */
595
596/*====================================================================*/
597
598static int tcic_get_status(struct pcmcia_socket *sock, u_int *value)
599{
600 u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
601 u_char reg;
602
603 tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
604 | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
605 reg = tcic_getb(TCIC_SSTAT);
606 *value = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0;
607 *value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
608 if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
609 *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
610 } else {
611 *value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0;
612 *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
613 *value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
614 }
615 reg = tcic_getb(TCIC_PWR);
616 if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock)))
617 *value |= SS_POWERON;
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200618 dev_dbg(&sock->dev, "GetStatus(%d) = %#2.2x\n", psock, *value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 return 0;
620} /* tcic_get_status */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
622/*====================================================================*/
623
624static int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
625{
626 u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
627 u_char reg;
628 u_short scf1, scf2;
629
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200630 dev_dbg(&sock->dev, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 "io_irq %d, csc_mask %#2.2x)\n", psock, state->flags,
632 state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
633 tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG);
634
635 reg = tcic_getb(TCIC_PWR);
636 reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock));
637
638 if (state->Vcc == 50) {
639 switch (state->Vpp) {
640 case 0: reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break;
641 case 50: reg |= TCIC_PWR_VCC(psock); break;
642 case 120: reg |= TCIC_PWR_VPP(psock); break;
643 default: return -EINVAL;
644 }
645 } else if (state->Vcc != 0)
646 return -EINVAL;
647
648 if (reg != tcic_getb(TCIC_PWR))
649 tcic_setb(TCIC_PWR, reg);
650
651 reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT;
652 if (state->flags & SS_OUTPUT_ENA) {
653 tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA);
654 reg |= TCIC_ILOCK_CRESENA;
655 } else
656 tcic_setb(TCIC_SCTRL, 0);
657 if (state->flags & SS_RESET)
658 reg |= TCIC_ILOCK_CRESET;
659 tcic_aux_setb(TCIC_AUX_ILOCK, reg);
660
661 tcic_setw(TCIC_ADDR, TCIC_SCF1(psock));
662 scf1 = TCIC_SCF1_FINPACK;
663 scf1 |= TCIC_IRQ(state->io_irq);
664 if (state->flags & SS_IOCARD) {
665 scf1 |= TCIC_SCF1_IOSTS;
666 if (state->flags & SS_SPKR_ENA)
667 scf1 |= TCIC_SCF1_SPKR;
668 if (state->flags & SS_DMA_MODE)
669 scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT;
670 }
671 tcic_setw(TCIC_DATA, scf1);
672
673 /* Some general setup stuff, and configure status interrupt */
674 reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250);
675 tcic_aux_setb(TCIC_AUX_WCTL, reg);
676 tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00|
677 TCIC_IRQ(cs_irq));
678
679 /* Card status change interrupt mask */
680 tcic_setw(TCIC_ADDR, TCIC_SCF2(psock));
681 scf2 = TCIC_SCF2_MALL;
682 if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD;
683 if (state->flags & SS_IOCARD) {
684 if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1;
685 } else {
686 if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1;
687 if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2;
688 if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY;
689 }
690 tcic_setw(TCIC_DATA, scf2);
691 /* For the ISA bus, the irq should be active-high totem-pole */
692 tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH);
693
694 return 0;
695} /* tcic_set_socket */
696
697/*====================================================================*/
698
699static int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
700{
701 u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
702 u_int addr;
703 u_short base, len, ioctl;
704
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200705 dev_dbg(&sock->dev, "SetIOMap(%d, %d, %#2.2x, %d ns, "
Randy Dunlap01373042009-10-11 18:50:09 -0700706 "%#llx-%#llx)\n", psock, io->map, io->flags, io->speed,
707 (unsigned long long)io->start, (unsigned long long)io->stop);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
709 (io->stop < io->start)) return -EINVAL;
710 tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
711 addr = TCIC_IWIN(psock, io->map);
712
713 base = io->start; len = io->stop - io->start;
714 /* Check to see that len+1 is power of two, etc */
715 if ((len & (len+1)) || (base & len)) return -EINVAL;
716 base |= (len+1)>>1;
717 tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
718 tcic_setw(TCIC_DATA, base);
719
720 ioctl = (psock << TCIC_ICTL_SS_SHFT);
721 ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0;
722 ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0;
723 ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK;
724 if (!(io->flags & MAP_AUTOSZ)) {
725 ioctl |= TCIC_ICTL_QUIET;
726 ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8;
727 }
728 tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
729 tcic_setw(TCIC_DATA, ioctl);
730
731 return 0;
732} /* tcic_set_io_map */
733
734/*====================================================================*/
735
736static int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
737{
738 u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
739 u_short addr, ctl;
740 u_long base, len, mmap;
741
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200742 dev_dbg(&sock->dev, "SetMemMap(%d, %d, %#2.2x, %d ns, "
Greg Kroah-Hartman490ab722006-06-12 15:17:34 -0700743 "%#llx-%#llx, %#x)\n", psock, mem->map, mem->flags,
744 mem->speed, (unsigned long long)mem->res->start,
745 (unsigned long long)mem->res->end, mem->card_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 if ((mem->map > 3) || (mem->card_start > 0x3ffffff) ||
747 (mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) ||
748 (mem->res->start > mem->res->end) || (mem->speed > 1000))
749 return -EINVAL;
750 tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
751 addr = TCIC_MWIN(psock, mem->map);
752
753 base = mem->res->start; len = mem->res->end - mem->res->start;
754 if ((len & (len+1)) || (base & len)) return -EINVAL;
755 if (len == 0x0fff)
756 base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT;
757 else
758 base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT;
759 tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X);
760 tcic_setw(TCIC_DATA, base);
761
762 mmap = mem->card_start - mem->res->start;
763 mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK;
764 if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG;
765 tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X);
766 tcic_setw(TCIC_DATA, mmap);
767
768 ctl = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT);
769 ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK;
770 ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8;
771 ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0;
772 ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0;
773 tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X);
774 tcic_setw(TCIC_DATA, ctl);
775
776 return 0;
777} /* tcic_set_mem_map */
778
779/*====================================================================*/
780
781static int tcic_init(struct pcmcia_socket *s)
782{
783 int i;
784 struct resource res = { .start = 0, .end = 0x1000 };
785 pccard_io_map io = { 0, 0, 0, 0, 1 };
786 pccard_mem_map mem = { .res = &res, };
787
788 for (i = 0; i < 2; i++) {
789 io.map = i;
790 tcic_set_io_map(s, &io);
791 }
792 for (i = 0; i < 5; i++) {
793 mem.map = i;
794 tcic_set_mem_map(s, &mem);
795 }
796 return 0;
797}
798
799static struct pccard_operations tcic_operations = {
800 .init = tcic_init,
801 .get_status = tcic_get_status,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 .set_socket = tcic_set_socket,
803 .set_io_map = tcic_set_io_map,
804 .set_mem_map = tcic_set_mem_map,
805};
806
807/*====================================================================*/
808
809module_init(init_tcic);
810module_exit(exit_tcic);