net: phy: marvell: fix paged access races

For paged accesses to be truely safe, we need to hold the bus lock to
prevent anyone else gaining access to the registers while we modify
them.

The phydev->lock mutex does not do this: userspace via the MII ioctl
can still sneak in and read or write any register while we are on a
different page, and the suspend/resume methods can be called by a
thread different to the thread polling the phy status.

Races have been observed with mvneta on SolidRun Clearfog with phylink,
particularly between the phylib worker reading the PHYs status, and
the thread resuming mvneta, calling phy_start() which then calls
through to m88e1121_config_aneg_rgmii_delays(), which tries to
read-modify-write the MSCR register:

	CPU0			CPU1
	marvell_read_status_page()
	marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE)
	...
				m88e1121_config_aneg_rgmii_delays()
				set_page(MII_MARVELL_MSCR_PAGE)
				phy_read(phydev, MII_88E1121_PHY_MSCR_REG)
	marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
	...
				phy_write(phydev, MII_88E1121_PHY_MSCR_REG)

The result of this is we end up writing the copper page register 21,
which causes the copper PHY to be disabled, and the link partner sees
the link immediately go down.

Solve this by taking the bus lock instead of the PHY lock, thereby
preventing other accesses to the PHY while we are accessing other PHY
pages.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Russell King 2018-01-02 10:58:48 +00:00 committed by David S. Miller
parent 78ffc4acce
commit 424ca4c551
1 changed files with 138 additions and 218 deletions

View File

@ -83,7 +83,7 @@
#define MII_88E1121_PHY_MSCR_REG 21 #define MII_88E1121_PHY_MSCR_REG 21
#define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5) #define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5)
#define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4) #define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4)
#define MII_88E1121_PHY_MSCR_DELAY_MASK (~(BIT(5) | BIT(4))) #define MII_88E1121_PHY_MSCR_DELAY_MASK (BIT(5) | BIT(4))
#define MII_88E1121_MISC_TEST 0x1a #define MII_88E1121_MISC_TEST 0x1a
#define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK 0x1f00 #define MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK 0x1f00
@ -177,9 +177,14 @@ struct marvell_priv {
struct device *hwmon_dev; struct device *hwmon_dev;
}; };
static int marvell_get_page(struct phy_device *phydev) static int marvell_read_page(struct phy_device *phydev)
{ {
return phy_read(phydev, MII_MARVELL_PHY_PAGE); return __phy_read(phydev, MII_MARVELL_PHY_PAGE);
}
static int marvell_write_page(struct phy_device *phydev, int page)
{
return __phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
} }
static int marvell_set_page(struct phy_device *phydev, int page) static int marvell_set_page(struct phy_device *phydev, int page)
@ -187,19 +192,6 @@ static int marvell_set_page(struct phy_device *phydev, int page)
return phy_write(phydev, MII_MARVELL_PHY_PAGE, page); return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
} }
static int marvell_get_set_page(struct phy_device *phydev, int page)
{
int oldpage = marvell_get_page(phydev);
if (oldpage < 0)
return oldpage;
if (page != oldpage)
return marvell_set_page(phydev, page);
return 0;
}
static int marvell_ack_interrupt(struct phy_device *phydev) static int marvell_ack_interrupt(struct phy_device *phydev)
{ {
int err; int err;
@ -399,7 +391,7 @@ static int m88e1111_config_aneg(struct phy_device *phydev)
static int marvell_of_reg_init(struct phy_device *phydev) static int marvell_of_reg_init(struct phy_device *phydev)
{ {
const __be32 *paddr; const __be32 *paddr;
int len, i, saved_page, current_page, ret; int len, i, saved_page, current_page, ret = 0;
if (!phydev->mdio.dev.of_node) if (!phydev->mdio.dev.of_node)
return 0; return 0;
@ -409,12 +401,11 @@ static int marvell_of_reg_init(struct phy_device *phydev)
if (!paddr || len < (4 * sizeof(*paddr))) if (!paddr || len < (4 * sizeof(*paddr)))
return 0; return 0;
saved_page = marvell_get_page(phydev); saved_page = phy_save_page(phydev);
if (saved_page < 0) if (saved_page < 0)
return saved_page; goto err;
current_page = saved_page; current_page = saved_page;
ret = 0;
len /= sizeof(*paddr); len /= sizeof(*paddr);
for (i = 0; i < len - 3; i += 4) { for (i = 0; i < len - 3; i += 4) {
u16 page = be32_to_cpup(paddr + i); u16 page = be32_to_cpup(paddr + i);
@ -425,14 +416,14 @@ static int marvell_of_reg_init(struct phy_device *phydev)
if (page != current_page) { if (page != current_page) {
current_page = page; current_page = page;
ret = marvell_set_page(phydev, page); ret = marvell_write_page(phydev, page);
if (ret < 0) if (ret < 0)
goto err; goto err;
} }
val = 0; val = 0;
if (mask) { if (mask) {
val = phy_read(phydev, reg); val = __phy_read(phydev, reg);
if (val < 0) { if (val < 0) {
ret = val; ret = val;
goto err; goto err;
@ -441,17 +432,12 @@ static int marvell_of_reg_init(struct phy_device *phydev)
} }
val |= val_bits; val |= val_bits;
ret = phy_write(phydev, reg, val); ret = __phy_write(phydev, reg, val);
if (ret < 0) if (ret < 0)
goto err; goto err;
} }
err: err:
if (current_page != saved_page) { return phy_restore_page(phydev, saved_page, ret);
i = marvell_set_page(phydev, saved_page);
if (ret == 0)
ret = i;
}
return ret;
} }
#else #else
static int marvell_of_reg_init(struct phy_device *phydev) static int marvell_of_reg_init(struct phy_device *phydev)
@ -462,34 +448,21 @@ static int marvell_of_reg_init(struct phy_device *phydev)
static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev) static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev)
{ {
int err, oldpage, mscr; int mscr;
oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE);
if (oldpage < 0)
return oldpage;
mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG);
if (mscr < 0) {
err = mscr;
goto out;
}
mscr &= MII_88E1121_PHY_MSCR_DELAY_MASK;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
mscr |= (MII_88E1121_PHY_MSCR_RX_DELAY | mscr = MII_88E1121_PHY_MSCR_RX_DELAY |
MII_88E1121_PHY_MSCR_TX_DELAY); MII_88E1121_PHY_MSCR_TX_DELAY;
else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
mscr |= MII_88E1121_PHY_MSCR_RX_DELAY; mscr = MII_88E1121_PHY_MSCR_RX_DELAY;
else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
mscr |= MII_88E1121_PHY_MSCR_TX_DELAY; mscr = MII_88E1121_PHY_MSCR_TX_DELAY;
else
mscr = 0;
err = phy_write(phydev, MII_88E1121_PHY_MSCR_REG, mscr); return phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
MII_88E1121_PHY_MSCR_REG,
out: MII_88E1121_PHY_MSCR_DELAY_MASK, mscr);
marvell_set_page(phydev, oldpage);
return err;
} }
static int m88e1121_config_aneg(struct phy_device *phydev) static int m88e1121_config_aneg(struct phy_device *phydev)
@ -515,20 +488,11 @@ static int m88e1121_config_aneg(struct phy_device *phydev)
static int m88e1318_config_aneg(struct phy_device *phydev) static int m88e1318_config_aneg(struct phy_device *phydev)
{ {
int err, oldpage, mscr; int err;
oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE); err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
if (oldpage < 0) MII_88E1318S_PHY_MSCR1_REG,
return oldpage; 0, MII_88E1318S_PHY_MSCR1_PAD_ODD);
mscr = phy_read(phydev, MII_88E1318S_PHY_MSCR1_REG);
mscr |= MII_88E1318S_PHY_MSCR1_PAD_ODD;
err = phy_write(phydev, MII_88E1318S_PHY_MSCR1_REG, mscr);
if (err < 0)
return err;
err = marvell_set_page(phydev, oldpage);
if (err < 0) if (err < 0)
return err; return err;
@ -854,20 +818,15 @@ static int m88e1111_config_init(struct phy_device *phydev)
static int m88e1121_config_init(struct phy_device *phydev) static int m88e1121_config_init(struct phy_device *phydev)
{ {
int err, oldpage; int err;
oldpage = marvell_get_set_page(phydev, MII_MARVELL_LED_PAGE);
if (oldpage < 0)
return oldpage;
/* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */ /* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
err = phy_write(phydev, MII_88E1121_PHY_LED_CTRL, err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE,
MII_88E1121_PHY_LED_CTRL,
MII_88E1121_PHY_LED_DEF); MII_88E1121_PHY_LED_DEF);
if (err < 0) if (err < 0)
return err; return err;
marvell_set_page(phydev, oldpage);
/* Set marvell,reg-init configuration from device tree */ /* Set marvell,reg-init configuration from device tree */
return marvell_config_init(phydev); return marvell_config_init(phydev);
} }
@ -1398,100 +1357,98 @@ static int m88e1121_did_interrupt(struct phy_device *phydev)
static void m88e1318_get_wol(struct phy_device *phydev, static void m88e1318_get_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol) struct ethtool_wolinfo *wol)
{ {
int oldpage, ret = 0;
wol->supported = WAKE_MAGIC; wol->supported = WAKE_MAGIC;
wol->wolopts = 0; wol->wolopts = 0;
if (marvell_set_page(phydev, MII_MARVELL_WOL_PAGE) < 0) oldpage = phy_select_page(phydev, MII_MARVELL_WOL_PAGE);
return; if (oldpage < 0)
goto error;
if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) & ret = __phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE) if (ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
wol->wolopts |= WAKE_MAGIC; wol->wolopts |= WAKE_MAGIC;
if (marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE) < 0) error:
return; phy_restore_page(phydev, oldpage, ret);
} }
static int m88e1318_set_wol(struct phy_device *phydev, static int m88e1318_set_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol) struct ethtool_wolinfo *wol)
{ {
int err, oldpage, temp; int err = 0, oldpage;
oldpage = marvell_get_page(phydev); oldpage = phy_save_page(phydev);
if (oldpage < 0)
goto error;
if (wol->wolopts & WAKE_MAGIC) { if (wol->wolopts & WAKE_MAGIC) {
/* Explicitly switch to page 0x00, just to be sure */ /* Explicitly switch to page 0x00, just to be sure */
err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE); err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0) if (err < 0)
return err; goto error;
/* Enable the WOL interrupt */ /* Enable the WOL interrupt */
temp = phy_read(phydev, MII_88E1318S_PHY_CSIER); err = __phy_modify(phydev, MII_88E1318S_PHY_CSIER, 0,
temp |= MII_88E1318S_PHY_CSIER_WOL_EIE; MII_88E1318S_PHY_CSIER_WOL_EIE);
err = phy_write(phydev, MII_88E1318S_PHY_CSIER, temp);
if (err < 0) if (err < 0)
return err; goto error;
err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE); err = marvell_write_page(phydev, MII_MARVELL_LED_PAGE);
if (err < 0) if (err < 0)
return err; goto error;
/* Setup LED[2] as interrupt pin (active low) */ /* Setup LED[2] as interrupt pin (active low) */
temp = phy_read(phydev, MII_88E1318S_PHY_LED_TCR); err = __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR,
temp &= ~MII_88E1318S_PHY_LED_TCR_FORCE_INT; (u16)~MII_88E1318S_PHY_LED_TCR_FORCE_INT,
temp |= MII_88E1318S_PHY_LED_TCR_INTn_ENABLE; MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
temp |= MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW; MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
err = phy_write(phydev, MII_88E1318S_PHY_LED_TCR, temp);
if (err < 0) if (err < 0)
return err; goto error;
err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE); err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
if (err < 0) if (err < 0)
return err; goto error;
/* Store the device address for the magic packet */ /* Store the device address for the magic packet */
err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2, err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD2,
((phydev->attached_dev->dev_addr[5] << 8) | ((phydev->attached_dev->dev_addr[5] << 8) |
phydev->attached_dev->dev_addr[4])); phydev->attached_dev->dev_addr[4]));
if (err < 0) if (err < 0)
return err; goto error;
err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1, err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
((phydev->attached_dev->dev_addr[3] << 8) | ((phydev->attached_dev->dev_addr[3] << 8) |
phydev->attached_dev->dev_addr[2])); phydev->attached_dev->dev_addr[2]));
if (err < 0) if (err < 0)
return err; goto error;
err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0, err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
((phydev->attached_dev->dev_addr[1] << 8) | ((phydev->attached_dev->dev_addr[1] << 8) |
phydev->attached_dev->dev_addr[0])); phydev->attached_dev->dev_addr[0]));
if (err < 0) if (err < 0)
return err; goto error;
/* Clear WOL status and enable magic packet matching */ /* Clear WOL status and enable magic packet matching */
temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL); err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0,
temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS; MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
temp |= MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE; MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE);
err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
if (err < 0) if (err < 0)
return err; goto error;
} else { } else {
err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE); err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
if (err < 0) if (err < 0)
return err; goto error;
/* Clear WOL status and disable magic packet matching */ /* Clear WOL status and disable magic packet matching */
temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL); err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS; (u16)~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE,
temp &= ~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE; MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
if (err < 0) if (err < 0)
return err; goto error;
} }
err = marvell_set_page(phydev, oldpage); error:
if (err < 0) return phy_restore_page(phydev, oldpage, err);
return err;
return 0;
} }
static int marvell_get_sset_count(struct phy_device *phydev) static int marvell_get_sset_count(struct phy_device *phydev)
@ -1519,14 +1476,10 @@ static u64 marvell_get_stat(struct phy_device *phydev, int i)
{ {
struct marvell_hw_stat stat = marvell_hw_stats[i]; struct marvell_hw_stat stat = marvell_hw_stats[i];
struct marvell_priv *priv = phydev->priv; struct marvell_priv *priv = phydev->priv;
int oldpage, val; int val;
u64 ret; u64 ret;
oldpage = marvell_get_set_page(phydev, stat.page); val = phy_read_paged(phydev, stat.page, stat.reg);
if (oldpage < 0)
return UINT64_MAX;
val = phy_read(phydev, stat.reg);
if (val < 0) { if (val < 0) {
ret = UINT64_MAX; ret = UINT64_MAX;
} else { } else {
@ -1535,8 +1488,6 @@ static u64 marvell_get_stat(struct phy_device *phydev, int i)
ret = priv->stats[i]; ret = priv->stats[i];
} }
marvell_set_page(phydev, oldpage);
return ret; return ret;
} }
@ -1553,25 +1504,21 @@ static void marvell_get_stats(struct phy_device *phydev,
static int m88e1121_get_temp(struct phy_device *phydev, long *temp) static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
{ {
int oldpage; int oldpage;
int ret; int ret = 0;
int val; int val;
*temp = 0; *temp = 0;
mutex_lock(&phydev->lock); oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
if (oldpage < 0)
oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE); goto error;
if (oldpage < 0) {
mutex_unlock(&phydev->lock);
return oldpage;
}
/* Enable temperature sensor */ /* Enable temperature sensor */
ret = phy_read(phydev, MII_88E1121_MISC_TEST); ret = __phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0) if (ret < 0)
goto error; goto error;
ret = phy_write(phydev, MII_88E1121_MISC_TEST, ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN); ret | MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
if (ret < 0) if (ret < 0)
goto error; goto error;
@ -1579,14 +1526,14 @@ static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
/* Wait for temperature to stabilize */ /* Wait for temperature to stabilize */
usleep_range(10000, 12000); usleep_range(10000, 12000);
val = phy_read(phydev, MII_88E1121_MISC_TEST); val = __phy_read(phydev, MII_88E1121_MISC_TEST);
if (val < 0) { if (val < 0) {
ret = val; ret = val;
goto error; goto error;
} }
/* Disable temperature sensor */ /* Disable temperature sensor */
ret = phy_write(phydev, MII_88E1121_MISC_TEST, ret = __phy_write(phydev, MII_88E1121_MISC_TEST,
ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN); ret & ~MII_88E1121_MISC_TEST_TEMP_SENSOR_EN);
if (ret < 0) if (ret < 0)
goto error; goto error;
@ -1594,10 +1541,7 @@ static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000; *temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
error: error:
marvell_set_page(phydev, oldpage); return phy_restore_page(phydev, oldpage, ret);
mutex_unlock(&phydev->lock);
return ret;
} }
static int m88e1121_hwmon_read(struct device *dev, static int m88e1121_hwmon_read(struct device *dev,
@ -1671,118 +1615,64 @@ static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
static int m88e1510_get_temp(struct phy_device *phydev, long *temp) static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
{ {
int oldpage;
int ret; int ret;
*temp = 0; *temp = 0;
mutex_lock(&phydev->lock); ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
MII_88E1510_TEMP_SENSOR);
oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
if (oldpage < 0) {
mutex_unlock(&phydev->lock);
return oldpage;
}
ret = phy_read(phydev, MII_88E1510_TEMP_SENSOR);
if (ret < 0) if (ret < 0)
goto error; return ret;
*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000; *temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
error: return 0;
marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
} }
static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp) static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
{ {
int oldpage;
int ret; int ret;
*temp = 0; *temp = 0;
mutex_lock(&phydev->lock); ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
MII_88E1121_MISC_TEST);
oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
if (oldpage < 0) {
mutex_unlock(&phydev->lock);
return oldpage;
}
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0) if (ret < 0)
goto error; return ret;
*temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >> *temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25; MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
/* convert to mC */ /* convert to mC */
*temp *= 1000; *temp *= 1000;
error: return 0;
marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
} }
static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp) static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
{ {
int oldpage;
int ret;
mutex_lock(&phydev->lock);
oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
if (oldpage < 0) {
mutex_unlock(&phydev->lock);
return oldpage;
}
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0)
goto error;
temp = temp / 1000; temp = temp / 1000;
temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f); temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
ret = phy_write(phydev, MII_88E1121_MISC_TEST,
(ret & ~MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) |
(temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT));
error: return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
marvell_set_page(phydev, oldpage); MII_88E1121_MISC_TEST,
mutex_unlock(&phydev->lock); MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK,
temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT);
return ret;
} }
static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm) static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
{ {
int oldpage;
int ret; int ret;
*alarm = false; *alarm = false;
mutex_lock(&phydev->lock); ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
MII_88E1121_MISC_TEST);
oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
if (oldpage < 0) {
mutex_unlock(&phydev->lock);
return oldpage;
}
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0) if (ret < 0)
goto error; return ret;
*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ); *alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
error: return 0;
marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
} }
static int m88e1510_hwmon_read(struct device *dev, static int m88e1510_hwmon_read(struct device *dev,
@ -1978,6 +1868,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr, .config_intr = &marvell_config_intr,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -1995,6 +1887,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr, .config_intr = &marvell_config_intr,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2013,6 +1907,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr, .config_intr = &marvell_config_intr,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2030,6 +1926,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr, .config_intr = &marvell_config_intr,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2049,6 +1947,8 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt, .did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2070,6 +1970,8 @@ static struct phy_driver marvell_drivers[] = {
.set_wol = &m88e1318_set_wol, .set_wol = &m88e1318_set_wol,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2088,6 +1990,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr, .config_intr = &marvell_config_intr,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2105,6 +2009,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr, .config_intr = &marvell_config_intr,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2122,6 +2028,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr, .config_intr = &marvell_config_intr,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2138,6 +2046,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr, .config_intr = &marvell_config_intr,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2159,6 +2069,8 @@ static struct phy_driver marvell_drivers[] = {
.set_wol = &m88e1318_set_wol, .set_wol = &m88e1318_set_wol,
.resume = &marvell_resume, .resume = &marvell_resume,
.suspend = &marvell_suspend, .suspend = &marvell_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2179,6 +2091,8 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt, .did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2198,6 +2112,8 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt, .did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2217,6 +2133,8 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt, .did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,
@ -2236,6 +2154,8 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt, .did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume, .resume = &genphy_resume,
.suspend = &genphy_suspend, .suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count, .get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings, .get_strings = marvell_get_strings,
.get_stats = marvell_get_stats, .get_stats = marvell_get_stats,