blob: 12c49ee135e1edb36ce47a17d06f3be9ee2fe822 [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
Ming Lei7a192ec2009-02-06 23:40:12 +0800351static int tcic_drv_pcmcia_suspend(struct platform_device *dev,
352 pm_message_t state)
353{
Rafael J. Wysocki827b4642009-09-29 00:10:41 +0200354 return pcmcia_socket_dev_suspend(&dev->dev);
Ming Lei7a192ec2009-02-06 23:40:12 +0800355}
356
357static int tcic_drv_pcmcia_resume(struct platform_device *dev)
358{
359 return pcmcia_socket_dev_resume(&dev->dev);
360}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361/*====================================================================*/
362
Ming Lei7a192ec2009-02-06 23:40:12 +0800363static struct platform_driver tcic_driver = {
364 .driver = {
365 .name = "tcic-pcmcia",
366 .owner = THIS_MODULE,
367 },
368 .suspend = tcic_drv_pcmcia_suspend,
369 .resume = tcic_drv_pcmcia_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370};
371
372static struct platform_device tcic_device = {
373 .name = "tcic-pcmcia",
374 .id = 0,
375};
376
377
378static int __init init_tcic(void)
379{
380 int i, sock, ret = 0;
381 u_int mask, scan;
382
Ming Lei7a192ec2009-02-06 23:40:12 +0800383 if (platform_driver_register(&tcic_driver))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 return -1;
385
386 printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: ");
387 sock = 0;
388
389 if (!request_region(tcic_base, 16, "tcic-2")) {
390 printk("could not allocate ports,\n ");
Ming Lei7a192ec2009-02-06 23:40:12 +0800391 platform_driver_unregister(&tcic_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 return -ENODEV;
393 }
394 else {
395 tcic_setw(TCIC_ADDR, 0);
396 if (tcic_getw(TCIC_ADDR) == 0) {
397 tcic_setw(TCIC_ADDR, 0xc3a5);
398 if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
399 }
400 if (sock == 0) {
401 /* See if resetting the controller does any good */
402 tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET);
403 tcic_setb(TCIC_SCTRL, 0);
404 tcic_setw(TCIC_ADDR, 0);
405 if (tcic_getw(TCIC_ADDR) == 0) {
406 tcic_setw(TCIC_ADDR, 0xc3a5);
407 if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
408 }
409 }
410 }
411 if (sock == 0) {
412 printk("not found.\n");
413 release_region(tcic_base, 16);
Ming Lei7a192ec2009-02-06 23:40:12 +0800414 platform_driver_unregister(&tcic_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 return -ENODEV;
416 }
417
418 sockets = 0;
419 for (i = 0; i < sock; i++) {
420 if ((i == ignore) || is_active(i)) continue;
421 socket_table[sockets].psock = i;
422 socket_table[sockets].id = get_tcic_id();
423
424 socket_table[sockets].socket.owner = THIS_MODULE;
425 /* only 16-bit cards, memory windows must be size-aligned */
426 /* No PCI or CardBus support */
427 socket_table[sockets].socket.features = SS_CAP_PCCARD | SS_CAP_MEM_ALIGN;
428 /* irq 14, 11, 10, 7, 6, 5, 4, 3 */
429 socket_table[sockets].socket.irq_mask = 0x4cf8;
430 /* 4K minimum window size */
431 socket_table[sockets].socket.map_size = 0x1000;
432 sockets++;
433 }
434
435 switch (socket_table[0].id) {
436 case TCIC_ID_DB86082:
437 printk("DB86082"); break;
438 case TCIC_ID_DB86082A:
439 printk("DB86082A"); break;
440 case TCIC_ID_DB86084:
441 printk("DB86084"); break;
442 case TCIC_ID_DB86084A:
443 printk("DB86084A"); break;
444 case TCIC_ID_DB86072:
445 printk("DB86072"); break;
446 case TCIC_ID_DB86184:
447 printk("DB86184"); break;
448 case TCIC_ID_DB86082B:
449 printk("DB86082B"); break;
450 default:
451 printk("Unknown ID 0x%02x", socket_table[0].id);
452 }
453
454 /* Set up polling */
455 poll_timer.function = &tcic_timer;
456 poll_timer.data = 0;
457 init_timer(&poll_timer);
458
459 /* Build interrupt mask */
Joe Perchesad361c92009-07-06 13:05:40 -0700460 printk(KERN_CONT ", %d sockets\n", sockets);
461 printk(KERN_INFO " irq list (");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 if (irq_list_count == 0)
463 mask = irq_mask;
464 else
465 for (i = mask = 0; i < irq_list_count; i++)
466 mask |= (1<<irq_list[i]);
467
468 /* irq 14, 11, 10, 7, 6, 5, 4, 3 */
469 mask &= 0x4cf8;
470 /* Scan interrupts */
471 mask = irq_scan(mask);
472 for (i=0;i<sockets;i++)
473 socket_table[i].socket.irq_mask = mask;
474
475 /* Check for only two interrupts available */
476 scan = (mask & (mask-1));
477 if (((scan & (scan-1)) == 0) && (poll_interval == 0))
478 poll_interval = HZ;
479
480 if (poll_interval == 0) {
481 /* Avoid irq 12 unless it is explicitly requested */
482 u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
483 for (i = 15; i > 0; i--)
484 if ((cs_mask & (1 << i)) &&
485 (request_irq(i, tcic_interrupt, 0, "tcic",
486 tcic_interrupt) == 0))
487 break;
488 cs_irq = i;
489 if (cs_irq == 0) poll_interval = HZ;
490 }
491
492 if (socket_table[0].socket.irq_mask & (1 << 11))
493 printk("sktirq is irq 11, ");
494 if (cs_irq != 0)
495 printk("status change on irq %d\n", cs_irq);
496 else
497 printk("polled status, interval = %d ms\n",
498 poll_interval * 1000 / HZ);
499
500 for (i = 0; i < sockets; i++) {
501 tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT);
502 socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT);
503 }
504
505 /* jump start interrupt handler, if needed */
David Howells7d12e782006-10-05 14:55:46 +0100506 tcic_interrupt(0, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508 platform_device_register(&tcic_device);
509
510 for (i = 0; i < sockets; i++) {
511 socket_table[i].socket.ops = &tcic_operations;
512 socket_table[i].socket.resource_ops = &pccard_nonstatic_ops;
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200513 socket_table[i].socket.dev.parent = &tcic_device.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 ret = pcmcia_register_socket(&socket_table[i].socket);
515 if (ret && i)
516 pcmcia_unregister_socket(&socket_table[0].socket);
517 }
518
519 return ret;
520
521 return 0;
522
523} /* init_tcic */
524
525/*====================================================================*/
526
527static void __exit exit_tcic(void)
528{
529 int i;
530
531 del_timer_sync(&poll_timer);
532 if (cs_irq != 0) {
533 tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00);
534 free_irq(cs_irq, tcic_interrupt);
535 }
536 release_region(tcic_base, 16);
537
538 for (i = 0; i < sockets; i++) {
539 pcmcia_unregister_socket(&socket_table[i].socket);
540 }
541
542 platform_device_unregister(&tcic_device);
Ming Lei7a192ec2009-02-06 23:40:12 +0800543 platform_driver_unregister(&tcic_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544} /* exit_tcic */
545
546/*====================================================================*/
547
David Howells7d12e782006-10-05 14:55:46 +0100548static irqreturn_t tcic_interrupt(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549{
550 int i, quick = 0;
551 u_char latch, sstat;
552 u_short psock;
553 u_int events;
554 static volatile int active = 0;
555
556 if (active) {
557 printk(KERN_NOTICE "tcic: reentered interrupt handler!\n");
558 return IRQ_NONE;
559 } else
560 active = 1;
561
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200562 pr_debug("tcic_interrupt()\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
564 for (i = 0; i < sockets; i++) {
565 psock = socket_table[i].psock;
566 tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
567 | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
568 sstat = tcic_getb(TCIC_SSTAT);
569 latch = sstat ^ socket_table[psock].last_sstat;
570 socket_table[i].last_sstat = sstat;
571 if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) {
572 tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR);
573 quick = 1;
574 }
575 if (latch == 0)
576 continue;
577 events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0;
578 events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
579 if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
580 events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
581 } else {
582 events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0;
583 events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
584 events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
585 }
586 if (events) {
587 pcmcia_parse_events(&socket_table[i].socket, events);
588 }
589 }
590
591 /* Schedule next poll, if needed */
592 if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) {
593 poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval);
594 add_timer(&poll_timer);
595 tcic_timer_pending = 1;
596 }
597 active = 0;
598
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200599 pr_debug("interrupt done\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 return IRQ_HANDLED;
601} /* tcic_interrupt */
602
603static void tcic_timer(u_long data)
604{
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200605 pr_debug("tcic_timer()\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 tcic_timer_pending = 0;
David Howells7d12e782006-10-05 14:55:46 +0100607 tcic_interrupt(0, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608} /* tcic_timer */
609
610/*====================================================================*/
611
612static int tcic_get_status(struct pcmcia_socket *sock, u_int *value)
613{
614 u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
615 u_char reg;
616
617 tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
618 | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
619 reg = tcic_getb(TCIC_SSTAT);
620 *value = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0;
621 *value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
622 if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
623 *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
624 } else {
625 *value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0;
626 *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
627 *value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
628 }
629 reg = tcic_getb(TCIC_PWR);
630 if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock)))
631 *value |= SS_POWERON;
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200632 dev_dbg(&sock->dev, "GetStatus(%d) = %#2.2x\n", psock, *value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 return 0;
634} /* tcic_get_status */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
636/*====================================================================*/
637
638static int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
639{
640 u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
641 u_char reg;
642 u_short scf1, scf2;
643
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200644 dev_dbg(&sock->dev, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 "io_irq %d, csc_mask %#2.2x)\n", psock, state->flags,
646 state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
647 tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG);
648
649 reg = tcic_getb(TCIC_PWR);
650 reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock));
651
652 if (state->Vcc == 50) {
653 switch (state->Vpp) {
654 case 0: reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break;
655 case 50: reg |= TCIC_PWR_VCC(psock); break;
656 case 120: reg |= TCIC_PWR_VPP(psock); break;
657 default: return -EINVAL;
658 }
659 } else if (state->Vcc != 0)
660 return -EINVAL;
661
662 if (reg != tcic_getb(TCIC_PWR))
663 tcic_setb(TCIC_PWR, reg);
664
665 reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT;
666 if (state->flags & SS_OUTPUT_ENA) {
667 tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA);
668 reg |= TCIC_ILOCK_CRESENA;
669 } else
670 tcic_setb(TCIC_SCTRL, 0);
671 if (state->flags & SS_RESET)
672 reg |= TCIC_ILOCK_CRESET;
673 tcic_aux_setb(TCIC_AUX_ILOCK, reg);
674
675 tcic_setw(TCIC_ADDR, TCIC_SCF1(psock));
676 scf1 = TCIC_SCF1_FINPACK;
677 scf1 |= TCIC_IRQ(state->io_irq);
678 if (state->flags & SS_IOCARD) {
679 scf1 |= TCIC_SCF1_IOSTS;
680 if (state->flags & SS_SPKR_ENA)
681 scf1 |= TCIC_SCF1_SPKR;
682 if (state->flags & SS_DMA_MODE)
683 scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT;
684 }
685 tcic_setw(TCIC_DATA, scf1);
686
687 /* Some general setup stuff, and configure status interrupt */
688 reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250);
689 tcic_aux_setb(TCIC_AUX_WCTL, reg);
690 tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00|
691 TCIC_IRQ(cs_irq));
692
693 /* Card status change interrupt mask */
694 tcic_setw(TCIC_ADDR, TCIC_SCF2(psock));
695 scf2 = TCIC_SCF2_MALL;
696 if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD;
697 if (state->flags & SS_IOCARD) {
698 if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1;
699 } else {
700 if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1;
701 if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2;
702 if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY;
703 }
704 tcic_setw(TCIC_DATA, scf2);
705 /* For the ISA bus, the irq should be active-high totem-pole */
706 tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH);
707
708 return 0;
709} /* tcic_set_socket */
710
711/*====================================================================*/
712
713static int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
714{
715 u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
716 u_int addr;
717 u_short base, len, ioctl;
718
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200719 dev_dbg(&sock->dev, "SetIOMap(%d, %d, %#2.2x, %d ns, "
Randy Dunlap01373042009-10-11 18:50:09 -0700720 "%#llx-%#llx)\n", psock, io->map, io->flags, io->speed,
721 (unsigned long long)io->start, (unsigned long long)io->stop);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
723 (io->stop < io->start)) return -EINVAL;
724 tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
725 addr = TCIC_IWIN(psock, io->map);
726
727 base = io->start; len = io->stop - io->start;
728 /* Check to see that len+1 is power of two, etc */
729 if ((len & (len+1)) || (base & len)) return -EINVAL;
730 base |= (len+1)>>1;
731 tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
732 tcic_setw(TCIC_DATA, base);
733
734 ioctl = (psock << TCIC_ICTL_SS_SHFT);
735 ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0;
736 ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0;
737 ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK;
738 if (!(io->flags & MAP_AUTOSZ)) {
739 ioctl |= TCIC_ICTL_QUIET;
740 ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8;
741 }
742 tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
743 tcic_setw(TCIC_DATA, ioctl);
744
745 return 0;
746} /* tcic_set_io_map */
747
748/*====================================================================*/
749
750static int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
751{
752 u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
753 u_short addr, ctl;
754 u_long base, len, mmap;
755
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200756 dev_dbg(&sock->dev, "SetMemMap(%d, %d, %#2.2x, %d ns, "
Greg Kroah-Hartman490ab722006-06-12 15:17:34 -0700757 "%#llx-%#llx, %#x)\n", psock, mem->map, mem->flags,
758 mem->speed, (unsigned long long)mem->res->start,
759 (unsigned long long)mem->res->end, mem->card_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 if ((mem->map > 3) || (mem->card_start > 0x3ffffff) ||
761 (mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) ||
762 (mem->res->start > mem->res->end) || (mem->speed > 1000))
763 return -EINVAL;
764 tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
765 addr = TCIC_MWIN(psock, mem->map);
766
767 base = mem->res->start; len = mem->res->end - mem->res->start;
768 if ((len & (len+1)) || (base & len)) return -EINVAL;
769 if (len == 0x0fff)
770 base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT;
771 else
772 base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT;
773 tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X);
774 tcic_setw(TCIC_DATA, base);
775
776 mmap = mem->card_start - mem->res->start;
777 mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK;
778 if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG;
779 tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X);
780 tcic_setw(TCIC_DATA, mmap);
781
782 ctl = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT);
783 ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK;
784 ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8;
785 ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0;
786 ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0;
787 tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X);
788 tcic_setw(TCIC_DATA, ctl);
789
790 return 0;
791} /* tcic_set_mem_map */
792
793/*====================================================================*/
794
795static int tcic_init(struct pcmcia_socket *s)
796{
797 int i;
798 struct resource res = { .start = 0, .end = 0x1000 };
799 pccard_io_map io = { 0, 0, 0, 0, 1 };
800 pccard_mem_map mem = { .res = &res, };
801
802 for (i = 0; i < 2; i++) {
803 io.map = i;
804 tcic_set_io_map(s, &io);
805 }
806 for (i = 0; i < 5; i++) {
807 mem.map = i;
808 tcic_set_mem_map(s, &mem);
809 }
810 return 0;
811}
812
813static struct pccard_operations tcic_operations = {
814 .init = tcic_init,
815 .get_status = tcic_get_status,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 .set_socket = tcic_set_socket,
817 .set_io_map = tcic_set_io_map,
818 .set_mem_map = tcic_set_mem_map,
819};
820
821/*====================================================================*/
822
823module_init(init_tcic);
824module_exit(exit_tcic);