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, "%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                                        skb_put_data(skb, bcs->hw.hscx.rcvbuf,
 173                                                     count);
 174                                        skb_queue_tail(&bcs->rqueue, skb);
 175                                }
 176                        }
 177                }
 178                bcs->hw.hscx.rcvidx = 0;
 179                schedule_event(bcs, B_RCVBUFREADY);
 180        }
 181        if (val & 0x40) {       /* RPF */
 182                hscx_empty_fifo(bcs, fifo_size);
 183                if (bcs->mode == L1_MODE_TRANS) {
 184                        /* receive audio data */
 185                        if (!(skb = dev_alloc_skb(fifo_size)))
 186                                printk(KERN_WARNING "HiSax: receive out of memory\n");
 187                        else {
 188                                skb_put_data(skb, bcs->hw.hscx.rcvbuf,
 189                                             fifo_size);
 190                                skb_queue_tail(&bcs->rqueue, skb);
 191                        }
 192                        bcs->hw.hscx.rcvidx = 0;
 193                        schedule_event(bcs, B_RCVBUFREADY);
 194                }
 195        }
 196        if (val & 0x10) {       /* XPR */
 197                if (bcs->tx_skb) {
 198                        if (bcs->tx_skb->len) {
 199                                hscx_fill_fifo(bcs);
 200                                return;
 201                        } else {
 202                                if (test_bit(FLG_LLI_L1WAKEUP, &bcs->st->lli.flag) &&
 203                                    (PACKET_NOACK != bcs->tx_skb->pkt_type)) {
 204                                        u_long  flags;
 205                                        spin_lock_irqsave(&bcs->aclock, flags);
 206                                        bcs->ackcnt += bcs->hw.hscx.count;
 207                                        spin_unlock_irqrestore(&bcs->aclock, flags);
 208                                        schedule_event(bcs, B_ACKPENDING);
 209                                }
 210                                dev_kfree_skb_irq(bcs->tx_skb);
 211                                bcs->hw.hscx.count = 0;
 212                                bcs->tx_skb = NULL;
 213                        }
 214                }
 215                if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
 216                        bcs->hw.hscx.count = 0;
 217                        test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
 218                        hscx_fill_fifo(bcs);
 219                } else {
 220                        test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 221                        schedule_event(bcs, B_XMTBUFREADY);
 222                }
 223        }
 224}
 225
 226static void
 227hscx_int_main(struct IsdnCardState *cs, u_char val)
 228{
 229
 230        u_char exval;
 231        struct BCState *bcs;
 232
 233        if (val & 0x01) {
 234                bcs = cs->bcs + 1;
 235                exval = READHSCX(cs, 1, HSCX_EXIR);
 236                if (exval & 0x40) {
 237                        if (bcs->mode == 1)
 238                                hscx_fill_fifo(bcs);
 239                        else {
 240#ifdef ERROR_STATISTIC
 241                                bcs->err_tx++;
 242#endif
 243                                /* Here we lost an TX interrupt, so
 244                                 * restart transmitting the whole frame.
 245                                 */
 246                                if (bcs->tx_skb) {
 247                                        skb_push(bcs->tx_skb, bcs->hw.hscx.count);
 248                                        bcs->tx_cnt += bcs->hw.hscx.count;
 249                                        bcs->hw.hscx.count = 0;
 250                                }
 251                                WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
 252                                if (cs->debug & L1_DEB_WARN)
 253                                        debugl1(cs, "HSCX B EXIR %x Lost TX", exval);
 254                        }
 255                } else if (cs->debug & L1_DEB_HSCX)
 256                        debugl1(cs, "HSCX B EXIR %x", exval);
 257        }
 258        if (val & 0xf8) {
 259                if (cs->debug & L1_DEB_HSCX)
 260                        debugl1(cs, "HSCX B interrupt %x", val);
 261                hscx_interrupt(cs, val, 1);
 262        }
 263        if (val & 0x02) {
 264                bcs = cs->bcs;
 265                exval = READHSCX(cs, 0, HSCX_EXIR);
 266                if (exval & 0x40) {
 267                        if (bcs->mode == L1_MODE_TRANS)
 268                                hscx_fill_fifo(bcs);
 269                        else {
 270                                /* Here we lost an TX interrupt, so
 271                                 * restart transmitting the whole frame.
 272                                 */
 273#ifdef ERROR_STATISTIC
 274                                bcs->err_tx++;
 275#endif
 276                                if (bcs->tx_skb) {
 277                                        skb_push(bcs->tx_skb, bcs->hw.hscx.count);
 278                                        bcs->tx_cnt += bcs->hw.hscx.count;
 279                                        bcs->hw.hscx.count = 0;
 280                                }
 281                                WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
 282                                if (cs->debug & L1_DEB_WARN)
 283                                        debugl1(cs, "HSCX A EXIR %x Lost TX", exval);
 284                        }
 285                } else if (cs->debug & L1_DEB_HSCX)
 286                        debugl1(cs, "HSCX A EXIR %x", exval);
 287        }
 288        if (val & 0x04) {
 289                exval = READHSCX(cs, 0, HSCX_ISTA);
 290                if (cs->debug & L1_DEB_HSCX)
 291                        debugl1(cs, "HSCX A interrupt %x", exval);
 292                hscx_interrupt(cs, exval, 0);
 293        }
 294}
 295