Commit 989e9ad0 authored by David S. Miller's avatar David S. Miller

Merge branch 'libphy_mmd'

Vince Bridgers says:

====================
net: libphy: Add phy specific functions to access mmd regs

This set of patches addresses a problem found with the Micrel ksz9021 phy and
libphy, where the ksz9021 phy does not support mmd extended register access
per the IEEE specification as assumed by libphy. The first patch adds a
framework for phy specific support to specify their own function to access
extended phy registers, return a failure code if not supported, or to default
to libphy's IEEE defined method for accessing the mmd extended phy registers.

This issue was found by using the Synopsys EMAC and a Micrel ksz9021 phy on the
Altera Cyclone 5 SOC development kit. This patch was tested on the same system
in both positive and negative test cases.

V5: Revert name of mmd register access functions, check for phy specific
    driver override functions in mmd register access functions per
    Florian's comments to minimize source code changes
V4: Correct error when formatting V3 patch - erroneous text cut from code
V3: Correct formatting of function arguments, remove return statement from
    NULL functions, and add patch for PHY driver documentation per review
    comments.
V2: Split the original patch submission into seperate patches for the libphy
    framework required for the modification and for the Micrel Phy.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9e6492ec 49193a66
...@@ -272,6 +272,8 @@ Writing a PHY driver ...@@ -272,6 +272,8 @@ Writing a PHY driver
txtsamp: Requests a transmit timestamp at the PHY level for a 'skb' txtsamp: Requests a transmit timestamp at the PHY level for a 'skb'
set_wol: Enable Wake-on-LAN at the PHY level set_wol: Enable Wake-on-LAN at the PHY level
get_wol: Get the Wake-on-LAN status at the PHY level get_wol: Get the Wake-on-LAN status at the PHY level
read_mmd_indirect: Read PHY MMD indirect register
write_mmd_indirect: Write PHY MMD indirect register
Of these, only config_aneg and read_status are required to be Of these, only config_aneg and read_status are required to be
assigned by the driver code. The rest are optional. Also, it is assigned by the driver code. The rest are optional. Also, it is
...@@ -284,7 +286,21 @@ Writing a PHY driver ...@@ -284,7 +286,21 @@ Writing a PHY driver
Feel free to look at the Marvell, Cicada, and Davicom drivers in Feel free to look at the Marvell, Cicada, and Davicom drivers in
drivers/net/phy/ for examples (the lxt and qsemi drivers have drivers/net/phy/ for examples (the lxt and qsemi drivers have
not been tested as of this writing) not been tested as of this writing).
The PHY's MMD register accesses are handled by the PAL framework
by default, but can be overridden by a specific PHY driver if
required. This could be the case if a PHY was released for
manufacturing before the MMD PHY register definitions were
standardized by the IEEE. Most modern PHYs will be able to use
the generic PAL framework for accessing the PHY's MMD registers.
An example of such usage is for Energy Efficient Ethernet support,
implemented in the PAL. This support uses the PAL to access MMD
registers for EEE query and configuration if the PHY supports
the IEEE standard access mechanisms, or can use the PHY's specific
access interfaces if overridden by the specific PHY driver. See
the Micrel driver in drivers/net/phy/ for an example of how this
can be implemented.
Board Fixups Board Fixups
......
...@@ -420,6 +420,26 @@ static int ksz8873mll_config_aneg(struct phy_device *phydev) ...@@ -420,6 +420,26 @@ static int ksz8873mll_config_aneg(struct phy_device *phydev)
return 0; return 0;
} }
/* This routine returns -1 as an indication to the caller that the
* Micrel ksz9021 10/100/1000 PHY does not support standard IEEE
* MMD extended PHY registers.
*/
static int
ksz9021_rd_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum,
int regnum)
{
return -1;
}
/* This routine does nothing since the Micrel ksz9021 does not support
* standard IEEE MMD extended PHY registers.
*/
static void
ksz9021_wr_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum,
int regnum, u32 val)
{
}
static struct phy_driver ksphy_driver[] = { static struct phy_driver ksphy_driver[] = {
{ {
.phy_id = PHY_ID_KS8737, .phy_id = PHY_ID_KS8737,
...@@ -565,6 +585,8 @@ static struct phy_driver ksphy_driver[] = { ...@@ -565,6 +585,8 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = ksz9021_config_intr, .config_intr = ksz9021_config_intr,
.suspend = genphy_suspend, .suspend = genphy_suspend,
.resume = genphy_resume, .resume = genphy_resume,
.read_mmd_indirect = ksz9021_rd_mmd_phyreg,
.write_mmd_indirect = ksz9021_wr_mmd_phyreg,
.driver = { .owner = THIS_MODULE, }, .driver = { .owner = THIS_MODULE, },
}, { }, {
.phy_id = PHY_ID_KSZ9031, .phy_id = PHY_ID_KSZ9031,
......
...@@ -942,7 +942,7 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, ...@@ -942,7 +942,7 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
/** /**
* phy_read_mmd_indirect - reads data from the MMD registers * phy_read_mmd_indirect - reads data from the MMD registers
* @bus: the target MII bus * @phydev: The PHY device bus
* @prtad: MMD Address * @prtad: MMD Address
* @devad: MMD DEVAD * @devad: MMD DEVAD
* @addr: PHY address on the MII bus * @addr: PHY address on the MII bus
...@@ -955,18 +955,26 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, ...@@ -955,18 +955,26 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
* 3) Write reg 13 // MMD Data Command for MMD DEVAD * 3) Write reg 13 // MMD Data Command for MMD DEVAD
* 3) Read reg 14 // Read MMD data * 3) Read reg 14 // Read MMD data
*/ */
static int phy_read_mmd_indirect(struct mii_bus *bus, int prtad, int devad, static int phy_read_mmd_indirect(struct phy_device *phydev, int prtad,
int addr) int devad, int addr)
{ {
mmd_phy_indirect(bus, prtad, devad, addr); struct phy_driver *phydrv = phydev->drv;
int value = -1;
if (phydrv->read_mmd_indirect == NULL) {
mmd_phy_indirect(phydev->bus, prtad, devad, addr);
/* Read the content of the MMD's selected register */ /* Read the content of the MMD's selected register */
return bus->read(bus, addr, MII_MMD_DATA); value = phydev->bus->read(phydev->bus, addr, MII_MMD_DATA);
} else {
value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr);
}
return value;
} }
/** /**
* phy_write_mmd_indirect - writes data to the MMD registers * phy_write_mmd_indirect - writes data to the MMD registers
* @bus: the target MII bus * @phydev: The PHY device
* @prtad: MMD Address * @prtad: MMD Address
* @devad: MMD DEVAD * @devad: MMD DEVAD
* @addr: PHY address on the MII bus * @addr: PHY address on the MII bus
...@@ -980,13 +988,19 @@ static int phy_read_mmd_indirect(struct mii_bus *bus, int prtad, int devad, ...@@ -980,13 +988,19 @@ static int phy_read_mmd_indirect(struct mii_bus *bus, int prtad, int devad,
* 3) Write reg 13 // MMD Data Command for MMD DEVAD * 3) Write reg 13 // MMD Data Command for MMD DEVAD
* 3) Write reg 14 // Write MMD data * 3) Write reg 14 // Write MMD data
*/ */
static void phy_write_mmd_indirect(struct mii_bus *bus, int prtad, int devad, static void phy_write_mmd_indirect(struct phy_device *phydev, int prtad,
int addr, u32 data) int devad, int addr, u32 data)
{ {
mmd_phy_indirect(bus, prtad, devad, addr); struct phy_driver *phydrv = phydev->drv;
if (phydrv->write_mmd_indirect == NULL) {
mmd_phy_indirect(phydev->bus, prtad, devad, addr);
/* Write the data into MMD's selected register */ /* Write the data into MMD's selected register */
bus->write(bus, addr, MII_MMD_DATA, data); phydev->bus->write(phydev->bus, addr, MII_MMD_DATA, data);
} else {
phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data);
}
} }
/** /**
...@@ -1020,7 +1034,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) ...@@ -1020,7 +1034,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
return status; return status;
/* First check if the EEE ability is supported */ /* First check if the EEE ability is supported */
eee_cap = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE, eee_cap = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE,
MDIO_MMD_PCS, phydev->addr); MDIO_MMD_PCS, phydev->addr);
if (eee_cap < 0) if (eee_cap < 0)
return eee_cap; return eee_cap;
...@@ -1032,12 +1046,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) ...@@ -1032,12 +1046,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
/* Check which link settings negotiated and verify it in /* Check which link settings negotiated and verify it in
* the EEE advertising registers. * the EEE advertising registers.
*/ */
eee_lp = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE, eee_lp = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE,
MDIO_MMD_AN, phydev->addr); MDIO_MMD_AN, phydev->addr);
if (eee_lp < 0) if (eee_lp < 0)
return eee_lp; return eee_lp;
eee_adv = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, eee_adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
MDIO_MMD_AN, phydev->addr); MDIO_MMD_AN, phydev->addr);
if (eee_adv < 0) if (eee_adv < 0)
return eee_adv; return eee_adv;
...@@ -1052,15 +1066,16 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) ...@@ -1052,15 +1066,16 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
/* Configure the PHY to stop receiving xMII /* Configure the PHY to stop receiving xMII
* clock while it is signaling LPI. * clock while it is signaling LPI.
*/ */
int val = phy_read_mmd_indirect(phydev->bus, MDIO_CTRL1, int val = phy_read_mmd_indirect(phydev, MDIO_CTRL1,
MDIO_MMD_PCS, MDIO_MMD_PCS,
phydev->addr); phydev->addr);
if (val < 0) if (val < 0)
return val; return val;
val |= MDIO_PCS_CTRL1_CLKSTOP_EN; val |= MDIO_PCS_CTRL1_CLKSTOP_EN;
phy_write_mmd_indirect(phydev->bus, MDIO_CTRL1, phy_write_mmd_indirect(phydev, MDIO_CTRL1,
MDIO_MMD_PCS, phydev->addr, val); MDIO_MMD_PCS, phydev->addr,
val);
} }
return 0; /* EEE supported */ return 0; /* EEE supported */
...@@ -1079,7 +1094,7 @@ EXPORT_SYMBOL(phy_init_eee); ...@@ -1079,7 +1094,7 @@ EXPORT_SYMBOL(phy_init_eee);
*/ */
int phy_get_eee_err(struct phy_device *phydev) int phy_get_eee_err(struct phy_device *phydev)
{ {
return phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_WK_ERR, return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR,
MDIO_MMD_PCS, phydev->addr); MDIO_MMD_PCS, phydev->addr);
} }
EXPORT_SYMBOL(phy_get_eee_err); EXPORT_SYMBOL(phy_get_eee_err);
...@@ -1097,21 +1112,21 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) ...@@ -1097,21 +1112,21 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
int val; int val;
/* Get Supported EEE */ /* Get Supported EEE */
val = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE, val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE,
MDIO_MMD_PCS, phydev->addr); MDIO_MMD_PCS, phydev->addr);
if (val < 0) if (val < 0)
return val; return val;
data->supported = mmd_eee_cap_to_ethtool_sup_t(val); data->supported = mmd_eee_cap_to_ethtool_sup_t(val);
/* Get advertisement EEE */ /* Get advertisement EEE */
val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
MDIO_MMD_AN, phydev->addr); MDIO_MMD_AN, phydev->addr);
if (val < 0) if (val < 0)
return val; return val;
data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); data->advertised = mmd_eee_adv_to_ethtool_adv_t(val);
/* Get LP advertisement EEE */ /* Get LP advertisement EEE */
val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE, val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE,
MDIO_MMD_AN, phydev->addr); MDIO_MMD_AN, phydev->addr);
if (val < 0) if (val < 0)
return val; return val;
...@@ -1132,7 +1147,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) ...@@ -1132,7 +1147,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
{ {
int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised); int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised);
phy_write_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, MDIO_MMD_AN, phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN,
phydev->addr, val); phydev->addr, val);
return 0; return 0;
......
...@@ -545,6 +545,24 @@ struct phy_driver { ...@@ -545,6 +545,24 @@ struct phy_driver {
*/ */
void (*link_change_notify)(struct phy_device *dev); void (*link_change_notify)(struct phy_device *dev);
/* A function provided by a phy specific driver to override the
* the PHY driver framework support for reading a MMD register
* from the PHY. If not supported, return -1. This function is
* optional for PHY specific drivers, if not provided then the
* default MMD read function is used by the PHY framework.
*/
int (*read_mmd_indirect)(struct phy_device *dev, int ptrad,
int devnum, int regnum);
/* A function provided by a phy specific driver to override the
* the PHY driver framework support for writing a MMD register
* from the PHY. This function is optional for PHY specific drivers,
* if not provided then the default MMD read function is used by
* the PHY framework.
*/
void (*write_mmd_indirect)(struct phy_device *dev, int ptrad,
int devnum, int regnum, u32 val);
struct device_driver driver; struct device_driver driver;
}; };
#define to_phy_driver(d) container_of(d, struct phy_driver, driver) #define to_phy_driver(d) container_of(d, struct phy_driver, driver)
......
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