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