On Mon, Feb 27, 2006 at 11:45:51AM +0100, Seewer Philippe wrote:
> OK, found it.
> 
> The code in pcnet32_open assigns XCVR_INTERNAL to ecmd.port instead of 
> ecmd.transceiver, making mii_ethtool_sset fail and leaving all phys 
> configured to defaults.
> 
> On the Allied Telesyn FTX cards this means to use autoneg on tp when 
> default is fiber and 10/half on fiber when tp is default. Thats why the 
> fiber port didn't work, because it only supports 100Mbit. Overriding the 
> speed/duplex with ethtool makes it work though.
> 
> Assigning XCRV_INTERNAL to the correct attribute introduces a new 
> problem: Since all options are ignored the code forces all phys to 
> 10/half, so ethtool is needed again to reconfigure the settings.
> 
> Changing the option override in probe1 to |= works and configures all 
> phy's with the correct options (except for autoneg of course, which is 
> still specifically ignored).
> 
> I don't know whether it was the intention of the patch to force usage of 
> ethtool or not... Any suggestions?
> 
> Regards
> Philippe Seewer

I get the same results in my testing.  Good catch on the coding error
for ecmd.transciever.  Without adding some more specific code to support
the Allied Telesyn cards I don`t know how to get things to work without
using ethtool.

Maybe someone else has a better idea.

Here is my current patch for you to review.  I would like to submit it
for 2.6.17.

--- linux-2.6.16-rc5/drivers/net/pcnet32.c.orig Mon Jan  2 19:21:10 2006
+++ linux-2.6.16-rc5/drivers/net/pcnet32.c      Wed Mar  8 15:38:01 2006
@@ -22,8 +22,8 @@
  *************************************************************************/
 
 #define DRV_NAME       "pcnet32"
-#define DRV_VERSION    "1.31c"
-#define DRV_RELDATE    "01.Nov.2005"
+#define DRV_VERSION    "1.32"
+#define DRV_RELDATE    "08.Mar.2006"
 #define PFX            DRV_NAME ": "
 
 static const char *version =
@@ -133,7 +133,7 @@ static const char pcnet32_gstrings_test[
 };
 #define PCNET32_TEST_LEN (sizeof(pcnet32_gstrings_test) / ETH_GSTRING_LEN)
 
-#define PCNET32_NUM_REGS 168
+#define PCNET32_NUM_REGS 136
 
 #define MAX_UNITS 8    /* More are supported, limit only on options */
 static int options[MAX_UNITS];
@@ -265,6 +265,8 @@ static int homepna[MAX_UNITS];
  * v1.31c  01 Nov 2005 Don Fry Allied Telesyn 2700/2701 FX are 100Mbit only.
  *        Force 100Mbit FD if Auto (ASEL) is selected.
  *        See Bugzilla 2669 and 4551.
+ * v1.32   08 Mar20065 Thomas Bogendoerfer and Don Fry added Multi-Phy
+ *        handling for supporting AT-270x FTX cards with FX and Tx PHYs
  */
 
 
@@ -375,6 +377,7 @@ struct pcnet32_private {
     unsigned int       dirty_rx, dirty_tx; /* The ring entries to be free()ed. 
*/
     struct net_device_stats stats;
     char               tx_full;
+    char               phycount;       /* number of phys found */
     int                        options;
     unsigned int       shared_irq:1,   /* shared irq possible */
                        dxsuflo:1,      /* disable transmit stop on uflo */
@@ -384,6 +387,9 @@ struct pcnet32_private {
     struct timer_list  watchdog_timer;
     struct timer_list  blink_timer;
     u32                        msg_enable;     /* debug message level */
+
+    /* each bit indicates an available PHY */
+    u32                        phymask;
 };
 
 static void pcnet32_probe_vlbus(void);
@@ -415,6 +421,7 @@ static void pcnet32_get_regs(struct net_
 static void pcnet32_purge_tx_ring(struct net_device *dev);
 static int pcnet32_alloc_ring(struct net_device *dev, char *name);
 static void pcnet32_free_ring(struct net_device *dev);
+static void pcnet32_check_media(struct net_device *dev, int verbose);
 
 
 enum pci_flags_bit {
@@ -936,9 +943,14 @@ static int pcnet32_phys_id(struct net_de
     return 0;
 }
 
+#define PCNET32_REGS_PER_PHY   32
+#define PCNET32_MAX_PHYS       32
 static int pcnet32_get_regs_len(struct net_device *dev)
 {
-    return(PCNET32_NUM_REGS * sizeof(u16));
+    struct pcnet32_private *lp = dev->priv;
+    int j = lp->phycount * PCNET32_REGS_PER_PHY;
+
+    return((PCNET32_NUM_REGS + j) * sizeof(u16));
 }
 
 static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
@@ -998,9 +1010,14 @@ static void pcnet32_get_regs(struct net_
 
     /* read mii phy registers */
     if (lp->mii) {
-       for (i=0; i<32; i++) {
-           lp->a.write_bcr(ioaddr, 33, ((lp->mii_if.phy_id) << 5) | i);
-           *buff++ = lp->a.read_bcr(ioaddr, 34);
+       int j;
+       for (j=0; j<PCNET32_MAX_PHYS; j++) {
+           if (lp->phymask & (1 << j)) {
+               for (i=0; i<PCNET32_REGS_PER_PHY; i++) {
+                   lp->a.write_bcr(ioaddr, 33, (j << 5) | i);
+                   *buff++ = lp->a.read_bcr(ioaddr, 34);
+               }
+           }
        }
     }
 
@@ -1009,10 +1026,6 @@ static void pcnet32_get_regs(struct net_
        a->write_csr(ioaddr, 5, 0x0000);
     }
 
-    i = buff - (u16 *)ptr;
-    for (; i < PCNET32_NUM_REGS; i++)
-       *buff++ = 0;
-
     spin_unlock_irqrestore(&lp->lock, flags);
 }
 
@@ -1185,7 +1198,7 @@ pcnet32_probe1(unsigned long ioaddr, int
        if (cards_found < MAX_UNITS && homepna[cards_found])
            media |= 1;         /* switch to home wiring mode */
        if (pcnet32_debug & NETIF_MSG_PROBE)
-           printk(KERN_DEBUG PFX "media set to %sMbit mode.\n", 
+           printk(KERN_DEBUG PFX "media set to %sMbit mode.\n",
                    (media & 1) ? "1" : "10");
        a->write_bcr(ioaddr, 49, media);
        break;
@@ -1401,8 +1414,34 @@ pcnet32_probe1(unsigned long ioaddr, int
     }
 
     /* Set the mii phy_id so that we can query the link state */
-    if (lp->mii)
+    if (lp->mii) {
+       /* lp->phycount and lp->phymask are set to 0 by memset above */
+
        lp->mii_if.phy_id = ((lp->a.read_bcr (ioaddr, 33)) >> 5) & 0x1f;
+       /* scan for PHYs */
+       for (i=0; i<PCNET32_MAX_PHYS; i++) {
+           unsigned short id1, id2;
+
+           id1 = mdio_read(dev, i, MII_PHYSID1);
+           if (id1 == 0xffff)
+               continue;
+           id2 = mdio_read(dev, i, MII_PHYSID2);
+           if (id2 == 0xffff)
+               continue;
+           if (i == 31 && ((chip_version + 1) & 0xfffe) == 0x2624)
+               continue;       /* 79C971 & 79C972 have phantom phy at id 31 */
+           lp->phycount++;
+           lp->phymask |= (1 << i);
+           lp->mii_if.phy_id = i;
+           if (pcnet32_debug & NETIF_MSG_PROBE)
+               printk(KERN_INFO PFX "Found PHY %04x:%04x at address %d.\n",
+                       id1, id2, i);
+       }
+       lp->a.write_bcr(ioaddr, 33, (lp->mii_if.phy_id) << 5);
+       if (lp->phycount > 1) {
+           lp->options |= PCNET32_PORT_MII;
+       }
+    }
 
     init_timer (&lp->watchdog_timer);
     lp->watchdog_timer.data = (unsigned long) dev;
@@ -1625,7 +1664,7 @@ pcnet32_open(struct net_device *dev)
                        dev->name);
        }
     }
-    {
+    if (lp->phycount < 2) {
        /*
         * 24 Jun 2004 according AMD, in order to change the PHY,
         * DANAS (or DISPM for 79C976) must be set; then select the speed,
@@ -1651,6 +1690,54 @@ pcnet32_open(struct net_device *dev)
                lp->a.write_bcr(ioaddr, 32, val);
            }
        }
+    } else {
+       int first_phy = -1;
+       u16 bmcr;
+       u32 bcr9;
+       struct ethtool_cmd ecmd;
+
+       /*
+        * There is really no good other way to handle multiple PHYs
+        * other than turning off all automatics
+        */
+       val = lp->a.read_bcr(ioaddr, 32);
+       lp->a.write_bcr(ioaddr, 32, val & ~(1 << 7)); /* stop MII manager */
+
+       /* setup ecmd */
+       ecmd.port = PORT_MII;
+       ecmd.transceiver = XCVR_INTERNAL;
+       ecmd.autoneg = AUTONEG_DISABLE;
+       ecmd.speed = lp->options & PCNET32_PORT_100 ? SPEED_100 : SPEED_10;
+       bcr9 = lp->a.read_bcr(ioaddr, 9);
+
+       if (lp->options & PCNET32_PORT_FD) {
+           ecmd.duplex = DUPLEX_FULL;
+           bcr9 |= (1 << 0);
+       } else {
+           ecmd.duplex = DUPLEX_HALF;
+           bcr9 |= ~(1 << 0);
+       }
+       lp->a.write_bcr(ioaddr, 9, bcr9);
+
+       for (i=0; i<PCNET32_MAX_PHYS; i++) {
+           if (lp->phymask & (1 << i)) {
+               /* isolate all but the first PHY */
+               bmcr = mdio_read(dev, i, MII_BMCR);
+               if (first_phy == -1) {
+                   first_phy = i;
+                   mdio_write(dev, i, MII_BMCR, bmcr & ~BMCR_ISOLATE);
+               } else {
+                   mdio_write(dev, i, MII_BMCR, bmcr | BMCR_ISOLATE);
+               }
+               /* use mii_ethtool_sset to setup PHY */
+               lp->mii_if.phy_id = i;
+               ecmd.phy_address = i;
+               mii_ethtool_sset(&lp->mii_if, &ecmd);
+           }
+       }
+       lp->mii_if.phy_id = first_phy;
+       if (netif_msg_link(lp))
+           printk(KERN_INFO "%s: Using PHY number %d.\n", dev->name, 
first_phy);
     }
 
 #ifdef DO_DXSUFLO
@@ -1680,11 +1767,9 @@ pcnet32_open(struct net_device *dev)
 
     netif_start_queue(dev);
 
-    /* If we have mii, print the link status and start the watchdog */
-    if (lp->mii) {
-       mii_check_media (&lp->mii_if, netif_msg_link(lp), 1);
-       mod_timer (&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT);
-    }
+    /* Print the link status and start the watchdog */
+    pcnet32_check_media (dev, 1);
+    mod_timer (&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT);
 
     i = 0;
     while (i++ < 100)
@@ -2430,17 +2515,113 @@ static int pcnet32_ioctl(struct net_devi
     return rc;
 }
 
+static int pcnet32_check_otherphy(struct net_device *dev)
+{
+    struct pcnet32_private *lp = dev->priv;
+    struct mii_if_info mii;
+    u16 bmcr;
+    int i;
+
+    mii = lp->mii_if;
+
+    for (i = 0; i < PCNET32_MAX_PHYS; i++) {
+       if (i == lp->mii_if.phy_id)
+           continue; /* skip active phy */
+       if (lp->phymask & (1 << i)) {
+           mii.phy_id = i;
+           if (mii_link_ok(&mii)) {
+               /* found PHY with active link */
+               if (netif_msg_link(lp))
+                   printk(KERN_INFO "%s: Using PHY number %d.\n", dev->name, 
i);
+
+               /* isolate inactive phy */
+               bmcr = mdio_read(dev, lp->mii_if.phy_id, MII_BMCR);
+               mdio_write(dev, lp->mii_if.phy_id, MII_BMCR, bmcr | 
BMCR_ISOLATE);
+
+               /* de-isolate new phy */
+               bmcr = mdio_read(dev, i, MII_BMCR);
+               mdio_write(dev, i, MII_BMCR, bmcr & ~BMCR_ISOLATE);
+
+               /* set new phy address */
+               lp->mii_if.phy_id = i;
+               return 1;
+           }
+       }
+    }
+    return 0;
+}
+
+/*
+ * Show the status of the media.  Similar to mii_check_media however it
+ * correctly shows the link speed for all (tested) pcnet32 variants.
+ * Devices with no mii just report link state without speed.
+ *
+ * Caller is assumed to hold and release the lp->lock.
+ */
+
+static void pcnet32_check_media(struct net_device *dev, int verbose)
+{
+    struct pcnet32_private *lp = dev->priv;
+    int curr_link;
+    int prev_link = netif_carrier_ok(dev) ? 1 : 0;
+    u32 bcr9;
+
+    if (lp->mii) {
+       curr_link = mii_link_ok(&lp->mii_if);
+    } else {
+       ulong ioaddr = dev->base_addr;  /* card base I/O address */
+       curr_link = (lp->a.read_bcr(ioaddr, 4) != 0xc0);
+    }
+    if (!curr_link) {
+       if (prev_link || verbose) {
+           netif_carrier_off(dev);
+           if (netif_msg_link(lp))
+               printk(KERN_INFO "%s: link down\n", dev->name);
+       }
+       if (lp->phycount > 1) {
+           curr_link = pcnet32_check_otherphy(dev);
+           prev_link = 0;
+       }
+    } else if (verbose || !prev_link) {
+       netif_carrier_on(dev);
+       if (lp->mii) {
+           if (netif_msg_link(lp)) {
+               struct ethtool_cmd ecmd;
+               mii_ethtool_gset(&lp->mii_if, &ecmd);
+               printk(KERN_INFO "%s: link up, %sMbps, %s-duplex\n",
+                   dev->name,
+                   (ecmd.speed == SPEED_100) ? "100" : "10",
+                   (ecmd.duplex == DUPLEX_FULL) ? "full" : "half");
+           }
+           bcr9 = lp->a.read_bcr(dev->base_addr, 9);
+           if ((bcr9 & (1 << 0)) != lp->mii_if.full_duplex)   {
+               if (lp->mii_if.full_duplex)
+                   bcr9 |= (1 << 0);
+               else
+                   bcr9 &= ~(1 << 0);
+               lp->a.write_bcr(dev->base_addr, 9, bcr9);
+           }
+       } else {
+           if (netif_msg_link(lp))
+               printk(KERN_INFO "%s: link up\n", dev->name);
+       }
+    }
+}
+
+/*
+ * Check for loss of link and link establishment.
+ * Can not use mii_check_media because it does nothing if mode is forced.
+ */
+
 static void pcnet32_watchdog(struct net_device *dev)
 {
     struct pcnet32_private *lp = dev->priv;
     unsigned long flags;
 
     /* Print the link status if it has changed */
-    if (lp->mii) {
-       spin_lock_irqsave(&lp->lock, flags);
-       mii_check_media (&lp->mii_if, netif_msg_link(lp), 0);
-       spin_unlock_irqrestore(&lp->lock, flags);
-    }
+    spin_lock_irqsave(&lp->lock, flags);
+    pcnet32_check_media(dev, 0);
+    spin_unlock_irqrestore(&lp->lock, flags);
 
     mod_timer (&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT);
 }


-- 
Don Fry
[EMAIL PROTECTED]
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to