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
|
||||
* @dev: Network device to be configured
|
||||
|
@ -3024,11 +3091,25 @@ static int nv_update_linkspeed(struct net_device *dev)
|
|||
int newls = np->linkspeed;
|
||||
int newdup = np->duplex;
|
||||
int mii_status;
|
||||
u32 bmcr;
|
||||
int retval = 0;
|
||||
u32 control_1000, status_1000, phyreg, pause_flags, txreg;
|
||||
u32 txrxFlags = 0;
|
||||
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:
|
||||
* we want the current value.
|
||||
*/
|
||||
|
@ -4455,6 +4536,61 @@ static int nv_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam*
|
|||
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)
|
||||
{
|
||||
/* 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);
|
||||
u8 __iomem *base = get_hwbase(dev);
|
||||
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) {
|
||||
spin_lock_irq(&np->lock);
|
||||
|
@ -5124,6 +5267,12 @@ static int nv_open(struct net_device *dev)
|
|||
|
||||
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;
|
||||
out_drain:
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
if ((id->driver_data & DEV_HAS_PAUSEFRAME_TX_V1) ||
|
||||
(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->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_IP_CSUM | NETIF_F_SG) ?
|
||||
"csum " : "",
|
||||
dev->features & (NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX) ?
|
||||
"vlan " : "",
|
||||
dev->features & (NETIF_F_LOOPBACK) ?
|
||||
"loopback " : "",
|
||||
id->driver_data & DEV_HAS_POWER_CNTRL ? "pwrctl " : "",
|
||||
id->driver_data & DEV_HAS_MGMT_UNIT ? "mgmt " : "",
|
||||
id->driver_data & DEV_NEED_TIMERIRQ ? "timirq " : "",
|
||||
|
|
Loading…
Reference in a new issue