File: /usr/src/linux/drivers/isdn/hisax/asuscom.c

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