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