Commit b02fd9e3 authored by Matt Carlson's avatar Matt Carlson Committed by David S. Miller

tg3: Add libphy support.

This patch introduces the libphy support.
Signed-off-by: default avatarMatt Carlson <mcarlson@broadcom.com>
Signed-off-by: default avatarMichael Chan <mchan@broadcom.com>
Signed-off-by: default avatarBenjamin Li <benli@broadcom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 158d7abd
......@@ -1114,11 +1114,17 @@ static u8 tg3_resolve_flowctrl_1000X(u16 lcladv, u16 rmtadv)
static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv)
{
u8 autoneg;
u8 flowctrl = 0;
u32 old_rx_mode = tp->rx_mode;
u32 old_tx_mode = tp->tx_mode;
if (tp->link_config.autoneg == AUTONEG_ENABLE &&
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB)
autoneg = tp->mdio_bus.phy_map[PHY_ADDR]->autoneg;
else
autoneg = tp->link_config.autoneg;
if (autoneg == AUTONEG_ENABLE &&
(tp->tg3_flags & TG3_FLAG_PAUSE_AUTONEG)) {
if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES)
flowctrl = tg3_resolve_flowctrl_1000X(lcladv, rmtadv);
......@@ -1146,6 +1152,152 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv)
tw32_f(MAC_TX_MODE, tp->tx_mode);
}
static void tg3_adjust_link(struct net_device *dev)
{
u8 oldflowctrl, linkmesg = 0;
u32 mac_mode, lcl_adv, rmt_adv;
struct tg3 *tp = netdev_priv(dev);
struct phy_device *phydev = tp->mdio_bus.phy_map[PHY_ADDR];
spin_lock(&tp->lock);
mac_mode = tp->mac_mode & ~(MAC_MODE_PORT_MODE_MASK |
MAC_MODE_HALF_DUPLEX);
oldflowctrl = tp->link_config.active_flowctrl;
if (phydev->link) {
lcl_adv = 0;
rmt_adv = 0;
if (phydev->speed == SPEED_100 || phydev->speed == SPEED_10)
mac_mode |= MAC_MODE_PORT_MODE_MII;
else
mac_mode |= MAC_MODE_PORT_MODE_GMII;
if (phydev->duplex == DUPLEX_HALF)
mac_mode |= MAC_MODE_HALF_DUPLEX;
else {
lcl_adv = tg3_advert_flowctrl_1000T(
tp->link_config.flowctrl);
if (phydev->pause)
rmt_adv = LPA_PAUSE_CAP;
if (phydev->asym_pause)
rmt_adv |= LPA_PAUSE_ASYM;
}
tg3_setup_flow_control(tp, lcl_adv, rmt_adv);
} else
mac_mode |= MAC_MODE_PORT_MODE_GMII;
if (mac_mode != tp->mac_mode) {
tp->mac_mode = mac_mode;
tw32_f(MAC_MODE, tp->mac_mode);
udelay(40);
}
if (phydev->speed == SPEED_1000 && phydev->duplex == DUPLEX_HALF)
tw32(MAC_TX_LENGTHS,
((2 << TX_LENGTHS_IPG_CRS_SHIFT) |
(6 << TX_LENGTHS_IPG_SHIFT) |
(0xff << TX_LENGTHS_SLOT_TIME_SHIFT)));
else
tw32(MAC_TX_LENGTHS,
((2 << TX_LENGTHS_IPG_CRS_SHIFT) |
(6 << TX_LENGTHS_IPG_SHIFT) |
(32 << TX_LENGTHS_SLOT_TIME_SHIFT)));
if ((phydev->link && tp->link_config.active_speed == SPEED_INVALID) ||
(!phydev->link && tp->link_config.active_speed != SPEED_INVALID) ||
phydev->speed != tp->link_config.active_speed ||
phydev->duplex != tp->link_config.active_duplex ||
oldflowctrl != tp->link_config.active_flowctrl)
linkmesg = 1;
tp->link_config.active_speed = phydev->speed;
tp->link_config.active_duplex = phydev->duplex;
spin_unlock(&tp->lock);
if (linkmesg)
tg3_link_report(tp);
}
static int tg3_phy_init(struct tg3 *tp)
{
struct phy_device *phydev;
if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)
return 0;
/* Bring the PHY back to a known state. */
tg3_bmcr_reset(tp);
phydev = tp->mdio_bus.phy_map[PHY_ADDR];
/* Attach the MAC to the PHY. */
phydev = phy_connect(tp->dev, phydev->dev.bus_id,
tg3_adjust_link, 0, phydev->interface);
if (IS_ERR(phydev)) {
printk(KERN_ERR "%s: Could not attach to PHY\n", tp->dev->name);
return PTR_ERR(phydev);
}
tp->tg3_flags3 |= TG3_FLG3_PHY_CONNECTED;
/* Mask with MAC supported features. */
phydev->supported &= (PHY_GBIT_FEATURES |
SUPPORTED_Pause |
SUPPORTED_Asym_Pause);
phydev->advertising = phydev->supported;
printk(KERN_INFO
"%s: attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
tp->dev->name, phydev->drv->name, phydev->dev.bus_id);
return 0;
}
static void tg3_phy_start(struct tg3 *tp)
{
struct phy_device *phydev;
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return;
phydev = tp->mdio_bus.phy_map[PHY_ADDR];
if (tp->link_config.phy_is_low_power) {
tp->link_config.phy_is_low_power = 0;
phydev->speed = tp->link_config.orig_speed;
phydev->duplex = tp->link_config.orig_duplex;
phydev->autoneg = tp->link_config.orig_autoneg;
phydev->advertising = tp->link_config.orig_advertising;
}
phy_start(phydev);
phy_start_aneg(phydev);
}
static void tg3_phy_stop(struct tg3 *tp)
{
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return;
phy_stop(tp->mdio_bus.phy_map[PHY_ADDR]);
}
static void tg3_phy_fini(struct tg3 *tp)
{
if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED) {
phy_disconnect(tp->mdio_bus.phy_map[PHY_ADDR]);
tp->tg3_flags3 &= ~TG3_FLG3_PHY_CONNECTED;
}
}
static void tg3_phydsp_write(struct tg3 *tp, u32 reg, u32 val)
{
tg3_writephy(tp, MII_TG3_DSP_ADDRESS, reg);
......@@ -1798,7 +1950,40 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT);
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
if ((tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED) &&
!tp->link_config.phy_is_low_power) {
struct phy_device *phydev;
u32 advertising;
phydev = tp->mdio_bus.phy_map[PHY_ADDR];
tp->link_config.phy_is_low_power = 1;
tp->link_config.orig_speed = phydev->speed;
tp->link_config.orig_duplex = phydev->duplex;
tp->link_config.orig_autoneg = phydev->autoneg;
tp->link_config.orig_advertising = phydev->advertising;
advertising = ADVERTISED_TP |
ADVERTISED_Pause |
ADVERTISED_Autoneg |
ADVERTISED_10baseT_Half;
if ((tp->tg3_flags & TG3_FLAG_ENABLE_ASF) ||
(tp->tg3_flags & TG3_FLAG_WOL_ENABLE)) {
if (tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB)
advertising |=
ADVERTISED_100baseT_Half |
ADVERTISED_100baseT_Full |
ADVERTISED_10baseT_Full;
else
advertising |= ADVERTISED_10baseT_Full;
}
phydev->advertising = advertising;
phy_start_aneg(phydev);
}
} else {
if (tp->link_config.phy_is_low_power == 0) {
tp->link_config.phy_is_low_power = 1;
......@@ -4233,6 +4418,7 @@ static void tg3_poll_controller(struct net_device *dev)
static void tg3_reset_task(struct work_struct *work)
{
struct tg3 *tp = container_of(work, struct tg3, reset_task);
int err;
unsigned int restart_timer;
tg3_full_lock(tp, 0);
......@@ -4244,6 +4430,8 @@ static void tg3_reset_task(struct work_struct *work)
tg3_full_unlock(tp);
tg3_phy_stop(tp);
tg3_netif_stop(tp);
tg3_full_lock(tp, 1);
......@@ -4259,7 +4447,8 @@ static void tg3_reset_task(struct work_struct *work)
}
tg3_halt(tp, RESET_KIND_SHUTDOWN, 0);
if (tg3_init_hw(tp, 1))
err = tg3_init_hw(tp, 1);
if (err)
goto out;
tg3_netif_start(tp);
......@@ -4269,6 +4458,9 @@ static void tg3_reset_task(struct work_struct *work)
out:
tg3_full_unlock(tp);
if (!err)
tg3_phy_start(tp);
}
static void tg3_dump_short_state(struct tg3 *tp)
......@@ -4772,6 +4964,8 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
tg3_phy_stop(tp);
tg3_netif_stop(tp);
tg3_full_lock(tp, 1);
......@@ -4787,6 +4981,9 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu)
tg3_full_unlock(tp);
if (!err)
tg3_phy_start(tp);
return err;
}
......@@ -7864,6 +8061,8 @@ static int tg3_open(struct net_device *dev)
}
}
tg3_phy_start(tp);
tg3_full_lock(tp, 0);
add_timer(&tp->timer);
......@@ -8667,6 +8866,12 @@ static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct tg3 *tp = netdev_priv(dev);
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return -EAGAIN;
return phy_ethtool_gset(tp->mdio_bus.phy_map[PHY_ADDR], cmd);
}
cmd->supported = (SUPPORTED_Autoneg);
if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY))
......@@ -8702,6 +8907,12 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct tg3 *tp = netdev_priv(dev);
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return -EAGAIN;
return phy_ethtool_sset(tp->mdio_bus.phy_map[PHY_ADDR], cmd);
}
if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) {
/* These are the only valid advertisement bits allowed. */
if (cmd->autoneg == AUTONEG_ENABLE &&
......@@ -8828,7 +9039,6 @@ static int tg3_set_tso(struct net_device *dev, u32 value)
static int tg3_nway_reset(struct net_device *dev)
{
struct tg3 *tp = netdev_priv(dev);
u32 bmcr;
int r;
if (!netif_running(dev))
......@@ -8837,6 +9047,13 @@ static int tg3_nway_reset(struct net_device *dev)
if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)
return -EINVAL;
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return -EAGAIN;
r = phy_start_aneg(tp->mdio_bus.phy_map[PHY_ADDR]);
} else {
u32 bmcr;
spin_lock_bh(&tp->lock);
r = -EINVAL;
tg3_readphy(tp, MII_BMCR, &bmcr);
......@@ -8848,6 +9065,7 @@ static int tg3_nway_reset(struct net_device *dev)
r = 0;
}
spin_unlock_bh(&tp->lock);
}
return r;
}
......@@ -8889,6 +9107,7 @@ static int tg3_set_ringparam(struct net_device *dev, struct ethtool_ringparam *e
return -EINVAL;
if (netif_running(dev)) {
tg3_phy_stop(tp);
tg3_netif_stop(tp);
irq_sync = 1;
}
......@@ -8912,6 +9131,9 @@ static int tg3_set_ringparam(struct net_device *dev, struct ethtool_ringparam *e
tg3_full_unlock(tp);
if (irq_sync && !err)
tg3_phy_start(tp);
return err;
}
......@@ -8935,7 +9157,62 @@ static void tg3_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam
static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause)
{
struct tg3 *tp = netdev_priv(dev);
int irq_sync = 0, err = 0;
int err = 0;
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return -EAGAIN;
if (epause->autoneg) {
u32 newadv;
struct phy_device *phydev;
phydev = tp->mdio_bus.phy_map[PHY_ADDR];
if (epause->rx_pause) {
if (epause->tx_pause)
newadv = ADVERTISED_Pause;
else
newadv = ADVERTISED_Pause |
ADVERTISED_Asym_Pause;
} else if (epause->tx_pause) {
newadv = ADVERTISED_Asym_Pause;
} else
newadv = 0;
if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED) {
u32 oldadv = phydev->advertising &
(ADVERTISED_Pause |
ADVERTISED_Asym_Pause);
if (oldadv != newadv) {
phydev->advertising &=
~(ADVERTISED_Pause |
ADVERTISED_Asym_Pause);
phydev->advertising |= newadv;
err = phy_start_aneg(phydev);
}
} else {
tp->link_config.advertising &=
~(ADVERTISED_Pause |
ADVERTISED_Asym_Pause);
tp->link_config.advertising |= newadv;
}
} else {
if (epause->rx_pause)
tp->link_config.flowctrl |= TG3_FLOW_CTRL_RX;
else
tp->link_config.flowctrl &= ~TG3_FLOW_CTRL_RX;
if (epause->tx_pause)
tp->link_config.flowctrl |= TG3_FLOW_CTRL_TX;
else
tp->link_config.flowctrl &= ~TG3_FLOW_CTRL_TX;
if (netif_running(dev))
tg3_setup_flow_control(tp, 0, 0);
}
} else {
int irq_sync = 0;
if (netif_running(dev)) {
tg3_netif_stop(tp);
......@@ -8965,6 +9242,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
}
tg3_full_unlock(tp);
}
return err;
}
......@@ -9799,9 +10077,10 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest,
data[1] = 1;
}
if (etest->flags & ETH_TEST_FL_OFFLINE) {
int err, irq_sync = 0;
int err, err2 = 0, irq_sync = 0;
if (netif_running(dev)) {
tg3_phy_stop(tp);
tg3_netif_stop(tp);
irq_sync = 1;
}
......@@ -9842,11 +10121,15 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest,
tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
if (netif_running(dev)) {
tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE;
if (!tg3_restart_hw(tp, 1))
err2 = tg3_restart_hw(tp, 1);
if (!err2)
tg3_netif_start(tp);
}
tg3_full_unlock(tp);
if (irq_sync && !err2)
tg3_phy_start(tp);
}
if (tp->link_config.phy_is_low_power)
tg3_set_power_state(tp, PCI_D3hot);
......@@ -9859,6 +10142,12 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct tg3 *tp = netdev_priv(dev);
int err;
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return -EAGAIN;
return phy_mii_ioctl(tp->mdio_bus.phy_map[PHY_ADDR], data, cmd);
}
switch(cmd) {
case SIOCGMIIPHY:
data->phy_id = PHY_ADDR;
......@@ -11110,6 +11399,9 @@ static int __devinit tg3_phy_probe(struct tg3 *tp)
u32 hw_phy_id, hw_phy_id_masked;
int err;
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB)
return tg3_phy_init(tp);
/* Reading the PHY ID register can conflict with ASF
* firwmare access to the PHY hardware.
*/
......@@ -12043,6 +12335,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
printk(KERN_ERR PFX "(%s) phy probe failed, err %d\n",
pci_name(tp->pdev), err);
/* ... but do not return immediately ... */
tg3_mdio_fini(tp);
}
tg3_read_partno(tp);
......@@ -13163,8 +13456,10 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev)
flush_scheduled_work();
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB)
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
tg3_phy_fini(tp);
tg3_mdio_fini(tp);
}
unregister_netdev(dev);
if (tp->aperegs) {
......@@ -13198,6 +13493,7 @@ static int tg3_suspend(struct pci_dev *pdev, pm_message_t state)
return 0;
flush_scheduled_work();
tg3_phy_stop(tp);
tg3_netif_stop(tp);
del_timer_sync(&tp->timer);
......@@ -13215,10 +13511,13 @@ static int tg3_suspend(struct pci_dev *pdev, pm_message_t state)
err = tg3_set_power_state(tp, pci_choose_state(pdev, state));
if (err) {
int err2;
tg3_full_lock(tp, 0);
tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE;
if (tg3_restart_hw(tp, 1))
err2 = tg3_restart_hw(tp, 1);
if (err2)
goto out;
tp->timer.expires = jiffies + tp->timer_offset;
......@@ -13229,6 +13528,9 @@ static int tg3_suspend(struct pci_dev *pdev, pm_message_t state)
out:
tg3_full_unlock(tp);
if (!err2)
tg3_phy_start(tp);
}
return err;
......@@ -13266,6 +13568,9 @@ static int tg3_resume(struct pci_dev *pdev)
out:
tg3_full_unlock(tp);
if (!err)
tg3_phy_start(tp);
return err;
}
......
......@@ -2205,6 +2205,7 @@ struct tg3_link_config {
u16 orig_speed;
u8 orig_duplex;
u8 orig_autoneg;
u32 orig_advertising;
};
struct tg3_bufmgr_config {
......@@ -2483,6 +2484,7 @@ struct tg3 {
#define TG3_FLG3_USE_PHYLIB 0x00000010
#define TG3_FLG3_MDIOBUS_INITED 0x00000020
#define TG3_FLG3_MDIOBUS_PAUSED 0x00000040
#define TG3_FLG3_PHY_CONNECTED 0x00000080
struct timer_list timer;
u16 timer_counter;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment