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 setup_avm_a1(struct IsdnCard *card) 181{ 182 u_char val; 183 struct IsdnCardState *cs = card->cs; 184 char tmp[64]; 185 186 strcpy(tmp, avm_revision); 187 printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp)); 188 if (cs->typ != ISDN_CTYPE_A1) 189 return (0); 190 191 cs->hw.avm.cfg_reg = card->para[1] + 0x1800; 192 cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20; 193 cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20; 194 cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20; 195 cs->hw.avm.isacfifo = card->para[1] + 0x1000; 196 cs->hw.avm.hscxfifo[0] = card->para[1]; 197 cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800; 198 cs->irq = card->para[0]; 199 if (!request_region(cs->hw.avm.cfg_reg, 8, "avm cfg")) { 200 printk(KERN_WARNING 201 "HiSax: AVM A1 config port %x-%x already in use\n", 202 cs->hw.avm.cfg_reg, 203 cs->hw.avm.cfg_reg + 8); 204 return (0); 205 } 206 if (!request_region(cs->hw.avm.isac + 32, 32, "HiSax isac")) { 207 printk(KERN_WARNING 208 "HiSax: AVM A1 isac ports %x-%x already in use\n", 209 cs->hw.avm.isac + 32, 210 cs->hw.avm.isac + 64); 211 release_ioregs(cs, 0); 212 return (0); 213 } 214 if (!request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo")) { 215 printk(KERN_WARNING 216 "HiSax: AVM A1 isac fifo port %x already in use\n", 217 cs->hw.avm.isacfifo); 218 release_ioregs(cs, 1); 219 return (0); 220 } 221 if (!request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A")) { 222 printk(KERN_WARNING 223 "HiSax: AVM A1 hscx A ports %x-%x already in use\n", 224 cs->hw.avm.hscx[0] + 32, 225 cs->hw.avm.hscx[0] + 64); 226 release_ioregs(cs, 3); 227 return (0); 228 } 229 if (!request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo")) { 230 printk(KERN_WARNING 231 "HiSax: AVM A1 hscx A fifo port %x already in use\n", 232 cs->hw.avm.hscxfifo[0]); 233 release_ioregs(cs, 7); 234 return (0); 235 } 236 if (!request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B")) { 237 printk(KERN_WARNING 238 "HiSax: AVM A1 hscx B ports %x-%x already in use\n", 239 cs->hw.avm.hscx[1] + 32, 240 cs->hw.avm.hscx[1] + 64); 241 release_ioregs(cs, 0xf); 242 return (0); 243 } 244 if (!request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo")) { 245 printk(KERN_WARNING 246 "HiSax: AVM A1 hscx B fifo port %x already in use\n", 247 cs->hw.avm.hscxfifo[1]); 248 release_ioregs(cs, 0x1f); 249 return (0); 250 } 251 byteout(cs->hw.avm.cfg_reg, 0x0); 252 HZDELAY(HZ / 5 + 1); 253 byteout(cs->hw.avm.cfg_reg, 0x1); 254 HZDELAY(HZ / 5 + 1); 255 byteout(cs->hw.avm.cfg_reg, 0x0); 256 HZDELAY(HZ / 5 + 1); 257 val = cs->irq; 258 if (val == 9) 259 val = 2; 260 byteout(cs->hw.avm.cfg_reg + 1, val); 261 HZDELAY(HZ / 5 + 1); 262 byteout(cs->hw.avm.cfg_reg, 0x0); 263 HZDELAY(HZ / 5 + 1); 264 265 val = bytein(cs->hw.avm.cfg_reg); 266 printk(KERN_INFO "AVM A1: Byte at %x is %x\n", 267 cs->hw.avm.cfg_reg, val); 268 val = bytein(cs->hw.avm.cfg_reg + 3); 269 printk(KERN_INFO "AVM A1: Byte at %x is %x\n", 270 cs->hw.avm.cfg_reg + 3, val); 271 val = bytein(cs->hw.avm.cfg_reg + 2); 272 printk(KERN_INFO "AVM A1: Byte at %x is %x\n", 273 cs->hw.avm.cfg_reg + 2, val); 274 val = bytein(cs->hw.avm.cfg_reg); 275 printk(KERN_INFO "AVM A1: Byte at %x is %x\n", 276 cs->hw.avm.cfg_reg, val); 277 278 printk(KERN_INFO "HiSax: AVM A1 config irq:%d cfg:0x%X\n", 279 cs->irq, 280 cs->hw.avm.cfg_reg); 281 printk(KERN_INFO 282 "HiSax: isac:0x%X/0x%X\n", 283 cs->hw.avm.isac + 32, cs->hw.avm.isacfifo); 284 printk(KERN_INFO 285 "HiSax: hscx A:0x%X/0x%X hscx B:0x%X/0x%X\n", 286 cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0], 287 cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]); 288 289 cs->readisac = &ReadISAC; 290 cs->writeisac = &WriteISAC; 291 cs->readisacfifo = &ReadISACfifo; 292 cs->writeisacfifo = &WriteISACfifo; 293 cs->BC_Read_Reg = &ReadHSCX; 294 cs->BC_Write_Reg = &WriteHSCX; 295 cs->BC_Send_Data = &hscx_fill_fifo; 296 setup_isac(cs); 297 cs->cardmsg = &AVM_card_msg; 298 cs->irq_func = &avm_a1_interrupt; 299 ISACVersion(cs, "AVM A1:"); 300 if (HscxVersion(cs, "AVM A1:")) { 301 printk(KERN_WARNING 302 "AVM A1: wrong HSCX versions check IO address\n"); 303 release_ioregs(cs, 0x3f); 304 return (0); 305 } 306 return (1); 307} 308