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