Commit 318ff60e authored by David S. Miller's avatar David S. Miller

Merge branch 'net-Resolve-races-in-phy-accessors'

Russell King says:

====================
Resolve races in phy accessors

This series resolves races with various accesses to PHY registers.
The first five patches are necessary before we add phylink support
to mvneta, the remaining three are merely cleanups for unobserved
races, and hence are less critical.

There are two possible classes of races that can occur: where we
write to a page register that changes the meaning of a group of
other registers, and where we read-modify-write a register.

Resolve these races by performing the accesses under the mdio bus
lock, ensuring that no other user can access the bus while the
series of atomic operations are being performed.

These patches have been posted before, and have been modified
along the lines of previous feedback:

- The third patch was originally reviewed by Florian, but as I've
  added __phy_modify() to it, I've removed that attributation.
- Included generic page-based accessors as suggested last time
  around.
- Since we have the unlocked __phy_modify() in this patch series,
  it is sensible to include the changes for this to marvell.c -
  these accessors have to change anyway to avoid deadlocks on the
  mdio bus lock.

I haven't been able to test the at803x.c changes yet beyond compile
testing - although I do have systems with an ar8035 PHY.  However,
they should be straight forward to review.

This is targetted for net-next because the races have not been
found in existing drivers, but have been observed with phylink
integrated into mvneta - that's not to say that the races do not
exist today, they are just unobserved (probably through lack of
rigorous enough testing.)  The race provoking condition is detailed
in patch 5.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents be6e36d9 fea23fb5
......@@ -215,34 +215,22 @@ static int at803x_suspend(struct phy_device *phydev)
int value;
int wol_enabled;
mutex_lock(&phydev->lock);
value = phy_read(phydev, AT803X_INTR_ENABLE);
wol_enabled = value & AT803X_INTR_ENABLE_WOL;
value = phy_read(phydev, MII_BMCR);
if (wol_enabled)
value |= BMCR_ISOLATE;
value = BMCR_ISOLATE;
else
value |= BMCR_PDOWN;
value = BMCR_PDOWN;
phy_write(phydev, MII_BMCR, value);
mutex_unlock(&phydev->lock);
phy_modify(phydev, MII_BMCR, 0, value);
return 0;
}
static int at803x_resume(struct phy_device *phydev)
{
int value;
value = phy_read(phydev, MII_BMCR);
value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
phy_write(phydev, MII_BMCR, value);
return 0;
return phy_modify(phydev, MII_BMCR, ~(BMCR_PDOWN | BMCR_ISOLATE), 0);
}
static int at803x_probe(struct phy_device *phydev)
......
......@@ -83,7 +83,7 @@
#define MII_88E1121_PHY_MSCR_REG 21
#define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5)
#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_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK 0x1f00
......@@ -177,27 +177,19 @@ struct marvell_priv {
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_set_page(struct phy_device *phydev, int page)
static int marvell_write_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)
static int marvell_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;
return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
}
static int marvell_ack_interrupt(struct phy_device *phydev)
......@@ -399,7 +391,7 @@ static int m88e1111_config_aneg(struct phy_device *phydev)
static int marvell_of_reg_init(struct phy_device *phydev)
{
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)
return 0;
......@@ -409,12 +401,11 @@ static int marvell_of_reg_init(struct phy_device *phydev)
if (!paddr || len < (4 * sizeof(*paddr)))
return 0;
saved_page = marvell_get_page(phydev);
saved_page = phy_save_page(phydev);
if (saved_page < 0)
return saved_page;
goto err;
current_page = saved_page;
ret = 0;
len /= sizeof(*paddr);
for (i = 0; i < len - 3; i += 4) {
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) {
current_page = page;
ret = marvell_set_page(phydev, page);
ret = marvell_write_page(phydev, page);
if (ret < 0)
goto err;
}
val = 0;
if (mask) {
val = phy_read(phydev, reg);
val = __phy_read(phydev, reg);
if (val < 0) {
ret = val;
goto err;
......@@ -441,17 +432,12 @@ static int marvell_of_reg_init(struct phy_device *phydev)
}
val |= val_bits;
ret = phy_write(phydev, reg, val);
ret = __phy_write(phydev, reg, val);
if (ret < 0)
goto err;
}
err:
if (current_page != saved_page) {
i = marvell_set_page(phydev, saved_page);
if (ret == 0)
ret = i;
}
return ret;
return phy_restore_page(phydev, saved_page, ret);
}
#else
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)
{
int err, oldpage, 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;
int mscr;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
mscr |= (MII_88E1121_PHY_MSCR_RX_DELAY |
MII_88E1121_PHY_MSCR_TX_DELAY);
mscr = MII_88E1121_PHY_MSCR_RX_DELAY |
MII_88E1121_PHY_MSCR_TX_DELAY;
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)
mscr |= MII_88E1121_PHY_MSCR_TX_DELAY;
err = phy_write(phydev, MII_88E1121_PHY_MSCR_REG, mscr);
out:
marvell_set_page(phydev, oldpage);
mscr = MII_88E1121_PHY_MSCR_TX_DELAY;
else
mscr = 0;
return err;
return phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
MII_88E1121_PHY_MSCR_REG,
MII_88E1121_PHY_MSCR_DELAY_MASK, mscr);
}
static int m88e1121_config_aneg(struct phy_device *phydev)
......@@ -498,7 +471,7 @@ static int m88e1121_config_aneg(struct phy_device *phydev)
if (phy_interface_is_rgmii(phydev)) {
err = m88e1121_config_aneg_rgmii_delays(phydev);
if (err)
if (err < 0)
return err;
}
......@@ -515,20 +488,11 @@ static int m88e1121_config_aneg(struct phy_device *phydev)
static int m88e1318_config_aneg(struct phy_device *phydev)
{
int err, oldpage, mscr;
oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE);
if (oldpage < 0)
return oldpage;
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;
int err;
err = marvell_set_page(phydev, oldpage);
err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
MII_88E1318S_PHY_MSCR1_REG,
0, MII_88E1318S_PHY_MSCR1_PAD_ODD);
if (err < 0)
return err;
......@@ -700,19 +664,14 @@ static int m88e1116r_config_init(struct phy_device *phydev)
static int m88e3016_config_init(struct phy_device *phydev)
{
int reg;
int ret;
/* Enable Scrambler and Auto-Crossover */
reg = phy_read(phydev, MII_88E3016_PHY_SPEC_CTRL);
if (reg < 0)
return reg;
reg &= ~MII_88E3016_DISABLE_SCRAMBLER;
reg |= MII_88E3016_AUTO_MDIX_CROSSOVER;
reg = phy_write(phydev, MII_88E3016_PHY_SPEC_CTRL, reg);
if (reg < 0)
return reg;
ret = phy_modify(phydev, MII_88E3016_PHY_SPEC_CTRL,
~MII_88E3016_DISABLE_SCRAMBLER,
MII_88E3016_AUTO_MDIX_CROSSOVER);
if (ret < 0)
return ret;
return marvell_config_init(phydev);
}
......@@ -721,42 +680,34 @@ static int m88e1111_config_init_hwcfg_mode(struct phy_device *phydev,
u16 mode,
int fibre_copper_auto)
{
int temp;
temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
if (temp < 0)
return temp;
temp &= ~(MII_M1111_HWCFG_MODE_MASK |
MII_M1111_HWCFG_FIBER_COPPER_AUTO |
MII_M1111_HWCFG_FIBER_COPPER_RES);
temp |= mode;
if (fibre_copper_auto)
temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
mode |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
return phy_modify(phydev, MII_M1111_PHY_EXT_SR,
(u16)~(MII_M1111_HWCFG_MODE_MASK |
MII_M1111_HWCFG_FIBER_COPPER_AUTO |
MII_M1111_HWCFG_FIBER_COPPER_RES),
mode);
}
static int m88e1111_config_init_rgmii_delays(struct phy_device *phydev)
{
int temp;
temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
if (temp < 0)
return temp;
int delay;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
temp |= (MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY);
delay = MII_M1111_RGMII_RX_DELAY | MII_M1111_RGMII_TX_DELAY;
} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
temp &= ~MII_M1111_RGMII_TX_DELAY;
temp |= MII_M1111_RGMII_RX_DELAY;
delay = MII_M1111_RGMII_RX_DELAY;
} else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
temp &= ~MII_M1111_RGMII_RX_DELAY;
temp |= MII_M1111_RGMII_TX_DELAY;
delay = MII_M1111_RGMII_TX_DELAY;
} else {
delay = 0;
}
return phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
return phy_modify(phydev, MII_M1111_PHY_EXT_CR,
(u16)~(MII_M1111_RGMII_RX_DELAY |
MII_M1111_RGMII_TX_DELAY),
delay);
}
static int m88e1111_config_init_rgmii(struct phy_device *phydev)
......@@ -802,7 +753,7 @@ static int m88e1111_config_init_rtbi(struct phy_device *phydev)
int err;
err = m88e1111_config_init_rgmii_delays(phydev);
if (err)
if (err < 0)
return err;
err = m88e1111_config_init_hwcfg_mode(
......@@ -829,7 +780,7 @@ static int m88e1111_config_init(struct phy_device *phydev)
if (phy_interface_is_rgmii(phydev)) {
err = m88e1111_config_init_rgmii(phydev);
if (err)
if (err < 0)
return err;
}
......@@ -854,20 +805,15 @@ static int m88e1111_config_init(struct phy_device *phydev)
static int m88e1121_config_init(struct phy_device *phydev)
{
int err, oldpage;
oldpage = marvell_get_set_page(phydev, MII_MARVELL_LED_PAGE);
if (oldpage < 0)
return oldpage;
int err;
/* 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);
if (err < 0)
return err;
marvell_set_page(phydev, oldpage);
/* Set marvell,reg-init configuration from device tree */
return marvell_config_init(phydev);
}
......@@ -875,7 +821,6 @@ static int m88e1121_config_init(struct phy_device *phydev)
static int m88e1510_config_init(struct phy_device *phydev)
{
int err;
int temp;
/* SGMII-to-Copper mode initialization */
if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
......@@ -887,16 +832,15 @@ static int m88e1510_config_init(struct phy_device *phydev)
return err;
/* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */
temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1);
temp &= ~MII_88E1510_GEN_CTRL_REG_1_MODE_MASK;
temp |= MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII;
err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1,
~MII_88E1510_GEN_CTRL_REG_1_MODE_MASK,
MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII);
if (err < 0)
return err;
/* PHY reset is necessary after changing MODE[2:0] */
temp |= MII_88E1510_GEN_CTRL_REG_1_RESET;
err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp);
err = phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, 0,
MII_88E1510_GEN_CTRL_REG_1_RESET);
if (err < 0)
return err;
......@@ -1002,7 +946,6 @@ static int m88e1149_config_init(struct phy_device *phydev)
static int m88e1145_config_init_rgmii(struct phy_device *phydev)
{
int temp;
int err;
err = m88e1111_config_init_rgmii_delays(phydev);
......@@ -1014,15 +957,9 @@ static int m88e1145_config_init_rgmii(struct phy_device *phydev)
if (err < 0)
return err;
temp = phy_read(phydev, 0x1e);
if (temp < 0)
return temp;
temp &= 0xf03f;
temp |= 2 << 9; /* 36 ohm */
temp |= 2 << 6; /* 39 ohm */
err = phy_write(phydev, 0x1e, temp);
err = phy_modify(phydev, 0x1e, 0xf03f,
2 << 9 | /* 36 ohm */
2 << 6); /* 39 ohm */
if (err < 0)
return err;
......@@ -1398,100 +1335,98 @@ static int m88e1121_did_interrupt(struct phy_device *phydev)
static void m88e1318_get_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
int oldpage, ret = 0;
wol->supported = WAKE_MAGIC;
wol->wolopts = 0;
if (marvell_set_page(phydev, MII_MARVELL_WOL_PAGE) < 0)
return;
oldpage = phy_select_page(phydev, MII_MARVELL_WOL_PAGE);
if (oldpage < 0)
goto error;
if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) &
MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
ret = __phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
if (ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
wol->wolopts |= WAKE_MAGIC;
if (marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE) < 0)
return;
error:
phy_restore_page(phydev, oldpage, ret);
}
static int m88e1318_set_wol(struct phy_device *phydev,
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) {
/* 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)
return err;
goto error;
/* Enable the WOL interrupt */
temp = phy_read(phydev, MII_88E1318S_PHY_CSIER);
temp |= MII_88E1318S_PHY_CSIER_WOL_EIE;
err = phy_write(phydev, MII_88E1318S_PHY_CSIER, temp);
err = __phy_modify(phydev, MII_88E1318S_PHY_CSIER, 0,
MII_88E1318S_PHY_CSIER_WOL_EIE);
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)
return err;
goto error;
/* Setup LED[2] as interrupt pin (active low) */
temp = phy_read(phydev, MII_88E1318S_PHY_LED_TCR);
temp &= ~MII_88E1318S_PHY_LED_TCR_FORCE_INT;
temp |= MII_88E1318S_PHY_LED_TCR_INTn_ENABLE;
temp |= MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW;
err = phy_write(phydev, MII_88E1318S_PHY_LED_TCR, temp);
err = __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR,
(u16)~MII_88E1318S_PHY_LED_TCR_FORCE_INT,
MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
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)
return err;
goto error;
/* 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[4]));
if (err < 0)
return err;
err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
goto error;
err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD1,
((phydev->attached_dev->dev_addr[3] << 8) |
phydev->attached_dev->dev_addr[2]));
if (err < 0)
return err;
err = phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
goto error;
err = __phy_write(phydev, MII_88E1318S_PHY_MAGIC_PACKET_WORD0,
((phydev->attached_dev->dev_addr[1] << 8) |
phydev->attached_dev->dev_addr[0]));
if (err < 0)
return err;
goto error;
/* Clear WOL status and enable magic packet matching */
temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
temp |= MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0,
MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS |
MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE);
if (err < 0)
return err;
goto error;
} else {
err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE);
err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);
if (err < 0)
return err;
goto error;
/* Clear WOL status and disable magic packet matching */
temp = phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL);
temp |= MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS;
temp &= ~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE;
err = phy_write(phydev, MII_88E1318S_PHY_WOL_CTRL, temp);
err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL,
(u16)~MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE,
MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
if (err < 0)
return err;
goto error;
}
err = marvell_set_page(phydev, oldpage);
if (err < 0)
return err;
return 0;
error:
return phy_restore_page(phydev, oldpage, err);
}
static int marvell_get_sset_count(struct phy_device *phydev)
......@@ -1519,14 +1454,10 @@ static u64 marvell_get_stat(struct phy_device *phydev, int i)
{
struct marvell_hw_stat stat = marvell_hw_stats[i];
struct marvell_priv *priv = phydev->priv;
int oldpage, val;
int val;
u64 ret;
oldpage = marvell_get_set_page(phydev, stat.page);
if (oldpage < 0)
return UINT64_MAX;
val = phy_read(phydev, stat.reg);
val = phy_read_paged(phydev, stat.page, stat.reg);
if (val < 0) {
ret = UINT64_MAX;
} else {
......@@ -1535,8 +1466,6 @@ static u64 marvell_get_stat(struct phy_device *phydev, int i)
ret = priv->stats[i];
}
marvell_set_page(phydev, oldpage);
return ret;
}
......@@ -1553,25 +1482,21 @@ static void marvell_get_stats(struct phy_device *phydev,
static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
{
int oldpage;
int ret;
int ret = 0;
int val;
*temp = 0;
mutex_lock(&phydev->lock);
oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
if (oldpage < 0) {
mutex_unlock(&phydev->lock);
return oldpage;
}
oldpage = phy_select_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
if (oldpage < 0)
goto error;
/* Enable temperature sensor */
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
ret = __phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0)
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);
if (ret < 0)
goto error;
......@@ -1579,14 +1504,14 @@ static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
/* Wait for temperature to stabilize */
usleep_range(10000, 12000);
val = phy_read(phydev, MII_88E1121_MISC_TEST);
val = __phy_read(phydev, MII_88E1121_MISC_TEST);
if (val < 0) {
ret = val;
goto error;
}
/* 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);
if (ret < 0)
goto error;
......@@ -1594,10 +1519,7 @@ static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
error:
marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
return phy_restore_page(phydev, oldpage, ret);
}
static int m88e1121_hwmon_read(struct device *dev,
......@@ -1671,118 +1593,64 @@ static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
{
int oldpage;
int ret;
*temp = 0;
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_88E1510_TEMP_SENSOR);
ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
MII_88E1510_TEMP_SENSOR);
if (ret < 0)
goto error;
return ret;
*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
error:
marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
return 0;
}
static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
{
int oldpage;
int ret;
*temp = 0;
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);
ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
MII_88E1121_MISC_TEST);
if (ret < 0)
goto error;
return ret;
*temp = (((ret & MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK) >>
MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT) * 5) - 25;
/* convert to mC */
*temp *= 1000;
error:
marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
return 0;
}
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 = 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:
marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
MII_88E1121_MISC_TEST,
MII_88E1510_MISC_TEST_TEMP_THRESHOLD_MASK,
temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT);
}
static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
{
int oldpage;
int ret;
*alarm = false;
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);
ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE,
MII_88E1121_MISC_TEST);
if (ret < 0)
goto error;
*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
return ret;
error:
marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
return ret;
return 0;
}
static int m88e1510_hwmon_read(struct device *dev,
......@@ -1978,6 +1846,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -1995,6 +1865,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2013,6 +1885,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2030,6 +1904,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2049,6 +1925,8 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2070,6 +1948,8 @@ static struct phy_driver marvell_drivers[] = {
.set_wol = &m88e1318_set_wol,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2088,6 +1968,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2105,6 +1987,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2122,6 +2006,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2138,6 +2024,8 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2159,6 +2047,8 @@ static struct phy_driver marvell_drivers[] = {
.set_wol = &m88e1318_set_wol,
.resume = &marvell_resume,
.suspend = &marvell_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2179,6 +2069,8 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2198,6 +2090,8 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2217,6 +2111,8 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......@@ -2236,6 +2132,8 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
......
......@@ -514,6 +514,55 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
}
EXPORT_SYMBOL(mdiobus_scan);
/**
* __mdiobus_read - Unlocked version of the mdiobus_read function
* @bus: the mii_bus struct
* @addr: the phy address
* @regnum: register number to read
*
* Read a MDIO bus register. Caller must hold the mdio bus lock.
*
* NOTE: MUST NOT be called from interrupt context.
*/
int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
{
int retval;
WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock));
retval = bus->read(bus, addr, regnum);
trace_mdio_access(bus, 1, addr, regnum, retval, retval);
return retval;
}
EXPORT_SYMBOL(__mdiobus_read);
/**
* __mdiobus_write - Unlocked version of the mdiobus_write function
* @bus: the mii_bus struct
* @addr: the phy address
* @regnum: register number to write
* @val: value to write to @regnum
*
* Write a MDIO bus register. Caller must hold the mdio bus lock.
*
* NOTE: MUST NOT be called from interrupt context.
*/
int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
{
int err;
WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock));
err = bus->write(bus, addr, regnum, val);
trace_mdio_access(bus, 0, addr, regnum, val, err);
return err;
}
EXPORT_SYMBOL(__mdiobus_write);
/**
* mdiobus_read_nested - Nested version of the mdiobus_read function
* @bus: the mii_bus struct
......@@ -534,11 +583,9 @@ int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum)
BUG_ON(in_interrupt());
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
retval = bus->read(bus, addr, regnum);
retval = __mdiobus_read(bus, addr, regnum);
mutex_unlock(&bus->mdio_lock);
trace_mdio_access(bus, 1, addr, regnum, retval, retval);
return retval;
}
EXPORT_SYMBOL(mdiobus_read_nested);
......@@ -560,11 +607,9 @@ int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
BUG_ON(in_interrupt());
mutex_lock(&bus->mdio_lock);
retval = bus->read(bus, addr, regnum);
retval = __mdiobus_read(bus, addr, regnum);
mutex_unlock(&bus->mdio_lock);
trace_mdio_access(bus, 1, addr, regnum, retval, retval);
return retval;
}
EXPORT_SYMBOL(mdiobus_read);
......@@ -590,11 +635,9 @@ int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val)
BUG_ON(in_interrupt());
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
err = bus->write(bus, addr, regnum, val);
err = __mdiobus_write(bus, addr, regnum, val);
mutex_unlock(&bus->mdio_lock);
trace_mdio_access(bus, 0, addr, regnum, val, err);
return err;
}
EXPORT_SYMBOL(mdiobus_write_nested);
......@@ -617,11 +660,9 @@ int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
BUG_ON(in_interrupt());
mutex_lock(&bus->mdio_lock);
err = bus->write(bus, addr, regnum, val);
err = __mdiobus_write(bus, addr, regnum, val);
mutex_unlock(&bus->mdio_lock);
trace_mdio_access(bus, 0, addr, regnum, val, err);
return err;
}
EXPORT_SYMBOL(mdiobus_write);
......
......@@ -236,13 +236,14 @@ static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad,
u16 regnum)
{
/* Write the desired MMD Devad */
bus->write(bus, phy_addr, MII_MMD_CTRL, devad);
__mdiobus_write(bus, phy_addr, MII_MMD_CTRL, devad);
/* Write the desired MMD register address */
bus->write(bus, phy_addr, MII_MMD_DATA, regnum);
__mdiobus_write(bus, phy_addr, MII_MMD_DATA, regnum);
/* Select the Function : DATA with no post increment */
bus->write(bus, phy_addr, MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR);
__mdiobus_write(bus, phy_addr, MII_MMD_CTRL,
devad | MII_MMD_CTRL_NOINCR);
}
/**
......@@ -275,7 +276,7 @@ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
mmd_phy_indirect(bus, phy_addr, devad, regnum);
/* Read the content of the MMD's selected register */
val = bus->read(bus, phy_addr, MII_MMD_DATA);
val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
mutex_unlock(&bus->mdio_lock);
}
return val;
......@@ -314,7 +315,7 @@ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
mmd_phy_indirect(bus, phy_addr, devad, regnum);
/* Write the data into MMD's selected register */
bus->write(bus, phy_addr, MII_MMD_DATA, val);
__mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
mutex_unlock(&bus->mdio_lock);
ret = 0;
......@@ -322,3 +323,208 @@ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
return ret;
}
EXPORT_SYMBOL(phy_write_mmd);
/**
* __phy_modify() - Convenience function for modifying a PHY register
* @phydev: a pointer to a &struct phy_device
* @regnum: register number
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
*
* Unlocked helper function which allows a PHY register to be modified as
* new register value = (old register value & mask) | set
*/
int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
{
int ret, res;
ret = __phy_read(phydev, regnum);
if (ret >= 0) {
res = __phy_write(phydev, regnum, (ret & ~mask) | set);
if (res < 0)
ret = res;
}
return ret;
}
EXPORT_SYMBOL_GPL(__phy_modify);
/**
* phy_modify - Convenience function for modifying a given PHY register
* @phydev: the phy_device struct
* @regnum: register number to write
* @mask: bit mask of bits to clear
* @set: new value of bits set in mask to write to @regnum
*
* NOTE: MUST NOT be called from interrupt context,
* because the bus read/write functions may wait for an interrupt
* to conclude the operation.
*/
int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
{
int ret;
mutex_lock(&phydev->mdio.bus->mdio_lock);
ret = __phy_modify(phydev, regnum, mask, set);
mutex_unlock(&phydev->mdio.bus->mdio_lock);
return ret;
}
EXPORT_SYMBOL_GPL(phy_modify);
static int __phy_read_page(struct phy_device *phydev)
{
return phydev->drv->read_page(phydev);
}
static int __phy_write_page(struct phy_device *phydev, int page)
{
return phydev->drv->write_page(phydev, page);
}
/**
* phy_save_page() - take the bus lock and save the current page
* @phydev: a pointer to a &struct phy_device
*
* Take the MDIO bus lock, and return the current page number. On error,
* returns a negative errno. phy_restore_page() must always be called
* after this, irrespective of success or failure of this call.
*/
int phy_save_page(struct phy_device *phydev)
{
mutex_lock(&phydev->mdio.bus->mdio_lock);
return __phy_read_page(phydev);
}
EXPORT_SYMBOL_GPL(phy_save_page);
/**
* phy_select_page() - take the bus lock, save the current page, and set a page
* @phydev: a pointer to a &struct phy_device
* @page: desired page
*
* Take the MDIO bus lock to protect against concurrent access, save the
* current PHY page, and set the current page. On error, returns a
* negative errno, otherwise returns the previous page number.
* phy_restore_page() must always be called after this, irrespective
* of success or failure of this call.
*/
int phy_select_page(struct phy_device *phydev, int page)
{
int ret, oldpage;
oldpage = ret = phy_save_page(phydev);
if (ret < 0)
return ret;
if (oldpage != page) {
ret = __phy_write_page(phydev, page);
if (ret < 0)
return ret;
}
return oldpage;
}
EXPORT_SYMBOL_GPL(phy_select_page);
/**
* phy_restore_page() - restore the page register and release the bus lock
* @phydev: a pointer to a &struct phy_device
* @oldpage: the old page, return value from phy_save_page() or phy_select_page()
* @ret: operation's return code
*
* Release the MDIO bus lock, restoring @oldpage if it is a valid page.
* This function propagates the earliest error code from the group of
* operations.
*
* Returns:
* @oldpage if it was a negative value, otherwise
* @ret if it was a negative errno value, otherwise
* phy_write_page()'s negative value if it were in error, otherwise
* @ret.
*/
int phy_restore_page(struct phy_device *phydev, int oldpage, int ret)
{
int r;
if (oldpage >= 0) {
r = __phy_write_page(phydev, oldpage);
/* Propagate the operation return code if the page write
* was successful.
*/
if (ret >= 0 && r < 0)
ret = r;
} else {
/* Propagate the phy page selection error code */
ret = oldpage;
}
mutex_unlock(&phydev->mdio.bus->mdio_lock);
return ret;
}
EXPORT_SYMBOL_GPL(phy_restore_page);
/**
* phy_read_paged() - Convenience function for reading a paged register
* @phydev: a pointer to a &struct phy_device
* @page: the page for the phy
* @regnum: register number
*
* Same rules as for phy_read().
*/
int phy_read_paged(struct phy_device *phydev, int page, u32 regnum)
{
int ret = 0, oldpage;
oldpage = phy_select_page(phydev, page);
if (oldpage >= 0)
ret = __phy_read(phydev, regnum);
return phy_restore_page(phydev, oldpage, ret);
}
EXPORT_SYMBOL(phy_read_paged);
/**
* phy_write_paged() - Convenience function for writing a paged register
* @phydev: a pointer to a &struct phy_device
* @page: the page for the phy
* @regnum: register number
* @val: value to write
*
* Same rules as for phy_write().
*/
int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
{
int ret = 0, oldpage;
oldpage = phy_select_page(phydev, page);
if (oldpage >= 0)
ret = __phy_write(phydev, regnum, val);
return phy_restore_page(phydev, oldpage, ret);
}
EXPORT_SYMBOL(phy_write_paged);
/**
* phy_modify_paged() - Convenience function for modifying a paged register
* @phydev: a pointer to a &struct phy_device
* @page: the page for the phy
* @regnum: register number
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
*
* Same rules as for phy_read() and phy_write().
*/
int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
u16 mask, u16 set)
{
int ret = 0, oldpage;
oldpage = phy_select_page(phydev, page);
if (oldpage >= 0)
ret = __phy_modify(phydev, regnum, mask, set);
return phy_restore_page(phydev, oldpage, ret);
}
EXPORT_SYMBOL(phy_modify_paged);
......@@ -1368,9 +1368,8 @@ static int genphy_config_eee_advert(struct phy_device *phydev)
*/
int genphy_setup_forced(struct phy_device *phydev)
{
int ctl = phy_read(phydev, MII_BMCR);
u16 ctl = 0;
ctl &= BMCR_LOOPBACK | BMCR_ISOLATE | BMCR_PDOWN;
phydev->pause = 0;
phydev->asym_pause = 0;
......@@ -1382,7 +1381,8 @@ int genphy_setup_forced(struct phy_device *phydev)
if (DUPLEX_FULL == phydev->duplex)
ctl |= BMCR_FULLDPLX;
return phy_write(phydev, MII_BMCR, ctl);
return phy_modify(phydev, MII_BMCR,
BMCR_LOOPBACK | BMCR_ISOLATE | BMCR_PDOWN, ctl);
}
EXPORT_SYMBOL(genphy_setup_forced);
......@@ -1392,17 +1392,9 @@ EXPORT_SYMBOL(genphy_setup_forced);
*/
int genphy_restart_aneg(struct phy_device *phydev)
{
int ctl = phy_read(phydev, MII_BMCR);
if (ctl < 0)
return ctl;
ctl |= BMCR_ANENABLE | BMCR_ANRESTART;
/* Don't isolate the PHY if we're negotiating */
ctl &= ~BMCR_ISOLATE;
return phy_write(phydev, MII_BMCR, ctl);
return phy_modify(phydev, MII_BMCR, ~BMCR_ISOLATE,
BMCR_ANENABLE | BMCR_ANRESTART);
}
EXPORT_SYMBOL(genphy_restart_aneg);
......@@ -1668,44 +1660,20 @@ EXPORT_SYMBOL(genphy_config_init);
int genphy_suspend(struct phy_device *phydev)
{
int value;
mutex_lock(&phydev->lock);
value = phy_read(phydev, MII_BMCR);
phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
mutex_unlock(&phydev->lock);
return 0;
return phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);
}
EXPORT_SYMBOL(genphy_suspend);
int genphy_resume(struct phy_device *phydev)
{
int value;
value = phy_read(phydev, MII_BMCR);
phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
return 0;
return phy_modify(phydev, MII_BMCR, ~BMCR_PDOWN, 0);
}
EXPORT_SYMBOL(genphy_resume);
int genphy_loopback(struct phy_device *phydev, bool enable)
{
int value;
value = phy_read(phydev, MII_BMCR);
if (value < 0)
return value;
if (enable)
value |= BMCR_LOOPBACK;
else
value &= ~BMCR_LOOPBACK;
return phy_write(phydev, MII_BMCR, value);
return phy_modify(phydev, MII_BMCR, ~BMCR_LOOPBACK,
enable ? BMCR_LOOPBACK : 0);
}
EXPORT_SYMBOL(genphy_loopback);
......
......@@ -262,6 +262,9 @@ static inline u16 ethtool_adv_to_mmd_eee_adv_t(u32 adv)
return reg;
}
int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
......
......@@ -634,6 +634,9 @@ struct phy_driver {
int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum,
u16 val);
int (*read_page)(struct phy_device *dev);
int (*write_page)(struct phy_device *dev, int page);
/* Get the size and type of the eeprom contained within a plug-in
* module */
int (*module_info)(struct phy_device *dev,
......@@ -717,6 +720,18 @@ static inline int phy_read(struct phy_device *phydev, u32 regnum)
return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, regnum);
}
/**
* __phy_read - convenience function for reading a given PHY register
* @phydev: the phy_device struct
* @regnum: register number to read
*
* The caller must have taken the MDIO bus lock.
*/
static inline int __phy_read(struct phy_device *phydev, u32 regnum)
{
return __mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, regnum);
}
/**
* phy_write - Convenience function for writing a given PHY register
* @phydev: the phy_device struct
......@@ -732,6 +747,23 @@ static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val)
return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val);
}
/**
* __phy_write - Convenience function for writing a given PHY register
* @phydev: the phy_device struct
* @regnum: register number to write
* @val: value to write to @regnum
*
* The caller must have taken the MDIO bus lock.
*/
static inline int __phy_write(struct phy_device *phydev, u32 regnum, u16 val)
{
return __mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum,
val);
}
int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set);
int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set);
/**
* phy_interrupt_is_valid - Convenience function for testing a given PHY irq
* @phydev: the phy_device struct
......@@ -810,6 +842,14 @@ static inline bool phy_is_pseudo_fixed_link(struct phy_device *phydev)
*/
int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val);
int phy_save_page(struct phy_device *phydev);
int phy_select_page(struct phy_device *phydev, int page);
int phy_restore_page(struct phy_device *phydev, int oldpage, int ret);
int phy_read_paged(struct phy_device *phydev, int page, u32 regnum);
int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val);
int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
u16 mask, u16 set);
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
bool is_c45,
struct phy_c45_device_ids *c45_ids);
......
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