1/* $Id: avm_a1p.c,v 2.9.2.5 2004/01/24 20:47:19 keil Exp $ 2 * 3 * low level stuff for the following AVM cards: 4 * A1 PCMCIA 5 * FRITZ!Card PCMCIA 6 * FRITZ!Card PCMCIA 2.0 7 * 8 * Author Carsten Paeth 9 * Copyright by Carsten Paeth <calle@calle.de> 10 * 11 * This software may be used and distributed according to the terms 12 * of the GNU General Public License, incorporated herein by reference. 13 * 14 */ 15 16#include <linux/init.h> 17#include "hisax.h" 18#include "isac.h" 19#include "hscx.h" 20#include "isdnl1.h" 21 22/* register offsets */ 23#define ADDRREG_OFFSET 0x02 24#define DATAREG_OFFSET 0x03 25#define ASL0_OFFSET 0x04 26#define ASL1_OFFSET 0x05 27#define MODREG_OFFSET 0x06 28#define VERREG_OFFSET 0x07 29 30/* address offsets */ 31#define ISAC_FIFO_OFFSET 0x00 32#define ISAC_REG_OFFSET 0x20 33#define HSCX_CH_DIFF 0x40 34#define HSCX_FIFO_OFFSET 0x80 35#define HSCX_REG_OFFSET 0xa0 36 37/* read bits ASL0 */ 38#define ASL0_R_TIMER 0x10 /* active low */ 39#define ASL0_R_ISAC 0x20 /* active low */ 40#define ASL0_R_HSCX 0x40 /* active low */ 41#define ASL0_R_TESTBIT 0x80 42#define ASL0_R_IRQPENDING (ASL0_R_ISAC|ASL0_R_HSCX|ASL0_R_TIMER) 43 44/* write bits ASL0 */ 45#define ASL0_W_RESET 0x01 46#define ASL0_W_TDISABLE 0x02 47#define ASL0_W_TRESET 0x04 48#define ASL0_W_IRQENABLE 0x08 49#define ASL0_W_TESTBIT 0x80 50 51/* write bits ASL1 */ 52#define ASL1_W_LED0 0x10 53#define ASL1_W_LED1 0x20 54#define ASL1_W_ENABLE_S0 0xC0 55 56#define byteout(addr,val) outb(val,addr) 57#define bytein(addr) inb(addr) 58 59static const char *avm_revision = "$Revision: 2.9.2.5 $"; 60 61static inline u_char 62ReadISAC(struct IsdnCardState *cs, u_char offset) 63{ 64 u_char ret; 65 66 offset -= 0x20; 67 byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset); 68 ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET); 69 return ret; 70} 71 72static inline void 73WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) 74{ 75 offset -= 0x20; 76 byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset); 77 byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value); 78} 79 80static inline void 81ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) 82{ 83 byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET); 84 insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size); 85} 86 87static inline void 88WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) 89{ 90 byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET); 91 outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size); 92} 93 94static inline u_char 95ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) 96{ 97 u_char ret; 98 99 offset -= 0x20; 100 byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET, 101 HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset); 102 ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET); 103 return ret; 104} 105 106static inline void 107WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) 108{ 109 offset -= 0x20; 110 byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET, 111 HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset); 112 byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value); 113} 114 115static inline void 116ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size) 117{ 118 byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET, 119 HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF); 120 insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size); 121} 122 123static inline void 124WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size) 125{ 126 byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET, 127 HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF); 128 outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size); 129} 130 131/* 132 * fast interrupt HSCX stuff goes here 133 */ 134 135#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg) 136#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data) 137#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt) 138#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt) 139 140#include "hscx_irq.c" 141 142static irqreturn_t 143avm_a1p_interrupt(int intno, void *dev_id) 144{ 145 struct IsdnCardState *cs = dev_id; 146 u_char val, sval; 147 u_long flags; 148 149 spin_lock_irqsave(&cs->lock, flags); 150 while ((sval = (~bytein(cs->hw.avm.cfg_reg+ASL0_OFFSET) & ASL0_R_IRQPENDING))) { 151 if (cs->debug & L1_DEB_INTSTAT) 152 debugl1(cs, "avm IntStatus %x", sval); 153 if (sval & ASL0_R_HSCX) { 154 val = ReadHSCX(cs, 1, HSCX_ISTA); 155 if (val) 156 hscx_int_main(cs, val); 157 } 158 if (sval & ASL0_R_ISAC) { 159 val = ReadISAC(cs, ISAC_ISTA); 160 if (val) 161 isac_interrupt(cs, val); 162 } 163 } 164 WriteHSCX(cs, 0, HSCX_MASK, 0xff); 165 WriteHSCX(cs, 1, HSCX_MASK, 0xff); 166 WriteISAC(cs, ISAC_MASK, 0xff); 167 WriteISAC(cs, ISAC_MASK, 0x00); 168 WriteHSCX(cs, 0, HSCX_MASK, 0x00); 169 WriteHSCX(cs, 1, HSCX_MASK, 0x00); 170 spin_unlock_irqrestore(&cs->lock, flags); 171 return IRQ_HANDLED; 172} 173 174static int 175AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) 176{ 177 u_long flags; 178 179 switch (mt) { 180 case CARD_RESET: 181 spin_lock_irqsave(&cs->lock, flags); 182 byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); 183 HZDELAY(HZ / 5 + 1); 184 byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET); 185 HZDELAY(HZ / 5 + 1); 186 byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); 187 spin_unlock_irqrestore(&cs->lock, flags); 188 return 0; 189 190 case CARD_RELEASE: 191 /* free_irq is done in HiSax_closecard(). */ 192 /* free_irq(cs->irq, cs); */ 193 return 0; 194 195 case CARD_INIT: 196 spin_lock_irqsave(&cs->lock, flags); 197 byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE); 198 clear_pending_isac_ints(cs); 199 clear_pending_hscx_ints(cs); 200 inithscxisac(cs, 1); 201 inithscxisac(cs, 2); 202 spin_unlock_irqrestore(&cs->lock, flags); 203 return 0; 204 205 case CARD_TEST: 206 /* we really don't need it for the PCMCIA Version */ 207 return 0; 208 209 default: 210 /* all card drivers ignore others, so we do the same */ 211 return 0; 212 } 213 return 0; 214} 215 216int __devinit setup_avm_a1_pcmcia(struct IsdnCard *card) 217{ 218 u_char model, vers; 219 struct IsdnCardState *cs = card->cs; 220 char tmp[64]; 221 222 223 strcpy(tmp, avm_revision); 224 printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n", 225 HiSax_getrev(tmp)); 226 if (cs->typ != ISDN_CTYPE_A1_PCMCIA) 227 return (0); 228 229 cs->hw.avm.cfg_reg = card->para[1]; 230 cs->irq = card->para[0]; 231 232 233 byteout(cs->hw.avm.cfg_reg+ASL1_OFFSET, ASL1_W_ENABLE_S0); 234 byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); 235 HZDELAY(HZ / 5 + 1); 236 byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET); 237 HZDELAY(HZ / 5 + 1); 238 byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); 239 240 byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, ASL0_W_TDISABLE|ASL0_W_TRESET); 241 242 model = bytein(cs->hw.avm.cfg_reg+MODREG_OFFSET); 243 vers = bytein(cs->hw.avm.cfg_reg+VERREG_OFFSET); 244 245 printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n", 246 cs->hw.avm.cfg_reg, cs->irq, model, vers); 247 248 setup_isac(cs); 249 cs->readisac = &ReadISAC; 250 cs->writeisac = &WriteISAC; 251 cs->readisacfifo = &ReadISACfifo; 252 cs->writeisacfifo = &WriteISACfifo; 253 cs->BC_Read_Reg = &ReadHSCX; 254 cs->BC_Write_Reg = &WriteHSCX; 255 cs->BC_Send_Data = &hscx_fill_fifo; 256 cs->cardmsg = &AVM_card_msg; 257 cs->irq_flags = IRQF_SHARED; 258 cs->irq_func = &avm_a1p_interrupt; 259 260 ISACVersion(cs, "AVM A1 PCMCIA:"); 261 if (HscxVersion(cs, "AVM A1 PCMCIA:")) { 262 printk(KERN_WARNING 263 "AVM A1 PCMCIA: wrong HSCX versions check IO address\n"); 264 return (0); 265 } 266 return (1); 267} 268