Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
59 | static const char *avm_revision = "$Revision: 2.9.2.5 $"; | |
60 | ||
61 | static inline u_char | |
62 | ReadISAC(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 | ||
72 | static inline void | |
73 | WriteISAC(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 | ||
80 | static inline void | |
81 | ReadISACfifo(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 | ||
87 | static inline void | |
88 | WriteISACfifo(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 | ||
94 | static inline u_char | |
95 | ReadHSCX(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 | ||
106 | static inline void | |
107 | WriteHSCX(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 | ||
115 | static inline void | |
116 | ReadHSCXfifo(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 | ||
123 | static inline void | |
124 | WriteHSCXfifo(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 | ||
142 | static irqreturn_t | |
7d12e780 | 143 | avm_a1p_interrupt(int intno, void *dev_id) |
1da177e4 LT |
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 | ||
174 | static int | |
175 | AVM_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 | ||
f4e64333 | 216 | int __devinit setup_avm_a1_pcmcia(struct IsdnCard *card) |
1da177e4 LT |
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; | |
9ba02bec | 257 | cs->irq_flags = IRQF_SHARED; |
1da177e4 LT |
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 | } |