sfc: Add power-management and wake-on-LAN support
Wake-on-LAN is a stub for Falcon, but will be implemented fully for new NICs. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
78c1f0a065
commit
89c758fa47
4 changed files with 146 additions and 0 deletions
|
@ -2243,11 +2243,107 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int efx_pm_freeze(struct device *dev)
|
||||||
|
{
|
||||||
|
struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
|
||||||
|
|
||||||
|
efx->state = STATE_FINI;
|
||||||
|
|
||||||
|
netif_device_detach(efx->net_dev);
|
||||||
|
|
||||||
|
efx_stop_all(efx);
|
||||||
|
efx_fini_channels(efx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int efx_pm_thaw(struct device *dev)
|
||||||
|
{
|
||||||
|
struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
|
||||||
|
|
||||||
|
efx->state = STATE_INIT;
|
||||||
|
|
||||||
|
efx_init_channels(efx);
|
||||||
|
|
||||||
|
mutex_lock(&efx->mac_lock);
|
||||||
|
efx->phy_op->reconfigure(efx);
|
||||||
|
mutex_unlock(&efx->mac_lock);
|
||||||
|
|
||||||
|
efx_start_all(efx);
|
||||||
|
|
||||||
|
netif_device_attach(efx->net_dev);
|
||||||
|
|
||||||
|
efx->state = STATE_RUNNING;
|
||||||
|
|
||||||
|
efx->type->resume_wol(efx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int efx_pm_poweroff(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||||
|
struct efx_nic *efx = pci_get_drvdata(pci_dev);
|
||||||
|
|
||||||
|
efx->type->fini(efx);
|
||||||
|
|
||||||
|
efx->reset_pending = RESET_TYPE_NONE;
|
||||||
|
|
||||||
|
pci_save_state(pci_dev);
|
||||||
|
return pci_set_power_state(pci_dev, PCI_D3hot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used for both resume and restore */
|
||||||
|
static int efx_pm_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||||
|
struct efx_nic *efx = pci_get_drvdata(pci_dev);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = pci_set_power_state(pci_dev, PCI_D0);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
pci_restore_state(pci_dev);
|
||||||
|
rc = pci_enable_device(pci_dev);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
pci_set_master(efx->pci_dev);
|
||||||
|
rc = efx->type->reset(efx, RESET_TYPE_ALL);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
rc = efx->type->init(efx);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
efx_pm_thaw(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int efx_pm_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
efx_pm_freeze(dev);
|
||||||
|
rc = efx_pm_poweroff(dev);
|
||||||
|
if (rc)
|
||||||
|
efx_pm_resume(dev);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dev_pm_ops efx_pm_ops = {
|
||||||
|
.suspend = efx_pm_suspend,
|
||||||
|
.resume = efx_pm_resume,
|
||||||
|
.freeze = efx_pm_freeze,
|
||||||
|
.thaw = efx_pm_thaw,
|
||||||
|
.poweroff = efx_pm_poweroff,
|
||||||
|
.restore = efx_pm_resume,
|
||||||
|
};
|
||||||
|
|
||||||
static struct pci_driver efx_pci_driver = {
|
static struct pci_driver efx_pci_driver = {
|
||||||
.name = EFX_DRIVER_NAME,
|
.name = EFX_DRIVER_NAME,
|
||||||
.id_table = efx_pci_table,
|
.id_table = efx_pci_table,
|
||||||
.probe = efx_pci_probe,
|
.probe = efx_pci_probe,
|
||||||
.remove = efx_pci_remove,
|
.remove = efx_pci_remove,
|
||||||
|
.driver.pm = &efx_pm_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
|
|
|
@ -739,6 +739,21 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void efx_ethtool_get_wol(struct net_device *net_dev,
|
||||||
|
struct ethtool_wolinfo *wol)
|
||||||
|
{
|
||||||
|
struct efx_nic *efx = netdev_priv(net_dev);
|
||||||
|
return efx->type->get_wol(efx, wol);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int efx_ethtool_set_wol(struct net_device *net_dev,
|
||||||
|
struct ethtool_wolinfo *wol)
|
||||||
|
{
|
||||||
|
struct efx_nic *efx = netdev_priv(net_dev);
|
||||||
|
return efx->type->set_wol(efx, wol->wolopts);
|
||||||
|
}
|
||||||
|
|
||||||
const struct ethtool_ops efx_ethtool_ops = {
|
const struct ethtool_ops efx_ethtool_ops = {
|
||||||
.get_settings = efx_ethtool_get_settings,
|
.get_settings = efx_ethtool_get_settings,
|
||||||
.set_settings = efx_ethtool_set_settings,
|
.set_settings = efx_ethtool_set_settings,
|
||||||
|
@ -767,4 +782,6 @@ const struct ethtool_ops efx_ethtool_ops = {
|
||||||
.get_strings = efx_ethtool_get_strings,
|
.get_strings = efx_ethtool_get_strings,
|
||||||
.phys_id = efx_ethtool_phys_id,
|
.phys_id = efx_ethtool_phys_id,
|
||||||
.get_ethtool_stats = efx_ethtool_get_stats,
|
.get_ethtool_stats = efx_ethtool_get_stats,
|
||||||
|
.get_wol = efx_ethtool_get_wol,
|
||||||
|
.set_wol = efx_ethtool_set_wol,
|
||||||
};
|
};
|
||||||
|
|
|
@ -3243,6 +3243,27 @@ void falcon_stop_nic_stats(struct efx_nic *efx)
|
||||||
spin_unlock_bh(&efx->stats_lock);
|
spin_unlock_bh(&efx->stats_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
*
|
||||||
|
* Wake on LAN
|
||||||
|
*
|
||||||
|
**************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void falcon_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol)
|
||||||
|
{
|
||||||
|
wol->supported = 0;
|
||||||
|
wol->wolopts = 0;
|
||||||
|
memset(&wol->sopass, 0, sizeof(wol->sopass));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int falcon_set_wol(struct efx_nic *efx, u32 type)
|
||||||
|
{
|
||||||
|
if (type != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
*
|
*
|
||||||
* Revision-dependent attributes used by efx.c
|
* Revision-dependent attributes used by efx.c
|
||||||
|
@ -3266,6 +3287,9 @@ struct efx_nic_type falcon_a1_nic_type = {
|
||||||
.push_irq_moderation = falcon_push_irq_moderation,
|
.push_irq_moderation = falcon_push_irq_moderation,
|
||||||
.push_multicast_hash = falcon_push_multicast_hash,
|
.push_multicast_hash = falcon_push_multicast_hash,
|
||||||
.reconfigure_port = falcon_reconfigure_port,
|
.reconfigure_port = falcon_reconfigure_port,
|
||||||
|
.get_wol = falcon_get_wol,
|
||||||
|
.set_wol = falcon_set_wol,
|
||||||
|
.resume_wol = efx_port_dummy_op_void,
|
||||||
.default_mac_ops = &falcon_xmac_operations,
|
.default_mac_ops = &falcon_xmac_operations,
|
||||||
|
|
||||||
.revision = EFX_REV_FALCON_A1,
|
.revision = EFX_REV_FALCON_A1,
|
||||||
|
@ -3299,6 +3323,9 @@ struct efx_nic_type falcon_b0_nic_type = {
|
||||||
.push_irq_moderation = falcon_push_irq_moderation,
|
.push_irq_moderation = falcon_push_irq_moderation,
|
||||||
.push_multicast_hash = falcon_push_multicast_hash,
|
.push_multicast_hash = falcon_push_multicast_hash,
|
||||||
.reconfigure_port = falcon_reconfigure_port,
|
.reconfigure_port = falcon_reconfigure_port,
|
||||||
|
.get_wol = falcon_get_wol,
|
||||||
|
.set_wol = falcon_set_wol,
|
||||||
|
.resume_wol = efx_port_dummy_op_void,
|
||||||
.default_mac_ops = &falcon_xmac_operations,
|
.default_mac_ops = &falcon_xmac_operations,
|
||||||
|
|
||||||
.revision = EFX_REV_FALCON_B0,
|
.revision = EFX_REV_FALCON_B0,
|
||||||
|
|
|
@ -861,6 +861,9 @@ static inline const char *efx_dev_name(struct efx_nic *efx)
|
||||||
* @push_irq_moderation: Apply interrupt moderation value
|
* @push_irq_moderation: Apply interrupt moderation value
|
||||||
* @push_multicast_hash: Apply multicast hash table
|
* @push_multicast_hash: Apply multicast hash table
|
||||||
* @reconfigure_port: Push loopback/power/txdis changes to the MAC and PHY
|
* @reconfigure_port: Push loopback/power/txdis changes to the MAC and PHY
|
||||||
|
* @get_wol: Get WoL configuration from driver state
|
||||||
|
* @set_wol: Push WoL configuration to the NIC
|
||||||
|
* @resume_wol: Synchronise WoL state between driver and MC (e.g. after resume)
|
||||||
* @default_mac_ops: efx_mac_operations to set at startup
|
* @default_mac_ops: efx_mac_operations to set at startup
|
||||||
* @revision: Hardware architecture revision
|
* @revision: Hardware architecture revision
|
||||||
* @mem_map_size: Memory BAR mapped size
|
* @mem_map_size: Memory BAR mapped size
|
||||||
|
@ -894,6 +897,9 @@ struct efx_nic_type {
|
||||||
void (*push_irq_moderation)(struct efx_channel *channel);
|
void (*push_irq_moderation)(struct efx_channel *channel);
|
||||||
void (*push_multicast_hash)(struct efx_nic *efx);
|
void (*push_multicast_hash)(struct efx_nic *efx);
|
||||||
int (*reconfigure_port)(struct efx_nic *efx);
|
int (*reconfigure_port)(struct efx_nic *efx);
|
||||||
|
void (*get_wol)(struct efx_nic *efx, struct ethtool_wolinfo *wol);
|
||||||
|
int (*set_wol)(struct efx_nic *efx, u32 type);
|
||||||
|
void (*resume_wol)(struct efx_nic *efx);
|
||||||
struct efx_mac_operations *default_mac_ops;
|
struct efx_mac_operations *default_mac_ops;
|
||||||
|
|
||||||
int revision;
|
int revision;
|
||||||
|
|
Loading…
Reference in a new issue