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