diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 2e1c23731de..7ca2ff97c36 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -967,3 +968,283 @@ void phy_state_machine(struct work_struct *work) schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ); } + +static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, + int addr) +{ + /* Write the desired MMD Devad */ + bus->write(bus, addr, MII_MMD_CTRL, devad); + + /* Write the desired MMD register address */ + bus->write(bus, addr, MII_MMD_DATA, prtad); + + /* Select the Function : DATA with no post increment */ + bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); +} + +/** + * phy_read_mmd_indirect - reads data from the MMD registers + * @bus: the target MII bus + * @prtad: MMD Address + * @devad: MMD DEVAD + * @addr: PHY address on the MII bus + * + * Description: it reads data from the MMD registers (clause 22 to access to + * clause 45) of the specified phy address. + * To read these register we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Read reg 14 // Read MMD data + */ +static int phy_read_mmd_indirect(struct mii_bus *bus, int prtad, int devad, + int addr) +{ + u32 ret; + + mmd_phy_indirect(bus, prtad, devad, addr); + + /* Read the content of the MMD's selected register */ + ret = bus->read(bus, addr, MII_MMD_DATA); + + return ret; +} + +/** + * phy_write_mmd_indirect - writes data to the MMD registers + * @bus: the target MII bus + * @prtad: MMD Address + * @devad: MMD DEVAD + * @addr: PHY address on the MII bus + * @data: data to write in the MMD register + * + * Description: Write data from the MMD registers of the specified + * phy address. + * To write these register we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Write reg 14 // Write MMD data + */ +static void phy_write_mmd_indirect(struct mii_bus *bus, int prtad, int devad, + int addr, u32 data) +{ + mmd_phy_indirect(bus, prtad, devad, addr); + + /* Write the data into MMD's selected register */ + bus->write(bus, addr, MII_MMD_DATA, data); +} + +static u32 phy_eee_to_adv(u16 eee_adv) +{ + u32 adv = 0; + + if (eee_adv & MDIO_EEE_100TX) + adv |= ADVERTISED_100baseT_Full; + if (eee_adv & MDIO_EEE_1000T) + adv |= ADVERTISED_1000baseT_Full; + if (eee_adv & MDIO_EEE_10GT) + adv |= ADVERTISED_10000baseT_Full; + if (eee_adv & MDIO_EEE_1000KX) + adv |= ADVERTISED_1000baseKX_Full; + if (eee_adv & MDIO_EEE_10GKX4) + adv |= ADVERTISED_10000baseKX4_Full; + if (eee_adv & MDIO_EEE_10GKR) + adv |= ADVERTISED_10000baseKR_Full; + + return adv; +} + +static u32 phy_eee_to_supported(u16 eee_caported) +{ + u32 supported = 0; + + if (eee_caported & MDIO_EEE_100TX) + supported |= SUPPORTED_100baseT_Full; + if (eee_caported & MDIO_EEE_1000T) + supported |= SUPPORTED_1000baseT_Full; + if (eee_caported & MDIO_EEE_10GT) + supported |= SUPPORTED_10000baseT_Full; + if (eee_caported & MDIO_EEE_1000KX) + supported |= SUPPORTED_1000baseKX_Full; + if (eee_caported & MDIO_EEE_10GKX4) + supported |= SUPPORTED_10000baseKX4_Full; + if (eee_caported & MDIO_EEE_10GKR) + supported |= SUPPORTED_10000baseKR_Full; + + return supported; +} + +static u16 phy_adv_to_eee(u32 adv) +{ + u16 reg = 0; + + if (adv & ADVERTISED_100baseT_Full) + reg |= MDIO_EEE_100TX; + if (adv & ADVERTISED_1000baseT_Full) + reg |= MDIO_EEE_1000T; + if (adv & ADVERTISED_10000baseT_Full) + reg |= MDIO_EEE_10GT; + if (adv & ADVERTISED_1000baseKX_Full) + reg |= MDIO_EEE_1000KX; + if (adv & ADVERTISED_10000baseKX4_Full) + reg |= MDIO_EEE_10GKX4; + if (adv & ADVERTISED_10000baseKR_Full) + reg |= MDIO_EEE_10GKR; + + return reg; +} + +/** + * phy_init_eee - init and check the EEE feature + * @phydev: target phy_device struct + * @clk_stop_enable: PHY may stop the clock during LPI + * + * Description: it checks if the Energy-Efficient Ethernet (EEE) + * is supported by looking at the MMD registers 3.20 and 7.60/61 + * and it programs the MMD register 3.0 setting the "Clock stop enable" + * bit if required. + */ +int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) +{ + int ret = -EPROTONOSUPPORT; + + /* According to 802.3az,the EEE is supported only in full duplex-mode. + * Also EEE feature is active when core is operating with MII, GMII + * or RGMII. + */ + if ((phydev->duplex == DUPLEX_FULL) && + ((phydev->interface == PHY_INTERFACE_MODE_MII) || + (phydev->interface == PHY_INTERFACE_MODE_GMII) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII))) { + int eee_lp, eee_cap, eee_adv; + u32 lp, cap, adv; + int idx, status; + + /* Read phy status to properly get the right settings */ + status = phy_read_status(phydev); + if (status) + return status; + + /* First check if the EEE ability is supported */ + eee_cap = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE, + MDIO_MMD_PCS, phydev->addr); + if (eee_cap < 0) + return eee_cap; + + cap = phy_eee_to_supported(eee_cap); + if (!cap) + goto eee_exit; + + /* Check which link settings negotiated and verify it in + * the EEE advertising registers. + */ + eee_lp = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE, + MDIO_MMD_AN, phydev->addr); + if (eee_lp < 0) + return eee_lp; + + eee_adv = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, + MDIO_MMD_AN, phydev->addr); + if (eee_adv < 0) + return eee_adv; + + adv = phy_eee_to_adv(eee_adv); + lp = phy_eee_to_adv(eee_lp); + idx = phy_find_setting(phydev->speed, phydev->duplex); + if ((lp & adv & settings[idx].setting)) + goto eee_exit; + + if (clk_stop_enable) { + /* Configure the PHY to stop receiving xMII + * clock while it is signaling LPI. + */ + int val = phy_read_mmd_indirect(phydev->bus, MDIO_CTRL1, + MDIO_MMD_PCS, + phydev->addr); + if (val < 0) + return val; + + val |= MDIO_PCS_CTRL1_CLKSTOP_EN; + phy_write_mmd_indirect(phydev->bus, MDIO_CTRL1, + MDIO_MMD_PCS, phydev->addr, val); + } + + ret = 0; /* EEE supported */ + } + +eee_exit: + return ret; +} +EXPORT_SYMBOL(phy_init_eee); + +/** + * phy_get_eee_err - report the EEE wake error count + * @phydev: target phy_device struct + * + * Description: it is to report the number of time where the PHY + * failed to complete its normal wake sequence. + */ +int phy_get_eee_err(struct phy_device *phydev) +{ + return phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_WK_ERR, + MDIO_MMD_PCS, phydev->addr); + +} +EXPORT_SYMBOL(phy_get_eee_err); + +/** + * phy_ethtool_get_eee - get EEE supported and status + * @phydev: target phy_device struct + * @data: ethtool_eee data + * + * Description: it reportes the Supported/Advertisement/LP Advertisement + * capabilities. + */ +int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) +{ + int val; + + /* Get Supported EEE */ + val = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE, + MDIO_MMD_PCS, phydev->addr); + if (val < 0) + return val; + data->supported = phy_eee_to_supported(val); + + /* Get advertisement EEE */ + val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, + MDIO_MMD_AN, phydev->addr); + if (val < 0) + return val; + data->advertised = phy_eee_to_adv(val); + + /* Get LP advertisement EEE */ + val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE, + MDIO_MMD_AN, phydev->addr); + if (val < 0) + return val; + data->lp_advertised = phy_eee_to_adv(val); + + return 0; +} +EXPORT_SYMBOL(phy_ethtool_get_eee); + +/** + * phy_ethtool_set_eee - set EEE supported and status + * @phydev: target phy_device struct + * @data: ethtool_eee data + * + * Description: it is to program the Advertisement EEE register. + */ +int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) +{ + int val; + + val = phy_adv_to_eee(data->advertised); + phy_write_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, MDIO_MMD_AN, + phydev->addr, val); + + return 0; +} +EXPORT_SYMBOL(phy_ethtool_set_eee); diff --git a/include/linux/mdio.h b/include/linux/mdio.h index dfb947959ec..7cccafe50e7 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -43,7 +43,11 @@ #define MDIO_PKGID2 15 #define MDIO_AN_ADVERTISE 16 /* AN advertising (base page) */ #define MDIO_AN_LPA 19 /* AN LP abilities (base page) */ +#define MDIO_PCS_EEE_ABLE 20 /* EEE Capability register */ +#define MDIO_PCS_EEE_WK_ERR 22 /* EEE wake error counter */ #define MDIO_PHYXS_LNSTAT 24 /* PHY XGXS lane state */ +#define MDIO_AN_EEE_ADV 60 /* EEE advertisement */ +#define MDIO_AN_EEE_LPABLE 61 /* EEE link partner ability */ /* Media-dependent registers. */ #define MDIO_PMA_10GBT_SWAPPOL 130 /* 10GBASE-T pair swap & polarity */ @@ -56,7 +60,6 @@ #define MDIO_PCS_10GBRT_STAT2 33 /* 10GBASE-R/-T PCS status 2 */ #define MDIO_AN_10GBT_CTRL 32 /* 10GBASE-T auto-negotiation control */ #define MDIO_AN_10GBT_STAT 33 /* 10GBASE-T auto-negotiation status */ -#define MDIO_AN_EEE_ADV 60 /* EEE advertisement */ /* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */ #define MDIO_PMA_LASI_RXCTRL 0x9000 /* RX_ALARM control */ @@ -82,6 +85,7 @@ #define MDIO_AN_CTRL1_RESTART BMCR_ANRESTART #define MDIO_AN_CTRL1_ENABLE BMCR_ANENABLE #define MDIO_AN_CTRL1_XNP 0x2000 /* Enable extended next page */ +#define MDIO_PCS_CTRL1_CLKSTOP_EN 0x400 /* Stop the clock during LPI */ /* 10 Gb/s */ #define MDIO_CTRL1_SPEED10G (MDIO_CTRL1_SPEEDSELEXT | 0x00) @@ -237,9 +241,25 @@ #define MDIO_AN_10GBT_STAT_MS 0x4000 /* Master/slave config */ #define MDIO_AN_10GBT_STAT_MSFLT 0x8000 /* Master/slave config fault */ -/* AN EEE Advertisement register. */ -#define MDIO_AN_EEE_ADV_100TX 0x0002 /* Advertise 100TX EEE cap */ -#define MDIO_AN_EEE_ADV_1000T 0x0004 /* Advertise 1000T EEE cap */ +/* EEE Supported/Advertisement/LP Advertisement registers. + * + * EEE capability Register (3.20), Advertisement (7.60) and + * Link partner ability (7.61) registers have and can use the same identical + * bit masks. + */ +#define MDIO_AN_EEE_ADV_100TX 0x0002 /* Advertise 100TX EEE cap */ +#define MDIO_AN_EEE_ADV_1000T 0x0004 /* Advertise 1000T EEE cap */ +/* Note: the two defines above can be potentially used by the user-land + * and cannot remove them now. + * So, we define the new generic MDIO_EEE_100TX and MDIO_EEE_1000T macros + * using the previous ones (that can be considered obsolete). + */ +#define MDIO_EEE_100TX MDIO_AN_EEE_ADV_100TX /* 100TX EEE cap */ +#define MDIO_EEE_1000T MDIO_AN_EEE_ADV_1000T /* 1000T EEE cap */ +#define MDIO_EEE_10GT 0x0008 /* 10GT EEE cap */ +#define MDIO_EEE_1000KX 0x0010 /* 1000KX EEE cap */ +#define MDIO_EEE_10GKX4 0x0020 /* 10G KX4 EEE cap */ +#define MDIO_EEE_10GKR 0x0040 /* 10G KR EEE cap */ /* LASI RX_ALARM control/status registers. */ #define MDIO_PMA_LASI_RX_PHYXSLFLT 0x0001 /* PHY XS RX local fault */ diff --git a/include/linux/mii.h b/include/linux/mii.h index 2783eca629a..8ef3a7a1159 100644 --- a/include/linux/mii.h +++ b/include/linux/mii.h @@ -21,6 +21,8 @@ #define MII_EXPANSION 0x06 /* Expansion register */ #define MII_CTRL1000 0x09 /* 1000BASE-T control */ #define MII_STAT1000 0x0a /* 1000BASE-T status */ +#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ +#define MII_MMD_DATA 0x0e /* MMD Access Data Register */ #define MII_ESTATUS 0x0f /* Extended Status */ #define MII_DCOUNTER 0x12 /* Disconnect counter */ #define MII_FCSCOUNTER 0x13 /* False carrier counter */ @@ -141,6 +143,13 @@ #define FLOW_CTRL_TX 0x01 #define FLOW_CTRL_RX 0x02 +/* MMD Access Control register fields */ +#define MII_MMD_CTRL_DEVAD_MASK 0x1f /* Mask MMD DEVAD*/ +#define MII_MMD_CTRL_ADDR 0x0000 /* Address */ +#define MII_MMD_CTRL_NOINCR 0x4000 /* no post increment */ +#define MII_MMD_CTRL_INCR_RDWT 0x8000 /* post increment on reads & writes */ +#define MII_MMD_CTRL_INCR_ON_WT 0xC000 /* post increment on writes only */ + /* This structure is used in all SIOCxMIIxxx ioctl calls */ struct mii_ioctl_data { __u16 phy_id; diff --git a/include/linux/phy.h b/include/linux/phy.h index 7eac80a2557..c35299e4da8 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -554,6 +554,11 @@ int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, int (*run)(struct phy_device *)); int phy_scan_fixups(struct phy_device *phydev); +int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable); +int phy_get_eee_err(struct phy_device *phydev); +int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data); +int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data); + int __init mdio_bus_init(void); void mdio_bus_exit(void);