|
@@ -1115,6 +1115,130 @@ static int link_status_10g_serdes(struct niu *np, int *link_up_p)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int link_status_mii(struct niu *np, int *link_up_p)
|
|
|
+{
|
|
|
+ struct niu_link_config *lp = &np->link_config;
|
|
|
+ int err;
|
|
|
+ int bmsr, advert, ctrl1000, stat1000, lpa, bmcr, estatus;
|
|
|
+ int supported, advertising, active_speed, active_duplex;
|
|
|
+
|
|
|
+ err = mii_read(np, np->phy_addr, MII_BMCR);
|
|
|
+ if (unlikely(err < 0))
|
|
|
+ return err;
|
|
|
+ bmcr = err;
|
|
|
+
|
|
|
+ err = mii_read(np, np->phy_addr, MII_BMSR);
|
|
|
+ if (unlikely(err < 0))
|
|
|
+ return err;
|
|
|
+ bmsr = err;
|
|
|
+
|
|
|
+ err = mii_read(np, np->phy_addr, MII_ADVERTISE);
|
|
|
+ if (unlikely(err < 0))
|
|
|
+ return err;
|
|
|
+ advert = err;
|
|
|
+
|
|
|
+ err = mii_read(np, np->phy_addr, MII_LPA);
|
|
|
+ if (unlikely(err < 0))
|
|
|
+ return err;
|
|
|
+ lpa = err;
|
|
|
+
|
|
|
+ if (likely(bmsr & BMSR_ESTATEN)) {
|
|
|
+ err = mii_read(np, np->phy_addr, MII_ESTATUS);
|
|
|
+ if (unlikely(err < 0))
|
|
|
+ return err;
|
|
|
+ estatus = err;
|
|
|
+
|
|
|
+ err = mii_read(np, np->phy_addr, MII_CTRL1000);
|
|
|
+ if (unlikely(err < 0))
|
|
|
+ return err;
|
|
|
+ ctrl1000 = err;
|
|
|
+
|
|
|
+ err = mii_read(np, np->phy_addr, MII_STAT1000);
|
|
|
+ if (unlikely(err < 0))
|
|
|
+ return err;
|
|
|
+ stat1000 = err;
|
|
|
+ } else
|
|
|
+ estatus = ctrl1000 = stat1000 = 0;
|
|
|
+
|
|
|
+ supported = 0;
|
|
|
+ if (bmsr & BMSR_ANEGCAPABLE)
|
|
|
+ supported |= SUPPORTED_Autoneg;
|
|
|
+ if (bmsr & BMSR_10HALF)
|
|
|
+ supported |= SUPPORTED_10baseT_Half;
|
|
|
+ if (bmsr & BMSR_10FULL)
|
|
|
+ supported |= SUPPORTED_10baseT_Full;
|
|
|
+ if (bmsr & BMSR_100HALF)
|
|
|
+ supported |= SUPPORTED_100baseT_Half;
|
|
|
+ if (bmsr & BMSR_100FULL)
|
|
|
+ supported |= SUPPORTED_100baseT_Full;
|
|
|
+ if (estatus & ESTATUS_1000_THALF)
|
|
|
+ supported |= SUPPORTED_1000baseT_Half;
|
|
|
+ if (estatus & ESTATUS_1000_TFULL)
|
|
|
+ supported |= SUPPORTED_1000baseT_Full;
|
|
|
+ lp->supported = supported;
|
|
|
+
|
|
|
+ advertising = 0;
|
|
|
+ if (advert & ADVERTISE_10HALF)
|
|
|
+ advertising |= ADVERTISED_10baseT_Half;
|
|
|
+ if (advert & ADVERTISE_10FULL)
|
|
|
+ advertising |= ADVERTISED_10baseT_Full;
|
|
|
+ if (advert & ADVERTISE_100HALF)
|
|
|
+ advertising |= ADVERTISED_100baseT_Half;
|
|
|
+ if (advert & ADVERTISE_100FULL)
|
|
|
+ advertising |= ADVERTISED_100baseT_Full;
|
|
|
+ if (ctrl1000 & ADVERTISE_1000HALF)
|
|
|
+ advertising |= ADVERTISED_1000baseT_Half;
|
|
|
+ if (ctrl1000 & ADVERTISE_1000FULL)
|
|
|
+ advertising |= ADVERTISED_1000baseT_Full;
|
|
|
+
|
|
|
+ if (bmcr & BMCR_ANENABLE) {
|
|
|
+ int neg, neg1000;
|
|
|
+
|
|
|
+ lp->active_autoneg = 1;
|
|
|
+ advertising |= ADVERTISED_Autoneg;
|
|
|
+
|
|
|
+ neg = advert & lpa;
|
|
|
+ neg1000 = (ctrl1000 << 2) & stat1000;
|
|
|
+
|
|
|
+ if (neg1000 & (LPA_1000FULL | LPA_1000HALF))
|
|
|
+ active_speed = SPEED_1000;
|
|
|
+ else if (neg & LPA_100)
|
|
|
+ active_speed = SPEED_100;
|
|
|
+ else if (neg & (LPA_10HALF | LPA_10FULL))
|
|
|
+ active_speed = SPEED_10;
|
|
|
+ else
|
|
|
+ active_speed = SPEED_INVALID;
|
|
|
+
|
|
|
+ if ((neg1000 & LPA_1000FULL) || (neg & LPA_DUPLEX))
|
|
|
+ active_duplex = DUPLEX_FULL;
|
|
|
+ else if (active_speed != SPEED_INVALID)
|
|
|
+ active_duplex = DUPLEX_HALF;
|
|
|
+ else
|
|
|
+ active_duplex = DUPLEX_INVALID;
|
|
|
+ } else {
|
|
|
+ lp->active_autoneg = 0;
|
|
|
+
|
|
|
+ if ((bmcr & BMCR_SPEED1000) && !(bmcr & BMCR_SPEED100))
|
|
|
+ active_speed = SPEED_1000;
|
|
|
+ else if (bmcr & BMCR_SPEED100)
|
|
|
+ active_speed = SPEED_100;
|
|
|
+ else
|
|
|
+ active_speed = SPEED_10;
|
|
|
+
|
|
|
+ if (bmcr & BMCR_FULLDPLX)
|
|
|
+ active_duplex = DUPLEX_FULL;
|
|
|
+ else
|
|
|
+ active_duplex = DUPLEX_HALF;
|
|
|
+ }
|
|
|
+
|
|
|
+ lp->active_advertising = advertising;
|
|
|
+ lp->active_speed = active_speed;
|
|
|
+ lp->active_duplex = active_duplex;
|
|
|
+ *link_up_p = !!(bmsr & BMSR_LSTATUS);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int link_status_1g_rgmii(struct niu *np, int *link_up_p)
|
|
|
{
|
|
|
struct niu_link_config *lp = &np->link_config;
|
|
@@ -1171,6 +1295,22 @@ out:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int link_status_1g(struct niu *np, int *link_up_p)
|
|
|
+{
|
|
|
+ struct niu_link_config *lp = &np->link_config;
|
|
|
+ unsigned long flags;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&np->lock, flags);
|
|
|
+
|
|
|
+ err = link_status_mii(np, link_up_p);
|
|
|
+ lp->supported |= SUPPORTED_TP;
|
|
|
+ lp->active_advertising |= ADVERTISED_TP;
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&np->lock, flags);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int bcm8704_reset(struct niu *np)
|
|
|
{
|
|
|
int err, limit;
|
|
@@ -1676,39 +1816,88 @@ static int mii_init_common(struct niu *np)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
- /* XXX configurable XXX */
|
|
|
- /* XXX for now don't advertise half-duplex or asym pause... XXX */
|
|
|
- adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP;
|
|
|
- if (bmsr & BMSR_10FULL)
|
|
|
- adv |= ADVERTISE_10FULL;
|
|
|
- if (bmsr & BMSR_100FULL)
|
|
|
- adv |= ADVERTISE_100FULL;
|
|
|
- err = mii_write(np, np->phy_addr, MII_ADVERTISE, adv);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
-
|
|
|
- if (bmsr & BMSR_ESTATEN) {
|
|
|
- u16 ctrl1000 = 0;
|
|
|
-
|
|
|
- if (estat & ESTATUS_1000_TFULL)
|
|
|
- ctrl1000 |= ADVERTISE_1000FULL;
|
|
|
- err = mii_write(np, np->phy_addr, MII_CTRL1000, ctrl1000);
|
|
|
+ if (lp->autoneg) {
|
|
|
+ u16 ctrl1000;
|
|
|
+
|
|
|
+ adv = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP;
|
|
|
+ if ((bmsr & BMSR_10HALF) &&
|
|
|
+ (lp->advertising & ADVERTISED_10baseT_Half))
|
|
|
+ adv |= ADVERTISE_10HALF;
|
|
|
+ if ((bmsr & BMSR_10FULL) &&
|
|
|
+ (lp->advertising & ADVERTISED_10baseT_Full))
|
|
|
+ adv |= ADVERTISE_10FULL;
|
|
|
+ if ((bmsr & BMSR_100HALF) &&
|
|
|
+ (lp->advertising & ADVERTISED_100baseT_Half))
|
|
|
+ adv |= ADVERTISE_100HALF;
|
|
|
+ if ((bmsr & BMSR_100FULL) &&
|
|
|
+ (lp->advertising & ADVERTISED_100baseT_Full))
|
|
|
+ adv |= ADVERTISE_100FULL;
|
|
|
+ err = mii_write(np, np->phy_addr, MII_ADVERTISE, adv);
|
|
|
if (err)
|
|
|
return err;
|
|
|
+
|
|
|
+ if (likely(bmsr & BMSR_ESTATEN)) {
|
|
|
+ ctrl1000 = 0;
|
|
|
+ if ((estat & ESTATUS_1000_THALF) &&
|
|
|
+ (lp->advertising & ADVERTISED_1000baseT_Half))
|
|
|
+ ctrl1000 |= ADVERTISE_1000HALF;
|
|
|
+ if ((estat & ESTATUS_1000_TFULL) &&
|
|
|
+ (lp->advertising & ADVERTISED_1000baseT_Full))
|
|
|
+ ctrl1000 |= ADVERTISE_1000FULL;
|
|
|
+ err = mii_write(np, np->phy_addr,
|
|
|
+ MII_CTRL1000, ctrl1000);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
|
|
|
+ } else {
|
|
|
+ /* !lp->autoneg */
|
|
|
+ int fulldpx;
|
|
|
+
|
|
|
+ if (lp->duplex == DUPLEX_FULL) {
|
|
|
+ bmcr |= BMCR_FULLDPLX;
|
|
|
+ fulldpx = 1;
|
|
|
+ } else if (lp->duplex == DUPLEX_HALF)
|
|
|
+ fulldpx = 0;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (lp->speed == SPEED_1000) {
|
|
|
+ /* if X-full requested while not supported, or
|
|
|
+ X-half requested while not supported... */
|
|
|
+ if ((fulldpx && !(estat & ESTATUS_1000_TFULL)) ||
|
|
|
+ (!fulldpx && !(estat & ESTATUS_1000_THALF)))
|
|
|
+ return -EINVAL;
|
|
|
+ bmcr |= BMCR_SPEED1000;
|
|
|
+ } else if (lp->speed == SPEED_100) {
|
|
|
+ if ((fulldpx && !(bmsr & BMSR_100FULL)) ||
|
|
|
+ (!fulldpx && !(bmsr & BMSR_100HALF)))
|
|
|
+ return -EINVAL;
|
|
|
+ bmcr |= BMCR_SPEED100;
|
|
|
+ } else if (lp->speed == SPEED_10) {
|
|
|
+ if ((fulldpx && !(bmsr & BMSR_10FULL)) ||
|
|
|
+ (!fulldpx && !(bmsr & BMSR_10HALF)))
|
|
|
+ return -EINVAL;
|
|
|
+ } else
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
- bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
|
|
|
|
|
|
err = mii_write(np, np->phy_addr, MII_BMCR, bmcr);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
|
+#if 0
|
|
|
err = mii_read(np, np->phy_addr, MII_BMCR);
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
+ bmcr = err;
|
|
|
+
|
|
|
err = mii_read(np, np->phy_addr, MII_BMSR);
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
-#if 0
|
|
|
+ bmsr = err;
|
|
|
+
|
|
|
pr_info(PFX "Port %u after MII init bmcr[%04x] bmsr[%04x]\n",
|
|
|
np->port, bmcr, bmsr);
|
|
|
#endif
|
|
@@ -2054,87 +2243,6 @@ static int link_status_10g_hotplug(struct niu *np, int *link_up_p)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-static int link_status_1g(struct niu *np, int *link_up_p)
|
|
|
-{
|
|
|
- struct niu_link_config *lp = &np->link_config;
|
|
|
- u16 current_speed, bmsr;
|
|
|
- unsigned long flags;
|
|
|
- u8 current_duplex;
|
|
|
- int err, link_up;
|
|
|
-
|
|
|
- link_up = 0;
|
|
|
- current_speed = SPEED_INVALID;
|
|
|
- current_duplex = DUPLEX_INVALID;
|
|
|
-
|
|
|
- spin_lock_irqsave(&np->lock, flags);
|
|
|
-
|
|
|
- err = -EINVAL;
|
|
|
- if (np->link_config.loopback_mode != LOOPBACK_DISABLED)
|
|
|
- goto out;
|
|
|
-
|
|
|
- err = mii_read(np, np->phy_addr, MII_BMSR);
|
|
|
- if (err < 0)
|
|
|
- goto out;
|
|
|
-
|
|
|
- bmsr = err;
|
|
|
- if (bmsr & BMSR_LSTATUS) {
|
|
|
- u16 adv, lpa, common, estat;
|
|
|
-
|
|
|
- err = mii_read(np, np->phy_addr, MII_ADVERTISE);
|
|
|
- if (err < 0)
|
|
|
- goto out;
|
|
|
- adv = err;
|
|
|
-
|
|
|
- err = mii_read(np, np->phy_addr, MII_LPA);
|
|
|
- if (err < 0)
|
|
|
- goto out;
|
|
|
- lpa = err;
|
|
|
-
|
|
|
- common = adv & lpa;
|
|
|
-
|
|
|
- err = mii_read(np, np->phy_addr, MII_ESTATUS);
|
|
|
- if (err < 0)
|
|
|
- goto out;
|
|
|
- estat = err;
|
|
|
-
|
|
|
- link_up = 1;
|
|
|
- if (estat & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) {
|
|
|
- current_speed = SPEED_1000;
|
|
|
- if (estat & ESTATUS_1000_TFULL)
|
|
|
- current_duplex = DUPLEX_FULL;
|
|
|
- else
|
|
|
- current_duplex = DUPLEX_HALF;
|
|
|
- } else {
|
|
|
- if (common & ADVERTISE_100BASE4) {
|
|
|
- current_speed = SPEED_100;
|
|
|
- current_duplex = DUPLEX_HALF;
|
|
|
- } else if (common & ADVERTISE_100FULL) {
|
|
|
- current_speed = SPEED_100;
|
|
|
- current_duplex = DUPLEX_FULL;
|
|
|
- } else if (common & ADVERTISE_100HALF) {
|
|
|
- current_speed = SPEED_100;
|
|
|
- current_duplex = DUPLEX_HALF;
|
|
|
- } else if (common & ADVERTISE_10FULL) {
|
|
|
- current_speed = SPEED_10;
|
|
|
- current_duplex = DUPLEX_FULL;
|
|
|
- } else if (common & ADVERTISE_10HALF) {
|
|
|
- current_speed = SPEED_10;
|
|
|
- current_duplex = DUPLEX_HALF;
|
|
|
- } else
|
|
|
- link_up = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- lp->active_speed = current_speed;
|
|
|
- lp->active_duplex = current_duplex;
|
|
|
- err = 0;
|
|
|
-
|
|
|
-out:
|
|
|
- spin_unlock_irqrestore(&np->lock, flags);
|
|
|
-
|
|
|
- *link_up_p = link_up;
|
|
|
- return err;
|
|
|
-}
|
|
|
-
|
|
|
static int niu_link_status(struct niu *np, int *link_up_p)
|
|
|
{
|
|
|
const struct niu_phy_ops *ops = np->phy_ops;
|
|
@@ -5212,10 +5320,10 @@ static void niu_init_xif_xmac(struct niu *np)
|
|
|
if (np->flags & NIU_FLAGS_10G) {
|
|
|
val |= XMAC_CONFIG_MODE_XGMII;
|
|
|
} else {
|
|
|
- if (lp->active_speed == SPEED_100)
|
|
|
- val |= XMAC_CONFIG_MODE_MII;
|
|
|
- else
|
|
|
+ if (lp->active_speed == SPEED_1000)
|
|
|
val |= XMAC_CONFIG_MODE_GMII;
|
|
|
+ else
|
|
|
+ val |= XMAC_CONFIG_MODE_MII;
|
|
|
}
|
|
|
|
|
|
nw64_mac(XMAC_CONFIG, val);
|
|
@@ -6703,17 +6811,27 @@ static int niu_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
memset(cmd, 0, sizeof(*cmd));
|
|
|
cmd->phy_address = np->phy_addr;
|
|
|
cmd->supported = lp->supported;
|
|
|
- cmd->advertising = lp->advertising;
|
|
|
- cmd->autoneg = lp->autoneg;
|
|
|
+ cmd->advertising = lp->active_advertising;
|
|
|
+ cmd->autoneg = lp->active_autoneg;
|
|
|
cmd->speed = lp->active_speed;
|
|
|
cmd->duplex = lp->active_duplex;
|
|
|
+ cmd->port = (np->flags & NIU_FLAGS_FIBER) ? PORT_FIBRE : PORT_TP;
|
|
|
+ cmd->transceiver = (np->flags & NIU_FLAGS_XCVR_SERDES) ?
|
|
|
+ XCVR_EXTERNAL : XCVR_INTERNAL;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static int niu_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
{
|
|
|
- return -EINVAL;
|
|
|
+ struct niu *np = netdev_priv(dev);
|
|
|
+ struct niu_link_config *lp = &np->link_config;
|
|
|
+
|
|
|
+ lp->advertising = cmd->advertising;
|
|
|
+ lp->speed = cmd->speed;
|
|
|
+ lp->duplex = cmd->duplex;
|
|
|
+ lp->autoneg = cmd->autoneg;
|
|
|
+ return niu_init_link(np);
|
|
|
}
|
|
|
|
|
|
static u32 niu_get_msglevel(struct net_device *dev)
|
|
@@ -6728,6 +6846,16 @@ static void niu_set_msglevel(struct net_device *dev, u32 value)
|
|
|
np->msg_enable = value;
|
|
|
}
|
|
|
|
|
|
+static int niu_nway_reset(struct net_device *dev)
|
|
|
+{
|
|
|
+ struct niu *np = netdev_priv(dev);
|
|
|
+
|
|
|
+ if (np->link_config.autoneg)
|
|
|
+ return niu_init_link(np);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int niu_get_eeprom_len(struct net_device *dev)
|
|
|
{
|
|
|
struct niu *np = netdev_priv(dev);
|
|
@@ -7159,6 +7287,7 @@ static const struct ethtool_ops niu_ethtool_ops = {
|
|
|
.get_link = ethtool_op_get_link,
|
|
|
.get_msglevel = niu_get_msglevel,
|
|
|
.set_msglevel = niu_set_msglevel,
|
|
|
+ .nway_reset = niu_nway_reset,
|
|
|
.get_eeprom_len = niu_get_eeprom_len,
|
|
|
.get_eeprom = niu_get_eeprom,
|
|
|
.get_settings = niu_get_settings,
|
|
@@ -8258,7 +8387,9 @@ static void __devinit niu_link_config_init(struct niu *np)
|
|
|
ADVERTISED_10000baseT_Full |
|
|
|
ADVERTISED_Autoneg);
|
|
|
lp->speed = lp->active_speed = SPEED_INVALID;
|
|
|
- lp->duplex = lp->active_duplex = DUPLEX_INVALID;
|
|
|
+ lp->duplex = DUPLEX_FULL;
|
|
|
+ lp->active_duplex = DUPLEX_INVALID;
|
|
|
+ lp->autoneg = 1;
|
|
|
#if 0
|
|
|
lp->loopback_mode = LOOPBACK_MAC;
|
|
|
lp->active_speed = SPEED_10000;
|