import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / drivers / isdn / hisax / asuscom.c
1 /* $Id: asuscom.c,v 1.1.4.1 2001/11/20 14:19:35 kai Exp $
2  *
3  * low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
4  *
5  * Author       Karsten Keil
6  * Copyright    by Karsten Keil      <keil@isdn4linux.de>
7  *
8  * This software may be used and distributed according to the terms
9  * of the GNU General Public License, incorporated herein by reference.
10  *
11  * Thanks to  ASUSCOM NETWORK INC. Taiwan and  Dynalink NL for information
12  *
13  */
14
15 #define __NO_VERSION__
16 #include <linux/init.h>
17 #include <linux/isapnp.h>
18 #include "hisax.h"
19 #include "isac.h"
20 #include "ipac.h"
21 #include "hscx.h"
22 #include "isdnl1.h"
23
24 extern const char *CardType[];
25
26 const char *Asuscom_revision = "$Revision: 1.1.4.1 $";
27
28 #define byteout(addr,val) outb(val,addr)
29 #define bytein(addr) inb(addr)
30
31 #define ASUS_ISAC       0
32 #define ASUS_HSCX       1
33 #define ASUS_ADR        2
34 #define ASUS_CTRL_U7    3
35 #define ASUS_CTRL_POTS  5
36
37 #define ASUS_IPAC_ALE   0
38 #define ASUS_IPAC_DATA  1
39
40 #define ASUS_ISACHSCX   1
41 #define ASUS_IPAC       2
42
43 /* CARD_ADR (Write) */
44 #define ASUS_RESET      0x80    /* Bit 7 Reset-Leitung */
45
46 static inline u_char
47 readreg(unsigned int ale, unsigned int adr, u_char off)
48 {
49         register u_char ret;
50         long flags;
51
52         save_flags(flags);
53         cli();
54         byteout(ale, off);
55         ret = bytein(adr);
56         restore_flags(flags);
57         return (ret);
58 }
59
60 static inline void
61 readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
62 {
63         /* fifo read without cli because it's allready done  */
64
65         byteout(ale, off);
66         insb(adr, data, size);
67 }
68
69
70 static inline void
71 writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
72 {
73         long flags;
74
75         save_flags(flags);
76         cli();
77         byteout(ale, off);
78         byteout(adr, data);
79         restore_flags(flags);
80 }
81
82 static inline void
83 writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
84 {
85         /* fifo write without cli because it's allready done  */
86         byteout(ale, off);
87         outsb(adr, data, size);
88 }
89
90 /* Interface functions */
91
92 static u_char
93 ReadISAC(struct IsdnCardState *cs, u_char offset)
94 {
95         return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset));
96 }
97
98 static void
99 WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
100 {
101         writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value);
102 }
103
104 static void
105 ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
106 {
107         readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
108 }
109
110 static void
111 WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
112 {
113         writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
114 }
115
116 static u_char
117 ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
118 {
119         return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80));
120 }
121
122 static void
123 WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
124 {
125         writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80, value);
126 }
127
128 static void
129 ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
130 {
131         readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
132 }
133
134 static void
135 WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
136 {
137         writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
138 }
139
140 static u_char
141 ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
142 {
143         return (readreg(cs->hw.asus.adr,
144                         cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0)));
145 }
146
147 static void
148 WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
149 {
150         writereg(cs->hw.asus.adr,
151                  cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value);
152 }
153
154 /*
155  * fast interrupt HSCX stuff goes here
156  */
157
158 #define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \
159                 cs->hw.asus.hscx, reg + (nr ? 0x40 : 0))
160 #define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \
161                 cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data)
162
163 #define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \
164                 cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
165
166 #define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \
167                 cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
168
169 #include "hscx_irq.c"
170
171 static void
172 asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs)
173 {
174         struct IsdnCardState *cs = dev_id;
175         u_char val;
176
177         if (!cs) {
178                 printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n");
179                 return;
180         }
181         val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
182       Start_HSCX:
183         if (val)
184                 hscx_int_main(cs, val);
185         val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
186       Start_ISAC:
187         if (val)
188                 isac_interrupt(cs, val);
189         val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
190         if (val) {
191                 if (cs->debug & L1_DEB_HSCX)
192                         debugl1(cs, "HSCX IntStat after IntRoutine");
193                 goto Start_HSCX;
194         }
195         val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
196         if (val) {
197                 if (cs->debug & L1_DEB_ISAC)
198                         debugl1(cs, "ISAC IntStat after IntRoutine");
199                 goto Start_ISAC;
200         }
201         writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF);
202         writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF);
203         writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF);
204         writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0);
205         writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0);
206         writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0);
207 }
208
209 static void
210 asuscom_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
211 {
212         struct IsdnCardState *cs = dev_id;
213         u_char ista, val, icnt = 5;
214
215         if (!cs) {
216                 printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n");
217                 return;
218         }
219         ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
220 Start_IPAC:
221         if (cs->debug & L1_DEB_IPAC)
222                 debugl1(cs, "IPAC ISTA %02X", ista);
223         if (ista & 0x0f) {
224                 val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
225                 if (ista & 0x01)
226                         val |= 0x01;
227                 if (ista & 0x04)
228                         val |= 0x02;
229                 if (ista & 0x08)
230                         val |= 0x04;
231                 if (val)
232                         hscx_int_main(cs, val);
233         }
234         if (ista & 0x20) {
235                 val = 0xfe & readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA | 0x80);
236                 if (val) {
237                         isac_interrupt(cs, val);
238                 }
239         }
240         if (ista & 0x10) {
241                 val = 0x01;
242                 isac_interrupt(cs, val);
243         }
244         ista  = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
245         if ((ista & 0x3f) && icnt) {
246                 icnt--;
247                 goto Start_IPAC;
248         }
249         if (!icnt)
250                 printk(KERN_WARNING "ASUS IRQ LOOP\n");
251         writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xFF);
252         writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xC0);
253 }
254
255 void
256 release_io_asuscom(struct IsdnCardState *cs)
257 {
258         int bytecnt = 8;
259
260         if (cs->hw.asus.cfg_reg)
261                 release_region(cs->hw.asus.cfg_reg, bytecnt);
262 }
263
264 static void
265 reset_asuscom(struct IsdnCardState *cs)
266 {
267         long flags;
268
269         if (cs->subtyp == ASUS_IPAC)
270                 writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x20);
271         else
272                 byteout(cs->hw.asus.adr, ASUS_RESET);   /* Reset On */
273         save_flags(flags);
274         sti();
275         set_current_state(TASK_UNINTERRUPTIBLE);
276         schedule_timeout((10*HZ)/1000);
277         if (cs->subtyp == ASUS_IPAC)
278                 writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0);
279         else
280                 byteout(cs->hw.asus.adr, 0);    /* Reset Off */
281         set_current_state(TASK_UNINTERRUPTIBLE);
282         schedule_timeout((10*HZ)/1000);
283         if (cs->subtyp == ASUS_IPAC) {
284                 writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0);
285                 writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ACFG, 0xff);
286                 writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_AOE, 0x0);
287                 writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xc0);
288                 writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_PCFG, 0x12);
289         }
290         restore_flags(flags);
291 }
292
293 static int
294 Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg)
295 {
296         switch (mt) {
297                 case CARD_RESET:
298                         reset_asuscom(cs);
299                         return(0);
300                 case CARD_RELEASE:
301                         release_io_asuscom(cs);
302                         return(0);
303                 case CARD_INIT:
304                         cs->debug |= L1_DEB_IPAC;
305                         inithscxisac(cs, 3);
306                         return(0);
307                 case CARD_TEST:
308                         return(0);
309         }
310         return(0);
311 }
312
313 #ifdef __ISAPNP__
314 static struct isapnp_device_id asus_ids[] __initdata = {
315         { ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688),
316           ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688), 
317           (unsigned long) "Asus1688 PnP" },
318         { ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1690),
319           ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1690), 
320           (unsigned long) "Asus1690 PnP" },
321         { ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0020),
322           ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0020), 
323           (unsigned long) "Isurf2 PnP" },
324         { ISAPNP_VENDOR('E', 'L', 'F'), ISAPNP_FUNCTION(0x0000),
325           ISAPNP_VENDOR('E', 'L', 'F'), ISAPNP_FUNCTION(0x0000), 
326           (unsigned long) "Iscas TE320" },
327         { 0, }
328 };
329
330 static struct isapnp_device_id *adev = &asus_ids[0];
331 static struct pci_bus *pnp_c __devinitdata = NULL;
332 #endif
333
334 int __init
335 setup_asuscom(struct IsdnCard *card)
336 {
337         int bytecnt;
338         struct IsdnCardState *cs = card->cs;
339         u_char val;
340         char tmp[64];
341
342         strcpy(tmp, Asuscom_revision);
343         printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp));
344         if (cs->typ != ISDN_CTYPE_ASUSCOM)
345                 return (0);
346 #ifdef __ISAPNP__
347         if (!card->para[1] && isapnp_present()) {
348                 struct pci_bus *pb;
349                 struct pci_dev *pd;
350
351                 while(adev->card_vendor) {
352                         if ((pb = isapnp_find_card(adev->card_vendor,
353                                 adev->card_device, pnp_c))) {
354                                 pnp_c = pb;
355                                 pd = NULL;
356                                 if ((pd = isapnp_find_dev(pnp_c,
357                                         adev->vendor, adev->function, pd))) {
358                                         printk(KERN_INFO "HiSax: %s detected\n",
359                                                 (char *)adev->driver_data);
360                                         pd->prepare(pd);
361                                         pd->deactivate(pd);
362                                         pd->activate(pd);
363                                         card->para[1] = pd->resource[0].start;
364                                         card->para[0] = pd->irq_resource[0].start;
365                                         if (!card->para[0] || !card->para[1]) {
366                                                 printk(KERN_ERR "AsusPnP:some resources are missing %ld/%lx\n",
367                                                 card->para[0], card->para[1]);
368                                                 pd->deactivate(pd);
369                                                 return(0);
370                                         }
371                                         break;
372                                 } else {
373                                         printk(KERN_ERR "AsusPnP: PnP error card found, no device\n");
374                                 }
375                         }
376                         adev++;
377                         pnp_c=NULL;
378                 } 
379                 if (!adev->card_vendor) {
380                         printk(KERN_INFO "AsusPnP: no ISAPnP card found\n");
381                         return(0);
382                 }
383         }
384 #endif
385         bytecnt = 8;
386         cs->hw.asus.cfg_reg = card->para[1];
387         cs->irq = card->para[0];
388         if (check_region((cs->hw.asus.cfg_reg), bytecnt)) {
389                 printk(KERN_WARNING
390                        "HiSax: %s config port %x-%x already in use\n",
391                        CardType[card->typ],
392                        cs->hw.asus.cfg_reg,
393                        cs->hw.asus.cfg_reg + bytecnt);
394                 return (0);
395         } else {
396                 request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn");
397         }
398         printk(KERN_INFO "ISDNLink: defined at 0x%x IRQ %d\n",
399                 cs->hw.asus.cfg_reg, cs->irq);
400         cs->BC_Read_Reg = &ReadHSCX;
401         cs->BC_Write_Reg = &WriteHSCX;
402         cs->BC_Send_Data = &hscx_fill_fifo;
403         cs->cardmsg = &Asus_card_msg;
404         val = readreg(cs->hw.asus.cfg_reg + ASUS_IPAC_ALE, 
405                 cs->hw.asus.cfg_reg + ASUS_IPAC_DATA, IPAC_ID);
406         if ((val == 1) || (val == 2)) {
407                 cs->subtyp = ASUS_IPAC;
408                 cs->hw.asus.adr  = cs->hw.asus.cfg_reg + ASUS_IPAC_ALE;
409                 cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
410                 cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
411                 test_and_set_bit(HW_IPAC, &cs->HW_Flags);
412                 cs->readisac = &ReadISAC_IPAC;
413                 cs->writeisac = &WriteISAC_IPAC;
414                 cs->readisacfifo = &ReadISACfifo_IPAC;
415                 cs->writeisacfifo = &WriteISACfifo_IPAC;
416                 cs->irq_func = &asuscom_interrupt_ipac;
417                 printk(KERN_INFO "Asus: IPAC version %x\n", val);
418         } else {
419                 cs->subtyp = ASUS_ISACHSCX;
420                 cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR;
421                 cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC;
422                 cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX;
423                 cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7;
424                 cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS;
425                 cs->readisac = &ReadISAC;
426                 cs->writeisac = &WriteISAC;
427                 cs->readisacfifo = &ReadISACfifo;
428                 cs->writeisacfifo = &WriteISACfifo;
429                 cs->irq_func = &asuscom_interrupt;
430                 ISACVersion(cs, "ISDNLink:");
431                 if (HscxVersion(cs, "ISDNLink:")) {
432                         printk(KERN_WARNING
433                         "ISDNLink: wrong HSCX versions check IO address\n");
434                         release_io_asuscom(cs);
435                         return (0);
436                 }
437         }
438         printk(KERN_INFO "ISDNLink: resetting card\n");
439         reset_asuscom(cs);
440         return (1);
441 }