diff options
author | Florian Fainelli <f.fainelli@gmail.com> | 2014-09-18 20:31:25 -0400 |
---|---|---|
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 @@ | |||
22 | #include <linux/of_irq.h> | 22 | #include <linux/of_irq.h> |
23 | #include <linux/of_address.h> | 23 | #include <linux/of_address.h> |
24 | #include <net/dsa.h> | 24 | #include <net/dsa.h> |
25 | #include <linux/ethtool.h> | ||
25 | 26 | ||
26 | #include "bcm_sf2.h" | 27 | #include "bcm_sf2.h" |
27 | #include "bcm_sf2_regs.h" | 28 | #include "bcm_sf2_regs.h" |
@@ -242,6 +243,9 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port) | |||
242 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | 243 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
243 | u32 off, reg; | 244 | u32 off, reg; |
244 | 245 | ||
246 | if (priv->wol_ports_mask & (1 << port)) | ||
247 | return; | ||
248 | |||
245 | if (dsa_is_cpu_port(ds, port)) | 249 | if (dsa_is_cpu_port(ds, port)) |
246 | off = CORE_IMP_CTL; | 250 | off = CORE_IMP_CTL; |
247 | else | 251 | else |
@@ -689,6 +693,58 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) | |||
689 | return 0; | 693 | return 0; |
690 | } | 694 | } |
691 | 695 | ||
696 | static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, | ||
697 | struct ethtool_wolinfo *wol) | ||
698 | { | ||
699 | struct net_device *p = ds->dst[ds->index].master_netdev; | ||
700 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
701 | struct ethtool_wolinfo pwol; | ||
702 | |||
703 | /* Get the parent device WoL settings */ | ||
704 | p->ethtool_ops->get_wol(p, &pwol); | ||
705 | |||
706 | /* Advertise the parent device supported settings */ | ||
707 | wol->supported = pwol.supported; | ||
708 | memset(&wol->sopass, 0, sizeof(wol->sopass)); | ||
709 | |||
710 | if (pwol.wolopts & WAKE_MAGICSECURE) | ||
711 | memcpy(&wol->sopass, pwol.sopass, sizeof(wol->sopass)); | ||
712 | |||
713 | if (priv->wol_ports_mask & (1 << port)) | ||
714 | wol->wolopts = pwol.wolopts; | ||
715 | else | ||
716 | wol->wolopts = 0; | ||
717 | } | ||
718 | |||
719 | static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, | ||
720 | struct ethtool_wolinfo *wol) | ||
721 | { | ||
722 | struct net_device *p = ds->dst[ds->index].master_netdev; | ||
723 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
724 | s8 cpu_port = ds->dst[ds->index].cpu_port; | ||
725 | struct ethtool_wolinfo pwol; | ||
726 | |||
727 | p->ethtool_ops->get_wol(p, &pwol); | ||
728 | if (wol->wolopts & ~pwol.supported) | ||
729 | return -EINVAL; | ||
730 | |||
731 | if (wol->wolopts) | ||
732 | priv->wol_ports_mask |= (1 << port); | ||
733 | else | ||
734 | priv->wol_ports_mask &= ~(1 << port); | ||
735 | |||
736 | /* If we have at least one port enabled, make sure the CPU port | ||
737 | * is also enabled. If the CPU port is the last one enabled, we disable | ||
738 | * it since this configuration does not make sense. | ||
739 | */ | ||
740 | if (priv->wol_ports_mask && priv->wol_ports_mask != (1 << cpu_port)) | ||
741 | priv->wol_ports_mask |= (1 << cpu_port); | ||
742 | else | ||
743 | priv->wol_ports_mask &= ~(1 << cpu_port); | ||
744 | |||
745 | return p->ethtool_ops->set_wol(p, wol); | ||
746 | } | ||
747 | |||
692 | static struct dsa_switch_driver bcm_sf2_switch_driver = { | 748 | static struct dsa_switch_driver bcm_sf2_switch_driver = { |
693 | .tag_protocol = DSA_TAG_PROTO_BRCM, | 749 | .tag_protocol = DSA_TAG_PROTO_BRCM, |
694 | .priv_size = sizeof(struct bcm_sf2_priv), | 750 | .priv_size = sizeof(struct bcm_sf2_priv), |
@@ -705,6 +761,8 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = { | |||
705 | .fixed_link_update = bcm_sf2_sw_fixed_link_update, | 761 | .fixed_link_update = bcm_sf2_sw_fixed_link_update, |
706 | .suspend = bcm_sf2_sw_suspend, | 762 | .suspend = bcm_sf2_sw_suspend, |
707 | .resume = bcm_sf2_sw_resume, | 763 | .resume = bcm_sf2_sw_resume, |
764 | .get_wol = bcm_sf2_sw_get_wol, | ||
765 | .set_wol = bcm_sf2_sw_set_wol, | ||
708 | }; | 766 | }; |
709 | 767 | ||
710 | static int __init bcm_sf2_init(void) | 768 | 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 { | |||
70 | struct bcm_sf2_hw_params hw_params; | 70 | struct bcm_sf2_hw_params hw_params; |
71 | 71 | ||
72 | struct bcm_sf2_port_status port_sts[DSA_MAX_PORTS]; | 72 | struct bcm_sf2_port_status port_sts[DSA_MAX_PORTS]; |
73 | |||
74 | /* Mask of ports enabled for Wake-on-LAN */ | ||
75 | u32 wol_ports_mask; | ||
73 | }; | 76 | }; |
74 | 77 | ||
75 | struct bcm_sf2_hw_stats { | 78 | struct bcm_sf2_hw_stats { |