struct net_device_stats netstats;
        int activity;
        __be32 iscsi_ipv4addr;
+
+       int link_fault; /* link fault was detected */
 };
 
 enum {                         /* adapter flags */
        struct delayed_work adap_check_task;
        struct work_struct ext_intr_handler_task;
        struct work_struct fatal_error_handler_task;
+       struct work_struct link_fault_handler_task;
 
        struct dentry *debugfs_root;
 
 void t3_os_link_changed(struct adapter *adapter, int port_id, int link_status,
                        int speed, int duplex, int fc);
 void t3_os_phymod_changed(struct adapter *adap, int port_id);
+void t3_os_link_fault(struct adapter *adapter, int port_id, int state);
+void t3_os_link_fault_handler(struct adapter *adapter, int port_id);
 
 void t3_sge_start(struct adapter *adap);
 void t3_sge_stop(struct adapter *adap);
 
        unsigned long num_toggled; /* # times toggled TxEn due to stuck TX */
        unsigned long num_resets;  /* # times reset due to stuck TX */
 
+       unsigned long link_faults;  /* # detected link faults */
 };
 
 struct tp_mib_stats {
 void t3_intr_enable(struct adapter *adapter);
 void t3_intr_disable(struct adapter *adapter);
 void t3_intr_clear(struct adapter *adapter);
+void t3_xgm_intr_enable(struct adapter *adapter, int idx);
+void t3_xgm_intr_disable(struct adapter *adapter, int idx);
 void t3_port_intr_enable(struct adapter *adapter, int idx);
 void t3_port_intr_disable(struct adapter *adapter, int idx);
 void t3_port_intr_clear(struct adapter *adapter, int idx);
 int t3_phy_intr_handler(struct adapter *adapter);
 
 void t3_link_changed(struct adapter *adapter, int port_id);
+void t3_link_fault(struct adapter *adapter, int port_id);
 int t3_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc);
 const struct adapter_info *t3_get_adapter_info(unsigned int board_id);
 int t3_seeprom_read(struct adapter *adapter, u32 addr, __le32 *data);
 
 int t3_mac_reset(struct cmac *mac);
 void t3b_pcs_reset(struct cmac *mac);
+void t3_mac_disable_exact_filters(struct cmac *mac);
+void t3_mac_enable_exact_filters(struct cmac *mac);
 int t3_mac_enable(struct cmac *mac, int which);
 int t3_mac_disable(struct cmac *mac, int which);
 int t3_mac_set_mtu(struct cmac *mac, unsigned int mtu);
 
        }
 }
 
+void t3_os_link_fault(struct adapter *adap, int port_id, int state)
+{
+       struct net_device *dev = adap->port[port_id];
+       struct port_info *pi = netdev_priv(dev);
+
+       if (state == netif_carrier_ok(dev))
+               return;
+
+       if (state) {
+               struct cmac *mac = &pi->mac;
+
+               netif_carrier_on(dev);
+
+               /* Clear local faults */
+               t3_xgm_intr_disable(adap, pi->port_id);
+               t3_read_reg(adap, A_XGM_INT_STATUS +
+                                   pi->mac.offset);
+               t3_write_reg(adap,
+                            A_XGM_INT_CAUSE + pi->mac.offset,
+                            F_XGM_INT);
+
+               t3_set_reg_field(adap,
+                                A_XGM_INT_ENABLE +
+                                pi->mac.offset,
+                                F_XGM_INT, F_XGM_INT);
+               t3_xgm_intr_enable(adap, pi->port_id);
+
+               t3_mac_enable(mac, MAC_DIRECTION_TX);
+       } else
+               netif_carrier_off(dev);
+
+       link_report(dev);
+}
+
 /**
  *     t3_os_link_changed - handle link status changes
  *     @adapter: the adapter associated with the link change
        if (link_stat != netif_carrier_ok(dev)) {
                if (link_stat) {
                        t3_mac_enable(mac, MAC_DIRECTION_RX);
+
+                       /* Clear local faults */
+                       t3_xgm_intr_disable(adapter, pi->port_id);
+                       t3_read_reg(adapter, A_XGM_INT_STATUS +
+                                   pi->mac.offset);
+                       t3_write_reg(adapter,
+                                    A_XGM_INT_CAUSE + pi->mac.offset,
+                                    F_XGM_INT);
+
+                       t3_set_reg_field(adapter,
+                                        A_XGM_INT_ENABLE + pi->mac.offset,
+                                        F_XGM_INT, F_XGM_INT);
+                       t3_xgm_intr_enable(adapter, pi->port_id);
+
                        netif_carrier_on(dev);
                } else {
                        netif_carrier_off(dev);
-                       pi->phy.ops->power_down(&pi->phy, 1);
+
+                       t3_xgm_intr_disable(adapter, pi->port_id);
+                       t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset);
+                       t3_set_reg_field(adapter,
+                                        A_XGM_INT_ENABLE + pi->mac.offset,
+                                        F_XGM_INT, 0);
+
+                       if (is_10G(adapter))
+                               pi->phy.ops->power_down(&pi->phy, 1);
+
+                       t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset);
                        t3_mac_disable(mac, MAC_DIRECTION_RX);
                        t3_link_start(&pi->phy, mac, &pi->link_config);
                }
        struct port_info *pi = netdev_priv(dev);
        struct adapter *adapter = pi->adapter;
 
+       /* Stop link fault interrupts */
+       t3_xgm_intr_disable(adapter, pi->port_id);
+       t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset);
+
        t3_port_intr_disable(adapter, pi->port_id);
        netif_tx_stop_all_queues(dev);
        pi->phy.ops->power_down(&pi->phy, 1);
        "CheckTXEnToggled   ",
        "CheckResets        ",
 
+       "LinkFaults         ",
 };
 
 static int get_sset_count(struct net_device *dev, int sset)
 
        *data++ = s->num_toggled;
        *data++ = s->num_resets;
+
+       *data++ = s->link_faults;
 }
 
 static inline void reg_block_dump(struct adapter *ap, void *buf,
                struct net_device *dev = adapter->port[i];
                struct port_info *p = netdev_priv(dev);
 
-               if (!(p->phy.caps & SUPPORTED_IRQ) && netif_running(dev))
+               spin_lock_irq(&adapter->work_lock);
+               if (p->link_fault) {
+                       spin_unlock_irq(&adapter->work_lock);
+                       continue;
+               }
+               spin_unlock_irq(&adapter->work_lock);
+
+               if (!(p->phy.caps & SUPPORTED_IRQ) && netif_running(dev)) {
+                       t3_xgm_intr_disable(adapter, i);
+                       t3_read_reg(adapter, A_XGM_INT_STATUS + p->mac.offset);
+
                        t3_link_changed(adapter, i);
+                       t3_xgm_intr_enable(adapter, i);
+               }
        }
 }
 
 {
        struct adapter *adapter = container_of(work, struct adapter,
                                               ext_intr_handler_task);
+       int i;
+
+       /* Disable link fault interrupts */
+       for_each_port(adapter, i) {
+               struct net_device *dev = adapter->port[i];
+               struct port_info *p = netdev_priv(dev);
+
+               t3_xgm_intr_disable(adapter, i);
+               t3_read_reg(adapter, A_XGM_INT_STATUS + p->mac.offset);
+       }
 
+       /* Re-enable link fault interrupts */
        t3_phy_intr_handler(adapter);
 
+       for_each_port(adapter, i)
+               t3_xgm_intr_enable(adapter, i);
+
        /* Now reenable external interrupts */
        spin_lock_irq(&adapter->work_lock);
        if (adapter->slow_intr_mask) {
        spin_unlock(&adapter->work_lock);
 }
 
+static void link_fault_task(struct work_struct *work)
+{
+       struct adapter *adapter = container_of(work, struct adapter,
+                                              link_fault_handler_task);
+       int i;
+
+       for_each_port(adapter, i) {
+               struct net_device *netdev = adapter->port[i];
+               struct port_info *pi = netdev_priv(netdev);
+
+               if (pi->link_fault)
+                       t3_link_fault(adapter, i);
+       }
+}
+
+void t3_os_link_fault_handler(struct adapter *adapter, int port_id)
+{
+       struct net_device *netdev = adapter->port[port_id];
+       struct port_info *pi = netdev_priv(netdev);
+
+       spin_lock(&adapter->work_lock);
+       pi->link_fault = 1;
+       queue_work(cxgb3_wq, &adapter->link_fault_handler_task);
+       spin_unlock(&adapter->work_lock);
+}
+
 static int t3_adapter_error(struct adapter *adapter, int reset)
 {
        int i, ret = 0;
                CH_ALERT(adapter, "FW status: 0x%x, 0x%x, 0x%x, 0x%x\n",
                         fw_status[0], fw_status[1],
                         fw_status[2], fw_status[3]);
-
 }
 
 /**
 
        INIT_LIST_HEAD(&adapter->adapter_list);
        INIT_WORK(&adapter->ext_intr_handler_task, ext_intr_task);
+       INIT_WORK(&adapter->link_fault_handler_task, link_fault_task);
        INIT_WORK(&adapter->fatal_error_handler_task, fatal_error_task);
        INIT_DELAYED_WORK(&adapter->adap_check_task, t3_adap_check_task);
 
 
 
 #define A_XGM_RX_EXACT_MATCH_LOW_8 0x854
 
+#define A_XGM_INT_STATUS 0x86c
+
+#define S_LINKFAULTCHANGE    9
+#define V_LINKFAULTCHANGE(x) ((x) << S_LINKFAULTCHANGE)
+#define F_LINKFAULTCHANGE    V_LINKFAULTCHANGE(1U)
+
+#define A_XGM_XGM_INT_ENABLE 0x874
+#define A_XGM_XGM_INT_DISABLE 0x878
+
 #define A_XGM_STAT_CTRL 0x880
 
 #define S_CLRSTATS    2
 #define V_XAUIPCSALIGNCHANGE(x) ((x) << S_XAUIPCSALIGNCHANGE)
 #define F_XAUIPCSALIGNCHANGE    V_XAUIPCSALIGNCHANGE(1U)
 
+#define S_XGM_INT    0
+#define V_XGM_INT(x) ((x) << S_XGM_INT)
+#define F_XGM_INT    V_XGM_INT(1U)
+
 #define A_XGM_INT_CAUSE 0x8d8
 
 #define A_XGM_XAUI_ACT_CTRL 0x8dc
 
        return ret;
 }
 
+static void t3_gate_rx_traffic(struct cmac *mac, u32 *rx_cfg,
+                              u32 *rx_hash_high, u32 *rx_hash_low)
+{
+       /* stop Rx unicast traffic */
+       t3_mac_disable_exact_filters(mac);
+
+       /* stop broadcast, multicast, promiscuous mode traffic */
+       *rx_cfg = t3_read_reg(mac->adapter, A_XGM_RX_CFG);
+       t3_set_reg_field(mac->adapter, A_XGM_RX_CFG,
+                        F_ENHASHMCAST | F_DISBCAST | F_COPYALLFRAMES,
+                        F_DISBCAST);
+
+       *rx_hash_high = t3_read_reg(mac->adapter, A_XGM_RX_HASH_HIGH);
+       t3_write_reg(mac->adapter, A_XGM_RX_HASH_HIGH, 0);
+
+       *rx_hash_low = t3_read_reg(mac->adapter, A_XGM_RX_HASH_LOW);
+       t3_write_reg(mac->adapter, A_XGM_RX_HASH_LOW, 0);
+
+       /* Leave time to drain max RX fifo */
+       msleep(1);
+}
+
+static void t3_open_rx_traffic(struct cmac *mac, u32 rx_cfg,
+                              u32 rx_hash_high, u32 rx_hash_low)
+{
+       t3_mac_enable_exact_filters(mac);
+       t3_set_reg_field(mac->adapter, A_XGM_RX_CFG,
+                        F_ENHASHMCAST | F_DISBCAST | F_COPYALLFRAMES,
+                        rx_cfg);
+       t3_write_reg(mac->adapter, A_XGM_RX_HASH_HIGH, rx_hash_high);
+       t3_write_reg(mac->adapter, A_XGM_RX_HASH_LOW, rx_hash_low);
+}
 
 /**
  *     t3_link_changed - handle interface link changes
        struct cphy *phy = &pi->phy;
        struct cmac *mac = &pi->mac;
        struct link_config *lc = &pi->link_config;
+       int force_link_down = 0;
 
        phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc);
 
+       if (!lc->link_ok && link_ok) {
+               u32 rx_cfg, rx_hash_high, rx_hash_low;
+               u32 status;
+
+               t3_xgm_intr_enable(adapter, port_id);
+               t3_gate_rx_traffic(mac, &rx_cfg, &rx_hash_high, &rx_hash_low);
+               t3_write_reg(adapter, A_XGM_RX_CTRL + mac->offset, 0);
+               t3_mac_enable(mac, MAC_DIRECTION_RX);
+
+               status = t3_read_reg(adapter, A_XGM_INT_STATUS + mac->offset);
+               if (status & F_LINKFAULTCHANGE) {
+                       mac->stats.link_faults++;
+                       force_link_down = 1;
+               }
+               t3_open_rx_traffic(mac, rx_cfg, rx_hash_high, rx_hash_low);
+
+               if (force_link_down) {
+                       t3_os_link_fault_handler(adapter, port_id);
+                       return;
+               }
+       }
+
        if (lc->requested_fc & PAUSE_AUTONEG)
                fc &= lc->requested_fc;
        else
        t3_os_link_changed(adapter, port_id, link_ok, speed, duplex, fc);
 }
 
+void t3_link_fault(struct adapter *adapter, int port_id)
+{
+       struct port_info *pi = adap2pinfo(adapter, port_id);
+       struct cmac *mac = &pi->mac;
+       struct cphy *phy = &pi->phy;
+       struct link_config *lc = &pi->link_config;
+       int link_ok, speed, duplex, fc, link_fault;
+       u32 rx_cfg, rx_hash_high, rx_hash_low;
+
+       t3_gate_rx_traffic(mac, &rx_cfg, &rx_hash_high, &rx_hash_low);
+
+       if (adapter->params.rev > 0 && uses_xaui(adapter))
+               t3_write_reg(adapter, A_XGM_XAUI_ACT_CTRL + mac->offset, 0);
+
+       t3_write_reg(adapter, A_XGM_RX_CTRL + mac->offset, 0);
+       t3_mac_enable(mac, MAC_DIRECTION_RX);
+
+       t3_open_rx_traffic(mac, rx_cfg, rx_hash_high, rx_hash_low);
+
+       link_fault = t3_read_reg(adapter,
+                                A_XGM_INT_STATUS + mac->offset);
+       link_fault &= F_LINKFAULTCHANGE;
+
+       phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc);
+
+       if (link_fault) {
+               lc->link_ok = 0;
+               lc->speed = SPEED_INVALID;
+               lc->duplex = DUPLEX_INVALID;
+
+               t3_os_link_fault(adapter, port_id, 0);
+
+               /* Account link faults only when the phy reports a link up */
+               if (link_ok)
+                       mac->stats.link_faults++;
+
+               msleep(1000);
+               t3_os_link_fault_handler(adapter, port_id);
+       } else {
+               if (link_ok)
+                       t3_write_reg(adapter, A_XGM_XAUI_ACT_CTRL + mac->offset,
+                                    F_TXACTENABLE | F_RXEN);
+
+               pi->link_fault = 0;
+               lc->link_ok = (unsigned char)link_ok;
+               lc->speed = speed < 0 ? SPEED_INVALID : speed;
+               lc->duplex = duplex < 0 ? DUPLEX_INVALID : duplex;
+               t3_os_link_fault(adapter, port_id, link_ok);
+       }
+}
+
 /**
  *     t3_link_start - apply link configuration to MAC/PHY
  *     @phy: the PHY to setup
                       V_TX1TPPARERRENB(M_TX1TPPARERRENB) | \
                       V_RXTPPARERRENB(M_RXTPPARERRENB) | \
                       V_MCAPARERRENB(M_MCAPARERRENB))
+#define XGM_EXTRA_INTR_MASK (F_LINKFAULTCHANGE)
 #define PL_INTR_MASK (F_T3DBG | F_XGMAC0_0 | F_XGMAC0_1 | F_MC5A | F_PM1_TX | \
                      F_PM1_RX | F_ULP2_TX | F_ULP2_RX | F_TP1 | F_CIM | \
                      F_MC7_CM | F_MC7_PMTX | F_MC7_PMRX | F_SGE3 | F_PCIM0 | \
                      F_MPS0 | F_CPL_SWITCH)
-
 /*
  * Interrupt handler for the PCIX1 module.
  */
                mac->stats.xaui_pcs_ctc_err++;
        if (cause & F_XAUIPCSALIGNCHANGE)
                mac->stats.xaui_pcs_align_change++;
+       if (cause & F_XGM_INT) {
+               t3_set_reg_field(adap,
+                                A_XGM_INT_ENABLE + mac->offset,
+                                F_XGM_INT, 0);
+               mac->stats.link_faults++;
+
+               t3_os_link_fault_handler(adap, idx);
+       }
 
        t3_write_reg(adap, A_XGM_INT_CAUSE + mac->offset, cause);
+
        if (cause & XGM_INTR_FATAL)
                t3_fatal_err(adap);
+
        return cause != 0;
 }
 
        t3_read_reg(adapter, A_PL_INT_CAUSE0);  /* flush */
 }
 
+void t3_xgm_intr_enable(struct adapter *adapter, int idx)
+{
+       struct port_info *pi = adap2pinfo(adapter, idx);
+
+       t3_write_reg(adapter, A_XGM_XGM_INT_ENABLE + pi->mac.offset,
+                    XGM_EXTRA_INTR_MASK);
+}
+
+void t3_xgm_intr_disable(struct adapter *adapter, int idx)
+{
+       struct port_info *pi = adap2pinfo(adapter, idx);
+
+       t3_write_reg(adapter, A_XGM_XGM_INT_DISABLE + pi->mac.offset,
+                    0x7ff);
+}
+
 /**
  *     t3_port_intr_enable - enable port-specific interrupts
  *     @adapter: associated adapter
 
        /* re-enable nic traffic */
        t3_set_reg_field(adap, A_MPS_CFG, F_ENFORCEPKT, 1);
 
+       /*  Set: re-enable NIC traffic */
+       t3_set_reg_field(adap, A_MPS_CFG, F_ENFORCEPKT, 1);
+
        return 0;
 }
 
        return 0;
 }
 
-static void disable_exact_filters(struct cmac *mac)
+void t3_mac_disable_exact_filters(struct cmac *mac)
 {
        unsigned int i, reg = mac->offset + A_XGM_RX_EXACT_MATCH_LOW_1;
 
        t3_read_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_LOW_1);  /* flush */
 }
 
-static void enable_exact_filters(struct cmac *mac)
+void t3_mac_enable_exact_filters(struct cmac *mac)
 {
        unsigned int i, reg = mac->offset + A_XGM_RX_EXACT_MATCH_HIGH_1;
 
 
        if (adap->params.rev >= T3_REV_B2 &&
            (t3_read_reg(adap, A_XGM_RX_CTRL + mac->offset) & F_RXEN)) {
-               disable_exact_filters(mac);
+               t3_mac_disable_exact_filters(mac);
                v = t3_read_reg(adap, A_XGM_RX_CFG + mac->offset);
                t3_set_reg_field(adap, A_XGM_RX_CFG + mac->offset,
                                 F_ENHASHMCAST | F_COPYALLFRAMES, F_DISBCAST);
                if (t3_wait_op_done(adap, reg + mac->offset,
                                    F_RXFIFO_EMPTY, 1, 20, 5)) {
                        t3_write_reg(adap, A_XGM_RX_CFG + mac->offset, v);
-                       enable_exact_filters(mac);
+                       t3_mac_enable_exact_filters(mac);
                        return -EIO;
                }
                t3_set_reg_field(adap, A_XGM_RX_MAX_PKT_SIZE + mac->offset,
                                 V_RXMAXPKTSIZE(M_RXMAXPKTSIZE),
                                 V_RXMAXPKTSIZE(mtu));
                t3_write_reg(adap, A_XGM_RX_CFG + mac->offset, v);
-               enable_exact_filters(mac);
+               t3_mac_enable_exact_filters(mac);
        } else
                t3_set_reg_field(adap, A_XGM_RX_MAX_PKT_SIZE + mac->offset,
                                 V_RXMAXPKTSIZE(M_RXMAXPKTSIZE),