1/* $Id: avm_a1.c,v 2.15.2.4 2004/01/13 21:46:03 keil Exp $ 2 * 3 * low level stuff for AVM A1 (Fritz) isdn 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 */ 12 13#include <linux/init.h> 14#include "hisax.h" 15#include "isac.h" 16#include "hscx.h" 17#include "isdnl1.h" 18 19static const char *avm_revision = "$Revision: 2.15.2.4 $"; 20 21#define AVM_A1_STAT_ISAC 0x01 22#define AVM_A1_STAT_HSCX 0x02 23#define AVM_A1_STAT_TIMER 0x04 24 25#define byteout(addr,val) outb(val,addr) 26#define bytein(addr) inb(addr) 27 28static inline u_char 29readreg(unsigned int adr, u_char off) 30{ 31 return (bytein(adr + off)); 32} 33 34static inline void 35writereg(unsigned int adr, u_char off, u_char data) 36{ 37 byteout(adr + off, data); 38} 39 40 41static inline void 42read_fifo(unsigned int adr, u_char * data, int size) 43{ 44 insb(adr, data, size); 45} 46 47static void 48write_fifo(unsigned int adr, u_char * data, int size) 49{ 50 outsb(adr, data, size); 51} 52 53/* Interface functions */ 54 55static u_char 56ReadISAC(struct IsdnCardState *cs, u_char offset) 57{ 58 return (readreg(cs->hw.avm.isac, offset)); 59} 60 61static void 62WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) 63{ 64 writereg(cs->hw.avm.isac, offset, value); 65} 66 67static void 68ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) 69{ 70 read_fifo(cs->hw.avm.isacfifo, data, size); 71} 72 73static void 74WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) 75{ 76 write_fifo(cs->hw.avm.isacfifo, data, size); 77} 78 79static u_char 80ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) 81{ 82 return (readreg(cs->hw.avm.hscx[hscx], offset)); 83} 84 85static void 86WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) 87{ 88 writereg(cs->hw.avm.hscx[hscx], offset, value); 89} 90 91/* 92 * fast interrupt HSCX stuff goes here 93 */ 94 95#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg) 96#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data) 97#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt) 98#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt) 99 100#include "hscx_irq.c" 101 102static irqreturn_t 103avm_a1_interrupt(int intno, void *dev_id) 104{ 105 struct IsdnCardState *cs = dev_id; 106 u_char val, sval; 107 u_long flags; 108 109 spin_lock_irqsave(&cs->lock, flags); 110 while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) { 111 if (!(sval & AVM_A1_STAT_TIMER)) { 112 byteout(cs->hw.avm.cfg_reg, 0x1E); 113 sval = bytein(cs->hw.avm.cfg_reg); 114 } else if (cs->debug & L1_DEB_INTSTAT) 115 debugl1(cs, "avm IntStatus %x", sval); 116 if (!(sval & AVM_A1_STAT_HSCX)) { 117 val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA); 118 if (val) 119 hscx_int_main(cs, val); 120 } 121 if (!(sval & AVM_A1_STAT_ISAC)) { 122 val = readreg(cs->hw.avm.isac, ISAC_ISTA); 123 if (val) 124 isac_interrupt(cs, val); 125 } 126 } 127 writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF); 128 writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF); 129 writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF); 130 writereg(cs->hw.avm.isac, ISAC_MASK, 0x0); 131 writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0); 132 writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0); 133 spin_unlock_irqrestore(&cs->lock, flags); 134 return IRQ_HANDLED; 135} 136 137static inline void 138release_ioregs(struct IsdnCardState *cs, int mask) 139{ 140 release_region(cs->hw.avm.cfg_reg, 8); 141 if (mask & 1) 142 release_region(cs->hw.avm.isac + 32, 32); 143 if (mask & 2) 144 release_region(cs->hw.avm.isacfifo, 1); 145 if (mask & 4) 146 release_region(cs->hw.avm.hscx[0] + 32, 32); 147 if (mask & 8) 148 release_region(cs->hw.avm.hscxfifo[0], 1); 149 if (mask & 0x10) 150 release_region(cs->hw.avm.hscx[1] + 32, 32); 151 if (mask & 0x20) 152 release_region(cs->hw.avm.hscxfifo[1], 1); 153} 154 155static int 156AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) 157{ 158 u_long flags; 159 160 switch (mt) { 161 case CARD_RESET: 162 return(0); 163 case CARD_RELEASE: 164 release_ioregs(cs, 0x3f); 165 return(0); 166 case CARD_INIT: 167 spin_lock_irqsave(&cs->lock, flags); 168 inithscxisac(cs, 1); 169 byteout(cs->hw.avm.cfg_reg, 0x16); 170 byteout(cs->hw.avm.cfg_reg, 0x1E); 171 inithscxisac(cs, 2); 172 spin_unlock_irqrestore(&cs->lock, flags); 173 return(0); 174 case CARD_TEST: 175 return(0); 176 } 177 return(0); 178} 179 180int __devinit 181setup_avm_a1(struct IsdnCard *card) 182{ 183 u_char val; 184 struct IsdnCardState *cs = card->cs; 185 char tmp[64]; 186 187 strcpy(tmp, avm_revision); 188 printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp)); 189 if (cs->typ != ISDN_CTYPE_A1) 190 return (0); 191 192 cs->hw.avm.cfg_reg = card->para[1] + 0x1800; 193 cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20; 194 cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20; 195 cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20; 196 cs->hw.avm.isacfifo = card->para[1] + 0x1000; 197 cs->hw.avm.hscxfifo[0] = card->para[1]; 198 cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800; 199 cs->irq = card->para[0]; 200 if (!request_region(cs->hw.avm.cfg_reg, 8, "avm cfg")) { 201 printk(KERN_WARNING 202 "HiSax: AVM A1 config port %x-%x already in use\n", 203 cs->hw.avm.cfg_reg, 204 cs->hw.avm.cfg_reg + 8); 205 return (0); 206 } 207 if (!request_region(cs->hw.avm.isac + 32, 32, "HiSax isac")) { 208 printk(KERN_WARNING 209 "HiSax: AVM A1 isac ports %x-%x already in use\n", 210 cs->hw.avm.isac + 32, 211 cs->hw.avm.isac + 64); 212 release_ioregs(cs, 0); 213 return (0); 214 } 215 if (!request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo")) { 216 printk(KERN_WARNING 217 "HiSax: AVM A1 isac fifo port %x already in use\n", 218 cs->hw.avm.isacfifo); 219 release_ioregs(cs, 1); 220 return (0); 221 } 222 if (!request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A")) { 223 printk(KERN_WARNING 224 "HiSax: AVM A1 hscx A ports %x-%x already in use\n", 225 cs->hw.avm.hscx[0] + 32, 226 cs->hw.avm.hscx[0] + 64); 227 release_ioregs(cs, 3); 228 return (0); 229 } 230 if (!request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo")) { 231 printk(KERN_WARNING 232 "HiSax: AVM A1 hscx A fifo port %x already in use\n", 233 cs->hw.avm.hscxfifo[0]); 234 release_ioregs(cs, 7); 235 return (0); 236 } 237 if (!request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B")) { 238 printk(KERN_WARNING 239 "HiSax: AVM A1 hscx B ports %x-%x already in use\n", 240 cs->hw.avm.hscx[1] + 32, 241 cs->hw.avm.hscx[1] + 64); 242 release_ioregs(cs, 0xf); 243 return (0); 244 } 245 if (!request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo")) { 246 printk(KERN_WARNING 247 "HiSax: AVM A1 hscx B fifo port %x already in use\n", 248 cs->hw.avm.hscxfifo[1]); 249 release_ioregs(cs, 0x1f); 250 return (0); 251 } 252 byteout(cs->hw.avm.cfg_reg, 0x0); 253 HZDELAY(HZ / 5 + 1); 254 byteout(cs->hw.avm.cfg_reg, 0x1); 255 HZDELAY(HZ / 5 + 1); 256 byteout(cs->hw.avm.cfg_reg, 0x0); 257 HZDELAY(HZ / 5 + 1); 258 val = cs->irq; 259 if (val == 9) 260 val = 2; 261 byteout(cs->hw.avm.cfg_reg + 1, val); 262 HZDELAY(HZ / 5 + 1); 263 byteout(cs->hw.avm.cfg_reg, 0x0); 264 HZDELAY(HZ / 5 + 1); 265 266 val = bytein(cs->hw.avm.cfg_reg); 267 printk(KERN_INFO "AVM A1: Byte at %x is %x\n", 268 cs->hw.avm.cfg_reg, val); 269 val = bytein(cs->hw.avm.cfg_reg + 3); 270 printk(KERN_INFO "AVM A1: Byte at %x is %x\n", 271 cs->hw.avm.cfg_reg + 3, val); 272 val = bytein(cs->hw.avm.cfg_reg + 2); 273 printk(KERN_INFO "AVM A1: Byte at %x is %x\n", 274 cs->hw.avm.cfg_reg + 2, val); 275 val = bytein(cs->hw.avm.cfg_reg); 276 printk(KERN_INFO "AVM A1: Byte at %x is %x\n", 277 cs->hw.avm.cfg_reg, val); 278 279 printk(KERN_INFO "HiSax: AVM A1 config irq:%d cfg:0x%X\n", 280 cs->irq, 281 cs->hw.avm.cfg_reg); 282 printk(KERN_INFO 283 "HiSax: isac:0x%X/0x%X\n", 284 cs->hw.avm.isac + 32, cs->hw.avm.isacfifo); 285 printk(KERN_INFO 286 "HiSax: hscx A:0x%X/0x%X hscx B:0x%X/0x%X\n", 287 cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0], 288 cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]); 289 290 cs->readisac = &ReadISAC; 291 cs->writeisac = &WriteISAC; 292 cs->readisacfifo = &ReadISACfifo; 293 cs->writeisacfifo = &WriteISACfifo; 294 cs->BC_Read_Reg = &ReadHSCX; 295 cs->BC_Write_Reg = &WriteHSCX; 296 cs->BC_Send_Data = &hscx_fill_fifo; 297 setup_isac(cs); 298 cs->cardmsg = &AVM_card_msg; 299 cs->irq_func = &avm_a1_interrupt; 300 ISACVersion(cs, "AVM A1:"); 301 if (HscxVersion(cs, "AVM A1:")) { 302 printk(KERN_WARNING 303 "AVM A1: wrong HSCX versions check IO address\n"); 304 release_ioregs(cs, 0x3f); 305 return (0); 306 } 307 return (1); 308} 309