linux/drivers/isdn/hisax/hscx_irq.c
<<
>>
Prefs
   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, 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, 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