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