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 4291458955e..fed7eba65ea 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;  |