]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/staging/sbe-2t3e3/intr.c
Merge branch 'master' into csb1725
[mv-sheeva.git] / drivers / staging / sbe-2t3e3 / intr.c
diff --git a/drivers/staging/sbe-2t3e3/intr.c b/drivers/staging/sbe-2t3e3/intr.c
new file mode 100644 (file)
index 0000000..7ad1a83
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * SBE 2T3E3 synchronous serial card driver for Linux
+ *
+ * Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This code is based on a driver written by SBE Inc.
+ */
+
+#include <linux/hdlc.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include "2t3e3.h"
+
+irqreturn_t t3e3_intr(int irq, void *dev_instance)
+{
+       struct channel *sc = dev_to_priv(dev_instance);
+       u32 val;
+       irqreturn_t ret = IRQ_NONE;
+
+       sc->interrupt_active = 1;
+
+       val = cpld_read(sc, SBE_2T3E3_CPLD_REG_PICSR);
+
+       if (val & SBE_2T3E3_CPLD_VAL_RECEIVE_LOSS_OF_SIGNAL_CHANGE) {
+               dev_dbg(&sc->pdev->dev,
+                       "Rx LOS Chng Int r=%02x (LOS|OOF=%02x)\n",
+                       val, (sc->s.LOS << 4) | sc->s.OOF);
+               cpld_LOS_update(sc);
+               ret = IRQ_HANDLED;
+       }
+
+       if (val & SBE_2T3E3_CPLD_VAL_INTERRUPT_FROM_ETHERNET_ASSERTED) {
+               dc_intr(sc);
+               ret = IRQ_HANDLED;
+       }
+
+       if (val & SBE_2T3E3_CPLD_VAL_INTERRUPT_FROM_FRAMER_ASSERTED) {
+               exar7250_intr(sc);
+               ret = IRQ_HANDLED;
+       }
+
+       /*
+         we don't care about other interrupt sources (DMO, LOS, LCV) because
+         they are handled by Framer too
+       */
+
+       sc->interrupt_active = 0;
+       return ret;
+}
+
+void dc_intr(struct channel *sc)
+{
+       u32 val;
+
+       /* disable ethernet interrupts */
+       /* grrr this clears interrupt summary bits !!! */
+       dc_write(sc->addr, SBE_2T3E3_21143_REG_INTERRUPT_ENABLE, 0);
+
+       while ((val = dc_read(sc->addr, SBE_2T3E3_21143_REG_STATUS)) &
+              (SBE_2T3E3_21143_VAL_RECEIVE_PROCESS_STOPPED |
+               SBE_2T3E3_21143_VAL_RECEIVE_BUFFER_UNAVAILABLE |
+               SBE_2T3E3_21143_VAL_RECEIVE_INTERRUPT |
+               SBE_2T3E3_21143_VAL_TRANSMIT_UNDERFLOW |
+               SBE_2T3E3_21143_VAL_TRANSMIT_BUFFER_UNAVAILABLE |
+               SBE_2T3E3_21143_VAL_TRANSMIT_PROCESS_STOPPED |
+               SBE_2T3E3_21143_VAL_TRANSMIT_INTERRUPT)) {
+               dc_write(sc->addr, SBE_2T3E3_21143_REG_STATUS, val);
+
+               dev_dbg(&sc->pdev->dev, "SBE 2T3E3: Ethernet controller interrupt! (CSR5 = %08X)\n",
+                       val);
+
+               if (val & (SBE_2T3E3_21143_VAL_RECEIVE_INTERRUPT |
+                          SBE_2T3E3_21143_VAL_RECEIVE_BUFFER_UNAVAILABLE |
+                          SBE_2T3E3_21143_VAL_RECEIVE_PROCESS_STOPPED)) {
+                       if (val & SBE_2T3E3_21143_VAL_RECEIVE_INTERRUPT)
+                               dev_dbg(&sc->pdev->dev,
+                                       "Receive interrupt (LOS=%d, OOF=%d)\n",
+                                       sc->s.LOS, sc->s.OOF);
+                       if (val & SBE_2T3E3_21143_VAL_RECEIVE_BUFFER_UNAVAILABLE)
+                               dev_dbg(&sc->pdev->dev,
+                                       "Receive buffer unavailable\n");
+                       if (val & SBE_2T3E3_21143_VAL_RECEIVE_PROCESS_STOPPED)
+                               dev_dbg(&sc->pdev->dev,
+                                       "Receive process stopped\n");
+                       dc_intr_rx(sc);
+               }
+
+               if (val & SBE_2T3E3_21143_VAL_TRANSMIT_UNDERFLOW) {
+                       dev_dbg(&sc->pdev->dev, "Transmit underflow\n");
+                       dc_intr_tx_underflow(sc);
+               }
+
+               if (val & (SBE_2T3E3_21143_VAL_TRANSMIT_BUFFER_UNAVAILABLE |
+                          SBE_2T3E3_21143_VAL_TRANSMIT_INTERRUPT |
+                          SBE_2T3E3_21143_VAL_TRANSMIT_PROCESS_STOPPED)) {
+                       if (val & SBE_2T3E3_21143_VAL_TRANSMIT_INTERRUPT)
+                               dev_dbg(&sc->pdev->dev, "Transmit interrupt\n");
+                       if (val & SBE_2T3E3_21143_VAL_TRANSMIT_BUFFER_UNAVAILABLE)
+                               dev_dbg(&sc->pdev->dev,
+                                       "Transmit buffer unavailable\n");
+                       if (val & SBE_2T3E3_21143_VAL_TRANSMIT_PROCESS_STOPPED)
+                               dev_dbg(&sc->pdev->dev,
+                                       "Transmit process stopped\n");
+                       dc_intr_tx(sc);
+               }
+       }
+
+       /* enable ethernet interrupts */
+       dc_write(sc->addr, SBE_2T3E3_21143_REG_INTERRUPT_ENABLE,
+                sc->ether.interrupt_enable_mask);
+}
+
+void dc_intr_rx(struct channel *sc)
+{
+       u32 current_read;
+       u32 error_mask, error;
+       t3e3_rx_desc_t *current_desc;
+       struct sk_buff *m, *m2;
+       unsigned rcv_len;
+
+       sc->rcv_count++; /* for the activity LED */
+
+       current_read = sc->ether.rx_ring_current_read;
+       dev_dbg(&sc->pdev->dev, "intr_rx current_read = %d\n", current_read);
+
+       /* when ethernet loopback is set, ignore framer signals */
+       if ((sc->p.loopback != SBE_2T3E3_LOOPBACK_ETHERNET) && sc->s.OOF) {
+               while (!(sc->ether.rx_ring[current_read].rdes0 &
+                        SBE_2T3E3_RX_DESC_21143_OWN)) {
+                       current_desc = &sc->ether.rx_ring[current_read];
+                       current_desc->rdes1 &= SBE_2T3E3_RX_DESC_END_OF_RING |
+                               SBE_2T3E3_RX_DESC_SECOND_ADDRESS_CHAINED;
+                       current_desc->rdes1 |= SBE_2T3E3_MTU;
+                       current_desc->rdes0 = SBE_2T3E3_RX_DESC_21143_OWN;
+                       current_read = (current_read + 1) % SBE_2T3E3_RX_DESC_RING_SIZE;
+               }
+               sc->ether.rx_ring_current_read = current_read;
+               return;
+       }
+
+       while (!(sc->ether.rx_ring[current_read].rdes0 &
+                SBE_2T3E3_RX_DESC_21143_OWN)) {
+               current_desc = &sc->ether.rx_ring[current_read];
+
+               dev_dbg(&sc->pdev->dev, "rdes0: %08X        rdes1: %08X\n",
+                       current_desc->rdes0, current_desc->rdes1);
+
+               m = sc->ether.rx_data[current_read];
+               rcv_len = (current_desc->rdes0 & SBE_2T3E3_RX_DESC_FRAME_LENGTH) >>
+                       SBE_2T3E3_RX_DESC_FRAME_LENGTH_SHIFT;
+
+               dev_dbg(&sc->pdev->dev, "mbuf was received (mbuf len = %d)\n",
+                       rcv_len);
+
+               switch (sc->p.crc) {
+               case SBE_2T3E3_CRC_16:
+                       rcv_len -= SBE_2T3E3_CRC16_LENGTH;
+                       break;
+               case SBE_2T3E3_CRC_32:
+                       rcv_len -= SBE_2T3E3_CRC32_LENGTH;
+                       break;
+               default:
+                       break;
+               }
+
+               if (current_desc->rdes0 & SBE_2T3E3_RX_DESC_LAST_DESC) {
+
+                       /* TODO: is collision possible? */
+                       error_mask = SBE_2T3E3_RX_DESC_DESC_ERROR |
+                               SBE_2T3E3_RX_DESC_COLLISION_SEEN |
+                               SBE_2T3E3_RX_DESC_DRIBBLING_BIT;
+
+                       switch (sc->p.frame_mode) {
+                       case SBE_2T3E3_FRAME_MODE_HDLC:
+                               error_mask |= SBE_2T3E3_RX_DESC_MII_ERROR;
+                               if (sc->p.crc == SBE_2T3E3_CRC_32)
+                                       error_mask |= SBE_2T3E3_RX_DESC_CRC_ERROR;
+                               break;
+                       case SBE_2T3E3_FRAME_MODE_TRANSPARENT:
+                       case SBE_2T3E3_FRAME_MODE_RAW:
+                               break;
+                       default:
+                               error_mask = 0;
+                       }
+
+                       if (sc->s.LOS) {
+                               error_mask &= ~(SBE_2T3E3_RX_DESC_DRIBBLING_BIT ||
+                                               SBE_2T3E3_RX_DESC_MII_ERROR);
+                       }
+
+                       error = current_desc->rdes0 & error_mask;
+                       if (error) {
+                               sc->s.in_errors++;
+                               dev_dbg(&sc->pdev->dev,
+                                       "error interrupt: NO_ERROR_MESSAGE = %d\n",
+                                       sc->r.flags & SBE_2T3E3_FLAG_NO_ERROR_MESSAGES ? 1 : 0);
+
+                               current_desc->rdes1 &= SBE_2T3E3_RX_DESC_END_OF_RING |
+                                       SBE_2T3E3_RX_DESC_SECOND_ADDRESS_CHAINED;
+                               current_desc->rdes1 |= SBE_2T3E3_MTU;
+                               current_desc->rdes0 = SBE_2T3E3_RX_DESC_21143_OWN;
+
+                               if (error & SBE_2T3E3_RX_DESC_DESC_ERROR) {
+                                       if (!(sc->r.flags & SBE_2T3E3_FLAG_NO_ERROR_MESSAGES))
+                                               dev_err(&sc->pdev->dev,
+                                                       "SBE 2T3E3: descriptor error\n");
+                                       sc->s.in_error_desc++;
+                               }
+
+                               if (error & SBE_2T3E3_RX_DESC_COLLISION_SEEN) {
+                                       if (!(sc->r.flags & SBE_2T3E3_FLAG_NO_ERROR_MESSAGES))
+                                               dev_err(&sc->pdev->dev,
+                                                       "SBE 2T3E3: collision seen\n");
+                                       sc->s.in_error_coll++;
+                               } else {
+                                       if (error & SBE_2T3E3_RX_DESC_DRIBBLING_BIT) {
+                                               if (!(sc->r.flags & SBE_2T3E3_FLAG_NO_ERROR_MESSAGES))
+                                                       dev_err(&sc->pdev->dev,
+                                                               "SBE 2T3E3: dribbling bits error\n");
+                                               sc->s.in_error_drib++;
+                                       }
+
+                                       if (error & SBE_2T3E3_RX_DESC_CRC_ERROR) {
+                                               if (!(sc->r.flags & SBE_2T3E3_FLAG_NO_ERROR_MESSAGES))
+                                                       dev_err(&sc->pdev->dev,
+                                                               "SBE 2T3E3: crc error\n");
+                                               sc->s.in_error_crc++;
+                                       }
+                               }
+
+                               if (error & SBE_2T3E3_RX_DESC_MII_ERROR) {
+                                       if (!(sc->r.flags & SBE_2T3E3_FLAG_NO_ERROR_MESSAGES))
+                                               dev_err(&sc->pdev->dev, "SBE 2T3E3: mii error\n");
+                                       sc->s.in_error_mii++;
+                               }
+
+                               current_read = (current_read + 1) % SBE_2T3E3_RX_DESC_RING_SIZE;
+                               sc->r.flags |= SBE_2T3E3_FLAG_NO_ERROR_MESSAGES;
+                               continue;
+                       }
+               }
+
+               current_desc->rdes1 &= SBE_2T3E3_RX_DESC_END_OF_RING |
+                       SBE_2T3E3_RX_DESC_SECOND_ADDRESS_CHAINED;
+               current_desc->rdes1 |= SBE_2T3E3_MTU;
+
+               if (rcv_len > 1600) {
+                       sc->s.in_errors++;
+                       sc->s.in_dropped++;
+                       if (!(sc->r.flags & SBE_2T3E3_FLAG_NO_ERROR_MESSAGES))
+                               dev_err(&sc->pdev->dev, "SBE 2T3E3: oversized rx: rdes0 = %08X\n",
+                                       current_desc->rdes0);
+               } else {
+                       m2 = dev_alloc_skb(MCLBYTES);
+                       if (m2 != NULL) {
+                               current_desc->rdes2 = virt_to_phys(m2->data);
+                               sc->ether.rx_data[current_read] = m2;
+                               sc->s.in_packets++;
+                               sc->s.in_bytes += rcv_len;
+                               m->dev = sc->dev;
+                               skb_put(m, rcv_len);
+                               skb_reset_mac_header(m);
+                               m->protocol = hdlc_type_trans(m, m->dev);
+                               netif_rx(m);
+
+                               /* good packet was received so we will show error messages again... */
+                               if (sc->r.flags & SBE_2T3E3_FLAG_NO_ERROR_MESSAGES) {
+                                       dev_dbg(&sc->pdev->dev,
+                                               "setting ERROR_MESSAGES->0\n");
+                                       sc->r.flags &= ~SBE_2T3E3_FLAG_NO_ERROR_MESSAGES;
+                               }
+
+                       } else {
+                               sc->s.in_errors++;
+                               sc->s.in_dropped++;
+                       }
+               }
+               current_desc->rdes0 = SBE_2T3E3_RX_DESC_21143_OWN;
+               current_read = (current_read + 1) % SBE_2T3E3_RX_DESC_RING_SIZE;
+       }
+
+       sc->ether.rx_ring_current_read = current_read;
+
+       dc_write(sc->addr, SBE_2T3E3_21143_REG_RECEIVE_POLL_DEMAND, 0xFFFFFFFF);
+}
+
+void dc_intr_tx(struct channel *sc)
+{
+       u32 current_read, current_write;
+       u32 last_segment, error;
+       t3e3_tx_desc_t *current_desc;
+
+       spin_lock(&sc->ether.tx_lock);
+
+       current_read = sc->ether.tx_ring_current_read;
+       current_write = sc->ether.tx_ring_current_write;
+
+       while (current_read != current_write) {
+               current_desc = &sc->ether.tx_ring[current_read];
+
+               if (current_desc->tdes0 & SBE_2T3E3_RX_DESC_21143_OWN)
+                       break;
+
+               dev_dbg(&sc->pdev->dev,
+                       "txeof: tdes0 = %08X        tdes1 = %08X\n",
+                       current_desc->tdes0, current_desc->tdes1);
+
+               error = current_desc->tdes0 & (SBE_2T3E3_TX_DESC_ERROR_SUMMARY |
+                                              SBE_2T3E3_TX_DESC_TRANSMIT_JABBER_TIMEOUT |
+                                              SBE_2T3E3_TX_DESC_LOSS_OF_CARRIER |
+                                              SBE_2T3E3_TX_DESC_NO_CARRIER |
+                                              SBE_2T3E3_TX_DESC_LINK_FAIL_REPORT |
+                                              SBE_2T3E3_TX_DESC_UNDERFLOW_ERROR |
+                                              SBE_2T3E3_TX_DESC_DEFFERED);
+
+               last_segment = current_desc->tdes1 & SBE_2T3E3_TX_DESC_LAST_SEGMENT;
+
+               current_desc->tdes0 = 0;
+               current_desc->tdes1 &= SBE_2T3E3_TX_DESC_END_OF_RING |
+                       SBE_2T3E3_TX_DESC_SECOND_ADDRESS_CHAINED;
+               current_desc->tdes2 = 0;
+               sc->ether.tx_free_cnt++;
+
+               if (last_segment != SBE_2T3E3_TX_DESC_LAST_SEGMENT) {
+                       current_read = (current_read + 1) % SBE_2T3E3_TX_DESC_RING_SIZE;
+                       continue;
+               }
+
+
+               if (sc->ether.tx_data[current_read]) {
+                       sc->s.out_packets++;
+                       sc->s.out_bytes += sc->ether.tx_data[current_read]->len;
+                       dev_kfree_skb_any(sc->ether.tx_data[current_read]);
+                       sc->ether.tx_data[current_read] = NULL;
+               }
+
+               if (error > 0) {
+                       sc->s.out_errors++;
+
+                       if (error & SBE_2T3E3_TX_DESC_TRANSMIT_JABBER_TIMEOUT) {
+                               dev_err(&sc->pdev->dev, "SBE 2T3E3: transmit jabber timeout\n");
+                               sc->s.out_error_jab++;
+                       }
+
+                       if (sc->p.loopback != SBE_2T3E3_LOOPBACK_ETHERNET) {
+                               if (error & SBE_2T3E3_TX_DESC_LOSS_OF_CARRIER) {
+                                       dev_err(&sc->pdev->dev, "SBE 2T3E3: loss of carrier\n");
+                                       sc->s.out_error_lost_carr++;
+                               }
+
+                               if (error & SBE_2T3E3_TX_DESC_NO_CARRIER) {
+                                       dev_err(&sc->pdev->dev, "SBE 2T3E3: no carrier\n");
+                                       sc->s.out_error_no_carr++;
+                               }
+                       }
+
+                       if (error & SBE_2T3E3_TX_DESC_LINK_FAIL_REPORT) {
+                               dev_err(&sc->pdev->dev, "SBE 2T3E3: link fail report\n");
+                               sc->s.out_error_link_fail++;
+                       }
+
+                       if (error & SBE_2T3E3_TX_DESC_UNDERFLOW_ERROR) {
+                               dev_err(&sc->pdev->dev, "SBE 2T3E3:"
+                                       " transmission underflow error\n");
+                               sc->s.out_error_underflow++;
+                               spin_unlock(&sc->ether.tx_lock);
+
+                               dc_restart(sc);
+                               return;
+                       }
+
+                       if (error & SBE_2T3E3_TX_DESC_DEFFERED) {
+                               dev_err(&sc->pdev->dev, "SBE 2T3E3: transmission deferred\n");
+                               sc->s.out_error_dereferred++;
+                       }
+               }
+
+               current_read = (current_read + 1) % SBE_2T3E3_TX_DESC_RING_SIZE;
+       }
+
+       sc->ether.tx_ring_current_read = current_read;
+
+       /* Relieve flow control when the TX queue is drained at least half way */
+       if (sc->ether.tx_full &&
+           (sc->ether.tx_free_cnt >= (SBE_2T3E3_TX_DESC_RING_SIZE / 2))) {
+               sc->ether.tx_full = 0;
+               netif_wake_queue(sc->dev);
+       }
+       spin_unlock(&sc->ether.tx_lock);
+}
+
+
+void dc_intr_tx_underflow(struct channel *sc)
+{
+       u32 val;
+
+       dc_transmitter_onoff(sc, SBE_2T3E3_OFF);
+
+       val = dc_read(sc->addr, SBE_2T3E3_21143_REG_OPERATION_MODE);
+       dc_clear_bits(sc->addr, SBE_2T3E3_21143_REG_OPERATION_MODE,
+                     SBE_2T3E3_21143_VAL_THRESHOLD_CONTROL_BITS);
+
+       switch (val & SBE_2T3E3_21143_VAL_THRESHOLD_CONTROL_BITS) {
+       case SBE_2T3E3_21143_VAL_THRESHOLD_CONTROL_BITS_1:
+               dc_set_bits(sc->addr, SBE_2T3E3_21143_REG_OPERATION_MODE,
+                           SBE_2T3E3_21143_VAL_THRESHOLD_CONTROL_BITS_2);
+               break;
+       case SBE_2T3E3_21143_VAL_THRESHOLD_CONTROL_BITS_2:
+               dc_set_bits(sc->addr, SBE_2T3E3_21143_REG_OPERATION_MODE,
+                           SBE_2T3E3_21143_VAL_THRESHOLD_CONTROL_BITS_3);
+               break;
+       case SBE_2T3E3_21143_VAL_THRESHOLD_CONTROL_BITS_3:
+               dc_set_bits(sc->addr, SBE_2T3E3_21143_REG_OPERATION_MODE,
+                           SBE_2T3E3_21143_VAL_THRESHOLD_CONTROL_BITS_4);
+               break;
+       case SBE_2T3E3_21143_VAL_THRESHOLD_CONTROL_BITS_4:
+       default:
+               dc_set_bits(sc->addr, SBE_2T3E3_21143_REG_OPERATION_MODE,
+                           SBE_2T3E3_21143_VAL_STORE_AND_FORWARD);
+               break;
+       }
+
+       dc_transmitter_onoff(sc, SBE_2T3E3_ON);
+}
+
+
+
+
+void exar7250_intr(struct channel *sc)
+{
+       u32 status, old_OOF;
+
+#if 0
+       /* disable interrupts */
+       exar7250_write(sc, SBE_2T3E3_FRAMER_REG_BLOCK_INTERRUPT_ENABLE, 0);
+#endif
+
+       old_OOF = sc->s.OOF;
+
+       status = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_BLOCK_INTERRUPT_STATUS);
+       dev_dbg(&sc->pdev->dev, "SBE 2T3E3: Framer interrupt! (REG[0x05] = %02X)\n", status);
+
+       switch (sc->p.frame_type) {
+       case SBE_2T3E3_FRAME_TYPE_E3_G751:
+       case SBE_2T3E3_FRAME_TYPE_E3_G832:
+               exar7250_E3_intr(sc, status);
+               break;
+
+       case SBE_2T3E3_FRAME_TYPE_T3_CBIT:
+       case SBE_2T3E3_FRAME_TYPE_T3_M13:
+               exar7250_T3_intr(sc, status);
+               break;
+
+       default:
+               break;
+       }
+
+       if (sc->s.OOF != old_OOF) {
+               if (sc->s.OOF) {
+                       if (sc->p.loopback == SBE_2T3E3_LOOPBACK_NONE) {
+                               dev_dbg(&sc->pdev->dev, "SBE 2T3E3: Disabling eth interrupts\n");
+                               /* turn off ethernet interrupts */
+                               dc_stop_intr(sc);
+                       }
+               } else if (sc->r.flags & SBE_2T3E3_FLAG_NETWORK_UP) {
+                       dev_dbg(&sc->pdev->dev, "SBE 2T3E3: Enabling eth interrupts\n");
+                       /* start interrupts */
+                       sc->s.OOF = 1;
+                       dc_intr_rx(sc);
+                       sc->s.OOF = 0;
+                       if (sc->p.receiver_on) {
+                               dc_receiver_onoff(sc, SBE_2T3E3_OFF);
+                               dc_receiver_onoff(sc, SBE_2T3E3_ON);
+                       }
+                       dc_start_intr(sc);
+               }
+       }
+#if 0
+       /* reenable interrupts */
+       exar7250_write(sc, SBE_2T3E3_FRAMER_REG_BLOCK_INTERRUPT_ENABLE,
+                      SBE_2T3E3_FRAMER_VAL_RX_INTERRUPT_ENABLE |
+                      SBE_2T3E3_FRAMER_VAL_TX_INTERRUPT_ENABLE
+               );
+#endif
+}
+
+
+void exar7250_T3_intr(struct channel *sc, u32 block_status)
+{
+       u32 status, result;
+
+       if (block_status & SBE_2T3E3_FRAMER_VAL_RX_INTERRUPT_STATUS) {
+               status = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_INTERRUPT_STATUS);
+
+               if (status) {
+                       dev_dbg(&sc->pdev->dev,
+                               "Framer interrupt T3 RX (REG[0x13] = %02X)\n",
+                               status);
+
+                       result = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_CONFIGURATION_STATUS);
+
+#if 0
+                       if (status & SBE_2T3E3_FRAMER_VAL_T3_RX_LOS_INTERRUPT_STATUS) {
+                               dev_dbg(&sc->pdev->dev,
+                                       "Framer interrupt T3: LOS\n");
+                               sc->s.LOS = result & SBE_2T3E3_FRAMER_VAL_T3_RX_LOS ? 1 : 0;
+
+                       }
+#else
+                       cpld_LOS_update(sc);
+#endif
+                       if (status & SBE_2T3E3_FRAMER_VAL_T3_RX_OOF_INTERRUPT_STATUS) {
+                               sc->s.OOF = result & SBE_2T3E3_FRAMER_VAL_T3_RX_OOF ? 1 : 0;
+                               dev_dbg(&sc->pdev->dev,
+                                       "Framer interrupt T3: OOF (%d)\n",
+                                       sc->s.OOF);
+                       }
+
+                       exar7250_write(sc, SBE_2T3E3_FRAMER_REG_T3_RX_INTERRUPT_ENABLE,
+                                      SBE_2T3E3_FRAMER_VAL_T3_RX_LOS_INTERRUPT_ENABLE |
+                                      SBE_2T3E3_FRAMER_VAL_T3_RX_OOF_INTERRUPT_ENABLE);
+#if 0
+                       SBE_2T3E3_FRAMER_VAL_T3_RX_CP_BIT_ERROR_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_T3_RX_LOS_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_T3_RX_AIS_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_T3_RX_IDLE_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_T3_RX_FERF_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_T3_RX_AIC_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_T3_RX_OOF_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_T3_RX_P_BIT_INTERRUPT_ENABLE
+#endif
+                               }
+
+               status = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_FEAC_INTERRUPT_ENABLE_STATUS);
+               if (status) {
+                       dev_dbg(&sc->pdev->dev,
+                               "Framer interrupt T3 RX (REG[0x17] = %02X)\n",
+                               status);
+#if 0
+                       exar7250_write(sc, SBE_2T3E3_FRAMER_REG_T3_RX_FEAC_INTERRUPT_ENABLE_STATUS,
+                                      SBE_2T3E3_FRAMER_VAL_T3_RX_FEAC_REMOVE_INTERRUPT_ENABLE |
+                                      SBE_2T3E3_FRAMER_VAL_T3_RX_FEAC_VALID_INTERRUPT_ENABLE
+                               );
+#endif
+               }
+
+               status = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_RX_LAPD_CONTROL);
+               if (status)
+                       dev_dbg(&sc->pdev->dev,
+                               "Framer interrupt T3 RX (REG[0x18] = %02X)\n",
+                               status);
+       }
+
+
+       if (block_status & SBE_2T3E3_FRAMER_VAL_TX_INTERRUPT_STATUS) {
+               status = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_TX_FEAC_CONFIGURATION_STATUS);
+               dev_dbg(&sc->pdev->dev, "SBE 2T3E3: Framer interrupt T3 TX (REG[0x31] = %02X)\n",
+                       status);
+
+               status = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_T3_TX_LAPD_STATUS);
+               dev_dbg(&sc->pdev->dev, "SBE 2T3E3: Framer interrupt T3 TX (REG[0x34] = %02X)\n",
+                       status);
+       }
+}
+
+
+void exar7250_E3_intr(struct channel *sc, u32 block_status)
+{
+       u32 status, result;
+
+       if (block_status & SBE_2T3E3_FRAMER_VAL_RX_INTERRUPT_STATUS) {
+               status = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_STATUS_1);
+
+               if (status) {
+                       dev_dbg(&sc->pdev->dev,
+                               "Framer interrupt E3 RX (REG[0x14] = %02X)\n",
+                               status);
+
+                       result = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_RX_CONFIGURATION_STATUS_2);
+
+#if 0
+                       if (status & SBE_2T3E3_FRAMER_VAL_E3_RX_LOS_INTERRUPT_STATUS) {
+                               dev_dbg(&sc->pdev->dev,
+                                       "Framer interrupt E3: LOS\n");
+                               sc->s.LOS = result & SBE_2T3E3_FRAMER_VAL_E3_RX_LOS ? 1 : 0;
+                       }
+#else
+                       cpld_LOS_update(sc);
+#endif
+                       if (status & SBE_2T3E3_FRAMER_VAL_E3_RX_OOF_INTERRUPT_STATUS) {
+                               sc->s.OOF = result & SBE_2T3E3_FRAMER_VAL_E3_RX_OOF ? 1 : 0;
+                               dev_dbg(&sc->pdev->dev,
+                                       "Framer interrupt E3: OOF (%d)\n",
+                                       sc->s.OOF);
+                       }
+
+                       exar7250_write(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_ENABLE_1,
+                                      SBE_2T3E3_FRAMER_VAL_E3_RX_OOF_INTERRUPT_ENABLE |
+                                      SBE_2T3E3_FRAMER_VAL_E3_RX_LOS_INTERRUPT_ENABLE
+                               );
+#if 0
+                       SBE_2T3E3_FRAMER_VAL_E3_RX_COFA_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_E3_RX_OOF_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_E3_RX_LOF_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_E3_RX_LOS_INTERRUPT_ENABLE |
+                               SBE_2T3E3_FRAMER_VAL_E3_RX_AIS_INTERRUPT_ENABLE
+#endif
+                               }
+
+               status = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_STATUS_2);
+               if (status) {
+                       dev_dbg(&sc->pdev->dev,
+                               "Framer interrupt E3 RX (REG[0x15] = %02X)\n",
+                               status);
+
+#if 0
+                       exar7250_write(sc, SBE_2T3E3_FRAMER_REG_E3_RX_INTERRUPT_ENABLE_2,
+                                      SBE_2T3E3_FRAMER_VAL_E3_RX_FEBE_INTERRUPT_ENABLE |
+                                      SBE_2T3E3_FRAMER_VAL_E3_RX_FERF_INTERRUPT_ENABLE |
+                                      SBE_2T3E3_FRAMER_VAL_E3_RX_FRAMING_BYTE_ERROR_INTERRUPT_ENABLE);
+#endif
+               }
+
+       }
+
+       if (block_status & SBE_2T3E3_FRAMER_VAL_TX_INTERRUPT_STATUS) {
+               status = exar7250_read(sc, SBE_2T3E3_FRAMER_REG_E3_TX_LAPD_STATUS);
+               dev_dbg(&sc->pdev->dev, "SBE 2T3E3: Framer interrupt E3 TX (REG[0x34] = %02X)\n",
+                       status);
+       }
+}