Commit 3ec4a3a4 authored by Scott Feldman's avatar Scott Feldman Committed by Jeff Garzik

[PATCH] Add ethtool cable diag test

* Feature Add: ethtool cable diag test.
* Some cleanup of the ethtool diags.
* Fixed bug in return code for ethtool diag results.
parent 383d8ed6
......@@ -60,7 +60,14 @@
#include <linux/if_vlan.h>
#include <linux/mii.h>
#define E100_REGS_LEN 1
#define E100_CABLE_UNKNOWN 0
#define E100_CABLE_OK 1
#define E100_CABLE_OPEN_NEAR 2 /* Open Circuit Near End */
#define E100_CABLE_OPEN_FAR 3 /* Open Circuit Far End */
#define E100_CABLE_SHORT_NEAR 4 /* Short Circuit Near End */
#define E100_CABLE_SHORT_FAR 5 /* Short Circuit Far End */
#define E100_REGS_LEN 2
/*
* Configure parameters for buffers per controller.
* If the machine this is being used on is a faster machine (i.e. > 150MHz)
......@@ -984,14 +991,13 @@ extern unsigned char e100_cu_unknown_state(struct e100_private *bdp);
#define TEST_TIMEOUT 0x08
enum test_offsets {
E100_EEPROM_TEST_FAIL = 0,
E100_CHIP_TIMEOUT,
E100_ROM_TEST_FAIL,
E100_REG_TEST_FAIL,
E100_MAC_TEST_FAIL,
E100_LPBK_MAC_FAIL,
E100_LPBK_PHY_FAIL,
E100_MAX_TEST_RES
test_link,
test_eeprom,
test_self_test,
test_loopback_mac,
test_loopback_phy,
cable_diag,
max_test_res, /* must be last */
};
#endif
......@@ -121,14 +121,13 @@ extern void e100_config_wol(struct e100_private *bdp);
extern u32 e100_run_diag(struct net_device *dev, u64 *test_info, u32 flags);
static int e100_ethtool_test(struct net_device *, struct ifreq *);
static int e100_ethtool_gstrings(struct net_device *, struct ifreq *);
static char *test_strings[] = {
"E100_EEPROM_TEST_FAIL",
"E100_CHIP_TIMEOUT",
"E100_ROM_TEST_FAIL",
"E100_REG_TEST_FAIL",
"E100_MAC_TEST_FAIL",
"E100_LPBK_MAC_FAIL",
"E100_LPBK_PHY_FAIL"
static char test_strings[][ETH_GSTRING_LEN] = {
"Link test (on/offline)",
"Eeprom test (on/offline)",
"Self test (offline)",
"Mac loopback (offline)",
"Phy loopback (offline)",
"Cable diagnostic (offline)"
};
static int e100_ethtool_led_blink(struct net_device *, struct ifreq *);
......@@ -3500,14 +3499,14 @@ e100_ethtool_test(struct net_device *dev, struct ifreq *ifr)
struct ethtool_test *info;
int rc = -EFAULT;
info = kmalloc(sizeof(*info) + E100_MAX_TEST_RES * sizeof(u64),
info = kmalloc(sizeof(*info) + max_test_res * sizeof(u64),
GFP_ATOMIC);
if (!info)
return -ENOMEM;
memset((void *) info, 0, sizeof(*info) +
E100_MAX_TEST_RES * sizeof(u64));
max_test_res * sizeof(u64));
if (copy_from_user(info, ifr->ifr_data, sizeof(*info)))
goto exit;
......@@ -3515,7 +3514,7 @@ e100_ethtool_test(struct net_device *dev, struct ifreq *ifr)
info->flags = e100_run_diag(dev, info->data, info->flags);
if (!copy_to_user(ifr->ifr_data, info,
sizeof(*info) + E100_MAX_TEST_RES * sizeof(u64)))
sizeof(*info) + max_test_res * sizeof(u64)))
rc = 0;
exit:
kfree(info);
......@@ -3590,7 +3589,7 @@ e100_ethtool_get_drvinfo(struct net_device *dev, struct ifreq *ifr)
info.n_stats = E100_STATS_LEN;
info.regdump_len = E100_REGS_LEN * sizeof(u32);
info.eedump_len = (bdp->eeprom_size << 1);
info.testinfo_len = E100_MAX_TEST_RES;
info.testinfo_len = max_test_res;
if (copy_to_user(ifr->ifr_data, &info, sizeof (info)))
return -EFAULT;
......@@ -3940,15 +3939,15 @@ static int e100_ethtool_gstrings(struct net_device *dev, struct ifreq *ifr)
switch (info.string_set) {
case ETH_SS_TEST: {
int ret = 0;
if (info.len > E100_MAX_TEST_RES)
info.len = E100_MAX_TEST_RES;
if (info.len > max_test_res)
info.len = max_test_res;
strings = kmalloc(info.len * ETH_GSTRING_LEN, GFP_ATOMIC);
if (!strings)
return -ENOMEM;
memset(strings, 0, info.len * ETH_GSTRING_LEN);
for (i = 0; i < info.len; i++) {
sprintf(strings + i * ETH_GSTRING_LEN, "%-31s",
sprintf(strings + i * ETH_GSTRING_LEN, "%s",
test_strings[i]);
}
if (copy_to_user(ifr->ifr_data, &info, sizeof (info)))
......
......@@ -25,7 +25,7 @@
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
#include "e100.h"
#include "e100_phy.h"
#include "e100_config.h"
extern u16 e100_eeprom_read(struct e100_private *, u16);
......@@ -46,6 +46,7 @@ static u8 e100_diag_loopback_alloc(struct e100_private *);
static void e100_diag_loopback_cu_ru_exec(struct e100_private *);
static u8 e100_diag_check_pkt(u8 *);
static void e100_diag_loopback_free(struct e100_private *);
static int e100_cable_diag(struct e100_private *bdp);
#define LB_PACKET_SIZE 1500
......@@ -60,46 +61,52 @@ u32
e100_run_diag(struct net_device *dev, u64 *test_info, u32 flags)
{
struct e100_private* bdp = dev->priv;
u8 test_result = true;
e100_isolate_driver(bdp);
u8 test_result = 0;
if (!e100_get_link_state(bdp)) {
test_result = ETH_TEST_FL_FAILED;
test_info[test_link] = true;
}
if (!e100_diag_eeprom(dev)) {
test_result = ETH_TEST_FL_FAILED;
test_info[test_eeprom] = true;
}
if (flags & ETH_TEST_FL_OFFLINE) {
u8 fail_mask;
fail_mask = e100_diag_selftest(dev);
if (fail_mask) {
test_result = false;
if (fail_mask & REGISTER_TEST_FAIL)
test_info [E100_REG_TEST_FAIL] = true;
if (fail_mask & ROM_TEST_FAIL)
test_info [E100_ROM_TEST_FAIL] = true;
if (fail_mask & SELF_TEST_FAIL)
test_info [E100_MAC_TEST_FAIL] = true;
if (fail_mask & TEST_TIMEOUT)
test_info [E100_CHIP_TIMEOUT] = true;
if (netif_running(dev)) {
spin_lock_bh(&dev->xmit_lock);
e100_close(dev);
spin_unlock_bh(&dev->xmit_lock);
}
if (e100_diag_selftest(dev)) {
test_result = ETH_TEST_FL_FAILED;
test_info[test_self_test] = true;
}
fail_mask = e100_diag_loopback(dev);
if (fail_mask) {
test_result = false;
test_result = ETH_TEST_FL_FAILED;
if (fail_mask & PHY_LOOPBACK)
test_info [E100_LPBK_PHY_FAIL] = true;
test_info[test_loopback_phy] = true;
if (fail_mask & MAC_LOOPBACK)
test_info [E100_LPBK_MAC_FAIL] = true;
test_info[test_loopback_mac] = true;
}
}
if (!e100_diag_eeprom(dev)) {
test_result = false;
test_info [E100_EEPROM_TEST_FAIL] = true;
test_info[cable_diag] = e100_cable_diag(bdp);
/* Need hw init regardless of netif_running */
e100_hw_init(bdp);
if (netif_running(dev)) {
e100_open(dev);
}
}
else {
test_info[test_self_test] = false;
test_info[test_loopback_phy] = false;
test_info[test_loopback_mac] = false;
test_info[cable_diag] = false;
}
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ * 2);
e100_deisolate_driver(bdp, false);
return flags | (test_result ? 0 : ETH_TEST_FL_FAILED);
return flags | test_result;
}
/**
......@@ -126,8 +133,6 @@ e100_diag_selftest(struct net_device *dev)
}
}
e100_configure_device(bdp);
return retval;
}
......@@ -165,14 +170,14 @@ e100_diag_loopback (struct net_device *dev)
u8 rc = 0;
printk(KERN_DEBUG "%s: PHY loopback test starts\n", dev->name);
e100_sw_reset(dev->priv, PORT_SELECTIVE_RESET);
e100_hw_init(dev->priv);
if (!e100_diag_one_loopback(dev, PHY_LOOPBACK)) {
rc |= PHY_LOOPBACK;
}
printk(KERN_DEBUG "%s: PHY loopback test ends\n", dev->name);
printk(KERN_DEBUG "%s: MAC loopback test starts\n", dev->name);
e100_sw_reset(dev->priv, PORT_SELECTIVE_RESET);
e100_hw_init(dev->priv);
if (!e100_diag_one_loopback(dev, MAC_LOOPBACK)) {
rc |= MAC_LOOPBACK;
}
......@@ -257,15 +262,10 @@ e100_diag_config_loopback(struct e100_private* bdp,
if (set_loopback)
/* Set PHY loopback mode */
e100_phy_set_loopback(bdp);
else { /* Back to normal speed and duplex */
if (bdp->params.e100_speed_duplex == E100_AUTONEG)
/* Reset PHY and do autoneg */
e100_phy_autoneg(bdp);
else
/* Reset PHY and force speed and duplex */
e100_force_speed_duplex(bdp);
}
/* Wait for PHY state change */
else
/* Reset PHY loopback mode */
e100_phy_reset(bdp);
/* Wait for PHY state change */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ);
} else { /* For MAC loopback wait 500 msec to take effect */
......@@ -348,10 +348,6 @@ static void
e100_diag_loopback_cu_ru_exec(struct e100_private *bdp)
{
/*load CU & RU base */
if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE, 0))
printk(KERN_ERR "e100: SCB_CUC_LOAD_BASE failed\n");
if(!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE, 0))
printk(KERN_ERR "e100: SCB_RUC_LOAD_BASE failed!\n");
if(!e100_wait_exec_cmplx(bdp, bdp->loopback.dma_handle, SCB_RUC_START, 0))
printk(KERN_ERR "e100: SCB_RUC_START failed!\n");
......@@ -433,3 +429,72 @@ e100_diag_loopback_free (struct e100_private *bdp)
bdp->loopback.dma_handle);
}
static int
e100_cable_diag(struct e100_private *bdp)
{
int saved_open_circut = 0xffff;
int saved_short_circut = 0xffff;
int saved_distance = 0xffff;
int saved_same = 0;
int cable_status = E100_CABLE_UNKNOWN;
int i;
/* If we have link, */
if (e100_get_link_state(bdp))
return E100_CABLE_OK;
if (bdp->rev_id < D102_REV_ID)
return E100_CABLE_UNKNOWN;
/* Disable MDI/MDI-X auto switching */
e100_mdi_write(bdp, MII_NCONFIG, bdp->phy_addr,
MDI_MDIX_RESET_ALL_MASK);
/* Set to 100 Full as required by cable test */
e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr,
BMCR_SPEED100 | BMCR_FULLDPLX);
/* Test up to 100 times */
for (i = 0; i < 100; i++) {
u16 ctrl_reg;
int distance, open_circut, short_circut, near_end;
/* Enable and execute cable test */
e100_mdi_write(bdp, HWI_CONTROL_REG, bdp->phy_addr,
(HWI_TEST_ENABLE | HWI_TEST_EXECUTE));
/* Wait for cable test finished */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/100 + 1);
/* Read results */
e100_mdi_read(bdp, HWI_CONTROL_REG, bdp->phy_addr, &ctrl_reg);
distance = ctrl_reg & HWI_TEST_DISTANCE;
open_circut = ctrl_reg & HWI_TEST_HIGHZ_PROBLEM;
short_circut = ctrl_reg & HWI_TEST_LOWZ_PROBLEM;
if ((distance == saved_distance) &&
(open_circut == saved_open_circut) &&
(short_circut == saved_short_circut))
saved_same++;
else {
saved_same = 0;
saved_distance = distance;
saved_open_circut = open_circut;
saved_short_circut = short_circut;
}
/* If results are the same 3 times */
if (saved_same == 3) {
near_end = ((distance * HWI_REGISTER_GRANULARITY) <
HWI_NEAR_END_BOUNDARY);
if (open_circut)
cable_status = (near_end) ?
E100_CABLE_OPEN_NEAR : E100_CABLE_OPEN_FAR;
if (short_circut)
cable_status = (near_end) ?
E100_CABLE_SHORT_NEAR : E100_CABLE_SHORT_FAR;
break;
}
}
/* Reset cable test */
e100_mdi_write(bdp, HWI_CONTROL_REG, bdp->phy_addr, HWI_RESET_ALL_MASK);
return cable_status;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment