blob: 9e2a15628de57af889115628a3c9e4aaca7e4a60 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*======================================================================
2
3 Device driver for Intel 82365 and compatible PC Card controllers.
4
5 i82365.c 1.265 1999/11/10 18:36:21
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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/types.h>
38#include <linux/fcntl.h>
39#include <linux/string.h>
40#include <linux/kernel.h>
41#include <linux/errno.h>
42#include <linux/timer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/ioport.h>
44#include <linux/delay.h>
45#include <linux/workqueue.h>
46#include <linux/interrupt.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#include <asm/irq.h>
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/ss.h>
55#include <pcmcia/cs.h>
56
57#include <linux/isapnp.h>
58
59/* ISA-bus controllers */
60#include "i82365.h"
61#include "cirrus.h"
62#include "vg468.h"
63#include "ricoh.h"
64
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
David Howells7d12e782006-10-05 14:55:46 +010066static irqreturn_t i365_count_irq(int, void *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070067static inline int _check_irq(int irq, int flags)
68{
69 if (request_irq(irq, i365_count_irq, flags, "x", i365_count_irq) != 0)
70 return -1;
71 free_irq(irq, i365_count_irq);
72 return 0;
73}
74
75/*====================================================================*/
76
77/* Parameters that can be set with 'insmod' */
78
79/* Default base address for i82365sl and other ISA chips */
80static unsigned long i365_base = 0x3e0;
81/* Should we probe at 0x3e2 for an extra ISA controller? */
82static int extra_sockets = 0;
83/* Specify a socket number to ignore */
84static int ignore = -1;
85/* Bit map or list of interrupts to choose from */
86static u_int irq_mask = 0xffff;
87static int irq_list[16];
Al Viro64a6f952007-10-14 19:35:30 +010088static unsigned int irq_list_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089/* The card status change interrupt -- 0 means autoselect */
90static int cs_irq = 0;
91
92/* Probe for safe interrupts? */
93static int do_scan = 1;
94/* Poll status interval -- 0 means default to interrupt */
95static int poll_interval = 0;
96/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */
97static int cycle_time = 120;
98
99/* Cirrus options */
100static int has_dma = -1;
101static int has_led = -1;
102static int has_ring = -1;
103static int dynamic_mode = 0;
104static int freq_bypass = -1;
105static int setup_time = -1;
106static int cmd_time = -1;
107static int recov_time = -1;
108
109/* Vadem options */
110static int async_clock = -1;
111static int cable_mode = -1;
112static int wakeup = 0;
113
114module_param(i365_base, ulong, 0444);
115module_param(ignore, int, 0444);
116module_param(extra_sockets, int, 0444);
117module_param(irq_mask, int, 0444);
118module_param_array(irq_list, int, &irq_list_count, 0444);
119module_param(cs_irq, int, 0444);
120module_param(async_clock, int, 0444);
121module_param(cable_mode, int, 0444);
122module_param(wakeup, int, 0444);
123
124module_param(do_scan, int, 0444);
125module_param(poll_interval, int, 0444);
126module_param(cycle_time, int, 0444);
127module_param(has_dma, int, 0444);
128module_param(has_led, int, 0444);
129module_param(has_ring, int, 0444);
130module_param(dynamic_mode, int, 0444);
131module_param(freq_bypass, int, 0444);
132module_param(setup_time, int, 0444);
133module_param(cmd_time, int, 0444);
134module_param(recov_time, int, 0444);
135
136/*====================================================================*/
137
138typedef struct cirrus_state_t {
139 u_char misc1, misc2;
140 u_char timer[6];
141} cirrus_state_t;
142
143typedef struct vg46x_state_t {
144 u_char ctl, ema;
145} vg46x_state_t;
146
147struct i82365_socket {
148 u_short type, flags;
149 struct pcmcia_socket socket;
150 unsigned int number;
Olof Johansson906da802008-02-04 22:27:35 -0800151 unsigned int ioaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 u_short psock;
153 u_char cs_irq, intr;
154 union {
155 cirrus_state_t cirrus;
156 vg46x_state_t vg46x;
157 } state;
158};
159
160/* Where we keep track of our sockets... */
161static int sockets = 0;
162static struct i82365_socket socket[8] = {
163 { 0, }, /* ... */
164};
165
166/* Default ISA interrupt mask */
167#define I365_MASK 0xdeb8 /* irq 15,14,12,11,10,9,7,5,4,3 */
168
169static int grab_irq;
170static DEFINE_SPINLOCK(isa_lock);
171#define ISA_LOCK(n, f) spin_lock_irqsave(&isa_lock, f)
172#define ISA_UNLOCK(n, f) spin_unlock_irqrestore(&isa_lock, f)
173
174static struct timer_list poll_timer;
175
176/*====================================================================*/
177
178/* These definitions must match the pcic table! */
179typedef enum pcic_id {
180 IS_I82365A, IS_I82365B, IS_I82365DF,
181 IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
182 IS_PD6710, IS_PD672X, IS_VT83C469,
183} pcic_id;
184
185/* Flags for classifying groups of controllers */
186#define IS_VADEM 0x0001
187#define IS_CIRRUS 0x0002
188#define IS_VIA 0x0010
189#define IS_UNKNOWN 0x0400
190#define IS_VG_PWR 0x0800
191#define IS_DF_PWR 0x1000
192#define IS_REGISTERED 0x2000
193#define IS_ALIVE 0x8000
194
195typedef struct pcic_t {
196 char *name;
197 u_short flags;
198} pcic_t;
199
200static pcic_t pcic[] = {
201 { "Intel i82365sl A step", 0 },
202 { "Intel i82365sl B step", 0 },
203 { "Intel i82365sl DF", IS_DF_PWR },
204 { "IBM Clone", 0 },
205 { "Ricoh RF5C296/396", 0 },
206 { "VLSI 82C146", 0 },
207 { "Vadem VG-468", IS_VADEM },
208 { "Vadem VG-469", IS_VADEM|IS_VG_PWR },
209 { "Cirrus PD6710", IS_CIRRUS },
210 { "Cirrus PD672x", IS_CIRRUS },
211 { "VIA VT83C469", IS_CIRRUS|IS_VIA },
212};
213
214#define PCIC_COUNT (sizeof(pcic)/sizeof(pcic_t))
215
216/*====================================================================*/
217
218static DEFINE_SPINLOCK(bus_lock);
219
220static u_char i365_get(u_short sock, u_short reg)
221{
222 unsigned long flags;
223 spin_lock_irqsave(&bus_lock,flags);
224 {
Olof Johansson906da802008-02-04 22:27:35 -0800225 unsigned int port = socket[sock].ioaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 u_char val;
227 reg = I365_REG(socket[sock].psock, reg);
228 outb(reg, port); val = inb(port+1);
229 spin_unlock_irqrestore(&bus_lock,flags);
230 return val;
231 }
232}
233
234static void i365_set(u_short sock, u_short reg, u_char data)
235{
236 unsigned long flags;
237 spin_lock_irqsave(&bus_lock,flags);
238 {
Olof Johansson906da802008-02-04 22:27:35 -0800239 unsigned int port = socket[sock].ioaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 u_char val = I365_REG(socket[sock].psock, reg);
241 outb(val, port); outb(data, port+1);
242 spin_unlock_irqrestore(&bus_lock,flags);
243 }
244}
245
246static void i365_bset(u_short sock, u_short reg, u_char mask)
247{
248 u_char d = i365_get(sock, reg);
249 d |= mask;
250 i365_set(sock, reg, d);
251}
252
253static void i365_bclr(u_short sock, u_short reg, u_char mask)
254{
255 u_char d = i365_get(sock, reg);
256 d &= ~mask;
257 i365_set(sock, reg, d);
258}
259
260static void i365_bflip(u_short sock, u_short reg, u_char mask, int b)
261{
262 u_char d = i365_get(sock, reg);
263 if (b)
264 d |= mask;
265 else
266 d &= ~mask;
267 i365_set(sock, reg, d);
268}
269
270static u_short i365_get_pair(u_short sock, u_short reg)
271{
272 u_short a, b;
273 a = i365_get(sock, reg);
274 b = i365_get(sock, reg+1);
275 return (a + (b<<8));
276}
277
278static void i365_set_pair(u_short sock, u_short reg, u_short data)
279{
280 i365_set(sock, reg, data & 0xff);
281 i365_set(sock, reg+1, data >> 8);
282}
283
284/*======================================================================
285
286 Code to save and restore global state information for Cirrus
287 PD67xx controllers, and to set and report global configuration
288 options.
289
290 The VIA controllers also use these routines, as they are mostly
291 Cirrus lookalikes, without the timing registers.
292
293======================================================================*/
294
295#define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b))))
296
297static void cirrus_get_state(u_short s)
298{
299 int i;
300 cirrus_state_t *p = &socket[s].state.cirrus;
301 p->misc1 = i365_get(s, PD67_MISC_CTL_1);
302 p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
303 p->misc2 = i365_get(s, PD67_MISC_CTL_2);
304 for (i = 0; i < 6; i++)
305 p->timer[i] = i365_get(s, PD67_TIME_SETUP(0)+i);
306}
307
308static void cirrus_set_state(u_short s)
309{
310 int i;
311 u_char misc;
312 cirrus_state_t *p = &socket[s].state.cirrus;
313
314 misc = i365_get(s, PD67_MISC_CTL_2);
315 i365_set(s, PD67_MISC_CTL_2, p->misc2);
316 if (misc & PD67_MC2_SUSPEND) mdelay(50);
317 misc = i365_get(s, PD67_MISC_CTL_1);
318 misc &= ~(PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
319 i365_set(s, PD67_MISC_CTL_1, misc | p->misc1);
320 for (i = 0; i < 6; i++)
321 i365_set(s, PD67_TIME_SETUP(0)+i, p->timer[i]);
322}
323
324static u_int __init cirrus_set_opts(u_short s, char *buf)
325{
326 struct i82365_socket *t = &socket[s];
327 cirrus_state_t *p = &socket[s].state.cirrus;
328 u_int mask = 0xffff;
329
330 if (has_ring == -1) has_ring = 1;
331 flip(p->misc2, PD67_MC2_IRQ15_RI, has_ring);
332 flip(p->misc2, PD67_MC2_DYNAMIC_MODE, dynamic_mode);
333 flip(p->misc2, PD67_MC2_FREQ_BYPASS, freq_bypass);
334 if (p->misc2 & PD67_MC2_IRQ15_RI)
335 strcat(buf, " [ring]");
336 if (p->misc2 & PD67_MC2_DYNAMIC_MODE)
337 strcat(buf, " [dyn mode]");
338 if (p->misc2 & PD67_MC2_FREQ_BYPASS)
339 strcat(buf, " [freq bypass]");
340 if (p->misc1 & PD67_MC1_INPACK_ENA)
341 strcat(buf, " [inpack]");
342 if (p->misc2 & PD67_MC2_IRQ15_RI)
343 mask &= ~0x8000;
344 if (has_led > 0) {
345 strcat(buf, " [led]");
346 mask &= ~0x1000;
347 }
348 if (has_dma > 0) {
349 strcat(buf, " [dma]");
350 mask &= ~0x0600;
351 }
352 if (!(t->flags & IS_VIA)) {
353 if (setup_time >= 0)
354 p->timer[0] = p->timer[3] = setup_time;
355 if (cmd_time > 0) {
356 p->timer[1] = cmd_time;
357 p->timer[4] = cmd_time*2+4;
358 }
359 if (p->timer[1] == 0) {
360 p->timer[1] = 6; p->timer[4] = 16;
361 if (p->timer[0] == 0)
362 p->timer[0] = p->timer[3] = 1;
363 }
364 if (recov_time >= 0)
365 p->timer[2] = p->timer[5] = recov_time;
366 buf += strlen(buf);
367 sprintf(buf, " [%d/%d/%d] [%d/%d/%d]", p->timer[0], p->timer[1],
368 p->timer[2], p->timer[3], p->timer[4], p->timer[5]);
369 }
370 return mask;
371}
372
373/*======================================================================
374
375 Code to save and restore global state information for Vadem VG468
376 and VG469 controllers, and to set and report global configuration
377 options.
378
379======================================================================*/
380
381static void vg46x_get_state(u_short s)
382{
383 vg46x_state_t *p = &socket[s].state.vg46x;
384 p->ctl = i365_get(s, VG468_CTL);
385 if (socket[s].type == IS_VG469)
386 p->ema = i365_get(s, VG469_EXT_MODE);
387}
388
389static void vg46x_set_state(u_short s)
390{
391 vg46x_state_t *p = &socket[s].state.vg46x;
392 i365_set(s, VG468_CTL, p->ctl);
393 if (socket[s].type == IS_VG469)
394 i365_set(s, VG469_EXT_MODE, p->ema);
395}
396
397static u_int __init vg46x_set_opts(u_short s, char *buf)
398{
399 vg46x_state_t *p = &socket[s].state.vg46x;
400
401 flip(p->ctl, VG468_CTL_ASYNC, async_clock);
402 flip(p->ema, VG469_MODE_CABLE, cable_mode);
403 if (p->ctl & VG468_CTL_ASYNC)
404 strcat(buf, " [async]");
405 if (p->ctl & VG468_CTL_INPACK)
406 strcat(buf, " [inpack]");
407 if (socket[s].type == IS_VG469) {
408 u_char vsel = i365_get(s, VG469_VSELECT);
409 if (vsel & VG469_VSEL_EXT_STAT) {
410 strcat(buf, " [ext mode]");
411 if (vsel & VG469_VSEL_EXT_BUS)
412 strcat(buf, " [isa buf]");
413 }
414 if (p->ema & VG469_MODE_CABLE)
415 strcat(buf, " [cable]");
416 if (p->ema & VG469_MODE_COMPAT)
417 strcat(buf, " [c step]");
418 }
419 return 0xffff;
420}
421
422/*======================================================================
423
424 Generic routines to get and set controller options
425
426======================================================================*/
427
428static void get_bridge_state(u_short s)
429{
430 struct i82365_socket *t = &socket[s];
431 if (t->flags & IS_CIRRUS)
432 cirrus_get_state(s);
433 else if (t->flags & IS_VADEM)
434 vg46x_get_state(s);
435}
436
437static void set_bridge_state(u_short s)
438{
439 struct i82365_socket *t = &socket[s];
440 if (t->flags & IS_CIRRUS)
441 cirrus_set_state(s);
442 else {
443 i365_set(s, I365_GBLCTL, 0x00);
444 i365_set(s, I365_GENCTL, 0x00);
445 }
446 i365_bflip(s, I365_INTCTL, I365_INTR_ENA, t->intr);
447 if (t->flags & IS_VADEM)
448 vg46x_set_state(s);
449}
450
451static u_int __init set_bridge_opts(u_short s, u_short ns)
452{
453 u_short i;
454 u_int m = 0xffff;
455 char buf[128];
456
457 for (i = s; i < s+ns; i++) {
458 if (socket[i].flags & IS_ALIVE) {
459 printk(KERN_INFO " host opts [%d]: already alive!\n", i);
460 continue;
461 }
462 buf[0] = '\0';
463 get_bridge_state(i);
464 if (socket[i].flags & IS_CIRRUS)
465 m = cirrus_set_opts(i, buf);
466 else if (socket[i].flags & IS_VADEM)
467 m = vg46x_set_opts(i, buf);
468 set_bridge_state(i);
469 printk(KERN_INFO " host opts [%d]:%s\n", i,
470 (*buf) ? buf : " none");
471 }
472 return m;
473}
474
475/*======================================================================
476
477 Interrupt testing code, for ISA and PCI interrupts
478
479======================================================================*/
480
481static volatile u_int irq_hits;
482static u_short irq_sock;
483
David Howells7d12e782006-10-05 14:55:46 +0100484static irqreturn_t i365_count_irq(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485{
486 i365_get(irq_sock, I365_CSC);
487 irq_hits++;
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200488 pr_debug("i82365: -> hit on irq %d\n", irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 return IRQ_HANDLED;
490}
491
492static u_int __init test_irq(u_short sock, int irq)
493{
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200494 pr_debug("i82365: testing ISA irq %d\n", irq);
Thomas Gleixnerdace1452006-07-01 19:29:38 -0700495 if (request_irq(irq, i365_count_irq, IRQF_PROBE_SHARED, "scan",
Andrew Morton13e87ec2006-04-27 18:39:18 -0700496 i365_count_irq) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 return 1;
498 irq_hits = 0; irq_sock = sock;
499 msleep(10);
500 if (irq_hits) {
501 free_irq(irq, i365_count_irq);
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200502 pr_debug("i82365: spurious hit!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 return 1;
504 }
505
506 /* Generate one interrupt */
507 i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4));
508 i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ);
509 udelay(1000);
510
511 free_irq(irq, i365_count_irq);
512
513 /* mask all interrupts */
514 i365_set(sock, I365_CSCINT, 0);
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200515 pr_debug("i82365: hits = %d\n", irq_hits);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
517 return (irq_hits != 1);
518}
519
520static u_int __init isa_scan(u_short sock, u_int mask0)
521{
522 u_int mask1 = 0;
523 int i;
524
525#ifdef __alpha__
526#define PIC 0x4d0
527 /* Don't probe level-triggered interrupts -- reserved for PCI */
528 mask0 &= ~(inb(PIC) | (inb(PIC+1) << 8));
529#endif
530
531 if (do_scan) {
532 set_bridge_state(sock);
533 i365_set(sock, I365_CSCINT, 0);
534 for (i = 0; i < 16; i++)
535 if ((mask0 & (1 << i)) && (test_irq(sock, i) == 0))
536 mask1 |= (1 << i);
537 for (i = 0; i < 16; i++)
538 if ((mask1 & (1 << i)) && (test_irq(sock, i) != 0))
539 mask1 ^= (1 << i);
540 }
541
542 printk(KERN_INFO " ISA irqs (");
543 if (mask1) {
544 printk("scanned");
545 } else {
546 /* Fallback: just find interrupts that aren't in use */
547 for (i = 0; i < 16; i++)
Thomas Gleixnerdace1452006-07-01 19:29:38 -0700548 if ((mask0 & (1 << i)) && (_check_irq(i, IRQF_PROBE_SHARED) == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 mask1 |= (1 << i);
550 printk("default");
551 /* If scan failed, default to polled status */
552 if (!cs_irq && (poll_interval == 0)) poll_interval = HZ;
553 }
554 printk(") = ");
555
556 for (i = 0; i < 16; i++)
557 if (mask1 & (1<<i))
558 printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
559 if (mask1 == 0) printk("none!");
560
561 return mask1;
562}
563
564/*====================================================================*/
565
566/* Time conversion functions */
567
568static int to_cycles(int ns)
569{
570 return ns/cycle_time;
571}
572
573/*====================================================================*/
574
Olof Johansson906da802008-02-04 22:27:35 -0800575static int __init identify(unsigned int port, u_short sock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576{
577 u_char val;
578 int type = -1;
579
580 /* Use the next free entry in the socket table */
581 socket[sockets].ioaddr = port;
582 socket[sockets].psock = sock;
583
584 /* Wake up a sleepy Cirrus controller */
585 if (wakeup) {
586 i365_bclr(sockets, PD67_MISC_CTL_2, PD67_MC2_SUSPEND);
587 /* Pause at least 50 ms */
588 mdelay(50);
589 }
590
591 if ((val = i365_get(sockets, I365_IDENT)) & 0x70)
592 return -1;
593 switch (val) {
594 case 0x82:
595 type = IS_I82365A; break;
596 case 0x83:
597 type = IS_I82365B; break;
598 case 0x84:
599 type = IS_I82365DF; break;
600 case 0x88: case 0x89: case 0x8a:
601 type = IS_IBM; break;
602 }
603
604 /* Check for Vadem VG-468 chips */
605 outb(0x0e, port);
606 outb(0x37, port);
607 i365_bset(sockets, VG468_MISC, VG468_MISC_VADEMREV);
608 val = i365_get(sockets, I365_IDENT);
609 if (val & I365_IDENT_VADEM) {
610 i365_bclr(sockets, VG468_MISC, VG468_MISC_VADEMREV);
611 type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468;
612 }
613
614 /* Check for Ricoh chips */
615 val = i365_get(sockets, RF5C_CHIP_ID);
616 if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396))
617 type = IS_RF5Cx96;
618
619 /* Check for Cirrus CL-PD67xx chips */
620 i365_set(sockets, PD67_CHIP_INFO, 0);
621 val = i365_get(sockets, PD67_CHIP_INFO);
622 if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) {
623 val = i365_get(sockets, PD67_CHIP_INFO);
624 if ((val & PD67_INFO_CHIP_ID) == 0) {
625 type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710;
626 i365_set(sockets, PD67_EXT_INDEX, 0xe5);
627 if (i365_get(sockets, PD67_EXT_INDEX) != 0xe5)
628 type = IS_VT83C469;
629 }
630 }
631 return type;
632} /* identify */
633
634/*======================================================================
635
636 See if a card is present, powered up, in IO mode, and already
637 bound to a (non PC Card) Linux driver. We leave these alone.
638
639 We make an exception for cards that seem to be serial devices.
640
641======================================================================*/
642
643static int __init is_alive(u_short sock)
644{
645 u_char stat;
Olof Johansson906da802008-02-04 22:27:35 -0800646 unsigned int start, stop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647
648 stat = i365_get(sock, I365_STATUS);
649 start = i365_get_pair(sock, I365_IO(0)+I365_W_START);
650 stop = i365_get_pair(sock, I365_IO(0)+I365_W_STOP);
651 if ((stat & I365_CS_DETECT) && (stat & I365_CS_POWERON) &&
652 (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) &&
653 (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(0)) &&
Dominik Brodowskif3549422005-06-27 16:28:55 -0700654 ((start & 0xfeef) != 0x02e8)) {
655 if (!request_region(start, stop-start+1, "i82365"))
656 return 1;
657 release_region(start, stop-start+1);
658 }
659
660 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661}
662
663/*====================================================================*/
664
Olof Johansson906da802008-02-04 22:27:35 -0800665static void __init add_socket(unsigned int port, int psock, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666{
667 socket[sockets].ioaddr = port;
668 socket[sockets].psock = psock;
669 socket[sockets].type = type;
670 socket[sockets].flags = pcic[type].flags;
671 if (is_alive(sockets))
672 socket[sockets].flags |= IS_ALIVE;
673 sockets++;
674}
675
676static void __init add_pcic(int ns, int type)
677{
678 u_int mask = 0, i, base;
679 int isa_irq = 0;
680 struct i82365_socket *t = &socket[sockets-ns];
681
682 base = sockets-ns;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 if (base == 0) printk("\n");
684 printk(KERN_INFO " %s", pcic[type].name);
Olof Johansson906da802008-02-04 22:27:35 -0800685 printk(" ISA-to-PCMCIA at port %#x ofs 0x%02x",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 t->ioaddr, t->psock*0x40);
687 printk(", %d socket%s\n", ns, ((ns > 1) ? "s" : ""));
688
689 /* Set host options, build basic interrupt mask */
690 if (irq_list_count == 0)
691 mask = irq_mask;
692 else
693 for (i = mask = 0; i < irq_list_count; i++)
694 mask |= (1<<irq_list[i]);
695 mask &= I365_MASK & set_bridge_opts(base, ns);
696 /* Scan for ISA interrupts */
697 mask = isa_scan(base, mask);
698
699 /* Poll if only two interrupts available */
700 if (!poll_interval) {
701 u_int tmp = (mask & 0xff20);
702 tmp = tmp & (tmp-1);
703 if ((tmp & (tmp-1)) == 0)
704 poll_interval = HZ;
705 }
706 /* Only try an ISA cs_irq if this is the first controller */
707 if (!grab_irq && (cs_irq || !poll_interval)) {
708 /* Avoid irq 12 unless it is explicitly requested */
709 u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
710 for (cs_irq = 15; cs_irq > 0; cs_irq--)
711 if ((cs_mask & (1 << cs_irq)) &&
Thomas Gleixnerdace1452006-07-01 19:29:38 -0700712 (_check_irq(cs_irq, IRQF_PROBE_SHARED) == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 break;
714 if (cs_irq) {
715 grab_irq = 1;
716 isa_irq = cs_irq;
717 printk(" status change on irq %d\n", cs_irq);
718 }
719 }
720
721 if (!isa_irq) {
722 if (poll_interval == 0)
723 poll_interval = HZ;
724 printk(" polling interval = %d ms\n",
725 poll_interval * 1000 / HZ);
726
727 }
728
729 /* Update socket interrupt information, capabilities */
730 for (i = 0; i < ns; i++) {
731 t[i].socket.features |= SS_CAP_PCCARD;
732 t[i].socket.map_size = 0x1000;
733 t[i].socket.irq_mask = mask;
734 t[i].cs_irq = isa_irq;
735 }
736
737} /* add_pcic */
738
739/*====================================================================*/
740
741#ifdef CONFIG_PNP
742static struct isapnp_device_id id_table[] __initdata = {
743 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
744 ISAPNP_FUNCTION(0x0e00), (unsigned long) "Intel 82365-Compatible" },
745 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
746 ISAPNP_FUNCTION(0x0e01), (unsigned long) "Cirrus Logic CL-PD6720" },
747 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
748 ISAPNP_FUNCTION(0x0e02), (unsigned long) "VLSI VL82C146" },
749 { 0 }
750};
751MODULE_DEVICE_TABLE(isapnp, id_table);
752
753static struct pnp_dev *i82365_pnpdev;
754#endif
755
756static void __init isa_probe(void)
757{
758 int i, j, sock, k, ns, id;
Olof Johansson906da802008-02-04 22:27:35 -0800759 unsigned int port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760#ifdef CONFIG_PNP
761 struct isapnp_device_id *devid;
762 struct pnp_dev *dev;
763
764 for (devid = id_table; devid->vendor; devid++) {
765 if ((dev = pnp_find_dev(NULL, devid->vendor, devid->function, NULL))) {
766
767 if (pnp_device_attach(dev) < 0)
768 continue;
769
770 if (pnp_activate_dev(dev) < 0) {
771 printk("activate failed\n");
772 pnp_device_detach(dev);
773 break;
774 }
775
776 if (!pnp_port_valid(dev, 0)) {
777 printk("invalid resources ?\n");
778 pnp_device_detach(dev);
779 break;
780 }
781 i365_base = pnp_port_start(dev, 0);
782 i82365_pnpdev = dev;
783 break;
784 }
785 }
786#endif
787
Dominik Brodowskif3549422005-06-27 16:28:55 -0700788 if (!request_region(i365_base, 2, "i82365")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 if (sockets == 0)
790 printk("port conflict at %#lx\n", i365_base);
791 return;
792 }
793
794 id = identify(i365_base, 0);
795 if ((id == IS_I82365DF) && (identify(i365_base, 1) != id)) {
796 for (i = 0; i < 4; i++) {
797 if (i == ignore) continue;
798 port = i365_base + ((i & 1) << 2) + ((i & 2) << 1);
799 sock = (i & 1) << 1;
800 if (identify(port, sock) == IS_I82365DF) {
801 add_socket(port, sock, IS_VLSI);
802 add_pcic(1, IS_VLSI);
803 }
804 }
805 } else {
806 for (i = 0; i < 8; i += 2) {
807 if (sockets && !extra_sockets && (i == 4))
808 break;
809 port = i365_base + 2*(i>>2);
810 sock = (i & 3);
811 id = identify(port, sock);
812 if (id < 0) continue;
813
814 for (j = ns = 0; j < 2; j++) {
815 /* Does the socket exist? */
816 if ((ignore == i+j) || (identify(port, sock+j) < 0))
817 continue;
818 /* Check for bad socket decode */
819 for (k = 0; k <= sockets; k++)
820 i365_set(k, I365_MEM(0)+I365_W_OFF, k);
821 for (k = 0; k <= sockets; k++)
822 if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k)
823 break;
824 if (k <= sockets) break;
825 add_socket(port, sock+j, id); ns++;
826 }
827 if (ns != 0) add_pcic(ns, id);
828 }
829 }
830}
831
832/*====================================================================*/
833
David Howells7d12e782006-10-05 14:55:46 +0100834static irqreturn_t pcic_interrupt(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835{
836 int i, j, csc;
837 u_int events, active;
838 u_long flags = 0;
839 int handled = 0;
840
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200841 pr_debug("pcic_interrupt(%d)\n", irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
843 for (j = 0; j < 20; j++) {
844 active = 0;
845 for (i = 0; i < sockets; i++) {
846 if (socket[i].cs_irq != irq)
847 continue;
848 handled = 1;
849 ISA_LOCK(i, flags);
850 csc = i365_get(i, I365_CSC);
851 if ((csc == 0) || (i365_get(i, I365_IDENT) & 0x70)) {
852 ISA_UNLOCK(i, flags);
853 continue;
854 }
855 events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
856
857 if (i365_get(i, I365_INTCTL) & I365_PC_IOCARD)
858 events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0;
859 else {
860 events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
861 events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
862 events |= (csc & I365_CSC_READY) ? SS_READY : 0;
863 }
864 ISA_UNLOCK(i, flags);
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200865 pr_debug("socket %d event 0x%02x\n", i, events);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
867 if (events)
868 pcmcia_parse_events(&socket[i].socket, events);
869
870 active |= events;
871 }
872 if (!active) break;
873 }
874 if (j == 20)
875 printk(KERN_NOTICE "i82365: infinite loop in interrupt handler\n");
876
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200877 pr_debug("pcic_interrupt done\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 return IRQ_RETVAL(handled);
879} /* pcic_interrupt */
880
881static void pcic_interrupt_wrapper(u_long data)
882{
David Howells7d12e782006-10-05 14:55:46 +0100883 pcic_interrupt(0, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 poll_timer.expires = jiffies + poll_interval;
885 add_timer(&poll_timer);
886}
887
888/*====================================================================*/
889
890static int i365_get_status(u_short sock, u_int *value)
891{
892 u_int status;
893
894 status = i365_get(sock, I365_STATUS);
895 *value = ((status & I365_CS_DETECT) == I365_CS_DETECT)
896 ? SS_DETECT : 0;
897
898 if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD)
899 *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG;
900 else {
901 *value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD;
902 *value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN;
903 }
904 *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0;
905 *value |= (status & I365_CS_READY) ? SS_READY : 0;
906 *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0;
907
908 if (socket[sock].type == IS_VG469) {
909 status = i365_get(sock, VG469_VSENSE);
910 if (socket[sock].psock & 1) {
911 *value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD;
912 *value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD;
913 } else {
914 *value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD;
915 *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD;
916 }
917 }
918
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200919 pr_debug("GetStatus(%d) = %#4.4x\n", sock, *value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 return 0;
921} /* i365_get_status */
922
923/*====================================================================*/
924
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925static int i365_set_socket(u_short sock, socket_state_t *state)
926{
927 struct i82365_socket *t = &socket[sock];
928 u_char reg;
929
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +0200930 pr_debug("SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
932 state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
933
934 /* First set global controller options */
935 set_bridge_state(sock);
936
937 /* IO card, RESET flag, IO interrupt */
938 reg = t->intr;
939 reg |= state->io_irq;
940 reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
941 reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
942 i365_set(sock, I365_INTCTL, reg);
943
944 reg = I365_PWR_NORESET;
945 if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO;
946 if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT;
947
948 if (t->flags & IS_CIRRUS) {
949 if (state->Vpp != 0) {
950 if (state->Vpp == 120)
951 reg |= I365_VPP1_12V;
952 else if (state->Vpp == state->Vcc)
953 reg |= I365_VPP1_5V;
954 else return -EINVAL;
955 }
956 if (state->Vcc != 0) {
957 reg |= I365_VCC_5V;
958 if (state->Vcc == 33)
959 i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
960 else if (state->Vcc == 50)
961 i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
962 else return -EINVAL;
963 }
964 } else if (t->flags & IS_VG_PWR) {
965 if (state->Vpp != 0) {
966 if (state->Vpp == 120)
967 reg |= I365_VPP1_12V;
968 else if (state->Vpp == state->Vcc)
969 reg |= I365_VPP1_5V;
970 else return -EINVAL;
971 }
972 if (state->Vcc != 0) {
973 reg |= I365_VCC_5V;
974 if (state->Vcc == 33)
975 i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC);
976 else if (state->Vcc == 50)
977 i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC);
978 else return -EINVAL;
979 }
980 } else if (t->flags & IS_DF_PWR) {
981 switch (state->Vcc) {
982 case 0: break;
983 case 33: reg |= I365_VCC_3V; break;
984 case 50: reg |= I365_VCC_5V; break;
985 default: return -EINVAL;
986 }
987 switch (state->Vpp) {
988 case 0: break;
989 case 50: reg |= I365_VPP1_5V; break;
990 case 120: reg |= I365_VPP1_12V; break;
991 default: return -EINVAL;
992 }
993 } else {
994 switch (state->Vcc) {
995 case 0: break;
996 case 50: reg |= I365_VCC_5V; break;
997 default: return -EINVAL;
998 }
999 switch (state->Vpp) {
1000 case 0: break;
1001 case 50: reg |= I365_VPP1_5V | I365_VPP2_5V; break;
1002 case 120: reg |= I365_VPP1_12V | I365_VPP2_12V; break;
1003 default: return -EINVAL;
1004 }
1005 }
1006
1007 if (reg != i365_get(sock, I365_POWER))
1008 i365_set(sock, I365_POWER, reg);
1009
1010 /* Chipset-specific functions */
1011 if (t->flags & IS_CIRRUS) {
1012 /* Speaker control */
1013 i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA,
1014 state->flags & SS_SPKR_ENA);
1015 }
1016
1017 /* Card status change interrupt mask */
1018 reg = t->cs_irq << 4;
1019 if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
1020 if (state->flags & SS_IOCARD) {
1021 if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG;
1022 } else {
1023 if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1;
1024 if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2;
1025 if (state->csc_mask & SS_READY) reg |= I365_CSC_READY;
1026 }
1027 i365_set(sock, I365_CSCINT, reg);
1028 i365_get(sock, I365_CSC);
1029
1030 return 0;
1031} /* i365_set_socket */
1032
1033/*====================================================================*/
1034
1035static int i365_set_io_map(u_short sock, struct pccard_io_map *io)
1036{
1037 u_char map, ioctl;
1038
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +02001039 pr_debug("SetIOMap(%d, %d, %#2.2x, %d ns, "
Randy Dunlap01373042009-10-11 18:50:09 -07001040 "%#llx-%#llx)\n", sock, io->map, io->flags, io->speed,
1041 (unsigned long long)io->start, (unsigned long long)io->stop);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 map = io->map;
1043 if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
1044 (io->stop < io->start)) return -EINVAL;
1045 /* Turn off the window before changing anything */
1046 if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map))
1047 i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map));
1048 i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start);
1049 i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop);
1050 ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map);
1051 if (io->speed) ioctl |= I365_IOCTL_WAIT(map);
1052 if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
1053 if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
1054 if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
1055 i365_set(sock, I365_IOCTL, ioctl);
1056 /* Turn on the window if necessary */
1057 if (io->flags & MAP_ACTIVE)
1058 i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map));
1059 return 0;
1060} /* i365_set_io_map */
1061
1062/*====================================================================*/
1063
1064static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
1065{
1066 u_short base, i;
1067 u_char map;
1068
Dominik Brodowskic9f50dd2009-10-23 12:56:46 +02001069 pr_debug("SetMemMap(%d, %d, %#2.2x, %d ns, %#llx-%#llx, "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 "%#x)\n", sock, mem->map, mem->flags, mem->speed,
Greg Kroah-Hartman490ab722006-06-12 15:17:34 -07001071 (unsigned long long)mem->res->start,
1072 (unsigned long long)mem->res->end, mem->card_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
1074 map = mem->map;
1075 if ((map > 4) || (mem->card_start > 0x3ffffff) ||
1076 (mem->res->start > mem->res->end) || (mem->speed > 1000))
1077 return -EINVAL;
1078 if ((mem->res->start > 0xffffff) || (mem->res->end > 0xffffff))
1079 return -EINVAL;
1080
1081 /* Turn off the window before changing anything */
1082 if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
1083 i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map));
1084
1085 base = I365_MEM(map);
1086 i = (mem->res->start >> 12) & 0x0fff;
1087 if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT;
1088 if (mem->flags & MAP_0WS) i |= I365_MEM_0WS;
1089 i365_set_pair(sock, base+I365_W_START, i);
1090
1091 i = (mem->res->end >> 12) & 0x0fff;
1092 switch (to_cycles(mem->speed)) {
1093 case 0: break;
1094 case 1: i |= I365_MEM_WS0; break;
1095 case 2: i |= I365_MEM_WS1; break;
1096 default: i |= I365_MEM_WS1 | I365_MEM_WS0; break;
1097 }
1098 i365_set_pair(sock, base+I365_W_STOP, i);
1099
1100 i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff;
1101 if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT;
1102 if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG;
1103 i365_set_pair(sock, base+I365_W_OFF, i);
1104
1105 /* Turn on the window if necessary */
1106 if (mem->flags & MAP_ACTIVE)
1107 i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map));
1108 return 0;
1109} /* i365_set_mem_map */
1110
1111#if 0 /* driver model ordering issue */
1112/*======================================================================
1113
1114 Routines for accessing socket information and register dumps via
1115 /sys/class/pcmcia_socket/...
1116
1117======================================================================*/
1118
1119static ssize_t show_info(struct class_device *class_dev, char *buf)
1120{
1121 struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev);
1122 return sprintf(buf, "type: %s\npsock: %d\n",
1123 pcic[s->type].name, s->psock);
1124}
1125
1126static ssize_t show_exca(struct class_device *class_dev, char *buf)
1127{
1128 struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev);
1129 unsigned short sock;
1130 int i;
1131 ssize_t ret = 0;
1132 unsigned long flags = 0;
1133
1134 sock = s->number;
1135
1136 ISA_LOCK(sock, flags);
1137 for (i = 0; i < 0x40; i += 4) {
1138 ret += sprintf(buf, "%02x %02x %02x %02x%s",
1139 i365_get(sock,i), i365_get(sock,i+1),
1140 i365_get(sock,i+2), i365_get(sock,i+3),
1141 ((i % 16) == 12) ? "\n" : " ");
1142 buf += ret;
1143 }
1144 ISA_UNLOCK(sock, flags);
1145
1146 return ret;
1147}
1148
1149static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
1150static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
1151#endif
1152
1153/*====================================================================*/
1154
1155/* this is horribly ugly... proper locking needs to be done here at
1156 * some time... */
1157#define LOCKED(x) do { \
1158 int retval; \
1159 unsigned long flags; \
1160 spin_lock_irqsave(&isa_lock, flags); \
1161 retval = x; \
1162 spin_unlock_irqrestore(&isa_lock, flags); \
1163 return retval; \
1164} while (0)
1165
1166
1167static int pcic_get_status(struct pcmcia_socket *s, u_int *value)
1168{
1169 unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
1170
1171 if (socket[sock].flags & IS_ALIVE) {
1172 *value = 0;
1173 return -EINVAL;
1174 }
1175
1176 LOCKED(i365_get_status(sock, value));
1177}
1178
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179static int pcic_set_socket(struct pcmcia_socket *s, socket_state_t *state)
1180{
1181 unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
1182
1183 if (socket[sock].flags & IS_ALIVE)
1184 return -EINVAL;
1185
1186 LOCKED(i365_set_socket(sock, state));
1187}
1188
1189static int pcic_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
1190{
1191 unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
1192 if (socket[sock].flags & IS_ALIVE)
1193 return -EINVAL;
1194
1195 LOCKED(i365_set_io_map(sock, io));
1196}
1197
1198static int pcic_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
1199{
1200 unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
1201 if (socket[sock].flags & IS_ALIVE)
1202 return -EINVAL;
1203
1204 LOCKED(i365_set_mem_map(sock, mem));
1205}
1206
1207static int pcic_init(struct pcmcia_socket *s)
1208{
1209 int i;
1210 struct resource res = { .start = 0, .end = 0x1000 };
1211 pccard_io_map io = { 0, 0, 0, 0, 1 };
1212 pccard_mem_map mem = { .res = &res, };
1213
1214 for (i = 0; i < 2; i++) {
1215 io.map = i;
1216 pcic_set_io_map(s, &io);
1217 }
1218 for (i = 0; i < 5; i++) {
1219 mem.map = i;
1220 pcic_set_mem_map(s, &mem);
1221 }
1222 return 0;
1223}
1224
Ming Lei7a192ec2009-02-06 23:40:12 +08001225
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226static struct pccard_operations pcic_operations = {
1227 .init = pcic_init,
1228 .get_status = pcic_get_status,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 .set_socket = pcic_set_socket,
1230 .set_io_map = pcic_set_io_map,
1231 .set_mem_map = pcic_set_mem_map,
1232};
1233
1234/*====================================================================*/
1235
Ming Lei7a192ec2009-02-06 23:40:12 +08001236static struct platform_driver i82365_driver = {
1237 .driver = {
1238 .name = "i82365",
1239 .owner = THIS_MODULE,
1240 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241};
1242
Dominik Brodowskidfb279c2005-11-10 16:26:13 +01001243static struct platform_device *i82365_device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244
1245static int __init init_i82365(void)
1246{
1247 int i, ret;
1248
Ming Lei7a192ec2009-02-06 23:40:12 +08001249 ret = platform_driver_register(&i82365_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 if (ret)
Leonardo Potenza2df69702008-04-01 23:47:09 +02001251 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252
Dominik Brodowskidfb279c2005-11-10 16:26:13 +01001253 i82365_device = platform_device_alloc("i82365", 0);
1254 if (i82365_device) {
1255 ret = platform_device_add(i82365_device);
1256 if (ret)
1257 platform_device_put(i82365_device);
1258 } else
1259 ret = -ENOMEM;
1260
Leonardo Potenza2df69702008-04-01 23:47:09 +02001261 if (ret)
1262 goto err_driver_unregister;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
1264 printk(KERN_INFO "Intel ISA PCIC probe: ");
1265 sockets = 0;
1266
1267 isa_probe();
1268
1269 if (sockets == 0) {
1270 printk("not found.\n");
Leonardo Potenza2df69702008-04-01 23:47:09 +02001271 ret = -ENODEV;
1272 goto err_dev_unregister;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 }
1274
1275 /* Set up interrupt handler(s) */
1276 if (grab_irq != 0)
Leonardo Potenza2df69702008-04-01 23:47:09 +02001277 ret = request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt);
1278
1279 if (ret)
1280 goto err_socket_release;
1281
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 /* register sockets with the pcmcia core */
1283 for (i = 0; i < sockets; i++) {
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001284 socket[i].socket.dev.parent = &i82365_device->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 socket[i].socket.ops = &pcic_operations;
1286 socket[i].socket.resource_ops = &pccard_nonstatic_ops;
1287 socket[i].socket.owner = THIS_MODULE;
1288 socket[i].number = i;
1289 ret = pcmcia_register_socket(&socket[i].socket);
1290 if (!ret)
1291 socket[i].flags |= IS_REGISTERED;
1292
1293#if 0 /* driver model ordering issue */
1294 class_device_create_file(&socket[i].socket.dev,
1295 &class_device_attr_info);
1296 class_device_create_file(&socket[i].socket.dev,
1297 &class_device_attr_exca);
1298#endif
1299 }
1300
1301 /* Finally, schedule a polling interrupt */
1302 if (poll_interval != 0) {
1303 poll_timer.function = pcic_interrupt_wrapper;
1304 poll_timer.data = 0;
1305 init_timer(&poll_timer);
1306 poll_timer.expires = jiffies + poll_interval;
1307 add_timer(&poll_timer);
1308 }
1309
1310 return 0;
Leonardo Potenza2df69702008-04-01 23:47:09 +02001311err_socket_release:
1312 for (i = 0; i < sockets; i++) {
1313 /* Turn off all interrupt sources! */
1314 i365_set(i, I365_CSCINT, 0);
1315 release_region(socket[i].ioaddr, 2);
1316 }
1317err_dev_unregister:
1318 platform_device_unregister(i82365_device);
1319 release_region(i365_base, 2);
1320#ifdef CONFIG_PNP
1321 if (i82365_pnpdev)
1322 pnp_disable_dev(i82365_pnpdev);
1323#endif
1324err_driver_unregister:
Ming Lei7a192ec2009-02-06 23:40:12 +08001325 platform_driver_unregister(&i82365_driver);
Leonardo Potenza2df69702008-04-01 23:47:09 +02001326err_out:
1327 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328} /* init_i82365 */
1329
1330static void __exit exit_i82365(void)
1331{
1332 int i;
1333
1334 for (i = 0; i < sockets; i++) {
1335 if (socket[i].flags & IS_REGISTERED)
1336 pcmcia_unregister_socket(&socket[i].socket);
1337 }
Dominik Brodowskidfb279c2005-11-10 16:26:13 +01001338 platform_device_unregister(i82365_device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 if (poll_interval != 0)
1340 del_timer_sync(&poll_timer);
1341 if (grab_irq != 0)
1342 free_irq(cs_irq, pcic_interrupt);
1343 for (i = 0; i < sockets; i++) {
1344 /* Turn off all interrupt sources! */
1345 i365_set(i, I365_CSCINT, 0);
1346 release_region(socket[i].ioaddr, 2);
1347 }
Dominik Brodowskif3549422005-06-27 16:28:55 -07001348 release_region(i365_base, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349#ifdef CONFIG_PNP
1350 if (i82365_pnpdev)
1351 pnp_disable_dev(i82365_pnpdev);
1352#endif
Ming Lei7a192ec2009-02-06 23:40:12 +08001353 platform_driver_unregister(&i82365_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354} /* exit_i82365 */
1355
1356module_init(init_i82365);
1357module_exit(exit_i82365);
1358MODULE_LICENSE("Dual MPL/GPL");
1359/*====================================================================*/