[PATCH] forcedeth: add support for flow control
This patch adds flow control support for tx and rx pause frames in forcedeth. Signed-Off-By: Ayaz Abdulla <aabdulla@nvidia.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
Родитель
48cf270e45
Коммит
eb91f61b22
|
@ -107,6 +107,7 @@
|
|||
* 0.52: 20 Jan 2006: Add MSI/MSIX support.
|
||||
* 0.53: 19 Mar 2006: Fix init from low power mode and add hw reset.
|
||||
* 0.54: 21 Mar 2006: Fix spin locks for multi irqs and cleanup.
|
||||
* 0.55: 22 Mar 2006: Add flow control (pause frame).
|
||||
*
|
||||
* Known bugs:
|
||||
* We suspect that on some hardware no TX done interrupts are generated.
|
||||
|
@ -118,7 +119,7 @@
|
|||
* DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
|
||||
* superfluous timer interrupts from the nic.
|
||||
*/
|
||||
#define FORCEDETH_VERSION "0.54"
|
||||
#define FORCEDETH_VERSION "0.55"
|
||||
#define DRV_NAME "forcedeth"
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -163,6 +164,7 @@
|
|||
#define DEV_HAS_MSI 0x0040 /* device supports MSI */
|
||||
#define DEV_HAS_MSI_X 0x0080 /* device supports MSI-X */
|
||||
#define DEV_HAS_POWER_CNTRL 0x0100 /* device supports power savings */
|
||||
#define DEV_HAS_PAUSEFRAME_TX 0x0200 /* device supports tx pause frames */
|
||||
|
||||
enum {
|
||||
NvRegIrqStatus = 0x000,
|
||||
|
@ -203,6 +205,7 @@ enum {
|
|||
NvRegMSIIrqMask = 0x030,
|
||||
#define NVREG_MSI_VECTOR_0_ENABLED 0x01
|
||||
NvRegMisc1 = 0x080,
|
||||
#define NVREG_MISC1_PAUSE_TX 0x01
|
||||
#define NVREG_MISC1_HD 0x02
|
||||
#define NVREG_MISC1_FORCE 0x3b0f3c
|
||||
|
||||
|
@ -214,7 +217,8 @@ enum {
|
|||
#define NVREG_XMITSTAT_BUSY 0x01
|
||||
|
||||
NvRegPacketFilterFlags = 0x8c,
|
||||
#define NVREG_PFF_ALWAYS 0x7F0008
|
||||
#define NVREG_PFF_PAUSE_RX 0x08
|
||||
#define NVREG_PFF_ALWAYS 0x7F0000
|
||||
#define NVREG_PFF_PROMISC 0x80
|
||||
#define NVREG_PFF_MYADDR 0x20
|
||||
|
||||
|
@ -277,6 +281,9 @@ enum {
|
|||
#define NVREG_TXRXCTL_VLANINS 0x00080
|
||||
NvRegTxRingPhysAddrHigh = 0x148,
|
||||
NvRegRxRingPhysAddrHigh = 0x14C,
|
||||
NvRegTxPauseFrame = 0x170,
|
||||
#define NVREG_TX_PAUSEFRAME_DISABLE 0x1ff0080
|
||||
#define NVREG_TX_PAUSEFRAME_ENABLE 0x0c00030
|
||||
NvRegMIIStatus = 0x180,
|
||||
#define NVREG_MIISTAT_ERROR 0x0001
|
||||
#define NVREG_MIISTAT_LINKCHANGE 0x0008
|
||||
|
@ -506,13 +513,10 @@ typedef union _ring_type {
|
|||
#define PHY_1000 0x2
|
||||
#define PHY_HALF 0x100
|
||||
|
||||
/* FIXME: MII defines that should be added to <linux/mii.h> */
|
||||
#define MII_1000BT_CR 0x09
|
||||
#define MII_1000BT_SR 0x0a
|
||||
#define ADVERTISE_1000FULL 0x0200
|
||||
#define ADVERTISE_1000HALF 0x0100
|
||||
#define LPA_1000FULL 0x0800
|
||||
#define LPA_1000HALF 0x0400
|
||||
#define NV_PAUSEFRAME_RX_CAPABLE 0x0001
|
||||
#define NV_PAUSEFRAME_TX_CAPABLE 0x0002
|
||||
#define NV_PAUSEFRAME_RX_ENABLE 0x0004
|
||||
#define NV_PAUSEFRAME_TX_ENABLE 0x0008
|
||||
|
||||
/* MSI/MSI-X defines */
|
||||
#define NV_MSI_X_MAX_VECTORS 8
|
||||
|
@ -602,6 +606,9 @@ struct fe_priv {
|
|||
/* msi/msi-x fields */
|
||||
u32 msi_flags;
|
||||
struct msix_entry msi_x_entry[NV_MSI_X_MAX_VECTORS];
|
||||
|
||||
/* flow control */
|
||||
u32 pause_flags;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -860,7 +867,7 @@ static int phy_init(struct net_device *dev)
|
|||
|
||||
/* set advertise register */
|
||||
reg = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
|
||||
reg |= (ADVERTISE_10HALF|ADVERTISE_10FULL|ADVERTISE_100HALF|ADVERTISE_100FULL|0x800|0x400);
|
||||
reg |= (ADVERTISE_10HALF|ADVERTISE_10FULL|ADVERTISE_100HALF|ADVERTISE_100FULL|ADVERTISE_PAUSE_ASYM|ADVERTISE_PAUSE_CAP);
|
||||
if (mii_rw(dev, np->phyaddr, MII_ADVERTISE, reg)) {
|
||||
printk(KERN_INFO "%s: phy write to advertise failed.\n", pci_name(np->pci_dev));
|
||||
return PHY_ERROR;
|
||||
|
@ -873,14 +880,14 @@ static int phy_init(struct net_device *dev)
|
|||
mii_status = mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
|
||||
if (mii_status & PHY_GIGABIT) {
|
||||
np->gigabit = PHY_GIGABIT;
|
||||
mii_control_1000 = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
|
||||
mii_control_1000 = mii_rw(dev, np->phyaddr, MII_CTRL1000, MII_READ);
|
||||
mii_control_1000 &= ~ADVERTISE_1000HALF;
|
||||
if (phyinterface & PHY_RGMII)
|
||||
mii_control_1000 |= ADVERTISE_1000FULL;
|
||||
else
|
||||
mii_control_1000 &= ~ADVERTISE_1000FULL;
|
||||
|
||||
if (mii_rw(dev, np->phyaddr, MII_1000BT_CR, mii_control_1000)) {
|
||||
if (mii_rw(dev, np->phyaddr, MII_CTRL1000, mii_control_1000)) {
|
||||
printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
|
||||
return PHY_ERROR;
|
||||
}
|
||||
|
@ -918,6 +925,8 @@ static int phy_init(struct net_device *dev)
|
|||
return PHY_ERROR;
|
||||
}
|
||||
}
|
||||
/* some phys clear out pause advertisment on reset, set it back */
|
||||
mii_rw(dev, np->phyaddr, MII_ADVERTISE, reg);
|
||||
|
||||
/* restart auto negotiation */
|
||||
mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
|
||||
|
@ -1550,7 +1559,6 @@ static void nv_rx_process(struct net_device *dev)
|
|||
u32 Flags;
|
||||
u32 vlanflags = 0;
|
||||
|
||||
|
||||
for (;;) {
|
||||
struct sk_buff *skb;
|
||||
int len;
|
||||
|
@ -1901,7 +1909,9 @@ static int nv_update_linkspeed(struct net_device *dev)
|
|||
{
|
||||
struct fe_priv *np = netdev_priv(dev);
|
||||
u8 __iomem *base = get_hwbase(dev);
|
||||
int adv, lpa;
|
||||
int adv = 0;
|
||||
int lpa = 0;
|
||||
int adv_lpa, adv_pause, lpa_pause;
|
||||
int newls = np->linkspeed;
|
||||
int newdup = np->duplex;
|
||||
int mii_status;
|
||||
|
@ -1954,8 +1964,8 @@ static int nv_update_linkspeed(struct net_device *dev)
|
|||
|
||||
retval = 1;
|
||||
if (np->gigabit == PHY_GIGABIT) {
|
||||
control_1000 = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
|
||||
status_1000 = mii_rw(dev, np->phyaddr, MII_1000BT_SR, MII_READ);
|
||||
control_1000 = mii_rw(dev, np->phyaddr, MII_CTRL1000, MII_READ);
|
||||
status_1000 = mii_rw(dev, np->phyaddr, MII_STAT1000, MII_READ);
|
||||
|
||||
if ((control_1000 & ADVERTISE_1000FULL) &&
|
||||
(status_1000 & LPA_1000FULL)) {
|
||||
|
@ -1973,21 +1983,21 @@ static int nv_update_linkspeed(struct net_device *dev)
|
|||
dev->name, adv, lpa);
|
||||
|
||||
/* FIXME: handle parallel detection properly */
|
||||
lpa = lpa & adv;
|
||||
if (lpa & LPA_100FULL) {
|
||||
adv_lpa = lpa & adv;
|
||||
if (adv_lpa & LPA_100FULL) {
|
||||
newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
|
||||
newdup = 1;
|
||||
} else if (lpa & LPA_100HALF) {
|
||||
} else if (adv_lpa & LPA_100HALF) {
|
||||
newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
|
||||
newdup = 0;
|
||||
} else if (lpa & LPA_10FULL) {
|
||||
} else if (adv_lpa & LPA_10FULL) {
|
||||
newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
|
||||
newdup = 1;
|
||||
} else if (lpa & LPA_10HALF) {
|
||||
} else if (adv_lpa & LPA_10HALF) {
|
||||
newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
|
||||
newdup = 0;
|
||||
} else {
|
||||
dprintk(KERN_DEBUG "%s: bad ability %04x - falling back to 10HD.\n", dev->name, lpa);
|
||||
dprintk(KERN_DEBUG "%s: bad ability %04x - falling back to 10HD.\n", dev->name, adv_lpa);
|
||||
newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
|
||||
newdup = 0;
|
||||
}
|
||||
|
@ -2030,6 +2040,56 @@ set_speed:
|
|||
writel(np->linkspeed, base + NvRegLinkSpeed);
|
||||
pci_push(base);
|
||||
|
||||
/* setup pause frame based on advertisement and link partner */
|
||||
np->pause_flags &= ~(NV_PAUSEFRAME_TX_ENABLE | NV_PAUSEFRAME_RX_ENABLE);
|
||||
|
||||
if (np->duplex != 0) {
|
||||
adv_pause = adv & (ADVERTISE_PAUSE_CAP| ADVERTISE_PAUSE_ASYM);
|
||||
lpa_pause = lpa & (LPA_PAUSE_CAP| LPA_PAUSE_ASYM);
|
||||
|
||||
switch (adv_pause) {
|
||||
case (ADVERTISE_PAUSE_CAP):
|
||||
if (lpa_pause & LPA_PAUSE_CAP) {
|
||||
np->pause_flags |= NV_PAUSEFRAME_TX_ENABLE | NV_PAUSEFRAME_RX_ENABLE;
|
||||
}
|
||||
break;
|
||||
case (ADVERTISE_PAUSE_ASYM):
|
||||
if (lpa_pause == (LPA_PAUSE_CAP| LPA_PAUSE_ASYM))
|
||||
{
|
||||
np->pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
|
||||
}
|
||||
break;
|
||||
case (ADVERTISE_PAUSE_CAP| ADVERTISE_PAUSE_ASYM):
|
||||
if (lpa_pause & LPA_PAUSE_CAP)
|
||||
{
|
||||
np->pause_flags |= NV_PAUSEFRAME_TX_ENABLE | NV_PAUSEFRAME_RX_ENABLE;
|
||||
}
|
||||
if (lpa_pause == LPA_PAUSE_ASYM)
|
||||
{
|
||||
np->pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (np->pause_flags & NV_PAUSEFRAME_RX_CAPABLE) {
|
||||
u32 pff = readl(base + NvRegPacketFilterFlags) & ~NVREG_PFF_PAUSE_RX;
|
||||
if (np->pause_flags & NV_PAUSEFRAME_RX_ENABLE)
|
||||
writel(pff|NVREG_PFF_PAUSE_RX, base + NvRegPacketFilterFlags);
|
||||
else
|
||||
writel(pff, base + NvRegPacketFilterFlags);
|
||||
}
|
||||
if (np->pause_flags & NV_PAUSEFRAME_TX_CAPABLE) {
|
||||
u32 regmisc = readl(base + NvRegMisc1) & ~NVREG_MISC1_PAUSE_TX;
|
||||
if (np->pause_flags & NV_PAUSEFRAME_TX_ENABLE) {
|
||||
writel(NVREG_TX_PAUSEFRAME_ENABLE, base + NvRegTxPauseFrame);
|
||||
writel(regmisc|NVREG_MISC1_PAUSE_TX, base + NvRegMisc1);
|
||||
} else {
|
||||
writel(NVREG_TX_PAUSEFRAME_DISABLE, base + NvRegTxPauseFrame);
|
||||
writel(regmisc, base + NvRegMisc1);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -2441,7 +2501,7 @@ static int nv_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
|
|||
if (adv & ADVERTISE_100FULL)
|
||||
ecmd->advertising |= ADVERTISED_100baseT_Full;
|
||||
if (np->autoneg && np->gigabit == PHY_GIGABIT) {
|
||||
adv = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
|
||||
adv = mii_rw(dev, np->phyaddr, MII_CTRL1000, MII_READ);
|
||||
if (adv & ADVERTISE_1000FULL)
|
||||
ecmd->advertising |= ADVERTISED_1000baseT_Full;
|
||||
}
|
||||
|
@ -2505,23 +2565,23 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
|
|||
|
||||
/* advertise only what has been requested */
|
||||
adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
|
||||
adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
|
||||
adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
|
||||
if (ecmd->advertising & ADVERTISED_10baseT_Half)
|
||||
adv |= ADVERTISE_10HALF;
|
||||
if (ecmd->advertising & ADVERTISED_10baseT_Full)
|
||||
adv |= ADVERTISE_10FULL;
|
||||
adv |= ADVERTISE_10FULL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
|
||||
if (ecmd->advertising & ADVERTISED_100baseT_Half)
|
||||
adv |= ADVERTISE_100HALF;
|
||||
if (ecmd->advertising & ADVERTISED_100baseT_Full)
|
||||
adv |= ADVERTISE_100FULL;
|
||||
adv |= ADVERTISE_100FULL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
|
||||
mii_rw(dev, np->phyaddr, MII_ADVERTISE, adv);
|
||||
|
||||
if (np->gigabit == PHY_GIGABIT) {
|
||||
adv = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
|
||||
adv = mii_rw(dev, np->phyaddr, MII_CTRL1000, MII_READ);
|
||||
adv &= ~ADVERTISE_1000FULL;
|
||||
if (ecmd->advertising & ADVERTISED_1000baseT_Full)
|
||||
adv |= ADVERTISE_1000FULL;
|
||||
mii_rw(dev, np->phyaddr, MII_1000BT_CR, adv);
|
||||
mii_rw(dev, np->phyaddr, MII_CTRL1000, adv);
|
||||
}
|
||||
|
||||
bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
|
||||
|
@ -2534,22 +2594,22 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
|
|||
np->autoneg = 0;
|
||||
|
||||
adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
|
||||
adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
|
||||
adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
|
||||
if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_HALF)
|
||||
adv |= ADVERTISE_10HALF;
|
||||
if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_FULL)
|
||||
adv |= ADVERTISE_10FULL;
|
||||
adv |= ADVERTISE_10FULL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
|
||||
if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_HALF)
|
||||
adv |= ADVERTISE_100HALF;
|
||||
if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_FULL)
|
||||
adv |= ADVERTISE_100FULL;
|
||||
adv |= ADVERTISE_100FULL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
|
||||
mii_rw(dev, np->phyaddr, MII_ADVERTISE, adv);
|
||||
np->fixed_mode = adv;
|
||||
|
||||
if (np->gigabit == PHY_GIGABIT) {
|
||||
adv = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
|
||||
adv = mii_rw(dev, np->phyaddr, MII_CTRL1000, MII_READ);
|
||||
adv &= ~ADVERTISE_1000FULL;
|
||||
mii_rw(dev, np->phyaddr, MII_1000BT_CR, adv);
|
||||
mii_rw(dev, np->phyaddr, MII_CTRL1000, adv);
|
||||
}
|
||||
|
||||
bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
|
||||
|
@ -2813,6 +2873,9 @@ static int nv_open(struct net_device *dev)
|
|||
|
||||
writel(0, base + NvRegAdapterControl);
|
||||
|
||||
if (np->pause_flags & NV_PAUSEFRAME_TX_CAPABLE)
|
||||
writel(NVREG_TX_PAUSEFRAME_DISABLE, base + NvRegTxPauseFrame);
|
||||
|
||||
/* 2) initialize descriptor rings */
|
||||
set_bufsize(dev);
|
||||
oom = nv_init_ring(dev);
|
||||
|
@ -3098,6 +3161,12 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
|
|||
np->msi_flags |= NV_MSI_X_CAPABLE;
|
||||
}
|
||||
|
||||
np->pause_flags = NV_PAUSEFRAME_RX_CAPABLE;
|
||||
if (id->driver_data & DEV_HAS_PAUSEFRAME_TX) {
|
||||
np->pause_flags |= NV_PAUSEFRAME_TX_CAPABLE;
|
||||
}
|
||||
|
||||
|
||||
err = -ENOMEM;
|
||||
np->base = ioremap(addr, np->register_size);
|
||||
if (!np->base)
|
||||
|
@ -3358,11 +3427,11 @@ static struct pci_device_id pci_tbl[] = {
|
|||
},
|
||||
{ /* MCP55 Ethernet Controller */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_14),
|
||||
.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL,
|
||||
.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX,
|
||||
},
|
||||
{ /* MCP55 Ethernet Controller */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_15),
|
||||
.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL,
|
||||
.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX,
|
||||
},
|
||||
{0,},
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче