diff options
Diffstat (limited to 'drivers/isdn/hisax/avm_a1p.c')
-rw-r--r-- | drivers/isdn/hisax/avm_a1p.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/drivers/isdn/hisax/avm_a1p.c b/drivers/isdn/hisax/avm_a1p.c new file mode 100644 index 000000000000..d643bb32ad09 --- /dev/null +++ b/drivers/isdn/hisax/avm_a1p.c | |||
@@ -0,0 +1,268 @@ | |||
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 | ||
143 | avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs) | ||
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 | |||
216 | int | ||
217 | setup_avm_a1_pcmcia(struct IsdnCard *card) | ||
218 | { | ||
219 | u_char model, vers; | ||
220 | struct IsdnCardState *cs = card->cs; | ||
221 | char tmp[64]; | ||
222 | |||
223 | |||
224 | strcpy(tmp, avm_revision); | ||
225 | printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n", | ||
226 | HiSax_getrev(tmp)); | ||
227 | if (cs->typ != ISDN_CTYPE_A1_PCMCIA) | ||
228 | return (0); | ||
229 | |||
230 | cs->hw.avm.cfg_reg = card->para[1]; | ||
231 | cs->irq = card->para[0]; | ||
232 | |||
233 | |||
234 | byteout(cs->hw.avm.cfg_reg+ASL1_OFFSET, ASL1_W_ENABLE_S0); | ||
235 | byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); | ||
236 | HZDELAY(HZ / 5 + 1); | ||
237 | byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET); | ||
238 | HZDELAY(HZ / 5 + 1); | ||
239 | byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); | ||
240 | |||
241 | byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, ASL0_W_TDISABLE|ASL0_W_TRESET); | ||
242 | |||
243 | model = bytein(cs->hw.avm.cfg_reg+MODREG_OFFSET); | ||
244 | vers = bytein(cs->hw.avm.cfg_reg+VERREG_OFFSET); | ||
245 | |||
246 | printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n", | ||
247 | cs->hw.avm.cfg_reg, cs->irq, model, vers); | ||
248 | |||
249 | setup_isac(cs); | ||
250 | cs->readisac = &ReadISAC; | ||
251 | cs->writeisac = &WriteISAC; | ||
252 | cs->readisacfifo = &ReadISACfifo; | ||
253 | cs->writeisacfifo = &WriteISACfifo; | ||
254 | cs->BC_Read_Reg = &ReadHSCX; | ||
255 | cs->BC_Write_Reg = &WriteHSCX; | ||
256 | cs->BC_Send_Data = &hscx_fill_fifo; | ||
257 | cs->cardmsg = &AVM_card_msg; | ||
258 | cs->irq_flags = SA_SHIRQ; | ||
259 | cs->irq_func = &avm_a1p_interrupt; | ||
260 | |||
261 | ISACVersion(cs, "AVM A1 PCMCIA:"); | ||
262 | if (HscxVersion(cs, "AVM A1 PCMCIA:")) { | ||
263 | printk(KERN_WARNING | ||
264 | "AVM A1 PCMCIA: wrong HSCX versions check IO address\n"); | ||
265 | return (0); | ||
266 | } | ||
267 | return (1); | ||
268 | } | ||