sfc: Add power-management and wake-on-LAN support
authorBen Hutchings <bhutchings@solarflare.com>
Sun, 29 Nov 2009 03:43:07 +0000 (03:43 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 30 Nov 2009 00:46:29 +0000 (16:46 -0800)
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>
drivers/net/sfc/efx.c
drivers/net/sfc/ethtool.c
drivers/net/sfc/falcon.c
drivers/net/sfc/net_driver.h

index 14ef27fa84166f676ac82c91504c7bdddb8fb459..b016719d8f676f958be68394bfc683eabe333a1a 100644 (file)
@@ -2243,11 +2243,107 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
        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 = {
        .name           = EFX_DRIVER_NAME,
        .id_table       = efx_pci_table,
        .probe          = efx_pci_probe,
        .remove         = efx_pci_remove,
+       .driver.pm      = &efx_pm_ops,
 };
 
 /**************************************************************************
index d95d0fa399ff816dd03d3931fa2f51eec9d3f083..b4c6ea1b9c07475f168e47b6c85c1e56e12b395f 100644 (file)
@@ -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 = {
        .get_settings           = efx_ethtool_get_settings,
        .set_settings           = efx_ethtool_set_settings,
@@ -767,4 +782,6 @@ const struct ethtool_ops efx_ethtool_ops = {
        .get_strings            = efx_ethtool_get_strings,
        .phys_id                = efx_ethtool_phys_id,
        .get_ethtool_stats      = efx_ethtool_get_stats,
+       .get_wol                = efx_ethtool_get_wol,
+       .set_wol                = efx_ethtool_set_wol,
 };
index 3466616c01c0b1be1f149318ad4936ceeac506a9..8f2c5830553862bf1ce1ee0ff6b8f251a11d9ea6 100644 (file)
@@ -3243,6 +3243,27 @@ void falcon_stop_nic_stats(struct efx_nic *efx)
        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
@@ -3266,6 +3287,9 @@ struct efx_nic_type falcon_a1_nic_type = {
        .push_irq_moderation = falcon_push_irq_moderation,
        .push_multicast_hash = falcon_push_multicast_hash,
        .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,
 
        .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_multicast_hash = falcon_push_multicast_hash,
        .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,
 
        .revision = EFX_REV_FALCON_B0,
index f63a05c4e38bd7ac738a0b9382717ede53266379..a9fde82aeeae1d99976aa648eb2a9f87c714a53b 100644 (file)
@@ -861,6 +861,9 @@ static inline const char *efx_dev_name(struct efx_nic *efx)
  * @push_irq_moderation: Apply interrupt moderation value
  * @push_multicast_hash: Apply multicast hash table
  * @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
  * @revision: Hardware architecture revision
  * @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_multicast_hash)(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;
 
        int revision;