From: Christopher Lalancette <clalance@redhat.com>
To: linux-kernel@vger.kernel.org
Subject: PATCH: ethtool support for forcedeth for 2.4.33-pre1
Date: Thu, 12 Jan 2006 09:41:25 -0500 [thread overview]
Message-ID: <43C66A95.4080505@redhat.com> (raw)
All,
The patch below adds improved ethtool support for forcedeth for
2.4.33-pre1. The patch was originally written against 2.4.21, but
applies to 2.4.33-pre1 as well. This patch adds support for get/set
settings, get regs, and nway rst.
Chris Lalancette
--- linux-2.4.21/drivers/net/forcedeth.c.v0.30 2006-01-11
16:28:20.557859000 -0500
+++ linux-2.4.21/drivers/net/forcedeth.c 2006-01-11
16:32:32.750417000 -0500
@@ -212,6 +212,7 @@
#define NVREG_LINKSPEED_10 1000
#define NVREG_LINKSPEED_100 100
#define NVREG_LINKSPEED_1000 50
+#define NVREG_LINKSPEED_MASK (0xFFF)
NvRegUnknownSetupReg5 = 0x130,
#define NVREG_UNKSETUP5_BIT31 (1<<31)
NvRegUnknownSetupReg3 = 0x13c,
@@ -443,6 +444,8 @@
int in_shutdown;
u32 linkspeed;
int duplex;
+ int autoneg;
+ int fixed_mode;
int phyaddr;
int wolenabled;
unsigned int phy_oui;
@@ -802,11 +805,238 @@
return 0;
}
+static int nv_update_linkspeed(struct net_device *dev);
+static int nv_get_settings(struct net_device *dev, struct ethtool_cmd
*ecmd)
+{
+ struct fe_priv *np = get_nvpriv(dev);
+ int adv;
+
+ spin_lock_irq(&np->lock);
+ ecmd->port = PORT_MII;
+ if (!netif_running(dev)) {
+ /* We do not track link speed / duplex setting if the
+ * interface is disabled. Force a link check */
+ nv_update_linkspeed(dev);
+ }
+ switch(np->linkspeed & (NVREG_LINKSPEED_MASK)) {
+ case NVREG_LINKSPEED_10:
+ ecmd->speed = SPEED_10;
+ break;
+ case NVREG_LINKSPEED_100:
+ ecmd->speed = SPEED_100;
+ break;
+ case NVREG_LINKSPEED_1000:
+ ecmd->speed = SPEED_1000;
+ break;
+ }
+ ecmd->duplex = DUPLEX_HALF;
+ if (np->duplex)
+ ecmd->duplex = DUPLEX_FULL;
+
+ ecmd->autoneg = np->autoneg;
+
+ ecmd->advertising = ADVERTISED_MII;
+ if (np->autoneg) {
+ ecmd->advertising |= ADVERTISED_Autoneg;
+ adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
+ } else {
+ adv = np->fixed_mode;
+ }
+
+ if (adv & ADVERTISE_10HALF)
+ ecmd->advertising |= ADVERTISED_10baseT_Half;
+ if (adv & ADVERTISE_10FULL)
+ ecmd->advertising |= ADVERTISED_10baseT_Full;
+ if (adv & ADVERTISE_100HALF)
+ ecmd->advertising |= ADVERTISED_100baseT_Half;
+ 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);
+ if (adv & ADVERTISE_1000FULL)
+ ecmd->advertising |= ADVERTISED_1000baseT_Full;
+ }
+
+ ecmd->supported = (SUPPORTED_Autoneg |
+ SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+ SUPPORTED_MII);
+ if (np->gigabit == PHY_GIGABIT)
+ ecmd->supported |= SUPPORTED_1000baseT_Full;
+
+ ecmd->phy_address = np->phyaddr;
+ ecmd->transceiver = XCVR_EXTERNAL;
+
+ /* ignore maxtxpkt, maxrxpkt for now */
+ spin_unlock_irq(&np->lock);
+ return 0;
+}
+
+static void nv_linkchange(struct net_device *dev);
+static int nv_set_settings(struct net_device *dev, struct ethtool_cmd
*ecmd)
+{
+ struct fe_priv *np = get_nvpriv(dev);
+
+ if (ecmd->port != PORT_MII)
+ return -EINVAL;
+ if (ecmd->transceiver != XCVR_EXTERNAL)
+ return -EINVAL;
+ if (ecmd->phy_address != np->phyaddr) {
+ /* TODO: support switching between multiple phys. Should be
+ * trivial, but not enabled due to lack of test hardware. */
+ return -EINVAL;
+ }
+ if (ecmd->autoneg == AUTONEG_ENABLE) {
+ u32 mask;
+
+ mask = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
+ if (np->gigabit == PHY_GIGABIT)
+ mask |= ADVERTISED_1000baseT_Full;
+
+ if ((ecmd->advertising & mask) == 0)
+ return -EINVAL;
+
+ } else if (ecmd->autoneg == AUTONEG_DISABLE) {
+ /* Note: autonegotiation disable, speed 1000 intentionally
+ * forbidden - noone should need that. */
+
+ if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100)
+ return -EINVAL;
+ if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&np->lock);
+ if (ecmd->autoneg == AUTONEG_ENABLE) {
+ int adv, bmcr;
+
+ np->autoneg = 1;
+
+ /* advertise only what has been requested */
+ adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+ if (ecmd->advertising & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (ecmd->advertising & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (ecmd->advertising & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (ecmd->advertising & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ 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 &= ~ADVERTISE_1000FULL;
+ if (ecmd->advertising & ADVERTISED_1000baseT_Full)
+ adv |= ADVERTISE_1000FULL;
+ mii_rw(dev, np->phyaddr, MII_1000BT_CR, adv);
+ }
+
+ bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
+ bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
+
+ } else {
+ int adv, bmcr;
+
+ np->autoneg = 0;
+
+ adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+ if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_HALF)
+ adv |= ADVERTISE_10HALF;
+ if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_FULL)
+ adv |= ADVERTISE_10FULL;
+ if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_HALF)
+ adv |= ADVERTISE_100HALF;
+ if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_FULL)
+ adv |= ADVERTISE_100FULL;
+ 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 &= ~ADVERTISE_1000FULL;
+ mii_rw(dev, np->phyaddr, MII_1000BT_CR, adv);
+ }
+
+ bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
+ bmcr |= ~(BMCR_ANENABLE|BMCR_SPEED100|BMCR_FULLDPLX);
+ if (adv & (ADVERTISE_10FULL|ADVERTISE_100FULL))
+ bmcr |= BMCR_FULLDPLX;
+ if (adv & (ADVERTISE_100HALF|ADVERTISE_100FULL))
+ bmcr |= BMCR_SPEED100;
+ mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
+
+ if (netif_running(dev)) {
+ /* Wait a bit and then reconfigure the nic. */
+ udelay(10);
+ nv_linkchange(dev);
+ }
+ }
+ spin_unlock_irq(&np->lock);
+
+ return 0;
+}
+
+#define FORCEDETH_REGS_VER 1
+#define FORCEDETH_REGS_SIZE 0x400 /* 256 32-bit registers */
+
+static int nv_get_regs_len(struct net_device *dev)
+{
+ return FORCEDETH_REGS_SIZE;
+}
+
+static void nv_get_regs(struct net_device *dev, struct ethtool_regs
*regs, void *buf)
+{
+ struct fe_priv *np = get_nvpriv(dev);
+ u8 *base = get_hwbase(dev);
+ u32 *rbuf = buf;
+ int i;
+
+ regs->version = FORCEDETH_REGS_VER;
+ spin_lock_irq(&np->lock);
+ for (i=0;i<FORCEDETH_REGS_SIZE/sizeof(u32);i++)
+ rbuf[i] = readl(base + i*sizeof(u32));
+ spin_unlock_irq(&np->lock);
+}
+
+static int nv_nway_reset(struct net_device *dev)
+{
+ struct fe_priv *np = get_nvpriv(dev);
+ int ret;
+
+ spin_lock_irq(&np->lock);
+ if (np->autoneg) {
+ int bmcr;
+
+ bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
+ bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
+
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ spin_unlock_irq(&np->lock);
+
+ return ret;
+}
+
static struct ethtool_ops ops = {
.get_drvinfo = nv_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_wol = nv_get_wol,
.set_wol = nv_set_wol,
+ .get_settings = nv_get_settings,
+ .set_settings = nv_set_settings,
+ .get_regs_len = nv_get_regs_len,
+ .get_regs = nv_get_regs,
+ .nway_reset = nv_nway_reset,
};
/*
@@ -1286,6 +1516,25 @@
goto set_speed;
}
+ if (np->autoneg == 0) {
+ dprintk(KERN_DEBUG "%s: nv_update_linkspeed: autoneg off, PHY
set to 0x%04x.\n",
+ dev->name, np->fixed_mode);
+ if (np->fixed_mode & LPA_100FULL) {
+ newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
+ newdup = 1;
+ } else if (np->fixed_mode & LPA_100HALF) {
+ newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
+ newdup = 0;
+ } else if (np->fixed_mode & LPA_10FULL) {
+ newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
+ newdup = 1;
+ } else {
+ newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
+ newdup = 0;
+ }
+ retval = 1;
+ goto set_speed;
+ }
/* check auto negotiation is complete */
if (!(mii_status & BMSR_ANEGCOMPLETE)) {
/* still in autonegotiation - configure nic for 10 MBit HD and
wait. */
@@ -1362,9 +1611,9 @@
phyreg &= ~(PHY_HALF|PHY_100|PHY_1000);
if (np->duplex == 0)
phyreg |= PHY_HALF;
- if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
+ if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_100)
phyreg |= PHY_100;
- else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
+ else if ((np->linkspeed & NVREG_LINKSPEED_MASK) ==
NVREG_LINKSPEED_1000)
phyreg |= PHY_1000;
writel(phyreg, base + NvRegPhyInterface);
@@ -1865,6 +2114,10 @@
phy_init(dev);
}
+ np->linkspeed = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
+ np->duplex = 0;
+ np->autoneg = 1;
+
err = register_netdev(dev);
if (err) {
printk(KERN_INFO "forcedeth: unable to register netdev: %d\n",
err);
reply other threads:[~2006-01-12 14:41 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=43C66A95.4080505@redhat.com \
--to=clalance@redhat.com \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.