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