diff options
Diffstat (limited to 'drivers/net/sungem.c')
-rw-r--r-- | drivers/net/sungem.c | 144 |
1 files changed, 88 insertions, 56 deletions
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 4291458..fed7eba 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -1142,6 +1142,70 @@ static int gem_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } +static void gem_pcs_reset(struct gem *gp) +{ + int limit; + u32 val; + + /* Reset PCS unit. */ + val = readl(gp->regs + PCS_MIICTRL); + val |= PCS_MIICTRL_RST; + writel(val, gp->regs + PCS_MIICTRL); + + limit = 32; + while (readl(gp->regs + PCS_MIICTRL) & PCS_MIICTRL_RST) { + udelay(100); + if (limit-- <= 0) + break; + } + if (limit <= 0) + printk(KERN_WARNING "%s: PCS reset bit would not clear.\n", + gp->dev->name); +} + +static void gem_pcs_reinit_adv(struct gem *gp) +{ + u32 val; + + /* Make sure PCS is disabled while changing advertisement + * configuration. + */ + val = readl(gp->regs + PCS_CFG); + val &= ~(PCS_CFG_ENABLE | PCS_CFG_TO); + writel(val, gp->regs + PCS_CFG); + + /* Advertise all capabilities except assymetric + * pause. + */ + val = readl(gp->regs + PCS_MIIADV); + val |= (PCS_MIIADV_FD | PCS_MIIADV_HD | + PCS_MIIADV_SP | PCS_MIIADV_AP); + writel(val, gp->regs + PCS_MIIADV); + + /* Enable and restart auto-negotiation, disable wrapback/loopback, + * and re-enable PCS. + */ + val = readl(gp->regs + PCS_MIICTRL); + val |= (PCS_MIICTRL_RAN | PCS_MIICTRL_ANE); + val &= ~PCS_MIICTRL_WB; + writel(val, gp->regs + PCS_MIICTRL); + + val = readl(gp->regs + PCS_CFG); + val |= PCS_CFG_ENABLE; + writel(val, gp->regs + PCS_CFG); + + /* Make sure serialink loopback is off. The meaning + * of this bit is logically inverted based upon whether + * you are in Serialink or SERDES mode. + */ + val = readl(gp->regs + PCS_SCTRL); + if (gp->phy_type == phy_serialink) + val &= ~PCS_SCTRL_LOOP; + else + val |= PCS_SCTRL_LOOP; + writel(val, gp->regs + PCS_SCTRL); +} + #define STOP_TRIES 32 /* Must be invoked under gp->lock and gp->tx_lock. */ @@ -1168,6 +1232,9 @@ static void gem_reset(struct gem *gp) if (limit <= 0) printk(KERN_ERR "%s: SW reset is ghetto.\n", gp->dev->name); + + if (gp->phy_type == phy_serialink || gp->phy_type == phy_serdes) + gem_pcs_reinit_adv(gp); } /* Must be invoked under gp->lock and gp->tx_lock. */ @@ -1324,7 +1391,7 @@ static int gem_set_link_modes(struct gem *gp) gp->phy_type == phy_serdes) { u32 pcs_lpa = readl(gp->regs + PCS_MIILP); - if (pcs_lpa & PCS_MIIADV_FD) + if ((pcs_lpa & PCS_MIIADV_FD) || gp->phy_type == phy_serdes) full_duplex = 1; speed = SPEED_1000; } @@ -1488,6 +1555,9 @@ static void gem_link_timer(unsigned long data) val = readl(gp->regs + PCS_MIISTAT); if ((val & PCS_MIISTAT_LS) != 0) { + if (gp->lstate == link_up) + goto restart; + gp->lstate = link_up; netif_carrier_on(gp->dev); (void)gem_set_link_modes(gp); @@ -1708,61 +1778,8 @@ static void gem_init_phy(struct gem *gp) if (gp->phy_mii.def && gp->phy_mii.def->ops->init) gp->phy_mii.def->ops->init(&gp->phy_mii); } else { - u32 val; - int limit; - - /* Reset PCS unit. */ - val = readl(gp->regs + PCS_MIICTRL); - val |= PCS_MIICTRL_RST; - writeb(val, gp->regs + PCS_MIICTRL); - - limit = 32; - while (readl(gp->regs + PCS_MIICTRL) & PCS_MIICTRL_RST) { - udelay(100); - if (limit-- <= 0) - break; - } - if (limit <= 0) - printk(KERN_WARNING "%s: PCS reset bit would not clear.\n", - gp->dev->name); - - /* Make sure PCS is disabled while changing advertisement - * configuration. - */ - val = readl(gp->regs + PCS_CFG); - val &= ~(PCS_CFG_ENABLE | PCS_CFG_TO); - writel(val, gp->regs + PCS_CFG); - - /* Advertise all capabilities except assymetric - * pause. - */ - val = readl(gp->regs + PCS_MIIADV); - val |= (PCS_MIIADV_FD | PCS_MIIADV_HD | - PCS_MIIADV_SP | PCS_MIIADV_AP); - writel(val, gp->regs + PCS_MIIADV); - - /* Enable and restart auto-negotiation, disable wrapback/loopback, - * and re-enable PCS. - */ - val = readl(gp->regs + PCS_MIICTRL); - val |= (PCS_MIICTRL_RAN | PCS_MIICTRL_ANE); - val &= ~PCS_MIICTRL_WB; - writel(val, gp->regs + PCS_MIICTRL); - - val = readl(gp->regs + PCS_CFG); - val |= PCS_CFG_ENABLE; - writel(val, gp->regs + PCS_CFG); - - /* Make sure serialink loopback is off. The meaning - * of this bit is logically inverted based upon whether - * you are in Serialink or SERDES mode. - */ - val = readl(gp->regs + PCS_SCTRL); - if (gp->phy_type == phy_serialink) - val &= ~PCS_SCTRL_LOOP; - else - val |= PCS_SCTRL_LOOP; - writel(val, gp->regs + PCS_SCTRL); + gem_pcs_reset(gp); + gem_pcs_reinit_adv(gp); } /* Default aneg parameters */ @@ -2680,6 +2697,21 @@ static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) cmd->speed = 0; cmd->duplex = cmd->port = cmd->phy_address = cmd->transceiver = cmd->autoneg = 0; + + /* serdes means usually a Fibre connector, with most fixed */ + if (gp->phy_type == phy_serdes) { + cmd->port = PORT_FIBRE; + cmd->supported = (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | SUPPORTED_Autoneg | + SUPPORTED_Pause | SUPPORTED_Asym_Pause); + cmd->advertising = cmd->supported; + cmd->transceiver = XCVR_INTERNAL; + if (gp->lstate == link_up) + cmd->speed = SPEED_1000; + cmd->duplex = DUPLEX_FULL; + cmd->autoneg = 1; + } } cmd->maxtxpkt = cmd->maxrxpkt = 0; |