This patch adds flow control support for tx and rx pause frames in
forcedeth.

Signed-Off-By: Ayaz Abdulla <[EMAIL PROTECTED]>




-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may 
contain
confidential information.  Any unauthorized review, use, disclosure or 
distribution
is prohibited.  If you are not the intended recipient, please contact the 
sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------
--- orig-2.6/drivers/net/forcedeth.c    2006-02-09 17:19:26.000000000 -0500
+++ new-2.6/drivers/net/forcedeth.c     2006-02-09 17:19:30.000000000 -0500
@@ -105,6 +105,7 @@
  *     0.50: 20 Jan 2006: Add 8021pq tagging support.
  *     0.51: 20 Jan 2006: Add 64bit consistent memory allocation for rings.
  *     0.52: 20 Jan 2006: Add MSI/MSIX support.
+ *     0.53: 20 Jan 2006: Add flow control (pause frame).
  *
  * Known bugs:
  * We suspect that on some hardware no TX done interrupts are generated.
@@ -116,7 +117,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.52"
+#define FORCEDETH_VERSION              "0.53"
 #define DRV_NAME                       "forcedeth"
 
 #include <linux/module.h>
@@ -159,6 +160,7 @@
 #define DEV_HAS_VLAN            0x0020  /* device supports vlan tagging and 
striping */
 #define DEV_HAS_MSI             0x0040  /* device supports MSI */
 #define DEV_HAS_MSI_X           0x0080  /* device supports MSI-X */
+#define DEV_HAS_PAUSEFRAME_TX   0x0100  /* device supports tx pause frames */
 
 enum {
        NvRegIrqStatus = 0x000,
@@ -199,6 +201,7 @@
        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
 
@@ -208,7 +211,8 @@
 #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
 
@@ -271,6 +275,9 @@
 #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
@@ -494,13 +501,10 @@
 #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
@@ -588,6 +592,9 @@
        /* msi/msi-x fields */
        u32 msi_flags;
        struct msix_entry msi_x_entry[NV_MSI_X_MAX_VECTORS];
+
+       /* flow control */
+       u32 pause_flags;
 };
 
 /*
@@ -780,7 +787,7 @@
 
        /* 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;
@@ -793,14 +800,14 @@
        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;
                }
@@ -838,6 +845,8 @@
                        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);
@@ -1451,7 +1460,6 @@
        u32 Flags;
        u32 vlanflags = 0;
 
-
        for (;;) {
                struct sk_buff *skb;
                int len;
@@ -1818,7 +1826,9 @@
 {
        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;
@@ -1871,8 +1881,8 @@
 
        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)) {
@@ -1890,21 +1900,21 @@
                                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;
        }
@@ -1947,6 +1957,56 @@
        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;
 }
 
@@ -2356,7 +2416,7 @@
        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;
        }
@@ -2420,23 +2480,23 @@
 
                /* 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);
@@ -2449,22 +2509,22 @@
                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);
@@ -2619,6 +2679,9 @@
 
        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);
@@ -2877,7 +2940,7 @@
        unsigned long addr;
        u8 __iomem *base;
        int err, i;
-
+       
        dev = alloc_etherdev(sizeof(struct fe_priv));
        err = -ENOMEM;
        if (!dev)
@@ -2984,6 +3047,12 @@
                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, NV_PCI_REGSZ);
        if (!np->base)
@@ -3230,11 +3299,11 @@
        },
        {       /* 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,
+               .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_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,
+               .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_PAUSEFRAME_TX,
        },
        {0,},
 };


Reply via email to