net-forcedeth: Add internal loopback support for forcedeth NICs.
Support enabling/disabling/querying internal loopback mode for forcedeth NICs using ethtool. Signed-off-by: Sanjay Hortikar <horti@google.com> Signed-off-by: Mahesh Bandewar <maheshb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
63ce40e4fd
commit
e19df76a11
|
@ -3003,6 +3003,73 @@ static void nv_update_pause(struct net_device *dev, u32 pause_flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nv_force_linkspeed(struct net_device *dev, int speed, int duplex)
|
||||||
|
{
|
||||||
|
struct fe_priv *np = netdev_priv(dev);
|
||||||
|
u8 __iomem *base = get_hwbase(dev);
|
||||||
|
u32 phyreg, txreg;
|
||||||
|
int mii_status;
|
||||||
|
|
||||||
|
np->linkspeed = NVREG_LINKSPEED_FORCE|speed;
|
||||||
|
np->duplex = duplex;
|
||||||
|
|
||||||
|
/* see if gigabit phy */
|
||||||
|
mii_status = mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
|
||||||
|
if (mii_status & PHY_GIGABIT) {
|
||||||
|
np->gigabit = PHY_GIGABIT;
|
||||||
|
phyreg = readl(base + NvRegSlotTime);
|
||||||
|
phyreg &= ~(0x3FF00);
|
||||||
|
if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10)
|
||||||
|
phyreg |= NVREG_SLOTTIME_10_100_FULL;
|
||||||
|
else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
|
||||||
|
phyreg |= NVREG_SLOTTIME_10_100_FULL;
|
||||||
|
else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
|
||||||
|
phyreg |= NVREG_SLOTTIME_1000_FULL;
|
||||||
|
writel(phyreg, base + NvRegSlotTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
phyreg = readl(base + NvRegPhyInterface);
|
||||||
|
phyreg &= ~(PHY_HALF|PHY_100|PHY_1000);
|
||||||
|
if (np->duplex == 0)
|
||||||
|
phyreg |= PHY_HALF;
|
||||||
|
if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_100)
|
||||||
|
phyreg |= PHY_100;
|
||||||
|
else if ((np->linkspeed & NVREG_LINKSPEED_MASK) ==
|
||||||
|
NVREG_LINKSPEED_1000)
|
||||||
|
phyreg |= PHY_1000;
|
||||||
|
writel(phyreg, base + NvRegPhyInterface);
|
||||||
|
|
||||||
|
if (phyreg & PHY_RGMII) {
|
||||||
|
if ((np->linkspeed & NVREG_LINKSPEED_MASK) ==
|
||||||
|
NVREG_LINKSPEED_1000)
|
||||||
|
txreg = NVREG_TX_DEFERRAL_RGMII_1000;
|
||||||
|
else
|
||||||
|
txreg = NVREG_TX_DEFERRAL_RGMII_10_100;
|
||||||
|
} else {
|
||||||
|
txreg = NVREG_TX_DEFERRAL_DEFAULT;
|
||||||
|
}
|
||||||
|
writel(txreg, base + NvRegTxDeferral);
|
||||||
|
|
||||||
|
if (np->desc_ver == DESC_VER_1) {
|
||||||
|
txreg = NVREG_TX_WM_DESC1_DEFAULT;
|
||||||
|
} else {
|
||||||
|
if ((np->linkspeed & NVREG_LINKSPEED_MASK) ==
|
||||||
|
NVREG_LINKSPEED_1000)
|
||||||
|
txreg = NVREG_TX_WM_DESC2_3_1000;
|
||||||
|
else
|
||||||
|
txreg = NVREG_TX_WM_DESC2_3_DEFAULT;
|
||||||
|
}
|
||||||
|
writel(txreg, base + NvRegTxWatermark);
|
||||||
|
|
||||||
|
writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
|
||||||
|
base + NvRegMisc1);
|
||||||
|
pci_push(base);
|
||||||
|
writel(np->linkspeed, base + NvRegLinkSpeed);
|
||||||
|
pci_push(base);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nv_update_linkspeed: Setup the MAC according to the link partner
|
* nv_update_linkspeed: Setup the MAC according to the link partner
|
||||||
* @dev: Network device to be configured
|
* @dev: Network device to be configured
|
||||||
|
@ -3024,11 +3091,25 @@ static int nv_update_linkspeed(struct net_device *dev)
|
||||||
int newls = np->linkspeed;
|
int newls = np->linkspeed;
|
||||||
int newdup = np->duplex;
|
int newdup = np->duplex;
|
||||||
int mii_status;
|
int mii_status;
|
||||||
|
u32 bmcr;
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
u32 control_1000, status_1000, phyreg, pause_flags, txreg;
|
u32 control_1000, status_1000, phyreg, pause_flags, txreg;
|
||||||
u32 txrxFlags = 0;
|
u32 txrxFlags = 0;
|
||||||
u32 phy_exp;
|
u32 phy_exp;
|
||||||
|
|
||||||
|
/* If device loopback is enabled, set carrier on and enable max link
|
||||||
|
* speed.
|
||||||
|
*/
|
||||||
|
bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
|
||||||
|
if (bmcr & BMCR_LOOPBACK) {
|
||||||
|
if (netif_running(dev)) {
|
||||||
|
nv_force_linkspeed(dev, NVREG_LINKSPEED_1000, 1);
|
||||||
|
if (!netif_carrier_ok(dev))
|
||||||
|
netif_carrier_on(dev);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* BMSR_LSTATUS is latched, read it twice:
|
/* BMSR_LSTATUS is latched, read it twice:
|
||||||
* we want the current value.
|
* we want the current value.
|
||||||
*/
|
*/
|
||||||
|
@ -4455,6 +4536,61 @@ static int nv_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam*
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nv_set_loopback(struct net_device *dev, u32 features)
|
||||||
|
{
|
||||||
|
struct fe_priv *np = netdev_priv(dev);
|
||||||
|
unsigned long flags;
|
||||||
|
u32 miicontrol;
|
||||||
|
int err, retval = 0;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&np->lock, flags);
|
||||||
|
miicontrol = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
|
||||||
|
if (features & NETIF_F_LOOPBACK) {
|
||||||
|
if (miicontrol & BMCR_LOOPBACK) {
|
||||||
|
spin_unlock_irqrestore(&np->lock, flags);
|
||||||
|
netdev_info(dev, "Loopback already enabled\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
nv_disable_irq(dev);
|
||||||
|
/* Turn on loopback mode */
|
||||||
|
miicontrol |= BMCR_LOOPBACK | BMCR_FULLDPLX | BMCR_SPEED1000;
|
||||||
|
err = mii_rw(dev, np->phyaddr, MII_BMCR, miicontrol);
|
||||||
|
if (err) {
|
||||||
|
retval = PHY_ERROR;
|
||||||
|
spin_unlock_irqrestore(&np->lock, flags);
|
||||||
|
phy_init(dev);
|
||||||
|
} else {
|
||||||
|
if (netif_running(dev)) {
|
||||||
|
/* Force 1000 Mbps full-duplex */
|
||||||
|
nv_force_linkspeed(dev, NVREG_LINKSPEED_1000,
|
||||||
|
1);
|
||||||
|
/* Force link up */
|
||||||
|
netif_carrier_on(dev);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&np->lock, flags);
|
||||||
|
netdev_info(dev,
|
||||||
|
"Internal PHY loopback mode enabled.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!(miicontrol & BMCR_LOOPBACK)) {
|
||||||
|
spin_unlock_irqrestore(&np->lock, flags);
|
||||||
|
netdev_info(dev, "Loopback already disabled\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
nv_disable_irq(dev);
|
||||||
|
/* Turn off loopback */
|
||||||
|
spin_unlock_irqrestore(&np->lock, flags);
|
||||||
|
netdev_info(dev, "Internal PHY loopback mode disabled.\n");
|
||||||
|
phy_init(dev);
|
||||||
|
}
|
||||||
|
msleep(500);
|
||||||
|
spin_lock_irqsave(&np->lock, flags);
|
||||||
|
nv_enable_irq(dev);
|
||||||
|
spin_unlock_irqrestore(&np->lock, flags);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
static u32 nv_fix_features(struct net_device *dev, u32 features)
|
static u32 nv_fix_features(struct net_device *dev, u32 features)
|
||||||
{
|
{
|
||||||
/* vlan is dependent on rx checksum offload */
|
/* vlan is dependent on rx checksum offload */
|
||||||
|
@ -4490,6 +4626,13 @@ static int nv_set_features(struct net_device *dev, u32 features)
|
||||||
struct fe_priv *np = netdev_priv(dev);
|
struct fe_priv *np = netdev_priv(dev);
|
||||||
u8 __iomem *base = get_hwbase(dev);
|
u8 __iomem *base = get_hwbase(dev);
|
||||||
u32 changed = dev->features ^ features;
|
u32 changed = dev->features ^ features;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
if ((changed & NETIF_F_LOOPBACK) && netif_running(dev)) {
|
||||||
|
retval = nv_set_loopback(dev, features);
|
||||||
|
if (retval != 0)
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
if (changed & NETIF_F_RXCSUM) {
|
if (changed & NETIF_F_RXCSUM) {
|
||||||
spin_lock_irq(&np->lock);
|
spin_lock_irq(&np->lock);
|
||||||
|
@ -5124,6 +5267,12 @@ static int nv_open(struct net_device *dev)
|
||||||
|
|
||||||
spin_unlock_irq(&np->lock);
|
spin_unlock_irq(&np->lock);
|
||||||
|
|
||||||
|
/* If the loopback feature was set while the device was down, make sure
|
||||||
|
* that it's set correctly now.
|
||||||
|
*/
|
||||||
|
if (dev->features & NETIF_F_LOOPBACK)
|
||||||
|
nv_set_loopback(dev, dev->features);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
out_drain:
|
out_drain:
|
||||||
nv_drain_rxtx(dev);
|
nv_drain_rxtx(dev);
|
||||||
|
@ -5328,6 +5477,9 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
|
||||||
|
|
||||||
dev->features |= dev->hw_features;
|
dev->features |= dev->hw_features;
|
||||||
|
|
||||||
|
/* Add loopback capability to the device. */
|
||||||
|
dev->hw_features |= NETIF_F_LOOPBACK;
|
||||||
|
|
||||||
np->pause_flags = NV_PAUSEFRAME_RX_CAPABLE | NV_PAUSEFRAME_RX_REQ | NV_PAUSEFRAME_AUTONEG;
|
np->pause_flags = NV_PAUSEFRAME_RX_CAPABLE | NV_PAUSEFRAME_RX_REQ | NV_PAUSEFRAME_AUTONEG;
|
||||||
if ((id->driver_data & DEV_HAS_PAUSEFRAME_TX_V1) ||
|
if ((id->driver_data & DEV_HAS_PAUSEFRAME_TX_V1) ||
|
||||||
(id->driver_data & DEV_HAS_PAUSEFRAME_TX_V2) ||
|
(id->driver_data & DEV_HAS_PAUSEFRAME_TX_V2) ||
|
||||||
|
@ -5603,12 +5755,14 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
|
||||||
dev_info(&pci_dev->dev, "ifname %s, PHY OUI 0x%x @ %d, addr %pM\n",
|
dev_info(&pci_dev->dev, "ifname %s, PHY OUI 0x%x @ %d, addr %pM\n",
|
||||||
dev->name, np->phy_oui, np->phyaddr, dev->dev_addr);
|
dev->name, np->phy_oui, np->phyaddr, dev->dev_addr);
|
||||||
|
|
||||||
dev_info(&pci_dev->dev, "%s%s%s%s%s%s%s%s%s%sdesc-v%u\n",
|
dev_info(&pci_dev->dev, "%s%s%s%s%s%s%s%s%s%s%sdesc-v%u\n",
|
||||||
dev->features & NETIF_F_HIGHDMA ? "highdma " : "",
|
dev->features & NETIF_F_HIGHDMA ? "highdma " : "",
|
||||||
dev->features & (NETIF_F_IP_CSUM | NETIF_F_SG) ?
|
dev->features & (NETIF_F_IP_CSUM | NETIF_F_SG) ?
|
||||||
"csum " : "",
|
"csum " : "",
|
||||||
dev->features & (NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX) ?
|
dev->features & (NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX) ?
|
||||||
"vlan " : "",
|
"vlan " : "",
|
||||||
|
dev->features & (NETIF_F_LOOPBACK) ?
|
||||||
|
"loopback " : "",
|
||||||
id->driver_data & DEV_HAS_POWER_CNTRL ? "pwrctl " : "",
|
id->driver_data & DEV_HAS_POWER_CNTRL ? "pwrctl " : "",
|
||||||
id->driver_data & DEV_HAS_MGMT_UNIT ? "mgmt " : "",
|
id->driver_data & DEV_HAS_MGMT_UNIT ? "mgmt " : "",
|
||||||
id->driver_data & DEV_NEED_TIMERIRQ ? "timirq " : "",
|
id->driver_data & DEV_NEED_TIMERIRQ ? "timirq " : "",
|
||||||
|
|
Loading…
Reference in New Issue