Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 22363
b: refs/heads/master
c: ac62ef0
h: refs/heads/master
i:
  22361: 6d2f984
  22359: 3d21c91
v: v3
  • Loading branch information
Don Fry authored and Jeff Garzik committed Mar 21, 2006
1 parent 880cfac commit 78cd008
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 25 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 8368f31c8f51ef8ba61ce9fff7b94259777b6419
refs/heads/master: ac62ef043504d5c754357325cd514553ddabb046
236 changes: 212 additions & 24 deletions trunk/drivers/net/pcnet32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 "18.Mar.2006"
#define PFX DRV_NAME ": "

static const char * const version =
Expand Down Expand Up @@ -133,7 +133,7 @@ static const char pcnet32_gstrings_test[][ETH_GSTRING_LEN] = {
};
#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];
Expand Down Expand Up @@ -265,6 +265,9 @@ 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 18 Mar2006 Thomas Bogendoerfer and Don Fry added Multi-Phy
* handling for supporting AT-270x FTX cards with FX and Tx PHYs.
* Philippe Seewer assisted with auto negotiation and testing.
*/


Expand Down Expand Up @@ -375,6 +378,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 */
Expand All @@ -384,6 +388,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);
Expand Down Expand Up @@ -415,6 +422,7 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
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 {
Expand Down Expand Up @@ -936,9 +944,14 @@ static int pcnet32_phys_id(struct net_device *dev, u32 data)
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,
Expand Down Expand Up @@ -998,9 +1011,14 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,

/* 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);
}
}
}
}

Expand All @@ -1009,10 +1027,6 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
a->write_csr(ioaddr, 5, 0x0000);
}

i = buff - (u16 *)ptr;
for (; i < PCNET32_NUM_REGS; i++)
*buff++ = 0;

spin_unlock_irqrestore(&lp->lock, flags);
}

Expand Down Expand Up @@ -1185,7 +1199,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
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;
Expand Down Expand Up @@ -1401,8 +1415,34 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
}

/* 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;
Expand Down Expand Up @@ -1625,7 +1665,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,
Expand All @@ -1651,6 +1691,62 @@ 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, 2);
lp->a.write_bcr(ioaddr, 2, val & ~2);
val = lp->a.read_bcr(ioaddr, 32);
lp->a.write_bcr(ioaddr, 32, val & ~(1 << 7)); /* stop MII manager */

if (!(lp->options & PCNET32_PORT_ASEL)) {
/* 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;
if (lp->options & PCNET32_PORT_ASEL) {
mii_ethtool_gset(&lp->mii_if, &ecmd);
ecmd.autoneg = AUTONEG_ENABLE;
}
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
Expand Down Expand Up @@ -1680,11 +1776,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)
Expand Down Expand Up @@ -2430,17 +2524,111 @@ static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return rc;
}

static int pcnet32_check_otherphy(struct net_device *dev)
{
struct pcnet32_private *lp = dev->priv;
struct mii_if_info mii = lp->mii_if;
u16 bmcr;
int i;

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);
}
Expand Down

0 comments on commit 78cd008

Please sign in to comment.