323 lines
8.3 KiB
Diff
323 lines
8.3 KiB
Diff
Index: drivers/net/phy/phy.c
|
|
===================================================================
|
|
--- drivers/net/phy/phy.c
|
|
+++ linux-imx/drivers/net/phy/phy.c
|
|
@@ -55,6 +55,7 @@ static const char *phy_state_to_str(enum
|
|
PHY_STATE_STR(NOLINK)
|
|
PHY_STATE_STR(FORCING)
|
|
PHY_STATE_STR(CHANGELINK)
|
|
+ PHY_STATE_STR(POWERED_DOWN)
|
|
PHY_STATE_STR(HALTED)
|
|
PHY_STATE_STR(RESUMING)
|
|
}
|
|
@@ -241,6 +242,33 @@ static void phy_sanitize_settings(struct
|
|
}
|
|
}
|
|
|
|
+static int phy_shut(struct phy_device *phydev)
|
|
+{
|
|
+ mutex_lock(&phydev->lock);
|
|
+ phydev->power = PHY_POWER_DOWN;
|
|
+ if (PHY_HALTED == phydev->state) {
|
|
+ mutex_unlock(&phydev->lock);
|
|
+ return 0;
|
|
+ }
|
|
+ phydev->state = PHY_POWERED_DOWN;
|
|
+ mutex_unlock(&phydev->lock);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int phy_unshut(struct phy_device *phydev)
|
|
+{
|
|
+ mutex_lock(&phydev->lock);
|
|
+ phydev->power = PHY_POWER_UP;
|
|
+ if (PHY_HALTED == phydev->state) {
|
|
+ mutex_unlock(&phydev->lock);
|
|
+ return 0;
|
|
+ }
|
|
+ phy_power_up(phydev);
|
|
+ phydev->state = PHY_CHANGELINK;
|
|
+ mutex_unlock(&phydev->lock);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/**
|
|
* phy_ethtool_sset - generic ethtool sset function, handles all the details
|
|
* @phydev: target phy_device struct
|
|
@@ -257,10 +285,28 @@ static void phy_sanitize_settings(struct
|
|
int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
|
|
{
|
|
u32 speed = ethtool_cmd_speed(cmd);
|
|
+ int ret;
|
|
|
|
if (cmd->phy_address != phydev->mdio.addr)
|
|
return -EINVAL;
|
|
|
|
+ if (PHY_POWERED_DOWN == phydev->state) {
|
|
+ if (cmd->power == PHY_POWER_UP) {
|
|
+ ret = phy_unshut(phydev);
|
|
+ if (ret < 0)
|
|
+ return -EIO;
|
|
+ phy_trigger_machine(phydev, true);
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+ else if (cmd->power == PHY_POWER_DOWN) {
|
|
+ ret = phy_shut(phydev);
|
|
+ if (ret < 0)
|
|
+ return -EIO;
|
|
+ phy_trigger_machine(phydev, true);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
/* We make sure that we don't pass unsupported values in to the PHY */
|
|
cmd->advertising &= phydev->supported;
|
|
|
|
@@ -308,10 +354,28 @@ int phy_ethtool_ksettings_set(struct phy
|
|
u8 duplex = cmd->base.duplex;
|
|
u32 speed = cmd->base.speed;
|
|
u32 advertising;
|
|
+ int ret;
|
|
|
|
if (cmd->base.phy_address != phydev->mdio.addr)
|
|
return -EINVAL;
|
|
|
|
+ if (PHY_POWERED_DOWN == phydev->state) {
|
|
+ if (cmd->base.power == PHY_POWER_UP) {
|
|
+ ret = phy_unshut(phydev);
|
|
+ if (ret < 0)
|
|
+ return -EIO;
|
|
+ phy_trigger_machine(phydev, true);
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+ else if (cmd->base.power == PHY_POWER_DOWN) {
|
|
+ ret = phy_shut(phydev);
|
|
+ if (ret < 0)
|
|
+ return -EIO;
|
|
+ phy_trigger_machine(phydev, true);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
ethtool_convert_link_mode_to_legacy_u32(&advertising,
|
|
cmd->link_modes.advertising);
|
|
|
|
@@ -379,6 +443,7 @@ void phy_ethtool_ksettings_get(struct ph
|
|
cmd->base.autoneg = phydev->autoneg;
|
|
cmd->base.eth_tp_mdix_ctrl = phydev->mdix_ctrl;
|
|
cmd->base.eth_tp_mdix = phydev->mdix;
|
|
+ cmd->base.power = phydev->power;
|
|
}
|
|
EXPORT_SYMBOL(phy_ethtool_ksettings_get);
|
|
|
|
@@ -881,7 +946,7 @@ void phy_state_machine(struct work_struc
|
|
struct delayed_work *dwork = to_delayed_work(work);
|
|
struct phy_device *phydev =
|
|
container_of(dwork, struct phy_device, state_queue);
|
|
- bool needs_aneg = false, do_suspend = false;
|
|
+ bool needs_aneg = false, do_suspend = false, do_power_down = false;
|
|
enum phy_state old_state;
|
|
int err = 0;
|
|
int old_link;
|
|
@@ -1014,6 +1079,14 @@ void phy_state_machine(struct work_struc
|
|
do_suspend = true;
|
|
}
|
|
break;
|
|
+ case PHY_POWERED_DOWN:
|
|
+ phy_read_status(phydev);
|
|
+ if (phydev->link) {
|
|
+ phydev->link = 0;
|
|
+ phy_link_down(phydev, true);
|
|
+ do_power_down = true;
|
|
+ }
|
|
+ break;
|
|
case PHY_RESUMING:
|
|
if (AUTONEG_ENABLE == phydev->autoneg) {
|
|
err = phy_aneg_done(phydev);
|
|
@@ -1059,8 +1132,14 @@ void phy_state_machine(struct work_struc
|
|
|
|
if (needs_aneg)
|
|
err = phy_start_aneg_priv(phydev, false);
|
|
- else if (do_suspend)
|
|
- phy_suspend(phydev);
|
|
+ else {
|
|
+ if (do_suspend)
|
|
+ phy_suspend(phydev);
|
|
+ if (do_power_down) {
|
|
+ phy_power_down(phydev);
|
|
+ phy_read_status(phydev);
|
|
+ }
|
|
+ }
|
|
|
|
if (err < 0)
|
|
phy_error(phydev);
|
|
Index: drivers/net/phy/phy_device.c
|
|
===================================================================
|
|
--- drivers/net/phy/phy_device.c
|
|
+++ linux-imx/drivers/net/phy/phy_device.c
|
|
@@ -1230,6 +1230,31 @@ out:
|
|
}
|
|
EXPORT_SYMBOL(phy_loopback);
|
|
|
|
+int phy_power_down(struct phy_device *phydev)
|
|
+{
|
|
+ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
|
|
+ int ret = 0;
|
|
+
|
|
+ if (phydev->drv && phydrv->suspend)
|
|
+ ret = phydrv->suspend(phydev);
|
|
+
|
|
+ return ret;
|
|
+
|
|
+}
|
|
+EXPORT_SYMBOL(phy_power_down);
|
|
+
|
|
+int phy_power_up(struct phy_device *phydev)
|
|
+{
|
|
+ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
|
|
+ int ret = 0;
|
|
+
|
|
+ if (phydev->drv && phydrv->resume)
|
|
+ ret = phydrv->resume(phydev);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(phy_power_up);
|
|
+
|
|
/* Generic PHY support and helper functions */
|
|
|
|
/**
|
|
@@ -1501,6 +1526,7 @@ EXPORT_SYMBOL(genphy_update_link);
|
|
*/
|
|
int genphy_read_status(struct phy_device *phydev)
|
|
{
|
|
+ int bmcr;
|
|
int adv;
|
|
int err;
|
|
int lpa;
|
|
@@ -1513,6 +1539,10 @@ int genphy_read_status(struct phy_device
|
|
if (err)
|
|
return err;
|
|
|
|
+ bmcr = phy_read(phydev, MII_BMCR);
|
|
+ if (bmcr < 0)
|
|
+ return bmcr;
|
|
+
|
|
phydev->lp_advertising = 0;
|
|
|
|
if (AUTONEG_ENABLE == phydev->autoneg) {
|
|
@@ -1567,11 +1597,6 @@ int genphy_read_status(struct phy_device
|
|
phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
|
|
}
|
|
} else {
|
|
- int bmcr = phy_read(phydev, MII_BMCR);
|
|
-
|
|
- if (bmcr < 0)
|
|
- return bmcr;
|
|
-
|
|
if (bmcr & BMCR_FULLDPLX)
|
|
phydev->duplex = DUPLEX_FULL;
|
|
else
|
|
@@ -1588,6 +1613,11 @@ int genphy_read_status(struct phy_device
|
|
phydev->asym_pause = 0;
|
|
}
|
|
|
|
+ if (bmcr & BMCR_PDOWN)
|
|
+ phydev->power = PHY_POWER_DOWN;
|
|
+ else
|
|
+ phydev->power = PHY_POWER_UP;
|
|
+
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(genphy_read_status);
|
|
Index: include/linux/phy.h
|
|
===================================================================
|
|
--- include/linux/phy.h
|
|
+++ linux-imx/include/linux/phy.h
|
|
@@ -348,6 +348,7 @@ enum phy_state {
|
|
PHY_NOLINK,
|
|
PHY_FORCING,
|
|
PHY_CHANGELINK,
|
|
+ PHY_POWERED_DOWN,
|
|
PHY_HALTED,
|
|
PHY_RESUMING
|
|
};
|
|
@@ -447,6 +448,8 @@ struct phy_device {
|
|
|
|
int link_timeout;
|
|
|
|
+ int power;
|
|
+
|
|
#ifdef CONFIG_LED_TRIGGER_PHY
|
|
struct phy_led_trigger *phy_led_triggers;
|
|
unsigned int phy_num_led_triggers;
|
|
@@ -819,6 +822,8 @@ int phy_suspend(struct phy_device *phyde
|
|
int phy_resume(struct phy_device *phydev);
|
|
int __phy_resume(struct phy_device *phydev);
|
|
int phy_loopback(struct phy_device *phydev, bool enable);
|
|
+int phy_power_up(struct phy_device *phydev);
|
|
+int phy_power_down(struct phy_device *phydev);
|
|
struct phy_device *phy_attach(struct net_device *dev, const char *bus_id,
|
|
phy_interface_t interface);
|
|
struct phy_device *phy_find_first(struct mii_bus *bus);
|
|
Index: include/uapi/linux/ethtool.h
|
|
===================================================================
|
|
--- include/uapi/linux/ethtool.h
|
|
+++ linux-imx/include/uapi/linux/ethtool.h
|
|
@@ -114,7 +114,10 @@ struct ethtool_cmd {
|
|
__u8 eth_tp_mdix;
|
|
__u8 eth_tp_mdix_ctrl;
|
|
__u32 lp_advertising;
|
|
- __u32 reserved[2];
|
|
+ __u8 power;
|
|
+ __u8 reserved2;
|
|
+ __u16 reserved3;
|
|
+ __u32 reserved[1];
|
|
};
|
|
|
|
static inline void ethtool_cmd_speed_set(struct ethtool_cmd *ep,
|
|
@@ -1664,6 +1667,11 @@ static inline int ethtool_validate_duple
|
|
#define ETH_MODULE_SFF_8436 0x4
|
|
#define ETH_MODULE_SFF_8436_LEN 256
|
|
|
|
+/* TIESSE Power, up or down. */
|
|
+#define PHY_POWER_IGNORE 0x0
|
|
+#define PHY_POWER_DOWN 0x1
|
|
+#define PHY_POWER_UP 0x2
|
|
+
|
|
/* Reset flags */
|
|
/* The reset() operation must clear the flags for the components which
|
|
* were actually reset. On successful return, the flags indicate the
|
|
@@ -1808,7 +1816,8 @@ struct ethtool_link_settings {
|
|
__u8 eth_tp_mdix_ctrl;
|
|
__s8 link_mode_masks_nwords;
|
|
__u8 transceiver;
|
|
- __u8 reserved1[3];
|
|
+ __u8 power;
|
|
+ __u8 reserved1[2];
|
|
__u32 reserved[7];
|
|
__u32 link_mode_masks[0];
|
|
/* layout of link_mode_masks fields:
|
|
Index: net/core/ethtool.c
|
|
===================================================================
|
|
--- net/core/ethtool.c
|
|
+++ linux-imx/net/core/ethtool.c
|
|
@@ -480,6 +480,8 @@ convert_legacy_settings_to_link_ksetting
|
|
= legacy_settings->eth_tp_mdix;
|
|
link_ksettings->base.eth_tp_mdix_ctrl
|
|
= legacy_settings->eth_tp_mdix_ctrl;
|
|
+ link_ksettings->base.power
|
|
+ = legacy_settings->power;
|
|
return retval;
|
|
}
|
|
|
|
@@ -526,6 +528,8 @@ convert_link_ksettings_to_legacy_setting
|
|
= link_ksettings->base.eth_tp_mdix_ctrl;
|
|
legacy_settings->transceiver
|
|
= link_ksettings->base.transceiver;
|
|
+ legacy_settings->power
|
|
+ = link_ksettings->base.power;
|
|
return retval;
|
|
}
|
|
|