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 @@ ...@@ -60,7 +60,14 @@
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/mii.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. * Configure parameters for buffers per controller.
* If the machine this is being used on is a faster machine (i.e. > 150MHz) * 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); ...@@ -984,14 +991,13 @@ extern unsigned char e100_cu_unknown_state(struct e100_private *bdp);
#define TEST_TIMEOUT 0x08 #define TEST_TIMEOUT 0x08
enum test_offsets { enum test_offsets {
E100_EEPROM_TEST_FAIL = 0, test_link,
E100_CHIP_TIMEOUT, test_eeprom,
E100_ROM_TEST_FAIL, test_self_test,
E100_REG_TEST_FAIL, test_loopback_mac,
E100_MAC_TEST_FAIL, test_loopback_phy,
E100_LPBK_MAC_FAIL, cable_diag,
E100_LPBK_PHY_FAIL, max_test_res, /* must be last */
E100_MAX_TEST_RES
}; };
#endif #endif
...@@ -121,14 +121,13 @@ extern void e100_config_wol(struct e100_private *bdp); ...@@ -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); 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_test(struct net_device *, struct ifreq *);
static int e100_ethtool_gstrings(struct net_device *, struct ifreq *); static int e100_ethtool_gstrings(struct net_device *, struct ifreq *);
static char *test_strings[] = { static char test_strings[][ETH_GSTRING_LEN] = {
"E100_EEPROM_TEST_FAIL", "Link test (on/offline)",
"E100_CHIP_TIMEOUT", "Eeprom test (on/offline)",
"E100_ROM_TEST_FAIL", "Self test (offline)",
"E100_REG_TEST_FAIL", "Mac loopback (offline)",
"E100_MAC_TEST_FAIL", "Phy loopback (offline)",
"E100_LPBK_MAC_FAIL", "Cable diagnostic (offline)"
"E100_LPBK_PHY_FAIL"
}; };
static int e100_ethtool_led_blink(struct net_device *, struct ifreq *); 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) ...@@ -3500,14 +3499,14 @@ e100_ethtool_test(struct net_device *dev, struct ifreq *ifr)
struct ethtool_test *info; struct ethtool_test *info;
int rc = -EFAULT; int rc = -EFAULT;
info = kmalloc(sizeof(*info) + E100_MAX_TEST_RES * sizeof(u64), info = kmalloc(sizeof(*info) + max_test_res * sizeof(u64),
GFP_ATOMIC); GFP_ATOMIC);
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
memset((void *) info, 0, sizeof(*info) + 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))) if (copy_from_user(info, ifr->ifr_data, sizeof(*info)))
goto exit; goto exit;
...@@ -3515,7 +3514,7 @@ e100_ethtool_test(struct net_device *dev, struct ifreq *ifr) ...@@ -3515,7 +3514,7 @@ e100_ethtool_test(struct net_device *dev, struct ifreq *ifr)
info->flags = e100_run_diag(dev, info->data, info->flags); info->flags = e100_run_diag(dev, info->data, info->flags);
if (!copy_to_user(ifr->ifr_data, info, 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; rc = 0;
exit: exit:
kfree(info); kfree(info);
...@@ -3590,7 +3589,7 @@ e100_ethtool_get_drvinfo(struct net_device *dev, struct ifreq *ifr) ...@@ -3590,7 +3589,7 @@ e100_ethtool_get_drvinfo(struct net_device *dev, struct ifreq *ifr)
info.n_stats = E100_STATS_LEN; info.n_stats = E100_STATS_LEN;
info.regdump_len = E100_REGS_LEN * sizeof(u32); info.regdump_len = E100_REGS_LEN * sizeof(u32);
info.eedump_len = (bdp->eeprom_size << 1); 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))) if (copy_to_user(ifr->ifr_data, &info, sizeof (info)))
return -EFAULT; return -EFAULT;
...@@ -3940,15 +3939,15 @@ static int e100_ethtool_gstrings(struct net_device *dev, struct ifreq *ifr) ...@@ -3940,15 +3939,15 @@ static int e100_ethtool_gstrings(struct net_device *dev, struct ifreq *ifr)
switch (info.string_set) { switch (info.string_set) {
case ETH_SS_TEST: { case ETH_SS_TEST: {
int ret = 0; int ret = 0;
if (info.len > E100_MAX_TEST_RES) if (info.len > max_test_res)
info.len = E100_MAX_TEST_RES; info.len = max_test_res;
strings = kmalloc(info.len * ETH_GSTRING_LEN, GFP_ATOMIC); strings = kmalloc(info.len * ETH_GSTRING_LEN, GFP_ATOMIC);
if (!strings) if (!strings)
return -ENOMEM; return -ENOMEM;
memset(strings, 0, info.len * ETH_GSTRING_LEN); memset(strings, 0, info.len * ETH_GSTRING_LEN);
for (i = 0; i < info.len; i++) { for (i = 0; i < info.len; i++) {
sprintf(strings + i * ETH_GSTRING_LEN, "%-31s", sprintf(strings + i * ETH_GSTRING_LEN, "%s",
test_strings[i]); test_strings[i]);
} }
if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) if (copy_to_user(ifr->ifr_data, &info, sizeof (info)))
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/ *******************************************************************************/
#include "e100.h" #include "e100_phy.h"
#include "e100_config.h" #include "e100_config.h"
extern u16 e100_eeprom_read(struct e100_private *, u16); extern u16 e100_eeprom_read(struct e100_private *, u16);
...@@ -46,6 +46,7 @@ static u8 e100_diag_loopback_alloc(struct e100_private *); ...@@ -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 void e100_diag_loopback_cu_ru_exec(struct e100_private *);
static u8 e100_diag_check_pkt(u8 *); static u8 e100_diag_check_pkt(u8 *);
static void e100_diag_loopback_free(struct e100_private *); static void e100_diag_loopback_free(struct e100_private *);
static int e100_cable_diag(struct e100_private *bdp);
#define LB_PACKET_SIZE 1500 #define LB_PACKET_SIZE 1500
...@@ -60,46 +61,52 @@ u32 ...@@ -60,46 +61,52 @@ u32
e100_run_diag(struct net_device *dev, u64 *test_info, u32 flags) e100_run_diag(struct net_device *dev, u64 *test_info, u32 flags)
{ {
struct e100_private* bdp = dev->priv; struct e100_private* bdp = dev->priv;
u8 test_result = true; u8 test_result = 0;
e100_isolate_driver(bdp);
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) { if (flags & ETH_TEST_FL_OFFLINE) {
u8 fail_mask; u8 fail_mask;
if (netif_running(dev)) {
fail_mask = e100_diag_selftest(dev); spin_lock_bh(&dev->xmit_lock);
if (fail_mask) { e100_close(dev);
test_result = false; spin_unlock_bh(&dev->xmit_lock);
if (fail_mask & REGISTER_TEST_FAIL) }
test_info [E100_REG_TEST_FAIL] = true; if (e100_diag_selftest(dev)) {
if (fail_mask & ROM_TEST_FAIL) test_result = ETH_TEST_FL_FAILED;
test_info [E100_ROM_TEST_FAIL] = true; test_info[test_self_test] = 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;
} }
fail_mask = e100_diag_loopback(dev); fail_mask = e100_diag_loopback(dev);
if (fail_mask) { if (fail_mask) {
test_result = false; test_result = ETH_TEST_FL_FAILED;
if (fail_mask & PHY_LOOPBACK) if (fail_mask & PHY_LOOPBACK)
test_info [E100_LPBK_PHY_FAIL] = true; test_info[test_loopback_phy] = true;
if (fail_mask & MAC_LOOPBACK) if (fail_mask & MAC_LOOPBACK)
test_info [E100_LPBK_MAC_FAIL] = true; test_info[test_loopback_mac] = true;
}
} }
if (!e100_diag_eeprom(dev)) { test_info[cable_diag] = e100_cable_diag(bdp);
test_result = false; /* Need hw init regardless of netif_running */
test_info [E100_EEPROM_TEST_FAIL] = true; 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); return flags | test_result;
schedule_timeout(HZ * 2);
e100_deisolate_driver(bdp, false);
return flags | (test_result ? 0 : ETH_TEST_FL_FAILED);
} }
/** /**
...@@ -126,8 +133,6 @@ e100_diag_selftest(struct net_device *dev) ...@@ -126,8 +133,6 @@ e100_diag_selftest(struct net_device *dev)
} }
} }
e100_configure_device(bdp);
return retval; return retval;
} }
...@@ -165,14 +170,14 @@ e100_diag_loopback (struct net_device *dev) ...@@ -165,14 +170,14 @@ e100_diag_loopback (struct net_device *dev)
u8 rc = 0; u8 rc = 0;
printk(KERN_DEBUG "%s: PHY loopback test starts\n", dev->name); 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)) { if (!e100_diag_one_loopback(dev, PHY_LOOPBACK)) {
rc |= PHY_LOOPBACK; rc |= PHY_LOOPBACK;
} }
printk(KERN_DEBUG "%s: PHY loopback test ends\n", dev->name); printk(KERN_DEBUG "%s: PHY loopback test ends\n", dev->name);
printk(KERN_DEBUG "%s: MAC loopback test starts\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)) { if (!e100_diag_one_loopback(dev, MAC_LOOPBACK)) {
rc |= MAC_LOOPBACK; rc |= MAC_LOOPBACK;
} }
...@@ -257,14 +262,9 @@ e100_diag_config_loopback(struct e100_private* bdp, ...@@ -257,14 +262,9 @@ e100_diag_config_loopback(struct e100_private* bdp,
if (set_loopback) if (set_loopback)
/* Set PHY loopback mode */ /* Set PHY loopback mode */
e100_phy_set_loopback(bdp); 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 else
/* Reset PHY and force speed and duplex */ /* Reset PHY loopback mode */
e100_force_speed_duplex(bdp); e100_phy_reset(bdp);
}
/* Wait for PHY state change */ /* Wait for PHY state change */
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ); schedule_timeout(HZ);
...@@ -348,10 +348,6 @@ static void ...@@ -348,10 +348,6 @@ static void
e100_diag_loopback_cu_ru_exec(struct e100_private *bdp) e100_diag_loopback_cu_ru_exec(struct e100_private *bdp)
{ {
/*load CU & RU base */ /*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)) if(!e100_wait_exec_cmplx(bdp, bdp->loopback.dma_handle, SCB_RUC_START, 0))
printk(KERN_ERR "e100: SCB_RUC_START failed!\n"); printk(KERN_ERR "e100: SCB_RUC_START failed!\n");
...@@ -433,3 +429,72 @@ e100_diag_loopback_free (struct e100_private *bdp) ...@@ -433,3 +429,72 @@ e100_diag_loopback_free (struct e100_private *bdp)
bdp->loopback.dma_handle); 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