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