aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorDon Fry <brazilnut@us.ibm.com>2006-03-20 18:26:03 -0500
committerJeff Garzik <jeff@garzik.org>2006-03-21 16:00:53 -0500
commitac62ef043504d5c754357325cd514553ddabb046 (patch)
treeb1c45d98f238852370d09b5cc2f37625005b9442 /drivers/net
parent8368f31c8f51ef8ba61ce9fff7b94259777b6419 (diff)
[PATCH] pcnet32: support boards with multiple phys
Boards with multiple PHYs were not being handled properly by the pcnet32 driver. This patch by Thomas Bogendoerfer with changes by me will allow Allied Telesyn 2700FTX and 2701FTX boards to use either the copper or the fiber interfaces. It has been tested on ia32 and ppc64 hardware. Philippe Seewer also tested and improved the patch. ethtool for pcnet32 already supports multiple phys. See also bugzilla bug 4219. Please apply to 2.6.16 Signed-off-by: Don Fry <brazilnut@us.ibm.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/pcnet32.c236
1 files changed, 212 insertions, 24 deletions
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index 7e900572eaf..1bc3f5bffb9 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -22,8 +22,8 @@
22 *************************************************************************/ 22 *************************************************************************/
23 23
24#define DRV_NAME "pcnet32" 24#define DRV_NAME "pcnet32"
25#define DRV_VERSION "1.31c" 25#define DRV_VERSION "1.32"
26#define DRV_RELDATE "01.Nov.2005" 26#define DRV_RELDATE "18.Mar.2006"
27#define PFX DRV_NAME ": " 27#define PFX DRV_NAME ": "
28 28
29static const char * const version = 29static const char * const version =
@@ -133,7 +133,7 @@ static const char pcnet32_gstrings_test[][ETH_GSTRING_LEN] = {
133}; 133};
134#define PCNET32_TEST_LEN (sizeof(pcnet32_gstrings_test) / ETH_GSTRING_LEN) 134#define PCNET32_TEST_LEN (sizeof(pcnet32_gstrings_test) / ETH_GSTRING_LEN)
135 135
136#define PCNET32_NUM_REGS 168 136#define PCNET32_NUM_REGS 136
137 137
138#define MAX_UNITS 8 /* More are supported, limit only on options */ 138#define MAX_UNITS 8 /* More are supported, limit only on options */
139static int options[MAX_UNITS]; 139static int options[MAX_UNITS];
@@ -265,6 +265,9 @@ static int homepna[MAX_UNITS];
265 * v1.31c 01 Nov 2005 Don Fry Allied Telesyn 2700/2701 FX are 100Mbit only. 265 * v1.31c 01 Nov 2005 Don Fry Allied Telesyn 2700/2701 FX are 100Mbit only.
266 * Force 100Mbit FD if Auto (ASEL) is selected. 266 * Force 100Mbit FD if Auto (ASEL) is selected.
267 * See Bugzilla 2669 and 4551. 267 * See Bugzilla 2669 and 4551.
268 * v1.32 18 Mar2006 Thomas Bogendoerfer and Don Fry added Multi-Phy
269 * handling for supporting AT-270x FTX cards with FX and Tx PHYs.
270 * Philippe Seewer assisted with auto negotiation and testing.
268 */ 271 */
269 272
270 273
@@ -375,6 +378,7 @@ struct pcnet32_private {
375 unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ 378 unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
376 struct net_device_stats stats; 379 struct net_device_stats stats;
377 char tx_full; 380 char tx_full;
381 char phycount; /* number of phys found */
378 int options; 382 int options;
379 unsigned int shared_irq:1, /* shared irq possible */ 383 unsigned int shared_irq:1, /* shared irq possible */
380 dxsuflo:1, /* disable transmit stop on uflo */ 384 dxsuflo:1, /* disable transmit stop on uflo */
@@ -384,6 +388,9 @@ struct pcnet32_private {
384 struct timer_list watchdog_timer; 388 struct timer_list watchdog_timer;
385 struct timer_list blink_timer; 389 struct timer_list blink_timer;
386 u32 msg_enable; /* debug message level */ 390 u32 msg_enable; /* debug message level */
391
392 /* each bit indicates an available PHY */
393 u32 phymask;
387}; 394};
388 395
389static void pcnet32_probe_vlbus(void); 396static void pcnet32_probe_vlbus(void);
@@ -415,6 +422,7 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
415static void pcnet32_purge_tx_ring(struct net_device *dev); 422static void pcnet32_purge_tx_ring(struct net_device *dev);
416static int pcnet32_alloc_ring(struct net_device *dev, char *name); 423static int pcnet32_alloc_ring(struct net_device *dev, char *name);
417static void pcnet32_free_ring(struct net_device *dev); 424static void pcnet32_free_ring(struct net_device *dev);
425static void pcnet32_check_media(struct net_device *dev, int verbose);
418 426
419 427
420enum pci_flags_bit { 428enum pci_flags_bit {
@@ -936,9 +944,14 @@ static int pcnet32_phys_id(struct net_device *dev, u32 data)
936 return 0; 944 return 0;
937} 945}
938 946
947#define PCNET32_REGS_PER_PHY 32
948#define PCNET32_MAX_PHYS 32
939static int pcnet32_get_regs_len(struct net_device *dev) 949static int pcnet32_get_regs_len(struct net_device *dev)
940{ 950{
941 return(PCNET32_NUM_REGS * sizeof(u16)); 951 struct pcnet32_private *lp = dev->priv;
952 int j = lp->phycount * PCNET32_REGS_PER_PHY;
953
954 return((PCNET32_NUM_REGS + j) * sizeof(u16));
942} 955}
943 956
944static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs, 957static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
@@ -998,9 +1011,14 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
998 1011
999 /* read mii phy registers */ 1012 /* read mii phy registers */
1000 if (lp->mii) { 1013 if (lp->mii) {
1001 for (i=0; i<32; i++) { 1014 int j;
1002 lp->a.write_bcr(ioaddr, 33, ((lp->mii_if.phy_id) << 5) | i); 1015 for (j=0; j<PCNET32_MAX_PHYS; j++) {
1003 *buff++ = lp->a.read_bcr(ioaddr, 34); 1016 if (lp->phymask & (1 << j)) {
1017 for (i=0; i<PCNET32_REGS_PER_PHY; i++) {
1018 lp->a.write_bcr(ioaddr, 33, (j << 5) | i);
1019 *buff++ = lp->a.read_bcr(ioaddr, 34);
1020 }
1021 }
1004 } 1022 }
1005 } 1023 }
1006 1024
@@ -1009,10 +1027,6 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
1009 a->write_csr(ioaddr, 5, 0x0000); 1027 a->write_csr(ioaddr, 5, 0x0000);
1010 } 1028 }
1011 1029
1012 i = buff - (u16 *)ptr;
1013 for (; i < PCNET32_NUM_REGS; i++)
1014 *buff++ = 0;
1015
1016 spin_unlock_irqrestore(&lp->lock, flags); 1030 spin_unlock_irqrestore(&lp->lock, flags);
1017} 1031}
1018 1032
@@ -1185,7 +1199,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
1185 if (cards_found < MAX_UNITS && homepna[cards_found]) 1199 if (cards_found < MAX_UNITS && homepna[cards_found])
1186 media |= 1; /* switch to home wiring mode */ 1200 media |= 1; /* switch to home wiring mode */
1187 if (pcnet32_debug & NETIF_MSG_PROBE) 1201 if (pcnet32_debug & NETIF_MSG_PROBE)
1188 printk(KERN_DEBUG PFX "media set to %sMbit mode.\n", 1202 printk(KERN_DEBUG PFX "media set to %sMbit mode.\n",
1189 (media & 1) ? "1" : "10"); 1203 (media & 1) ? "1" : "10");
1190 a->write_bcr(ioaddr, 49, media); 1204 a->write_bcr(ioaddr, 49, media);
1191 break; 1205 break;
@@ -1401,8 +1415,34 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
1401 } 1415 }
1402 1416
1403 /* Set the mii phy_id so that we can query the link state */ 1417 /* Set the mii phy_id so that we can query the link state */
1404 if (lp->mii) 1418 if (lp->mii) {
1419 /* lp->phycount and lp->phymask are set to 0 by memset above */
1420
1405 lp->mii_if.phy_id = ((lp->a.read_bcr (ioaddr, 33)) >> 5) & 0x1f; 1421 lp->mii_if.phy_id = ((lp->a.read_bcr (ioaddr, 33)) >> 5) & 0x1f;
1422 /* scan for PHYs */
1423 for (i=0; i<PCNET32_MAX_PHYS; i++) {
1424 unsigned short id1, id2;
1425
1426 id1 = mdio_read(dev, i, MII_PHYSID1);
1427 if (id1 == 0xffff)
1428 continue;
1429 id2 = mdio_read(dev, i, MII_PHYSID2);
1430 if (id2 == 0xffff)
1431 continue;
1432 if (i == 31 && ((chip_version + 1) & 0xfffe) == 0x2624)
1433 continue; /* 79C971 & 79C972 have phantom phy at id 31 */
1434 lp->phycount++;
1435 lp->phymask |= (1 << i);
1436 lp->mii_if.phy_id = i;
1437 if (pcnet32_debug & NETIF_MSG_PROBE)
1438 printk(KERN_INFO PFX "Found PHY %04x:%04x at address %d.\n",
1439 id1, id2, i);
1440 }
1441 lp->a.write_bcr(ioaddr, 33, (lp->mii_if.phy_id) << 5);
1442 if (lp->phycount > 1) {
1443 lp->options |= PCNET32_PORT_MII;
1444 }
1445 }
1406 1446
1407 init_timer (&lp->watchdog_timer); 1447 init_timer (&lp->watchdog_timer);
1408 lp->watchdog_timer.data = (unsigned long) dev; 1448 lp->watchdog_timer.data = (unsigned long) dev;
@@ -1625,7 +1665,7 @@ pcnet32_open(struct net_device *dev)
1625 dev->name); 1665 dev->name);
1626 } 1666 }
1627 } 1667 }
1628 { 1668 if (lp->phycount < 2) {
1629 /* 1669 /*
1630 * 24 Jun 2004 according AMD, in order to change the PHY, 1670 * 24 Jun 2004 according AMD, in order to change the PHY,
1631 * DANAS (or DISPM for 79C976) must be set; then select the speed, 1671 * DANAS (or DISPM for 79C976) must be set; then select the speed,
@@ -1651,6 +1691,62 @@ pcnet32_open(struct net_device *dev)
1651 lp->a.write_bcr(ioaddr, 32, val); 1691 lp->a.write_bcr(ioaddr, 32, val);
1652 } 1692 }
1653 } 1693 }
1694 } else {
1695 int first_phy = -1;
1696 u16 bmcr;
1697 u32 bcr9;
1698 struct ethtool_cmd ecmd;
1699
1700 /*
1701 * There is really no good other way to handle multiple PHYs
1702 * other than turning off all automatics
1703 */
1704 val = lp->a.read_bcr(ioaddr, 2);
1705 lp->a.write_bcr(ioaddr, 2, val & ~2);
1706 val = lp->a.read_bcr(ioaddr, 32);
1707 lp->a.write_bcr(ioaddr, 32, val & ~(1 << 7)); /* stop MII manager */
1708
1709 if (!(lp->options & PCNET32_PORT_ASEL)) {
1710 /* setup ecmd */
1711 ecmd.port = PORT_MII;
1712 ecmd.transceiver = XCVR_INTERNAL;
1713 ecmd.autoneg = AUTONEG_DISABLE;
1714 ecmd.speed = lp->options & PCNET32_PORT_100 ? SPEED_100 : SPEED_10;
1715 bcr9 = lp->a.read_bcr(ioaddr, 9);
1716
1717 if (lp->options & PCNET32_PORT_FD) {
1718 ecmd.duplex = DUPLEX_FULL;
1719 bcr9 |= (1 << 0);
1720 } else {
1721 ecmd.duplex = DUPLEX_HALF;
1722 bcr9 |= ~(1 << 0);
1723 }
1724 lp->a.write_bcr(ioaddr, 9, bcr9);
1725 }
1726
1727 for (i=0; i<PCNET32_MAX_PHYS; i++) {
1728 if (lp->phymask & (1 << i)) {
1729 /* isolate all but the first PHY */
1730 bmcr = mdio_read(dev, i, MII_BMCR);
1731 if (first_phy == -1) {
1732 first_phy = i;
1733 mdio_write(dev, i, MII_BMCR, bmcr & ~BMCR_ISOLATE);
1734 } else {
1735 mdio_write(dev, i, MII_BMCR, bmcr | BMCR_ISOLATE);
1736 }
1737 /* use mii_ethtool_sset to setup PHY */
1738 lp->mii_if.phy_id = i;
1739 ecmd.phy_address = i;
1740 if (lp->options & PCNET32_PORT_ASEL) {
1741 mii_ethtool_gset(&lp->mii_if, &ecmd);
1742 ecmd.autoneg = AUTONEG_ENABLE;
1743 }
1744 mii_ethtool_sset(&lp->mii_if, &ecmd);
1745 }
1746 }
1747 lp->mii_if.phy_id = first_phy;
1748 if (netif_msg_link(lp))
1749 printk(KERN_INFO "%s: Using PHY number %d.\n", dev->name, first_phy);
1654 } 1750 }
1655 1751
1656#ifdef DO_DXSUFLO 1752#ifdef DO_DXSUFLO
@@ -1680,11 +1776,9 @@ pcnet32_open(struct net_device *dev)
1680 1776
1681 netif_start_queue(dev); 1777 netif_start_queue(dev);
1682 1778
1683 /* If we have mii, print the link status and start the watchdog */ 1779 /* Print the link status and start the watchdog */
1684 if (lp->mii) { 1780 pcnet32_check_media (dev, 1);
1685 mii_check_media (&lp->mii_if, netif_msg_link(lp), 1); 1781 mod_timer (&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT);
1686 mod_timer (&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT);
1687 }
1688 1782
1689 i = 0; 1783 i = 0;
1690 while (i++ < 100) 1784 while (i++ < 100)
@@ -2430,17 +2524,111 @@ static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
2430 return rc; 2524 return rc;
2431} 2525}
2432 2526
2527static int pcnet32_check_otherphy(struct net_device *dev)
2528{
2529 struct pcnet32_private *lp = dev->priv;
2530 struct mii_if_info mii = lp->mii_if;
2531 u16 bmcr;
2532 int i;
2533
2534 for (i = 0; i < PCNET32_MAX_PHYS; i++) {
2535 if (i == lp->mii_if.phy_id)
2536 continue; /* skip active phy */
2537 if (lp->phymask & (1 << i)) {
2538 mii.phy_id = i;
2539 if (mii_link_ok(&mii)) {
2540 /* found PHY with active link */
2541 if (netif_msg_link(lp))
2542 printk(KERN_INFO "%s: Using PHY number %d.\n", dev->name, i);
2543
2544 /* isolate inactive phy */
2545 bmcr = mdio_read(dev, lp->mii_if.phy_id, MII_BMCR);
2546 mdio_write(dev, lp->mii_if.phy_id, MII_BMCR, bmcr | BMCR_ISOLATE);
2547
2548 /* de-isolate new phy */
2549 bmcr = mdio_read(dev, i, MII_BMCR);
2550 mdio_write(dev, i, MII_BMCR, bmcr & ~BMCR_ISOLATE);
2551
2552 /* set new phy address */
2553 lp->mii_if.phy_id = i;
2554 return 1;
2555 }
2556 }
2557 }
2558 return 0;
2559}
2560
2561/*
2562 * Show the status of the media. Similar to mii_check_media however it
2563 * correctly shows the link speed for all (tested) pcnet32 variants.
2564 * Devices with no mii just report link state without speed.
2565 *
2566 * Caller is assumed to hold and release the lp->lock.
2567 */
2568
2569static void pcnet32_check_media(struct net_device *dev, int verbose)
2570{
2571 struct pcnet32_private *lp = dev->priv;
2572 int curr_link;
2573 int prev_link = netif_carrier_ok(dev) ? 1 : 0;
2574 u32 bcr9;
2575
2576 if (lp->mii) {
2577 curr_link = mii_link_ok(&lp->mii_if);
2578 } else {
2579 ulong ioaddr = dev->base_addr; /* card base I/O address */
2580 curr_link = (lp->a.read_bcr(ioaddr, 4) != 0xc0);
2581 }
2582 if (!curr_link) {
2583 if (prev_link || verbose) {
2584 netif_carrier_off(dev);
2585 if (netif_msg_link(lp))
2586 printk(KERN_INFO "%s: link down\n", dev->name);
2587 }
2588 if (lp->phycount > 1) {
2589 curr_link = pcnet32_check_otherphy(dev);
2590 prev_link = 0;
2591 }
2592 } else if (verbose || !prev_link) {
2593 netif_carrier_on(dev);
2594 if (lp->mii) {
2595 if (netif_msg_link(lp)) {
2596 struct ethtool_cmd ecmd;
2597 mii_ethtool_gset(&lp->mii_if, &ecmd);
2598 printk(KERN_INFO "%s: link up, %sMbps, %s-duplex\n",
2599 dev->name,
2600 (ecmd.speed == SPEED_100) ? "100" : "10",
2601 (ecmd.duplex == DUPLEX_FULL) ? "full" : "half");
2602 }
2603 bcr9 = lp->a.read_bcr(dev->base_addr, 9);
2604 if ((bcr9 & (1 << 0)) != lp->mii_if.full_duplex) {
2605 if (lp->mii_if.full_duplex)
2606 bcr9 |= (1 << 0);
2607 else
2608 bcr9 &= ~(1 << 0);
2609 lp->a.write_bcr(dev->base_addr, 9, bcr9);
2610 }
2611 } else {
2612 if (netif_msg_link(lp))
2613 printk(KERN_INFO "%s: link up\n", dev->name);
2614 }
2615 }
2616}
2617
2618/*
2619 * Check for loss of link and link establishment.
2620 * Can not use mii_check_media because it does nothing if mode is forced.
2621 */
2622
2433static void pcnet32_watchdog(struct net_device *dev) 2623static void pcnet32_watchdog(struct net_device *dev)
2434{ 2624{
2435 struct pcnet32_private *lp = dev->priv; 2625 struct pcnet32_private *lp = dev->priv;
2436 unsigned long flags; 2626 unsigned long flags;
2437 2627
2438 /* Print the link status if it has changed */ 2628 /* Print the link status if it has changed */
2439 if (lp->mii) { 2629 spin_lock_irqsave(&lp->lock, flags);
2440 spin_lock_irqsave(&lp->lock, flags); 2630 pcnet32_check_media(dev, 0);
2441 mii_check_media (&lp->mii_if, netif_msg_link(lp), 0); 2631 spin_unlock_irqrestore(&lp->lock, flags);
2442 spin_unlock_irqrestore(&lp->lock, flags);
2443 }
2444 2632
2445 mod_timer (&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT); 2633 mod_timer (&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT);
2446} 2634}