Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: hscx_irq.c,v 1.18.2.3 2004/02/11 13:21:34 keil Exp $ |
2 | * | |
3 | * low level b-channel stuff for Siemens HSCX | |
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 | * This is an include file for fast inline IRQ stuff | |
12 | * | |
13 | */ | |
14 | ||
15 | ||
16 | static inline void | |
17 | waitforCEC(struct IsdnCardState *cs, int hscx) | |
18 | { | |
19 | int to = 50; | |
20 | ||
21 | while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) { | |
22 | udelay(1); | |
23 | to--; | |
24 | } | |
25 | if (!to) | |
26 | printk(KERN_WARNING "HiSax: waitforCEC timeout\n"); | |
27 | } | |
28 | ||
29 | ||
30 | static inline void | |
31 | waitforXFW(struct IsdnCardState *cs, int hscx) | |
32 | { | |
33 | int to = 50; | |
34 | ||
a947c8f0 | 35 | while (((READHSCX(cs, hscx, HSCX_STAR) & 0x44) != 0x40) && to) { |
1da177e4 LT |
36 | udelay(1); |
37 | to--; | |
38 | } | |
39 | if (!to) | |
40 | printk(KERN_WARNING "HiSax: waitforXFW timeout\n"); | |
41 | } | |
42 | ||
43 | static inline void | |
44 | WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data) | |
45 | { | |
46 | waitforCEC(cs, hscx); | |
47 | WRITEHSCX(cs, hscx, HSCX_CMDR, data); | |
48 | } | |
49 | ||
50 | ||
51 | ||
52 | static void | |
53 | hscx_empty_fifo(struct BCState *bcs, int count) | |
54 | { | |
55 | u_char *ptr; | |
56 | struct IsdnCardState *cs = bcs->cs; | |
57 | ||
58 | if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) | |
59 | debugl1(cs, "hscx_empty_fifo"); | |
60 | ||
61 | if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) { | |
62 | if (cs->debug & L1_DEB_WARN) | |
63 | debugl1(cs, "hscx_empty_fifo: incoming packet too large"); | |
64 | WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80); | |
65 | bcs->hw.hscx.rcvidx = 0; | |
66 | return; | |
67 | } | |
68 | ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; | |
69 | bcs->hw.hscx.rcvidx += count; | |
70 | READHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count); | |
71 | WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80); | |
72 | if (cs->debug & L1_DEB_HSCX_FIFO) { | |
73 | char *t = bcs->blog; | |
74 | ||
75 | t += sprintf(t, "hscx_empty_fifo %c cnt %d", | |
76 | bcs->hw.hscx.hscx ? 'B' : 'A', count); | |
77 | QuickHex(t, ptr, count); | |
78 | debugl1(cs, bcs->blog); | |
79 | } | |
80 | } | |
81 | ||
82 | static void | |
83 | hscx_fill_fifo(struct BCState *bcs) | |
84 | { | |
85 | struct IsdnCardState *cs = bcs->cs; | |
86 | int more, count; | |
87 | int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; | |
88 | u_char *ptr; | |
89 | ||
90 | if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) | |
91 | debugl1(cs, "hscx_fill_fifo"); | |
92 | ||
93 | if (!bcs->tx_skb) | |
94 | return; | |
95 | if (bcs->tx_skb->len <= 0) | |
96 | return; | |
97 | ||
98 | more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; | |
99 | if (bcs->tx_skb->len > fifo_size) { | |
100 | more = !0; | |
101 | count = fifo_size; | |
102 | } else | |
103 | count = bcs->tx_skb->len; | |
104 | ||
105 | waitforXFW(cs, bcs->hw.hscx.hscx); | |
106 | ptr = bcs->tx_skb->data; | |
107 | skb_pull(bcs->tx_skb, count); | |
108 | bcs->tx_cnt -= count; | |
109 | bcs->hw.hscx.count += count; | |
110 | WRITEHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count); | |
111 | WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa); | |
112 | if (cs->debug & L1_DEB_HSCX_FIFO) { | |
113 | char *t = bcs->blog; | |
114 | ||
115 | t += sprintf(t, "hscx_fill_fifo %c cnt %d", | |
116 | bcs->hw.hscx.hscx ? 'B' : 'A', count); | |
117 | QuickHex(t, ptr, count); | |
118 | debugl1(cs, bcs->blog); | |
119 | } | |
120 | } | |
121 | ||
858119e1 | 122 | static void |
1da177e4 LT |
123 | hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) |
124 | { | |
125 | u_char r; | |
126 | struct BCState *bcs = cs->bcs + hscx; | |
127 | struct sk_buff *skb; | |
128 | int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; | |
129 | int count; | |
130 | ||
131 | if (!test_bit(BC_FLG_INIT, &bcs->Flag)) | |
132 | return; | |
133 | ||
134 | if (val & 0x80) { /* RME */ | |
135 | r = READHSCX(cs, hscx, HSCX_RSTA); | |
136 | if ((r & 0xf0) != 0xa0) { | |
137 | if (!(r & 0x80)) { | |
138 | if (cs->debug & L1_DEB_WARN) | |
139 | debugl1(cs, "HSCX invalid frame"); | |
140 | #ifdef ERROR_STATISTIC | |
141 | bcs->err_inv++; | |
142 | #endif | |
143 | } | |
144 | if ((r & 0x40) && bcs->mode) { | |
145 | if (cs->debug & L1_DEB_WARN) | |
146 | debugl1(cs, "HSCX RDO mode=%d", | |
147 | bcs->mode); | |
148 | #ifdef ERROR_STATISTIC | |
149 | bcs->err_rdo++; | |
150 | #endif | |
151 | } | |
152 | if (!(r & 0x20)) { | |
153 | if (cs->debug & L1_DEB_WARN) | |
154 | debugl1(cs, "HSCX CRC error"); | |
155 | #ifdef ERROR_STATISTIC | |
156 | bcs->err_crc++; | |
157 | #endif | |
158 | } | |
159 | WriteHSCXCMDR(cs, hscx, 0x80); | |
160 | } else { | |
161 | count = READHSCX(cs, hscx, HSCX_RBCL) & ( | |
162 | test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f); | |
163 | if (count == 0) | |
164 | count = fifo_size; | |
165 | hscx_empty_fifo(bcs, count); | |
166 | if ((count = bcs->hw.hscx.rcvidx - 1) > 0) { | |
167 | if (cs->debug & L1_DEB_HSCX_FIFO) | |
168 | debugl1(cs, "HX Frame %d", count); | |
169 | if (!(skb = dev_alloc_skb(count))) | |
170 | printk(KERN_WARNING "HSCX: receive out of memory\n"); | |
171 | else { | |
172 | memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count); | |
173 | skb_queue_tail(&bcs->rqueue, skb); | |
174 | } | |
175 | } | |
176 | } | |
177 | bcs->hw.hscx.rcvidx = 0; | |
178 | schedule_event(bcs, B_RCVBUFREADY); | |
179 | } | |
180 | if (val & 0x40) { /* RPF */ | |
181 | hscx_empty_fifo(bcs, fifo_size); | |
182 | if (bcs->mode == L1_MODE_TRANS) { | |
183 | /* receive audio data */ | |
184 | if (!(skb = dev_alloc_skb(fifo_size))) | |
185 | printk(KERN_WARNING "HiSax: receive out of memory\n"); | |
186 | else { | |
187 | memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size); | |
188 | skb_queue_tail(&bcs->rqueue, skb); | |
189 | } | |
190 | bcs->hw.hscx.rcvidx = 0; | |
191 | schedule_event(bcs, B_RCVBUFREADY); | |
192 | } | |
193 | } | |
194 | if (val & 0x10) { /* XPR */ | |
195 | if (bcs->tx_skb) { | |
196 | if (bcs->tx_skb->len) { | |
197 | hscx_fill_fifo(bcs); | |
198 | return; | |
199 | } else { | |
200 | if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) && | |
201 | (PACKET_NOACK != bcs->tx_skb->pkt_type)) { | |
202 | u_long flags; | |
203 | spin_lock_irqsave(&bcs->aclock, flags); | |
204 | bcs->ackcnt += bcs->hw.hscx.count; | |
205 | spin_unlock_irqrestore(&bcs->aclock, flags); | |
206 | schedule_event(bcs, B_ACKPENDING); | |
207 | } | |
208 | dev_kfree_skb_irq(bcs->tx_skb); | |
209 | bcs->hw.hscx.count = 0; | |
210 | bcs->tx_skb = NULL; | |
211 | } | |
212 | } | |
213 | if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { | |
214 | bcs->hw.hscx.count = 0; | |
215 | test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); | |
216 | hscx_fill_fifo(bcs); | |
217 | } else { | |
218 | test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); | |
219 | schedule_event(bcs, B_XMTBUFREADY); | |
220 | } | |
221 | } | |
222 | } | |
223 | ||
858119e1 | 224 | static void |
1da177e4 LT |
225 | hscx_int_main(struct IsdnCardState *cs, u_char val) |
226 | { | |
227 | ||
228 | u_char exval; | |
229 | struct BCState *bcs; | |
230 | ||
231 | if (val & 0x01) { | |
232 | bcs = cs->bcs + 1; | |
233 | exval = READHSCX(cs, 1, HSCX_EXIR); | |
234 | if (exval & 0x40) { | |
235 | if (bcs->mode == 1) | |
236 | hscx_fill_fifo(bcs); | |
237 | else { | |
238 | #ifdef ERROR_STATISTIC | |
239 | bcs->err_tx++; | |
240 | #endif | |
241 | /* Here we lost an TX interrupt, so | |
242 | * restart transmitting the whole frame. | |
243 | */ | |
244 | if (bcs->tx_skb) { | |
245 | skb_push(bcs->tx_skb, bcs->hw.hscx.count); | |
246 | bcs->tx_cnt += bcs->hw.hscx.count; | |
247 | bcs->hw.hscx.count = 0; | |
248 | } | |
249 | WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01); | |
250 | if (cs->debug & L1_DEB_WARN) | |
251 | debugl1(cs, "HSCX B EXIR %x Lost TX", exval); | |
252 | } | |
253 | } else if (cs->debug & L1_DEB_HSCX) | |
254 | debugl1(cs, "HSCX B EXIR %x", exval); | |
255 | } | |
256 | if (val & 0xf8) { | |
257 | if (cs->debug & L1_DEB_HSCX) | |
258 | debugl1(cs, "HSCX B interrupt %x", val); | |
259 | hscx_interrupt(cs, val, 1); | |
260 | } | |
261 | if (val & 0x02) { | |
262 | bcs = cs->bcs; | |
263 | exval = READHSCX(cs, 0, HSCX_EXIR); | |
264 | if (exval & 0x40) { | |
265 | if (bcs->mode == L1_MODE_TRANS) | |
266 | hscx_fill_fifo(bcs); | |
267 | else { | |
268 | /* Here we lost an TX interrupt, so | |
269 | * restart transmitting the whole frame. | |
270 | */ | |
271 | #ifdef ERROR_STATISTIC | |
272 | bcs->err_tx++; | |
273 | #endif | |
274 | if (bcs->tx_skb) { | |
275 | skb_push(bcs->tx_skb, bcs->hw.hscx.count); | |
276 | bcs->tx_cnt += bcs->hw.hscx.count; | |
277 | bcs->hw.hscx.count = 0; | |
278 | } | |
279 | WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01); | |
280 | if (cs->debug & L1_DEB_WARN) | |
281 | debugl1(cs, "HSCX A EXIR %x Lost TX", exval); | |
282 | } | |
283 | } else if (cs->debug & L1_DEB_HSCX) | |
284 | debugl1(cs, "HSCX A EXIR %x", exval); | |
285 | } | |
286 | if (val & 0x04) { | |
287 | exval = READHSCX(cs, 0, HSCX_ISTA); | |
288 | if (cs->debug & L1_DEB_HSCX) | |
289 | debugl1(cs, "HSCX A interrupt %x", exval); | |
290 | hscx_interrupt(cs, exval, 0); | |
291 | } | |
292 | } |