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 16static inline void 17waitforCEC(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 30static inline void 31waitforXFW(struct IsdnCardState *cs, int hscx) 32{ 33 int to = 50; 34 35 while (((READHSCX(cs, hscx, HSCX_STAR) & 0x44) != 0x40) && to) { 36 udelay(1); 37 to--; 38 } 39 if (!to) 40 printk(KERN_WARNING "HiSax: waitforXFW timeout\n"); 41} 42 43static inline void 44WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data) 45{ 46 waitforCEC(cs, hscx); 47 WRITEHSCX(cs, hscx, HSCX_CMDR, data); 48} 49 50 51 52static void 53hscx_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, "%s", bcs->blog); 79 } 80} 81 82static void 83hscx_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, "%s", bcs->blog); 119 } 120} 121 122static void 123hscx_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 224static void 225hscx_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} 293