Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: teleint.c,v 1.16.2.5 2004/01/19 15:31:50 keil Exp $ |
2 | * | |
3 | * low level stuff for TeleInt 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 "hfc_2bs0.h" | |
17 | #include "isdnl1.h" | |
18 | ||
672c3fd9 | 19 | static const char *TeleInt_revision = "$Revision: 1.16.2.5 $"; |
1da177e4 LT |
20 | |
21 | #define byteout(addr,val) outb(val,addr) | |
22 | #define bytein(addr) inb(addr) | |
23 | ||
24 | static inline u_char | |
25 | readreg(unsigned int ale, unsigned int adr, u_char off) | |
26 | { | |
27 | register u_char ret; | |
28 | int max_delay = 2000; | |
29 | ||
30 | byteout(ale, off); | |
31 | ret = HFC_BUSY & bytein(ale); | |
32 | while (ret && --max_delay) | |
33 | ret = HFC_BUSY & bytein(ale); | |
34 | if (!max_delay) { | |
35 | printk(KERN_WARNING "TeleInt Busy not inactive\n"); | |
36 | return (0); | |
37 | } | |
38 | ret = bytein(adr); | |
39 | return (ret); | |
40 | } | |
41 | ||
42 | static inline void | |
43 | readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) | |
44 | { | |
45 | register u_char ret; | |
46 | register int max_delay = 20000; | |
47 | register int i; | |
48 | ||
49 | byteout(ale, off); | |
50 | for (i = 0; i<size; i++) { | |
51 | ret = HFC_BUSY & bytein(ale); | |
52 | while (ret && --max_delay) | |
53 | ret = HFC_BUSY & bytein(ale); | |
54 | if (!max_delay) { | |
55 | printk(KERN_WARNING "TeleInt Busy not inactive\n"); | |
56 | return; | |
57 | } | |
58 | data[i] = bytein(adr); | |
59 | } | |
60 | } | |
61 | ||
62 | ||
63 | static inline void | |
64 | writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) | |
65 | { | |
66 | register u_char ret; | |
67 | int max_delay = 2000; | |
68 | ||
69 | byteout(ale, off); | |
70 | ret = HFC_BUSY & bytein(ale); | |
71 | while (ret && --max_delay) | |
72 | ret = HFC_BUSY & bytein(ale); | |
73 | if (!max_delay) { | |
74 | printk(KERN_WARNING "TeleInt Busy not inactive\n"); | |
75 | return; | |
76 | } | |
77 | byteout(adr, data); | |
78 | } | |
79 | ||
80 | static inline void | |
81 | writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) | |
82 | { | |
83 | register u_char ret; | |
84 | register int max_delay = 20000; | |
85 | register int i; | |
86 | ||
87 | byteout(ale, off); | |
88 | for (i = 0; i<size; i++) { | |
89 | ret = HFC_BUSY & bytein(ale); | |
90 | while (ret && --max_delay) | |
91 | ret = HFC_BUSY & bytein(ale); | |
92 | if (!max_delay) { | |
93 | printk(KERN_WARNING "TeleInt Busy not inactive\n"); | |
94 | return; | |
95 | } | |
96 | byteout(adr, data[i]); | |
97 | } | |
98 | } | |
99 | ||
100 | /* Interface functions */ | |
101 | ||
102 | static u_char | |
103 | ReadISAC(struct IsdnCardState *cs, u_char offset) | |
104 | { | |
105 | cs->hw.hfc.cip = offset; | |
106 | return (readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset)); | |
107 | } | |
108 | ||
109 | static void | |
110 | WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) | |
111 | { | |
112 | cs->hw.hfc.cip = offset; | |
113 | writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset, value); | |
114 | } | |
115 | ||
116 | static void | |
117 | ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) | |
118 | { | |
119 | cs->hw.hfc.cip = 0; | |
120 | readfifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size); | |
121 | } | |
122 | ||
123 | static void | |
124 | WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) | |
125 | { | |
126 | cs->hw.hfc.cip = 0; | |
127 | writefifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size); | |
128 | } | |
129 | ||
130 | static u_char | |
131 | ReadHFC(struct IsdnCardState *cs, int data, u_char reg) | |
132 | { | |
133 | register u_char ret; | |
134 | ||
135 | if (data) { | |
136 | cs->hw.hfc.cip = reg; | |
137 | byteout(cs->hw.hfc.addr | 1, reg); | |
138 | ret = bytein(cs->hw.hfc.addr); | |
139 | if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) | |
140 | debugl1(cs, "hfc RD %02x %02x", reg, ret); | |
141 | } else | |
142 | ret = bytein(cs->hw.hfc.addr | 1); | |
143 | return (ret); | |
144 | } | |
145 | ||
146 | static void | |
147 | WriteHFC(struct IsdnCardState *cs, int data, u_char reg, u_char value) | |
148 | { | |
149 | byteout(cs->hw.hfc.addr | 1, reg); | |
150 | cs->hw.hfc.cip = reg; | |
151 | if (data) | |
152 | byteout(cs->hw.hfc.addr, value); | |
153 | if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) | |
154 | debugl1(cs, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value); | |
155 | } | |
156 | ||
157 | static irqreturn_t | |
7d12e780 | 158 | TeleInt_interrupt(int intno, void *dev_id) |
1da177e4 LT |
159 | { |
160 | struct IsdnCardState *cs = dev_id; | |
161 | u_char val; | |
162 | u_long flags; | |
163 | ||
164 | spin_lock_irqsave(&cs->lock, flags); | |
165 | val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); | |
166 | Start_ISAC: | |
167 | if (val) | |
168 | isac_interrupt(cs, val); | |
169 | val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); | |
170 | if (val) { | |
171 | if (cs->debug & L1_DEB_ISAC) | |
172 | debugl1(cs, "ISAC IntStat after IntRoutine"); | |
173 | goto Start_ISAC; | |
174 | } | |
175 | writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF); | |
176 | writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0); | |
177 | spin_unlock_irqrestore(&cs->lock, flags); | |
178 | return IRQ_HANDLED; | |
179 | } | |
180 | ||
181 | static void | |
182 | TeleInt_Timer(struct IsdnCardState *cs) | |
183 | { | |
184 | int stat = 0; | |
185 | u_long flags; | |
186 | ||
187 | spin_lock_irqsave(&cs->lock, flags); | |
188 | if (cs->bcs[0].mode) { | |
189 | stat |= 1; | |
190 | main_irq_hfc(&cs->bcs[0]); | |
191 | } | |
192 | if (cs->bcs[1].mode) { | |
193 | stat |= 2; | |
194 | main_irq_hfc(&cs->bcs[1]); | |
195 | } | |
196 | spin_unlock_irqrestore(&cs->lock, flags); | |
197 | stat = HZ/100; | |
198 | if (!stat) | |
199 | stat = 1; | |
200 | cs->hw.hfc.timer.expires = jiffies + stat; | |
201 | add_timer(&cs->hw.hfc.timer); | |
202 | } | |
203 | ||
672c3fd9 | 204 | static void |
1da177e4 LT |
205 | release_io_TeleInt(struct IsdnCardState *cs) |
206 | { | |
207 | del_timer(&cs->hw.hfc.timer); | |
208 | releasehfc(cs); | |
209 | if (cs->hw.hfc.addr) | |
210 | release_region(cs->hw.hfc.addr, 2); | |
211 | } | |
212 | ||
213 | static void | |
214 | reset_TeleInt(struct IsdnCardState *cs) | |
215 | { | |
216 | printk(KERN_INFO "TeleInt: resetting card\n"); | |
217 | cs->hw.hfc.cirm |= HFC_RESET; | |
218 | byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset On */ | |
219 | mdelay(10); | |
220 | cs->hw.hfc.cirm &= ~HFC_RESET; | |
221 | byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset Off */ | |
222 | mdelay(10); | |
223 | } | |
224 | ||
225 | static int | |
226 | TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg) | |
227 | { | |
228 | u_long flags; | |
229 | int delay; | |
230 | ||
231 | switch (mt) { | |
232 | case CARD_RESET: | |
233 | spin_lock_irqsave(&cs->lock, flags); | |
234 | reset_TeleInt(cs); | |
235 | spin_unlock_irqrestore(&cs->lock, flags); | |
236 | return(0); | |
237 | case CARD_RELEASE: | |
238 | release_io_TeleInt(cs); | |
239 | return(0); | |
240 | case CARD_INIT: | |
241 | spin_lock_irqsave(&cs->lock, flags); | |
242 | reset_TeleInt(cs); | |
243 | inithfc(cs); | |
244 | clear_pending_isac_ints(cs); | |
245 | initisac(cs); | |
246 | /* Reenable all IRQ */ | |
247 | cs->writeisac(cs, ISAC_MASK, 0); | |
248 | cs->writeisac(cs, ISAC_CMDR, 0x41); | |
249 | spin_unlock_irqrestore(&cs->lock, flags); | |
250 | delay = HZ/100; | |
251 | if (!delay) | |
252 | delay = 1; | |
253 | cs->hw.hfc.timer.expires = jiffies + delay; | |
254 | add_timer(&cs->hw.hfc.timer); | |
255 | return(0); | |
256 | case CARD_TEST: | |
257 | return(0); | |
258 | } | |
259 | return(0); | |
260 | } | |
261 | ||
67eb5db5 | 262 | int __devinit |
1da177e4 LT |
263 | setup_TeleInt(struct IsdnCard *card) |
264 | { | |
265 | struct IsdnCardState *cs = card->cs; | |
266 | char tmp[64]; | |
267 | ||
268 | strcpy(tmp, TeleInt_revision); | |
269 | printk(KERN_INFO "HiSax: TeleInt driver Rev. %s\n", HiSax_getrev(tmp)); | |
270 | if (cs->typ != ISDN_CTYPE_TELEINT) | |
271 | return (0); | |
272 | ||
273 | cs->hw.hfc.addr = card->para[1] & 0x3fe; | |
274 | cs->irq = card->para[0]; | |
275 | cs->hw.hfc.cirm = HFC_CIRM; | |
276 | cs->hw.hfc.isac_spcr = 0x00; | |
277 | cs->hw.hfc.cip = 0; | |
278 | cs->hw.hfc.ctmt = HFC_CTMT | HFC_CLTIMER; | |
279 | cs->bcs[0].hw.hfc.send = NULL; | |
280 | cs->bcs[1].hw.hfc.send = NULL; | |
281 | cs->hw.hfc.fifosize = 7 * 1024 + 512; | |
282 | cs->hw.hfc.timer.function = (void *) TeleInt_Timer; | |
283 | cs->hw.hfc.timer.data = (long) cs; | |
284 | init_timer(&cs->hw.hfc.timer); | |
285 | if (!request_region(cs->hw.hfc.addr, 2, "TeleInt isdn")) { | |
286 | printk(KERN_WARNING | |
8349304d | 287 | "HiSax: TeleInt config port %x-%x already in use\n", |
1da177e4 LT |
288 | cs->hw.hfc.addr, |
289 | cs->hw.hfc.addr + 2); | |
290 | return (0); | |
291 | } | |
292 | /* HW IO = IO */ | |
293 | byteout(cs->hw.hfc.addr, cs->hw.hfc.addr & 0xff); | |
294 | byteout(cs->hw.hfc.addr | 1, ((cs->hw.hfc.addr & 0x300) >> 8) | 0x54); | |
295 | switch (cs->irq) { | |
296 | case 3: | |
297 | cs->hw.hfc.cirm |= HFC_INTA; | |
298 | break; | |
299 | case 4: | |
300 | cs->hw.hfc.cirm |= HFC_INTB; | |
301 | break; | |
302 | case 5: | |
303 | cs->hw.hfc.cirm |= HFC_INTC; | |
304 | break; | |
305 | case 7: | |
306 | cs->hw.hfc.cirm |= HFC_INTD; | |
307 | break; | |
308 | case 10: | |
309 | cs->hw.hfc.cirm |= HFC_INTE; | |
310 | break; | |
311 | case 11: | |
312 | cs->hw.hfc.cirm |= HFC_INTF; | |
313 | break; | |
314 | default: | |
315 | printk(KERN_WARNING "TeleInt: wrong IRQ\n"); | |
316 | release_io_TeleInt(cs); | |
317 | return (0); | |
318 | } | |
319 | byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); | |
320 | byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.ctmt); | |
321 | ||
322 | printk(KERN_INFO "TeleInt: defined at 0x%x IRQ %d\n", | |
323 | cs->hw.hfc.addr, cs->irq); | |
324 | ||
325 | setup_isac(cs); | |
326 | cs->readisac = &ReadISAC; | |
327 | cs->writeisac = &WriteISAC; | |
328 | cs->readisacfifo = &ReadISACfifo; | |
329 | cs->writeisacfifo = &WriteISACfifo; | |
330 | cs->BC_Read_Reg = &ReadHFC; | |
331 | cs->BC_Write_Reg = &WriteHFC; | |
332 | cs->cardmsg = &TeleInt_card_msg; | |
333 | cs->irq_func = &TeleInt_interrupt; | |
334 | ISACVersion(cs, "TeleInt:"); | |
335 | return (1); | |
336 | } |