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