Commit 9b1e3656 authored by Divy Le Ray's avatar Divy Le Ray Committed by David S. Miller

cxgb3: commnonize LASI phy code

Add generic code to manage interrupt driven PHYs.
Do not reset the phy after link parameters update,
the new values might get lost.
Return early from link change notification
when the link parameters remain unchanged.
Signed-off-by: default avatarDivy Le Ray <divy@chelsio.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f231e0a5
...@@ -39,9 +39,6 @@ enum { ...@@ -39,9 +39,6 @@ enum {
AEL1002_PWR_DOWN_LO = 0xc012, AEL1002_PWR_DOWN_LO = 0xc012,
AEL1002_XFI_EQL = 0xc015, AEL1002_XFI_EQL = 0xc015,
AEL1002_LB_EN = 0xc017, AEL1002_LB_EN = 0xc017,
LASI_CTRL = 0x9002,
LASI_STAT = 0x9005
}; };
static void ael100x_txon(struct cphy *phy) static void ael100x_txon(struct cphy *phy)
...@@ -134,33 +131,6 @@ static int ael1006_reset(struct cphy *phy, int wait) ...@@ -134,33 +131,6 @@ static int ael1006_reset(struct cphy *phy, int wait)
return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
} }
static int ael1006_intr_enable(struct cphy *phy)
{
return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1);
}
static int ael1006_intr_disable(struct cphy *phy)
{
return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0);
}
static int ael1006_intr_clear(struct cphy *phy)
{
u32 val;
return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val);
}
static int ael1006_intr_handler(struct cphy *phy)
{
unsigned int status;
int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status);
if (err)
return err;
return (status & 1) ? cphy_cause_link_change : 0;
}
static int ael1006_power_down(struct cphy *phy, int enable) static int ael1006_power_down(struct cphy *phy, int enable)
{ {
return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
...@@ -169,10 +139,10 @@ static int ael1006_power_down(struct cphy *phy, int enable) ...@@ -169,10 +139,10 @@ static int ael1006_power_down(struct cphy *phy, int enable)
static struct cphy_ops ael1006_ops = { static struct cphy_ops ael1006_ops = {
.reset = ael1006_reset, .reset = ael1006_reset,
.intr_enable = ael1006_intr_enable, .intr_enable = t3_phy_lasi_intr_enable,
.intr_disable = ael1006_intr_disable, .intr_disable = t3_phy_lasi_intr_disable,
.intr_clear = ael1006_intr_clear, .intr_clear = t3_phy_lasi_intr_clear,
.intr_handler = ael1006_intr_handler, .intr_handler = t3_phy_lasi_intr_handler,
.get_link_status = ael100x_get_link_status, .get_link_status = ael100x_get_link_status,
.power_down = ael1006_power_down, .power_down = ael1006_power_down,
}; };
...@@ -189,10 +159,10 @@ int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, ...@@ -189,10 +159,10 @@ int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter,
static struct cphy_ops qt2045_ops = { static struct cphy_ops qt2045_ops = {
.reset = ael1006_reset, .reset = ael1006_reset,
.intr_enable = ael1006_intr_enable, .intr_enable = t3_phy_lasi_intr_enable,
.intr_disable = ael1006_intr_disable, .intr_disable = t3_phy_lasi_intr_disable,
.intr_clear = ael1006_intr_clear, .intr_clear = t3_phy_lasi_intr_clear,
.intr_handler = ael1006_intr_handler, .intr_handler = t3_phy_lasi_intr_handler,
.get_link_status = ael100x_get_link_status, .get_link_status = ael100x_get_link_status,
.power_down = ael1006_power_down, .power_down = ael1006_power_down,
}; };
......
...@@ -522,7 +522,20 @@ enum { ...@@ -522,7 +522,20 @@ enum {
MDIO_DEV_PMA_PMD = 1, MDIO_DEV_PMA_PMD = 1,
MDIO_DEV_WIS = 2, MDIO_DEV_WIS = 2,
MDIO_DEV_PCS = 3, MDIO_DEV_PCS = 3,
MDIO_DEV_XGXS = 4 MDIO_DEV_XGXS = 4,
MDIO_DEV_ANEG = 7,
MDIO_DEV_VEND1 = 30,
MDIO_DEV_VEND2 = 31
};
/* LASI control and status registers */
enum {
RX_ALARM_CTRL = 0x9000,
TX_ALARM_CTRL = 0x9001,
LASI_CTRL = 0x9002,
RX_ALARM_STAT = 0x9003,
TX_ALARM_STAT = 0x9004,
LASI_STAT = 0x9005
}; };
/* PHY loopback direction */ /* PHY loopback direction */
...@@ -665,6 +678,10 @@ int t3_mdio_change_bits(struct cphy *phy, int mmd, int reg, unsigned int clear, ...@@ -665,6 +678,10 @@ int t3_mdio_change_bits(struct cphy *phy, int mmd, int reg, unsigned int clear,
int t3_phy_reset(struct cphy *phy, int mmd, int wait); int t3_phy_reset(struct cphy *phy, int mmd, int wait);
int t3_phy_advertise(struct cphy *phy, unsigned int advert); int t3_phy_advertise(struct cphy *phy, unsigned int advert);
int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex); int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex);
int t3_phy_lasi_intr_enable(struct cphy *phy);
int t3_phy_lasi_intr_disable(struct cphy *phy);
int t3_phy_lasi_intr_clear(struct cphy *phy);
int t3_phy_lasi_intr_handler(struct cphy *phy);
void t3_intr_enable(struct adapter *adapter); void t3_intr_enable(struct adapter *adapter);
void t3_intr_disable(struct adapter *adapter); void t3_intr_disable(struct adapter *adapter);
......
...@@ -1516,11 +1516,22 @@ static int speed_duplex_to_caps(int speed, int duplex) ...@@ -1516,11 +1516,22 @@ static int speed_duplex_to_caps(int speed, int duplex)
static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{ {
int cap;
struct port_info *p = netdev_priv(dev); struct port_info *p = netdev_priv(dev);
struct link_config *lc = &p->link_config; struct link_config *lc = &p->link_config;
if (!(lc->supported & SUPPORTED_Autoneg)) if (!(lc->supported & SUPPORTED_Autoneg)) {
return -EOPNOTSUPP; /* can't change speed/duplex */ /*
* PHY offers a single speed/duplex. See if that's what's
* being requested.
*/
if (cmd->autoneg == AUTONEG_DISABLE) {
cap = speed_duplex_to_caps(cmd->speed, cmd->duplex);
if (lc->supported & cap)
return 0;
}
return -EINVAL;
}
if (cmd->autoneg == AUTONEG_DISABLE) { if (cmd->autoneg == AUTONEG_DISABLE) {
int cap = speed_duplex_to_caps(cmd->speed, cmd->duplex); int cap = speed_duplex_to_caps(cmd->speed, cmd->duplex);
...@@ -2195,7 +2206,7 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) ...@@ -2195,7 +2206,7 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
mmd = data->phy_id >> 8; mmd = data->phy_id >> 8;
if (!mmd) if (!mmd)
mmd = MDIO_DEV_PCS; mmd = MDIO_DEV_PCS;
else if (mmd > MDIO_DEV_XGXS) else if (mmd > MDIO_DEV_VEND2)
return -EINVAL; return -EINVAL;
ret = ret =
...@@ -2221,7 +2232,7 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) ...@@ -2221,7 +2232,7 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
mmd = data->phy_id >> 8; mmd = data->phy_id >> 8;
if (!mmd) if (!mmd)
mmd = MDIO_DEV_PCS; mmd = MDIO_DEV_PCS;
else if (mmd > MDIO_DEV_XGXS) else if (mmd > MDIO_DEV_VEND2)
return -EINVAL; return -EINVAL;
ret = ret =
......
...@@ -442,6 +442,33 @@ int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex) ...@@ -442,6 +442,33 @@ int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex)
return mdio_write(phy, 0, MII_BMCR, ctl); return mdio_write(phy, 0, MII_BMCR, ctl);
} }
int t3_phy_lasi_intr_enable(struct cphy *phy)
{
return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1);
}
int t3_phy_lasi_intr_disable(struct cphy *phy)
{
return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0);
}
int t3_phy_lasi_intr_clear(struct cphy *phy)
{
u32 val;
return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val);
}
int t3_phy_lasi_intr_handler(struct cphy *phy)
{
unsigned int status;
int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status);
if (err)
return err;
return (status & 1) ? cphy_cause_link_change : 0;
}
static const struct adapter_info t3_adap_info[] = { static const struct adapter_info t3_adap_info[] = {
{2, 0, {2, 0,
F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO2_OEN | F_GPIO4_OEN |
...@@ -1132,6 +1159,15 @@ void t3_link_changed(struct adapter *adapter, int port_id) ...@@ -1132,6 +1159,15 @@ void t3_link_changed(struct adapter *adapter, int port_id)
phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc); phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc);
if (lc->requested_fc & PAUSE_AUTONEG)
fc &= lc->requested_fc;
else
fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
if (link_ok == lc->link_ok && speed == lc->speed &&
duplex == lc->duplex && fc == lc->fc)
return; /* nothing changed */
if (link_ok != lc->link_ok && adapter->params.rev > 0 && if (link_ok != lc->link_ok && adapter->params.rev > 0 &&
uses_xaui(adapter)) { uses_xaui(adapter)) {
if (link_ok) if (link_ok)
...@@ -1142,10 +1178,6 @@ void t3_link_changed(struct adapter *adapter, int port_id) ...@@ -1142,10 +1178,6 @@ void t3_link_changed(struct adapter *adapter, int port_id)
lc->link_ok = link_ok; lc->link_ok = link_ok;
lc->speed = speed < 0 ? SPEED_INVALID : speed; lc->speed = speed < 0 ? SPEED_INVALID : speed;
lc->duplex = duplex < 0 ? DUPLEX_INVALID : duplex; lc->duplex = duplex < 0 ? DUPLEX_INVALID : duplex;
if (lc->requested_fc & PAUSE_AUTONEG)
fc &= lc->requested_fc;
else
fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
if (link_ok && speed >= 0 && lc->autoneg == AUTONEG_ENABLE) { if (link_ok && speed >= 0 && lc->autoneg == AUTONEG_ENABLE) {
/* Set MAC speed, duplex, and flow control to match PHY. */ /* Set MAC speed, duplex, and flow control to match PHY. */
...@@ -1191,7 +1223,6 @@ int t3_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc) ...@@ -1191,7 +1223,6 @@ int t3_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc)
fc); fc);
/* Also disables autoneg */ /* Also disables autoneg */
phy->ops->set_speed_duplex(phy, lc->speed, lc->duplex); phy->ops->set_speed_duplex(phy, lc->speed, lc->duplex);
phy->ops->reset(phy, 0);
} else } else
phy->ops->autoneg_enable(phy); phy->ops->autoneg_enable(phy);
} else { } else {
......
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