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 6f935cd30176..f4b0beec4d19 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c | |||
@@ -1141,6 +1141,70 @@ static int gem_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
1141 | return NETDEV_TX_OK; | 1141 | return NETDEV_TX_OK; |
1142 | } | 1142 | } |
1143 | 1143 | ||
1144 | static void gem_pcs_reset(struct gem *gp) | ||
1145 | { | ||
1146 | int limit; | ||
1147 | u32 val; | ||
1148 | |||
1149 | /* Reset PCS unit. */ | ||
1150 | val = readl(gp->regs + PCS_MIICTRL); | ||
1151 | val |= PCS_MIICTRL_RST; | ||
1152 | writel(val, gp->regs + PCS_MIICTRL); | ||
1153 | |||
1154 | limit = 32; | ||
1155 | while (readl(gp->regs + PCS_MIICTRL) & PCS_MIICTRL_RST) { | ||
1156 | udelay(100); | ||
1157 | if (limit-- <= 0) | ||
1158 | break; | ||
1159 | } | ||
1160 | if (limit <= 0) | ||
1161 | printk(KERN_WARNING "%s: PCS reset bit would not clear.\n", | ||
1162 | gp->dev->name); | ||
1163 | } | ||
1164 | |||
1165 | static void gem_pcs_reinit_adv(struct gem *gp) | ||
1166 | { | ||
1167 | u32 val; | ||
1168 | |||
1169 | /* Make sure PCS is disabled while changing advertisement | ||
1170 | * configuration. | ||
1171 | */ | ||
1172 | val = readl(gp->regs + PCS_CFG); | ||
1173 | val &= ~(PCS_CFG_ENABLE | PCS_CFG_TO); | ||
1174 | writel(val, gp->regs + PCS_CFG); | ||
1175 | |||
1176 | /* Advertise all capabilities except assymetric | ||
1177 | * pause. | ||
1178 | */ | ||
1179 | val = readl(gp->regs + PCS_MIIADV); | ||
1180 | val |= (PCS_MIIADV_FD | PCS_MIIADV_HD | | ||
1181 | PCS_MIIADV_SP | PCS_MIIADV_AP); | ||
1182 | writel(val, gp->regs + PCS_MIIADV); | ||
1183 | |||
1184 | /* Enable and restart auto-negotiation, disable wrapback/loopback, | ||
1185 | * and re-enable PCS. | ||
1186 | */ | ||
1187 | val = readl(gp->regs + PCS_MIICTRL); | ||
1188 | val |= (PCS_MIICTRL_RAN | PCS_MIICTRL_ANE); | ||
1189 | val &= ~PCS_MIICTRL_WB; | ||
1190 | writel(val, gp->regs + PCS_MIICTRL); | ||
1191 | |||
1192 | val = readl(gp->regs + PCS_CFG); | ||
1193 | val |= PCS_CFG_ENABLE; | ||
1194 | writel(val, gp->regs + PCS_CFG); | ||
1195 | |||
1196 | /* Make sure serialink loopback is off. The meaning | ||
1197 | * of this bit is logically inverted based upon whether | ||
1198 | * you are in Serialink or SERDES mode. | ||
1199 | */ | ||
1200 | val = readl(gp->regs + PCS_SCTRL); | ||
1201 | if (gp->phy_type == phy_serialink) | ||
1202 | val &= ~PCS_SCTRL_LOOP; | ||
1203 | else | ||
1204 | val |= PCS_SCTRL_LOOP; | ||
1205 | writel(val, gp->regs + PCS_SCTRL); | ||
1206 | } | ||
1207 | |||
1144 | #define STOP_TRIES 32 | 1208 | #define STOP_TRIES 32 |
1145 | 1209 | ||
1146 | /* Must be invoked under gp->lock and gp->tx_lock. */ | 1210 | /* Must be invoked under gp->lock and gp->tx_lock. */ |
@@ -1167,6 +1231,9 @@ static void gem_reset(struct gem *gp) | |||
1167 | 1231 | ||
1168 | if (limit <= 0) | 1232 | if (limit <= 0) |
1169 | printk(KERN_ERR "%s: SW reset is ghetto.\n", gp->dev->name); | 1233 | printk(KERN_ERR "%s: SW reset is ghetto.\n", gp->dev->name); |
1234 | |||
1235 | if (gp->phy_type == phy_serialink || gp->phy_type == phy_serdes) | ||
1236 | gem_pcs_reinit_adv(gp); | ||
1170 | } | 1237 | } |
1171 | 1238 | ||
1172 | /* Must be invoked under gp->lock and gp->tx_lock. */ | 1239 | /* Must be invoked under gp->lock and gp->tx_lock. */ |
@@ -1323,7 +1390,7 @@ static int gem_set_link_modes(struct gem *gp) | |||
1323 | gp->phy_type == phy_serdes) { | 1390 | gp->phy_type == phy_serdes) { |
1324 | u32 pcs_lpa = readl(gp->regs + PCS_MIILP); | 1391 | u32 pcs_lpa = readl(gp->regs + PCS_MIILP); |
1325 | 1392 | ||
1326 | if (pcs_lpa & PCS_MIIADV_FD) | 1393 | if ((pcs_lpa & PCS_MIIADV_FD) || gp->phy_type == phy_serdes) |
1327 | full_duplex = 1; | 1394 | full_duplex = 1; |
1328 | speed = SPEED_1000; | 1395 | speed = SPEED_1000; |
1329 | } | 1396 | } |
@@ -1487,6 +1554,9 @@ static void gem_link_timer(unsigned long data) | |||
1487 | val = readl(gp->regs + PCS_MIISTAT); | 1554 | val = readl(gp->regs + PCS_MIISTAT); |
1488 | 1555 | ||
1489 | if ((val & PCS_MIISTAT_LS) != 0) { | 1556 | if ((val & PCS_MIISTAT_LS) != 0) { |
1557 | if (gp->lstate == link_up) | ||
1558 | goto restart; | ||
1559 | |||
1490 | gp->lstate = link_up; | 1560 | gp->lstate = link_up; |
1491 | netif_carrier_on(gp->dev); | 1561 | netif_carrier_on(gp->dev); |
1492 | (void)gem_set_link_modes(gp); | 1562 | (void)gem_set_link_modes(gp); |
@@ -1707,61 +1777,8 @@ static void gem_init_phy(struct gem *gp) | |||
1707 | if (gp->phy_mii.def && gp->phy_mii.def->ops->init) | 1777 | if (gp->phy_mii.def && gp->phy_mii.def->ops->init) |
1708 | gp->phy_mii.def->ops->init(&gp->phy_mii); | 1778 | gp->phy_mii.def->ops->init(&gp->phy_mii); |
1709 | } else { | 1779 | } else { |
1710 | u32 val; | 1780 | gem_pcs_reset(gp); |
1711 | int limit; | 1781 | gem_pcs_reinit_adv(gp); |
1712 | |||
1713 | /* Reset PCS unit. */ | ||
1714 | val = readl(gp->regs + PCS_MIICTRL); | ||
1715 | val |= PCS_MIICTRL_RST; | ||
1716 | writel(val, gp->regs + PCS_MIICTRL); | ||
1717 | |||
1718 | limit = 32; | ||
1719 | while (readl(gp->regs + PCS_MIICTRL) & PCS_MIICTRL_RST) { | ||
1720 | udelay(100); | ||
1721 | if (limit-- <= 0) | ||
1722 | break; | ||
1723 | } | ||
1724 | if (limit <= 0) | ||
1725 | printk(KERN_WARNING "%s: PCS reset bit would not clear.\n", | ||
1726 | gp->dev->name); | ||
1727 | |||
1728 | /* Make sure PCS is disabled while changing advertisement | ||
1729 | * configuration. | ||
1730 | */ | ||
1731 | val = readl(gp->regs + PCS_CFG); | ||
1732 | val &= ~(PCS_CFG_ENABLE | PCS_CFG_TO); | ||
1733 | writel(val, gp->regs + PCS_CFG); | ||
1734 | |||
1735 | /* Advertise all capabilities except assymetric | ||
1736 | * pause. | ||
1737 | */ | ||
1738 | val = readl(gp->regs + PCS_MIIADV); | ||
1739 | val |= (PCS_MIIADV_FD | PCS_MIIADV_HD | | ||
1740 | PCS_MIIADV_SP | PCS_MIIADV_AP); | ||
1741 | writel(val, gp->regs + PCS_MIIADV); | ||
1742 | |||
1743 | /* Enable and restart auto-negotiation, disable wrapback/loopback, | ||
1744 | * and re-enable PCS. | ||
1745 | */ | ||
1746 | val = readl(gp->regs + PCS_MIICTRL); | ||
1747 | val |= (PCS_MIICTRL_RAN | PCS_MIICTRL_ANE); | ||
1748 | val &= ~PCS_MIICTRL_WB; | ||
1749 | writel(val, gp->regs + PCS_MIICTRL); | ||
1750 | |||
1751 | val = readl(gp->regs + PCS_CFG); | ||
1752 | val |= PCS_CFG_ENABLE; | ||
1753 | writel(val, gp->regs + PCS_CFG); | ||
1754 | |||
1755 | /* Make sure serialink loopback is off. The meaning | ||
1756 | * of this bit is logically inverted based upon whether | ||
1757 | * you are in Serialink or SERDES mode. | ||
1758 | */ | ||
1759 | val = readl(gp->regs + PCS_SCTRL); | ||
1760 | if (gp->phy_type == phy_serialink) | ||
1761 | val &= ~PCS_SCTRL_LOOP; | ||
1762 | else | ||
1763 | val |= PCS_SCTRL_LOOP; | ||
1764 | writel(val, gp->regs + PCS_SCTRL); | ||
1765 | } | 1782 | } |
1766 | 1783 | ||
1767 | /* Default aneg parameters */ | 1784 | /* Default aneg parameters */ |
@@ -2679,6 +2696,21 @@ static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | |||
2679 | cmd->speed = 0; | 2696 | cmd->speed = 0; |
2680 | cmd->duplex = cmd->port = cmd->phy_address = | 2697 | cmd->duplex = cmd->port = cmd->phy_address = |
2681 | cmd->transceiver = cmd->autoneg = 0; | 2698 | cmd->transceiver = cmd->autoneg = 0; |
2699 | |||
2700 | /* serdes means usually a Fibre connector, with most fixed */ | ||
2701 | if (gp->phy_type == phy_serdes) { | ||
2702 | cmd->port = PORT_FIBRE; | ||
2703 | cmd->supported = (SUPPORTED_1000baseT_Half | | ||
2704 | SUPPORTED_1000baseT_Full | | ||
2705 | SUPPORTED_FIBRE | SUPPORTED_Autoneg | | ||
2706 | SUPPORTED_Pause | SUPPORTED_Asym_Pause); | ||
2707 | cmd->advertising = cmd->supported; | ||
2708 | cmd->transceiver = XCVR_INTERNAL; | ||
2709 | if (gp->lstate == link_up) | ||
2710 | cmd->speed = SPEED_1000; | ||
2711 | cmd->duplex = DUPLEX_FULL; | ||
2712 | cmd->autoneg = 1; | ||
2713 | } | ||
2682 | } | 2714 | } |
2683 | cmd->maxtxpkt = cmd->maxrxpkt = 0; | 2715 | cmd->maxtxpkt = cmd->maxrxpkt = 0; |
2684 | 2716 | ||