diff options
author | Florian Fainelli <f.fainelli@gmail.com> | 2014-09-18 17:31:25 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-22 14:41:23 -0400 |
commit | 96e65d7f3f88d6e117ff80f3003d13c65e85cb03 (patch) | |
tree | d1103c98dd7273538de10bea5c0e259816e8c409 /drivers/net/dsa | |
parent | 19e57c4e6dc6b82a3204b801f4c5f27c7d007559 (diff) |
net: dsa: bcm_sf2: add support for Wake-on-LAN
In order for Wake-on-LAN to work properly, we query the parent network
device Wake-on-LAN features and advertise those. Similarly, when
configuring Wake-on-LAN on a per-port network interface, we make sure
that we do not accept something the master network devices does not
support.
Finally, we need to maintain a bitmask of the ports enabled for
Wake-on-LAN to prevent the suspend() callback from disabling a port that
is used for waking up the system.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/dsa')
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 58 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2.h | 3 |
2 files changed, 61 insertions, 0 deletions
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 29ceee366e44..d9b7da545063 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -22,6 +22,7 @@ #include <linux/of_irq.h> #include <linux/of_address.h> #include <net/dsa.h> +#include <linux/ethtool.h> #include "bcm_sf2.h" #include "bcm_sf2_regs.h" @@ -242,6 +243,9 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port) struct bcm_sf2_priv *priv = ds_to_priv(ds); u32 off, reg; + if (priv->wol_ports_mask & (1 << port)) + return; + if (dsa_is_cpu_port(ds, port)) off = CORE_IMP_CTL; else @@ -689,6 +693,58 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) return 0; } +static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, + struct ethtool_wolinfo *wol) +{ + struct net_device *p = ds->dst[ds->index].master_netdev; + struct bcm_sf2_priv *priv = ds_to_priv(ds); + struct ethtool_wolinfo pwol; + + /* Get the parent device WoL settings */ + p->ethtool_ops->get_wol(p, &pwol); + + /* Advertise the parent device supported settings */ + wol->supported = pwol.supported; + memset(&wol->sopass, 0, sizeof(wol->sopass)); + + if (pwol.wolopts & WAKE_MAGICSECURE) + memcpy(&wol->sopass, pwol.sopass, sizeof(wol->sopass)); + + if (priv->wol_ports_mask & (1 << port)) + wol->wolopts = pwol.wolopts; + else + wol->wolopts = 0; +} + +static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, + struct ethtool_wolinfo *wol) +{ + struct net_device *p = ds->dst[ds->index].master_netdev; + struct bcm_sf2_priv *priv = ds_to_priv(ds); + s8 cpu_port = ds->dst[ds->index].cpu_port; + struct ethtool_wolinfo pwol; + + p->ethtool_ops->get_wol(p, &pwol); + if (wol->wolopts & ~pwol.supported) + return -EINVAL; + + if (wol->wolopts) + priv->wol_ports_mask |= (1 << port); + else + priv->wol_ports_mask &= ~(1 << port); + + /* If we have at least one port enabled, make sure the CPU port + * is also enabled. If the CPU port is the last one enabled, we disable + * it since this configuration does not make sense. + */ + if (priv->wol_ports_mask && priv->wol_ports_mask != (1 << cpu_port)) + priv->wol_ports_mask |= (1 << cpu_port); + else + priv->wol_ports_mask &= ~(1 << cpu_port); + + return p->ethtool_ops->set_wol(p, wol); +} + static struct dsa_switch_driver bcm_sf2_switch_driver = { .tag_protocol = DSA_TAG_PROTO_BRCM, .priv_size = sizeof(struct bcm_sf2_priv), @@ -705,6 +761,8 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = { .fixed_link_update = bcm_sf2_sw_fixed_link_update, .suspend = bcm_sf2_sw_suspend, .resume = bcm_sf2_sw_resume, + .get_wol = bcm_sf2_sw_get_wol, + .set_wol = bcm_sf2_sw_set_wol, }; static int __init bcm_sf2_init(void) diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h index d3bd52dc40d2..8fd6c1451a84 100644 --- a/drivers/net/dsa/bcm_sf2.h +++ b/drivers/net/dsa/bcm_sf2.h @@ -70,6 +70,9 @@ struct bcm_sf2_priv { struct bcm_sf2_hw_params hw_params; struct bcm_sf2_port_status port_sts[DSA_MAX_PORTS]; + + /* Mask of ports enabled for Wake-on-LAN */ + u32 wol_ports_mask; }; struct bcm_sf2_hw_stats { |