|
@@ -1153,6 +1153,38 @@ int t3_cim_ctl_blk_read(struct adapter *adap, unsigned int addr,
|
|
|
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
|
|
@@ -1170,9 +1202,32 @@ void t3_link_changed(struct adapter *adapter, int port_id)
|
|
|
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
|
|
@@ -1202,6 +1257,57 @@ void t3_link_changed(struct adapter *adapter, int port_id)
|
|
|
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
|
|
@@ -1360,11 +1466,11 @@ static int t3_handle_intr_status(struct adapter *adapter, unsigned int reg,
|
|
|
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.
|
|
|
*/
|
|
@@ -1722,10 +1828,20 @@ static int mac_intr_handler(struct adapter *adap, unsigned int idx)
|
|
|
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;
|
|
|
}
|
|
|
|
|
@@ -1931,6 +2047,22 @@ void t3_intr_clear(struct adapter *adapter)
|
|
|
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
|