Skip to content

Commit

Permalink
ucc_geth: Implement suspend/resume and Wake-On-LAN support
Browse files Browse the repository at this point in the history
This patch implements suspend/resume and WOL support for UCC Ethernet
driver.

We support two wake up events: wake on PHY/link changes and wake
on magic packet.

In some CPUs (like MPC8569) QE shuts down during sleep, so magic packet
detection is unusable, and also on resume we should fully reinitialize
UCC structures.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Anton Vorontsov authored and David S. Miller committed Aug 31, 2009
1 parent bf5aec2 commit 2394905
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 0 deletions.
85 changes: 85 additions & 0 deletions drivers/net/ucc_geth.c
Original file line number Diff line number Diff line change
Expand Up @@ -3497,6 +3497,10 @@ static int ucc_geth_open(struct net_device *dev)
napi_enable(&ugeth->napi);
netif_start_queue(dev);

device_set_wakeup_capable(&dev->dev,
qe_alive_during_sleep() || ugeth->phydev->irq);
device_set_wakeup_enable(&dev->dev, ugeth->wol_en);

return err;

err:
Expand Down Expand Up @@ -3561,6 +3565,85 @@ static void ucc_geth_timeout(struct net_device *dev)
schedule_work(&ugeth->timeout_work);
}


#ifdef CONFIG_PM

static int ucc_geth_suspend(struct of_device *ofdev, pm_message_t state)
{
struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
struct ucc_geth_private *ugeth = netdev_priv(ndev);

if (!netif_running(ndev))
return 0;

napi_disable(&ugeth->napi);

/*
* Disable the controller, otherwise we'll wakeup on any network
* activity.
*/
ugeth_disable(ugeth, COMM_DIR_RX_AND_TX);

if (ugeth->wol_en & WAKE_MAGIC) {
setbits32(ugeth->uccf->p_uccm, UCC_GETH_UCCE_MPD);
setbits32(&ugeth->ug_regs->maccfg2, MACCFG2_MPE);
ucc_fast_enable(ugeth->uccf, COMM_DIR_RX_AND_TX);
} else if (!(ugeth->wol_en & WAKE_PHY)) {
phy_stop(ugeth->phydev);
}

return 0;
}

static int ucc_geth_resume(struct of_device *ofdev)
{
struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
struct ucc_geth_private *ugeth = netdev_priv(ndev);
int err;

if (!netif_running(ndev))
return 0;

if (qe_alive_during_sleep()) {
if (ugeth->wol_en & WAKE_MAGIC) {
ucc_fast_disable(ugeth->uccf, COMM_DIR_RX_AND_TX);
clrbits32(&ugeth->ug_regs->maccfg2, MACCFG2_MPE);
clrbits32(ugeth->uccf->p_uccm, UCC_GETH_UCCE_MPD);
}
ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
} else {
/*
* Full reinitialization is required if QE shuts down
* during sleep.
*/
ucc_geth_memclean(ugeth);

err = ucc_geth_init_mac(ugeth);
if (err) {
ugeth_err("%s: Cannot initialize MAC, aborting.",
ndev->name);
return err;
}
}

ugeth->oldlink = 0;
ugeth->oldspeed = 0;
ugeth->oldduplex = -1;

phy_stop(ugeth->phydev);
phy_start(ugeth->phydev);

napi_enable(&ugeth->napi);
netif_start_queue(ndev);

return 0;
}

#else
#define ucc_geth_suspend NULL
#define ucc_geth_resume NULL
#endif

static phy_interface_t to_phy_interface(const char *phy_connection_type)
{
if (strcasecmp(phy_connection_type, "mii") == 0)
Expand Down Expand Up @@ -3852,6 +3935,8 @@ static struct of_platform_driver ucc_geth_driver = {
.match_table = ucc_geth_match,
.probe = ucc_geth_probe,
.remove = ucc_geth_remove,
.suspend = ucc_geth_suspend,
.resume = ucc_geth_resume,
};

static int __init ucc_geth_init(void)
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ucc_geth.h
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,7 @@ struct ucc_geth_private {
int oldspeed;
int oldduplex;
int oldlink;
int wol_en;

struct device_node *node;
};
Expand Down
40 changes: 40 additions & 0 deletions drivers/net/ucc_geth_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,44 @@ uec_get_drvinfo(struct net_device *netdev,
drvinfo->regdump_len = uec_get_regs_len(netdev);
}

#ifdef CONFIG_PM

static void uec_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
struct ucc_geth_private *ugeth = netdev_priv(netdev);
struct phy_device *phydev = ugeth->phydev;

if (phydev && phydev->irq)
wol->supported |= WAKE_PHY;
if (qe_alive_during_sleep())
wol->supported |= WAKE_MAGIC;

wol->wolopts = ugeth->wol_en;
}

static int uec_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
struct ucc_geth_private *ugeth = netdev_priv(netdev);
struct phy_device *phydev = ugeth->phydev;

if (wol->wolopts & ~(WAKE_PHY | WAKE_MAGIC))
return -EINVAL;
else if (wol->wolopts & WAKE_PHY && (!phydev || !phydev->irq))
return -EINVAL;
else if (wol->wolopts & WAKE_MAGIC && !qe_alive_during_sleep())
return -EINVAL;

ugeth->wol_en = wol->wolopts;
device_set_wakeup_enable(&netdev->dev, ugeth->wol_en);

return 0;
}

#else
#define uec_get_wol NULL
#define uec_set_wol NULL
#endif /* CONFIG_PM */

static const struct ethtool_ops uec_ethtool_ops = {
.get_settings = uec_get_settings,
.set_settings = uec_set_settings,
Expand All @@ -377,6 +415,8 @@ static const struct ethtool_ops uec_ethtool_ops = {
.get_sset_count = uec_get_sset_count,
.get_strings = uec_get_strings,
.get_ethtool_stats = uec_get_ethtool_stats,
.get_wol = uec_get_wol,
.set_wol = uec_set_wol,
};

void uec_set_ethtool_ops(struct net_device *netdev)
Expand Down

0 comments on commit 2394905

Please sign in to comment.