Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 314943
b: refs/heads/master
c: a59a4d1
h: refs/heads/master
i:
  314941: 9b3d224
  314939: 7f20621
  314935: f012abd
  314927: b350177
  314911: 00893ec
  314879: c7b75a6
v: v3
  • Loading branch information
Giuseppe CAVALLARO authored and David S. Miller committed Jul 1, 2012
1 parent bec1376 commit 92a708f
Show file tree
Hide file tree
Showing 5 changed files with 320 additions and 5 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: d765955d2ae0b88781a0db3a5bacfe4241925e09
refs/heads/master: a59a4d1921664da63d801ba477950114c71c88c9
281 changes: 281 additions & 0 deletions trunk/drivers/net/phy/phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <linux/phy.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/mdio.h>

#include <linux/atomic.h>
#include <asm/io.h>
Expand Down Expand Up @@ -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);
28 changes: 24 additions & 4 deletions trunk/include/linux/mdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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 */
Expand All @@ -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)
Expand Down Expand Up @@ -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 */
Expand Down
9 changes: 9 additions & 0 deletions trunk/include/linux/mii.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions trunk/include/linux/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 92a708f

Please sign in to comment.