From ba4468dbf3364c35652dda14e2ef90c5cf09ab16 Mon Sep 17 00:00:00 2001 From: Jitendra Kalsaria Date: Sat, 9 Feb 2013 09:29:51 +0000 Subject: [PATCH] qlcnic: refactor 83xx diagnostic loopback test Cleanly separate 83xx diagnostic loopback test routines from 82xx Signed-off-by: Jitendra Kalsaria Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 4 +- .../ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 147 +++++++++++++++++- .../ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h | 1 + .../ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 28 +--- .../net/ethernet/qlogic/qlcnic/qlcnic_main.c | 10 +- 5 files changed, 158 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 3a840e741982..61b594c6d9d7 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -1442,7 +1442,9 @@ int qlcnic_send_lro_cleanup(struct qlcnic_adapter *adapter); void qlcnic_update_cmd_producer(struct qlcnic_host_tx_ring *); /* Functions from qlcnic_ethtool.c */ -int qlcnic_check_loopback_buff(unsigned char *data, u8 mac[]); +int qlcnic_check_loopback_buff(unsigned char *, u8 []); +int qlcnic_do_lb_test(struct qlcnic_adapter *, u8); +int qlcnic_loopback_test(struct net_device *, u8); /* Functions from qlcnic_main.c */ int qlcnic_reset_context(struct qlcnic_adapter *); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 3d628c638247..3d4813121d2e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -257,8 +257,6 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = { .config_intr_coal = qlcnic_83xx_config_intr_coal, .config_rss = qlcnic_83xx_config_rss, .config_hw_lro = qlcnic_83xx_config_hw_lro, - .config_loopback = qlcnic_83xx_set_lb_mode, - .clear_loopback = qlcnic_83xx_clear_lb_mode, .config_promisc_mode = qlcnic_83xx_nic_set_promisc, .change_l2_filter = qlcnic_83xx_change_l2_filter, .get_board_info = qlcnic_83xx_get_port_info, @@ -1118,6 +1116,100 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter, return err; } +static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test) +{ + struct qlcnic_adapter *adapter = netdev_priv(netdev); + struct qlcnic_host_sds_ring *sds_ring; + struct qlcnic_host_rds_ring *rds_ring; + u8 ring; + int ret; + + netif_device_detach(netdev); + + if (netif_running(netdev)) + __qlcnic_down(adapter, netdev); + + qlcnic_detach(adapter); + + adapter->max_sds_rings = 1; + adapter->ahw->diag_test = test; + adapter->ahw->linkup = 0; + + ret = qlcnic_attach(adapter); + if (ret) { + netif_device_attach(netdev); + return ret; + } + + ret = qlcnic_fw_create_ctx(adapter); + if (ret) { + qlcnic_detach(adapter); + netif_device_attach(netdev); + return ret; + } + + for (ring = 0; ring < adapter->max_rds_rings; ring++) { + rds_ring = &adapter->recv_ctx->rds_rings[ring]; + qlcnic_post_rx_buffers(adapter, rds_ring, ring); + } + + if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) { + for (ring = 0; ring < adapter->max_sds_rings; ring++) { + sds_ring = &adapter->recv_ctx->sds_rings[ring]; + qlcnic_83xx_enable_intr(adapter, sds_ring); + } + } + + if (adapter->ahw->diag_test == QLCNIC_LOOPBACK_TEST) { + /* disable and free mailbox interrupt */ + qlcnic_83xx_free_mbx_intr(adapter); + adapter->ahw->loopback_state = 0; + adapter->ahw->hw_ops->setup_link_event(adapter, 1); + } + + set_bit(__QLCNIC_DEV_UP, &adapter->state); + return 0; +} + +static void qlcnic_83xx_diag_free_res(struct net_device *netdev, + int max_sds_rings) +{ + struct qlcnic_adapter *adapter = netdev_priv(netdev); + struct qlcnic_host_sds_ring *sds_ring; + int ring, err; + + clear_bit(__QLCNIC_DEV_UP, &adapter->state); + if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) { + for (ring = 0; ring < adapter->max_sds_rings; ring++) { + sds_ring = &adapter->recv_ctx->sds_rings[ring]; + writel(1, sds_ring->crb_intr_mask); + } + } + + qlcnic_fw_destroy_ctx(adapter); + qlcnic_detach(adapter); + + if (adapter->ahw->diag_test == QLCNIC_LOOPBACK_TEST) { + err = qlcnic_83xx_setup_mbx_intr(adapter); + if (err) { + dev_err(&adapter->pdev->dev, + "%s: failed to setup mbx interrupt\n", + __func__); + goto out; + } + } + adapter->ahw->diag_test = 0; + adapter->max_sds_rings = max_sds_rings; + + if (qlcnic_attach(adapter)) + goto out; + + if (netif_running(netdev)) + __qlcnic_up(adapter, netdev); +out: + netif_device_attach(netdev); +} + int qlcnic_83xx_config_led(struct qlcnic_adapter *adapter, u32 state, u32 beacon) { @@ -1265,6 +1357,57 @@ int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode) return err; } +int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode) +{ + struct qlcnic_adapter *adapter = netdev_priv(netdev); + struct qlcnic_hardware_context *ahw = adapter->ahw; + int ret = 0, loop = 0, max_sds_rings = adapter->max_sds_rings; + + QLCDB(adapter, DRV, "%s loopback test in progress\n", + mode == QLCNIC_ILB_MODE ? "internal" : "external"); + if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { + dev_warn(&adapter->pdev->dev, + "Loopback test not supported for non privilege function\n"); + return ret; + } + + if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) + return -EBUSY; + + ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST); + if (ret) + goto fail_diag_alloc; + + ret = qlcnic_83xx_set_lb_mode(adapter, mode); + if (ret) + goto free_diag_res; + + /* Poll for link up event before running traffic */ + do { + msleep(500); + qlcnic_83xx_process_aen(adapter); + if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) { + dev_info(&adapter->pdev->dev, + "Firmware didn't sent link up event to loopback request\n"); + ret = -QLCNIC_FW_NOT_RESPOND; + qlcnic_83xx_clear_lb_mode(adapter, mode); + goto free_diag_res; + } + } while ((adapter->ahw->linkup && ahw->has_link_events) != 1); + + ret = qlcnic_do_lb_test(adapter, mode); + + qlcnic_83xx_clear_lb_mode(adapter, mode); + +free_diag_res: + qlcnic_83xx_diag_free_res(netdev, max_sds_rings); + +fail_diag_alloc: + adapter->max_sds_rings = max_sds_rings; + clear_bit(__QLCNIC_RESETTING, &adapter->state); + return ret; +} + int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode) { struct qlcnic_hardware_context *ahw = adapter->ahw; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h index 16c5df626616..87f2e08c31f4 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h @@ -428,6 +428,7 @@ int qlcnic_83xx_test_link(struct qlcnic_adapter *); int qlcnic_83xx_reg_test(struct qlcnic_adapter *); int qlcnic_83xx_get_regs_len(struct qlcnic_adapter *); int qlcnic_83xx_get_registers(struct qlcnic_adapter *, u32 *); +int qlcnic_83xx_loopback_test(struct net_device *, u8); int qlcnic_83xx_interrupt_test(struct qlcnic_adapter *, struct qlcnic_cmd_args *); int qlcnic_83xx_flash_test(struct qlcnic_adapter *); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index 6320d551c63d..58e255437d5a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -883,7 +883,7 @@ int qlcnic_check_loopback_buff(unsigned char *data, u8 mac[]) return memcmp(data, buff, QLCNIC_ILB_PKT_SIZE); } -static int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode) +int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode) { struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; struct qlcnic_host_sds_ring *sds_ring = &recv_ctx->sds_rings[0]; @@ -925,7 +925,7 @@ static int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode) return 0; } -static int qlcnic_loopback_test(struct net_device *netdev, u8 mode) +int qlcnic_loopback_test(struct net_device *netdev, u8 mode) { struct qlcnic_adapter *adapter = netdev_priv(netdev); int max_sds_rings = adapter->max_sds_rings; @@ -935,13 +935,14 @@ static int qlcnic_loopback_test(struct net_device *netdev, u8 mode) int ret; if (qlcnic_83xx_check(adapter)) - goto skip_cap; + return qlcnic_83xx_loopback_test(netdev, mode); + if (!(ahw->capabilities & QLCNIC_FW_CAPABILITY_MULTI_LOOPBACK)) { dev_info(&adapter->pdev->dev, "Firmware do not support loopback test\n"); return -EOPNOTSUPP; } -skip_cap: + dev_warn(&adapter->pdev->dev, "%s loopback test in progress\n", mode == QLCNIC_ILB_MODE ? "internal" : "external"); if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { @@ -962,9 +963,6 @@ static int qlcnic_loopback_test(struct net_device *netdev, u8 mode) if (ret) goto free_res; - if (qlcnic_83xx_check(adapter)) - goto skip_fw_msg; - ahw->diag_cnt = 0; do { msleep(500); @@ -979,21 +977,9 @@ static int qlcnic_loopback_test(struct net_device *netdev, u8 mode) goto free_res; } } while (!QLCNIC_IS_LB_CONFIGURED(ahw->loopback_state)); -skip_fw_msg: - if (qlcnic_83xx_check(adapter)) { - /* wait until firmware report link up before running traffic */ - loop = 0; - do { - msleep(500); - if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) { - dev_info(&adapter->pdev->dev, - "No linkup event after LB req\n"); - ret = -QLCNIC_FW_NOT_RESPOND; - goto free_res; - } - } while ((adapter->ahw->linkup && ahw->has_link_events) != 1); - } + ret = qlcnic_do_lb_test(adapter, mode); + qlcnic_clear_lb_mode(adapter, mode); free_res: diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index dcb990d3d559..30aa1f295224 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -1503,10 +1503,7 @@ void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings) if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) { for (ring = 0; ring < adapter->max_sds_rings; ring++) { sds_ring = &adapter->recv_ctx->sds_rings[ring]; - if (qlcnic_83xx_check(adapter)) - writel(1, sds_ring->crb_intr_mask); - else - qlcnic_disable_int(sds_ring); + qlcnic_disable_int(sds_ring); } } @@ -1599,10 +1596,7 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test) if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) { for (ring = 0; ring < adapter->max_sds_rings; ring++) { sds_ring = &adapter->recv_ctx->sds_rings[ring]; - if (qlcnic_82xx_check(adapter)) - qlcnic_enable_int(sds_ring); - else - qlcnic_83xx_enable_intr(adapter, sds_ring); + qlcnic_enable_int(sds_ring); } }