Add link update flow to wangxun 10/25/40G virtual functions. Get link status from pf in mbox, and if it is failed then check the vx_status, because vx_status switching is too slow. Signed-off-by: Mengyuan Lou --- drivers/net/ethernet/wangxun/libwx/wx_type.h | 2 + drivers/net/ethernet/wangxun/libwx/wx_vf.c | 126 ++++++++++++++++++ drivers/net/ethernet/wangxun/libwx/wx_vf.h | 14 ++ .../net/ethernet/wangxun/libwx/wx_vf_common.c | 91 +++++++++++++ .../net/ethernet/wangxun/libwx/wx_vf_common.h | 2 + .../ethernet/wangxun/txgbevf/txgbevf_main.c | 3 + 6 files changed, 238 insertions(+) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 58e9988388a7..42b0e65fe983 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -1206,6 +1206,8 @@ enum wx_pf_flags { WX_FLAG_PTP_PPS_ENABLED, WX_FLAG_NEED_LINK_CONFIG, WX_FLAG_NEED_SFP_RESET, + WX_FLAG_NEED_UPDATE_LINK, + WX_FLAG_NEED_DO_RESET, WX_PF_FLAGS_NBITS /* must be last */ }; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf.c b/drivers/net/ethernet/wangxun/libwx/wx_vf.c index 165b83e9098f..7567216a005f 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_vf.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf.c @@ -471,3 +471,129 @@ int wx_get_queues_vf(struct wx *wx, u32 *num_tcs, u32 *default_tc) return 0; } EXPORT_SYMBOL(wx_get_queues_vf); + +static int wx_get_link_status_from_pf(struct wx *wx, u32 *msgbuf) +{ + u32 links_reg = msgbuf[1]; + + if (msgbuf[1] & WX_PF_NOFITY_VF_NET_NOT_RUNNING) + wx->notify_down = true; + else + wx->notify_down = false; + + if (wx->notify_down) { + wx->link = false; + wx->speed = SPEED_UNKNOWN; + return 0; + } + + wx->link = WX_PFLINK_STATUS(links_reg); + wx->speed = WX_PFLINK_SPEED(links_reg); + + return 0; +} + +static int wx_pf_ping_vf(struct wx *wx, u32 *msgbuf) +{ + if (!(msgbuf[0] & WX_VT_MSGTYPE_CTS)) + /* msg is not CTS, we need to do reset */ + return -EINVAL; + + return 0; +} + +static struct wx_link_reg_fields wx_speed_lookup_vf[] = { + {wx_mac_unknown}, + {wx_mac_sp, SPEED_10000, SPEED_1000, SPEED_100, SPEED_UNKNOWN, SPEED_UNKNOWN}, + {wx_mac_em, SPEED_1000, SPEED_100, SPEED_10, SPEED_UNKNOWN, SPEED_UNKNOWN}, + {wx_mac_aml, SPEED_40000, SPEED_25000, SPEED_10000, SPEED_1000, SPEED_UNKNOWN}, + {wx_mac_aml40, SPEED_40000, SPEED_25000, SPEED_10000, SPEED_1000, SPEED_UNKNOWN}, +}; + +static void wx_check_physical_link(struct wx *wx) +{ + u32 val, link_val; + int ret; + + /* get link status from hw status reg + * for SFP+ modules and DA cables, it can take up to 500usecs + * before the link status is correct + */ + if (wx->mac.type == wx_mac_em) + ret = read_poll_timeout_atomic(rd32, val, val & GENMASK(4, 1), + 100, 500, false, wx, WX_VXSTATUS); + else + ret = read_poll_timeout_atomic(rd32, val, val & BIT(0), 100, + 500, false, wx, WX_VXSTATUS); + if (ret) { + wx->speed = SPEED_UNKNOWN; + wx->link = false; + return; + } + + wx->link = true; + link_val = WX_VXSTATUS_SPEED(val); + + if (link_val & BIT(0)) + wx->speed = wx_speed_lookup_vf[wx->mac.type].bit0_f; + else if (link_val & BIT(1)) + wx->speed = wx_speed_lookup_vf[wx->mac.type].bit1_f; + else if (link_val & BIT(2)) + wx->speed = wx_speed_lookup_vf[wx->mac.type].bit2_f; + else if (link_val & BIT(3)) + wx->speed = wx_speed_lookup_vf[wx->mac.type].bit3_f; + else + wx->speed = SPEED_UNKNOWN; +} + +int wx_check_mac_link_vf(struct wx *wx) +{ + struct wx_mbx_info *mbx = &wx->mbx; + u32 msgbuf[2] = {0}; + int ret = 0; + + if (!mbx->timeout) + goto out; + + wx_check_for_rst_vf(wx); + if (!wx_check_for_msg_vf(wx)) + ret = wx_read_mbx_vf(wx, msgbuf, 2); + if (ret) + goto out; + + switch (msgbuf[0] & GENMASK(8, 0)) { + case WX_PF_NOFITY_VF_LINK_STATUS | WX_PF_CONTROL_MSG: + ret = wx_get_link_status_from_pf(wx, msgbuf); + goto out; + case WX_PF_CONTROL_MSG: + ret = wx_pf_ping_vf(wx, msgbuf); + goto out; + case 0: + if (msgbuf[0] & WX_VT_MSGTYPE_NACK) { + /* msg is NACK, we must have lost CTS status */ + ret = -EBUSY; + goto out; + } + /* no message, check link status */ + wx_check_physical_link(wx); + goto out; + default: + break; + } + + if (!(msgbuf[0] & WX_VT_MSGTYPE_CTS)) { + /* msg is not CTS and is NACK we must have lost CTS status */ + if (msgbuf[0] & WX_VT_MSGTYPE_NACK) + ret = -EBUSY; + goto out; + } + + /* the pf is talking, if we timed out in the past we reinit */ + if (!mbx->timeout) { + ret = -EBUSY; + goto out; + } + +out: + return ret; +} diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf.h b/drivers/net/ethernet/wangxun/libwx/wx_vf.h index e863a74c291d..fec1126703e3 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_vf.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf.h @@ -94,6 +94,19 @@ #define WX_VXMRQC_RSS_EN BIT(8) #define WX_VXMRQC_RSS_HASH(f) FIELD_PREP(GENMASK(15, 13), f) +#define WX_PFLINK_STATUS(g) FIELD_GET(BIT(0), g) +#define WX_PFLINK_SPEED(g) FIELD_GET(GENMASK(31, 1), g) +#define WX_VXSTATUS_SPEED(g) FIELD_GET(GENMASK(4, 1), g) + +struct wx_link_reg_fields { + u32 mac_type; + u32 bit0_f; + u32 bit1_f; + u32 bit2_f; + u32 bit3_f; + u32 bit4_f; +}; + void wx_init_hw_vf(struct wx *wx); int wx_reset_hw_vf(struct wx *wx); void wx_get_mac_addr_vf(struct wx *wx, u8 *mac_addr); @@ -109,5 +122,6 @@ int wx_update_xcast_mode_vf(struct wx *wx, int xcast_mode); int wx_get_link_state_vf(struct wx *wx, u16 *link_state); int wx_set_vfta_vf(struct wx *wx, u32 vlan, u32 vind, bool vlan_on, bool vlvf_bypass); +int wx_check_mac_link_vf(struct wx *wx); #endif /* _WX_VF_H_ */ diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c index dc3ed0808e15..ade2bfe563aa 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c @@ -48,6 +48,7 @@ void wxvf_remove(struct pci_dev *pdev) struct wx *wx = pci_get_drvdata(pdev); struct net_device *netdev; + cancel_work_sync(&wx->service_task); netdev = wx->netdev; unregister_netdev(netdev); kfree(wx->vfinfo); @@ -64,6 +65,7 @@ static irqreturn_t wx_msix_misc_vf(int __always_unused irq, void *data) { struct wx *wx = data; + set_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags); /* Clear the interrupt */ if (netif_running(wx->netdev)) wr32(wx, WX_VXIMC, wx->eims_other); @@ -243,6 +245,24 @@ int wx_set_mac_vf(struct net_device *netdev, void *p) } EXPORT_SYMBOL(wx_set_mac_vf); +void wxvf_watchdog_update_link(struct wx *wx) +{ + int err; + + if (!test_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags)) + return; + + spin_lock_bh(&wx->mbx.mbx_lock); + err = wx_check_mac_link_vf(wx); + spin_unlock_bh(&wx->mbx.mbx_lock); + if (err) { + wx->link = false; + set_bit(WX_FLAG_NEED_DO_RESET, wx->flags); + } + clear_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags); +} +EXPORT_SYMBOL(wxvf_watchdog_update_link); + static void wxvf_irq_enable(struct wx *wx) { wr32(wx, WX_VXIMC, wx->eims_enable_mask); @@ -250,6 +270,11 @@ static void wxvf_irq_enable(struct wx *wx) static void wxvf_up_complete(struct wx *wx) { + /* Always set the carrier off */ + netif_carrier_off(wx->netdev); + mod_timer(&wx->service_timer, jiffies + HZ); + set_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags); + wx_configure_msix_vf(wx); smp_mb__before_atomic(); wx_napi_enable_all(wx); @@ -301,8 +326,10 @@ static void wxvf_down(struct wx *wx) { struct net_device *netdev = wx->netdev; + timer_delete_sync(&wx->service_timer); netif_tx_stop_all_queues(netdev); netif_tx_disable(netdev); + netif_carrier_off(netdev); wx_napi_disable_all(wx); wx_reset_vf(wx); @@ -310,6 +337,34 @@ static void wxvf_down(struct wx *wx) wx_clean_all_rx_rings(wx); } +static void wxvf_reinit_locked(struct wx *wx) +{ + while (test_and_set_bit(WX_STATE_RESETTING, wx->state)) + usleep_range(1000, 2000); + wxvf_down(wx); + wx_free_irq(wx); + wx_configure_vf(wx); + wx_request_msix_irqs_vf(wx); + wxvf_up_complete(wx); + clear_bit(WX_STATE_RESETTING, wx->state); +} + +static void wxvf_reset_subtask(struct wx *wx) +{ + if (!test_bit(WX_FLAG_NEED_DO_RESET, wx->flags)) + return; + clear_bit(WX_FLAG_NEED_DO_RESET, wx->flags); + + rtnl_lock(); + if (test_bit(WX_STATE_RESETTING, wx->state) || + !(netif_running(wx->netdev))) { + rtnl_unlock(); + return; + } + wxvf_reinit_locked(wx); + rtnl_unlock(); +} + int wxvf_close(struct net_device *netdev) { struct wx *wx = netdev_priv(netdev); @@ -321,3 +376,39 @@ int wxvf_close(struct net_device *netdev) return 0; } EXPORT_SYMBOL(wxvf_close); + +static void wxvf_link_config_subtask(struct wx *wx) +{ + struct net_device *netdev = wx->netdev; + + wxvf_watchdog_update_link(wx); + if (wx->link) { + if (netif_carrier_ok(netdev)) + return; + netif_carrier_on(netdev); + netdev_info(netdev, "Link is Up - %s\n", + phy_speed_to_str(wx->speed)); + } else { + if (!netif_carrier_ok(netdev)) + return; + netif_carrier_off(netdev); + netdev_info(netdev, "Link is Down\n"); + } +} + +static void wxvf_service_task(struct work_struct *work) +{ + struct wx *wx = container_of(work, struct wx, service_task); + + wxvf_link_config_subtask(wx); + wxvf_reset_subtask(wx); + wx_service_event_complete(wx); +} + +void wxvf_init_service(struct wx *wx) +{ + timer_setup(&wx->service_timer, wx_service_timer, 0); + INIT_WORK(&wx->service_task, wxvf_service_task); + clear_bit(WX_STATE_SERVICE_SCHED, wx->state); +} +EXPORT_SYMBOL(wxvf_init_service); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.h b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.h index 272743a3c878..cbbb1b178cb2 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.h @@ -14,7 +14,9 @@ void wx_reset_vf(struct wx *wx); void wx_set_rx_mode_vf(struct net_device *netdev); void wx_configure_vf(struct wx *wx); int wx_set_mac_vf(struct net_device *netdev, void *p); +void wxvf_watchdog_update_link(struct wx *wx); int wxvf_open(struct net_device *netdev); int wxvf_close(struct net_device *netdev); +void wxvf_init_service(struct wx *wx); #endif /* _WX_VF_COMMON_H_ */ diff --git a/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c b/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c index 57e67804b8b7..ebfce3cf753e 100644 --- a/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c +++ b/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c @@ -250,6 +250,7 @@ static int txgbevf_probe(struct pci_dev *pdev, eth_hw_addr_set(netdev, wx->mac.perm_addr); ether_addr_copy(netdev->perm_addr, wx->mac.addr); + wxvf_init_service(wx); err = wx_init_interrupt_scheme(wx); if (err) goto err_free_sw_init; @@ -266,6 +267,8 @@ static int txgbevf_probe(struct pci_dev *pdev, err_register: wx_clear_interrupt_scheme(wx); err_free_sw_init: + timer_delete_sync(&wx->service_timer); + cancel_work_sync(&wx->service_task); kfree(wx->vfinfo); kfree(wx->rss_key); kfree(wx->mac_table); -- 2.30.1