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