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
readreg(unsigned int ale,unsigned int adr,u_char off)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
readfifo(unsigned int ale,unsigned int adr,u_char off,u_char * data,int size)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
writereg(unsigned int ale,unsigned int adr,u_char off,u_char data)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
writefifo(unsigned int ale,unsigned int adr,u_char off,u_char * data,int size)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
ReadISAC(struct IsdnCardState * cs,u_char offset)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
WriteISAC(struct IsdnCardState * cs,u_char offset,u_char value)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
ReadISACfifo(struct IsdnCardState * cs,u_char * data,int size)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
WriteISACfifo(struct IsdnCardState * cs,u_char * data,int size)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
ReadISAC_IPAC(struct IsdnCardState * cs,u_char offset)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
WriteISAC_IPAC(struct IsdnCardState * cs,u_char offset,u_char value)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
ReadISACfifo_IPAC(struct IsdnCardState * cs,u_char * data,int size)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
WriteISACfifo_IPAC(struct IsdnCardState * cs,u_char * data,int size)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
ReadHSCX(struct IsdnCardState * cs,int hscx,u_char offset)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
WriteHSCX(struct IsdnCardState * cs,int hscx,u_char offset,u_char value)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
asuscom_interrupt(int intno,void * dev_id,struct pt_regs * regs)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
asuscom_interrupt_ipac(int intno,void * dev_id,struct pt_regs * regs)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
release_io_asuscom(struct IsdnCardState * cs)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
reset_asuscom(struct IsdnCardState * cs)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
Asus_card_msg(struct IsdnCardState * cs,int mt,void * arg)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
setup_asuscom(struct IsdnCard * card)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 }
442