Skip to content

Commit 50ee42b

Browse files
committed
Merge branch 'WoL-filters'
Florian Fainelli says: ==================== net: Support Wake-on-LAN using filters This is technically a v2, but this patch series builds on your feedback and defines the following: - a new WAKE_* bit: WAKE_FILTER which can be enabled alongside other type of Wake-on-LAN to support waking up on a programmed filter (match + action) - a new RX_CLS_FLOW_WAKE flow action which can be specified by an user when inserting a flow using ethtool::rxnfc, similar to the existing RX_CLS_FLOW_DISC The bcm_sf2 and bcmsysport drivers are updated accordingly to work in concert to allow matching packets at the switch level, identified by their filter location to be used as a match by the SYSTEM PORT (CPU/management controller) during Wake-on-LAN. Let me know if this looks better than the previous incarnation of the patch series. Attached is also the ethtool patch that I would be submitting once the uapi changes are committed. Thank you! Changes in v2: - bail out earlier in bcm_sf2_cfp's get_rxnfc if an error is encountered (Andrew) ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 1ba9828 + bb9051a commit 50ee42b

File tree

4 files changed

+239
-13
lines changed

4 files changed

+239
-13
lines changed

drivers/net/dsa/bcm_sf2_cfp.c

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,8 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
732732
struct ethtool_rx_flow_spec *fs)
733733
{
734734
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
735+
s8 cpu_port = ds->ports[port].cpu_dp->index;
736+
__u64 ring_cookie = fs->ring_cookie;
735737
unsigned int queue_num, port_num;
736738
int ret = -EINVAL;
737739

@@ -748,13 +750,19 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
748750
fs->location > bcm_sf2_cfp_rule_size(priv))
749751
return -EINVAL;
750752

753+
/* This rule is a Wake-on-LAN filter and we must specifically
754+
* target the CPU port in order for it to be working.
755+
*/
756+
if (ring_cookie == RX_CLS_FLOW_WAKE)
757+
ring_cookie = cpu_port * SF2_NUM_EGRESS_QUEUES;
758+
751759
/* We do not support discarding packets, check that the
752760
* destination port is enabled and that we are within the
753761
* number of ports supported by the switch
754762
*/
755-
port_num = fs->ring_cookie / SF2_NUM_EGRESS_QUEUES;
763+
port_num = ring_cookie / SF2_NUM_EGRESS_QUEUES;
756764

757-
if (fs->ring_cookie == RX_CLS_FLOW_DISC ||
765+
if (ring_cookie == RX_CLS_FLOW_DISC ||
758766
!(dsa_is_user_port(ds, port_num) ||
759767
dsa_is_cpu_port(ds, port_num)) ||
760768
port_num >= priv->hw_params.num_ports)
@@ -763,7 +771,7 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
763771
* We have a small oddity where Port 6 just does not have a
764772
* valid bit here (so we substract by one).
765773
*/
766-
queue_num = fs->ring_cookie % SF2_NUM_EGRESS_QUEUES;
774+
queue_num = ring_cookie % SF2_NUM_EGRESS_QUEUES;
767775
if (port_num >= 7)
768776
port_num -= 1;
769777

@@ -1188,6 +1196,7 @@ static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv,
11881196
int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,
11891197
struct ethtool_rxnfc *nfc, u32 *rule_locs)
11901198
{
1199+
struct net_device *p = ds->ports[port].cpu_dp->master;
11911200
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
11921201
int ret = 0;
11931202

@@ -1214,12 +1223,23 @@ int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,
12141223

12151224
mutex_unlock(&priv->cfp.lock);
12161225

1226+
if (ret)
1227+
return ret;
1228+
1229+
/* Pass up the commands to the attached master network device */
1230+
if (p->ethtool_ops->get_rxnfc) {
1231+
ret = p->ethtool_ops->get_rxnfc(p, nfc, rule_locs);
1232+
if (ret == -EOPNOTSUPP)
1233+
ret = 0;
1234+
}
1235+
12171236
return ret;
12181237
}
12191238

12201239
int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
12211240
struct ethtool_rxnfc *nfc)
12221241
{
1242+
struct net_device *p = ds->ports[port].cpu_dp->master;
12231243
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
12241244
int ret = 0;
12251245

@@ -1240,6 +1260,23 @@ int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
12401260

12411261
mutex_unlock(&priv->cfp.lock);
12421262

1263+
if (ret)
1264+
return ret;
1265+
1266+
/* Pass up the commands to the attached master network device.
1267+
* This can fail, so rollback the operation if we need to.
1268+
*/
1269+
if (p->ethtool_ops->set_rxnfc) {
1270+
ret = p->ethtool_ops->set_rxnfc(p, nfc);
1271+
if (ret && ret != -EOPNOTSUPP) {
1272+
mutex_lock(&priv->cfp.lock);
1273+
bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location);
1274+
mutex_unlock(&priv->cfp.lock);
1275+
} else {
1276+
ret = 0;
1277+
}
1278+
}
1279+
12431280
return ret;
12441281
}
12451282

drivers/net/ethernet/broadcom/bcmsysport.c

Lines changed: 186 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ static void bcm_sysport_get_wol(struct net_device *dev,
521521
struct bcm_sysport_priv *priv = netdev_priv(dev);
522522
u32 reg;
523523

524-
wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE;
524+
wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
525525
wol->wolopts = priv->wolopts;
526526

527527
if (!(priv->wolopts & WAKE_MAGICSECURE))
@@ -539,7 +539,7 @@ static int bcm_sysport_set_wol(struct net_device *dev,
539539
{
540540
struct bcm_sysport_priv *priv = netdev_priv(dev);
541541
struct device *kdev = &priv->pdev->dev;
542-
u32 supported = WAKE_MAGIC | WAKE_MAGICSECURE;
542+
u32 supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
543543

544544
if (!device_can_wakeup(kdev))
545545
return -ENOTSUPP;
@@ -1043,20 +1043,40 @@ static int bcm_sysport_poll(struct napi_struct *napi, int budget)
10431043

10441044
static void mpd_enable_set(struct bcm_sysport_priv *priv, bool enable)
10451045
{
1046-
u32 reg;
1046+
u32 reg, bit;
10471047

10481048
reg = umac_readl(priv, UMAC_MPD_CTRL);
10491049
if (enable)
10501050
reg |= MPD_EN;
10511051
else
10521052
reg &= ~MPD_EN;
10531053
umac_writel(priv, reg, UMAC_MPD_CTRL);
1054+
1055+
if (priv->is_lite)
1056+
bit = RBUF_ACPI_EN_LITE;
1057+
else
1058+
bit = RBUF_ACPI_EN;
1059+
1060+
reg = rbuf_readl(priv, RBUF_CONTROL);
1061+
if (enable)
1062+
reg |= bit;
1063+
else
1064+
reg &= ~bit;
1065+
rbuf_writel(priv, reg, RBUF_CONTROL);
10541066
}
10551067

10561068
static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv)
10571069
{
1070+
u32 reg;
1071+
10581072
/* Stop monitoring MPD interrupt */
1059-
intrl2_0_mask_set(priv, INTRL2_0_MPD);
1073+
intrl2_0_mask_set(priv, INTRL2_0_MPD | INTRL2_0_BRCM_MATCH_TAG);
1074+
1075+
/* Disable RXCHK, active filters and Broadcom tag matching */
1076+
reg = rxchk_readl(priv, RXCHK_CONTROL);
1077+
reg &= ~(RXCHK_BRCM_TAG_MATCH_MASK <<
1078+
RXCHK_BRCM_TAG_MATCH_SHIFT | RXCHK_EN | RXCHK_BRCM_TAG_EN);
1079+
rxchk_writel(priv, reg, RXCHK_CONTROL);
10601080

10611081
/* Clear the MagicPacket detection logic */
10621082
mpd_enable_set(priv, false);
@@ -1085,6 +1105,7 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
10851105
struct bcm_sysport_priv *priv = netdev_priv(dev);
10861106
struct bcm_sysport_tx_ring *txr;
10871107
unsigned int ring, ring_bit;
1108+
u32 reg;
10881109

10891110
priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
10901111
~intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
@@ -1111,7 +1132,14 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
11111132
bcm_sysport_tx_reclaim_all(priv);
11121133

11131134
if (priv->irq0_stat & INTRL2_0_MPD)
1114-
netdev_info(priv->netdev, "Wake-on-LAN interrupt!\n");
1135+
netdev_info(priv->netdev, "Wake-on-LAN (MPD) interrupt!\n");
1136+
1137+
if (priv->irq0_stat & INTRL2_0_BRCM_MATCH_TAG) {
1138+
reg = rxchk_readl(priv, RXCHK_BRCM_TAG_MATCH_STATUS) &
1139+
RXCHK_BRCM_TAG_MATCH_MASK;
1140+
netdev_info(priv->netdev,
1141+
"Wake-on-LAN (filters 0x%02x) interrupt!\n", reg);
1142+
}
11151143

11161144
if (!priv->is_lite)
11171145
goto out;
@@ -2096,6 +2124,132 @@ static int bcm_sysport_stop(struct net_device *dev)
20962124
return 0;
20972125
}
20982126

2127+
static int bcm_sysport_rule_find(struct bcm_sysport_priv *priv,
2128+
u64 location)
2129+
{
2130+
unsigned int index;
2131+
u32 reg;
2132+
2133+
for_each_set_bit(index, priv->filters, RXCHK_BRCM_TAG_MAX) {
2134+
reg = rxchk_readl(priv, RXCHK_BRCM_TAG(index));
2135+
reg >>= RXCHK_BRCM_TAG_CID_SHIFT;
2136+
reg &= RXCHK_BRCM_TAG_CID_MASK;
2137+
if (reg == location)
2138+
return index;
2139+
}
2140+
2141+
return -EINVAL;
2142+
}
2143+
2144+
static int bcm_sysport_rule_get(struct bcm_sysport_priv *priv,
2145+
struct ethtool_rxnfc *nfc)
2146+
{
2147+
int index;
2148+
2149+
/* This is not a rule that we know about */
2150+
index = bcm_sysport_rule_find(priv, nfc->fs.location);
2151+
if (index < 0)
2152+
return -EOPNOTSUPP;
2153+
2154+
nfc->fs.ring_cookie = RX_CLS_FLOW_WAKE;
2155+
2156+
return 0;
2157+
}
2158+
2159+
static int bcm_sysport_rule_set(struct bcm_sysport_priv *priv,
2160+
struct ethtool_rxnfc *nfc)
2161+
{
2162+
unsigned int index;
2163+
u32 reg;
2164+
2165+
/* We cannot match locations greater than what the classification ID
2166+
* permits (256 entries)
2167+
*/
2168+
if (nfc->fs.location > RXCHK_BRCM_TAG_CID_MASK)
2169+
return -E2BIG;
2170+
2171+
/* We cannot support flows that are not destined for a wake-up */
2172+
if (nfc->fs.ring_cookie != RX_CLS_FLOW_WAKE)
2173+
return -EOPNOTSUPP;
2174+
2175+
/* All filters are already in use, we cannot match more rules */
2176+
if (bitmap_weight(priv->filters, RXCHK_BRCM_TAG_MAX) ==
2177+
RXCHK_BRCM_TAG_MAX)
2178+
return -ENOSPC;
2179+
2180+
index = find_first_zero_bit(priv->filters, RXCHK_BRCM_TAG_MAX);
2181+
if (index > RXCHK_BRCM_TAG_MAX)
2182+
return -ENOSPC;
2183+
2184+
/* Location is the classification ID, and index is the position
2185+
* within one of our 8 possible filters to be programmed
2186+
*/
2187+
reg = rxchk_readl(priv, RXCHK_BRCM_TAG(index));
2188+
reg &= ~(RXCHK_BRCM_TAG_CID_MASK << RXCHK_BRCM_TAG_CID_SHIFT);
2189+
reg |= nfc->fs.location << RXCHK_BRCM_TAG_CID_SHIFT;
2190+
rxchk_writel(priv, reg, RXCHK_BRCM_TAG(index));
2191+
rxchk_writel(priv, 0xff00ffff, RXCHK_BRCM_TAG_MASK(index));
2192+
2193+
set_bit(index, priv->filters);
2194+
2195+
return 0;
2196+
}
2197+
2198+
static int bcm_sysport_rule_del(struct bcm_sysport_priv *priv,
2199+
u64 location)
2200+
{
2201+
int index;
2202+
2203+
/* This is not a rule that we know about */
2204+
index = bcm_sysport_rule_find(priv, location);
2205+
if (index < 0)
2206+
return -EOPNOTSUPP;
2207+
2208+
/* No need to disable this filter if it was enabled, this will
2209+
* be taken care of during suspend time by bcm_sysport_suspend_to_wol
2210+
*/
2211+
clear_bit(index, priv->filters);
2212+
2213+
return 0;
2214+
}
2215+
2216+
static int bcm_sysport_get_rxnfc(struct net_device *dev,
2217+
struct ethtool_rxnfc *nfc, u32 *rule_locs)
2218+
{
2219+
struct bcm_sysport_priv *priv = netdev_priv(dev);
2220+
int ret = -EOPNOTSUPP;
2221+
2222+
switch (nfc->cmd) {
2223+
case ETHTOOL_GRXCLSRULE:
2224+
ret = bcm_sysport_rule_get(priv, nfc);
2225+
break;
2226+
default:
2227+
break;
2228+
}
2229+
2230+
return ret;
2231+
}
2232+
2233+
static int bcm_sysport_set_rxnfc(struct net_device *dev,
2234+
struct ethtool_rxnfc *nfc)
2235+
{
2236+
struct bcm_sysport_priv *priv = netdev_priv(dev);
2237+
int ret = -EOPNOTSUPP;
2238+
2239+
switch (nfc->cmd) {
2240+
case ETHTOOL_SRXCLSRLINS:
2241+
ret = bcm_sysport_rule_set(priv, nfc);
2242+
break;
2243+
case ETHTOOL_SRXCLSRLDEL:
2244+
ret = bcm_sysport_rule_del(priv, nfc->fs.location);
2245+
break;
2246+
default:
2247+
break;
2248+
}
2249+
2250+
return ret;
2251+
}
2252+
20992253
static const struct ethtool_ops bcm_sysport_ethtool_ops = {
21002254
.get_drvinfo = bcm_sysport_get_drvinfo,
21012255
.get_msglevel = bcm_sysport_get_msglvl,
@@ -2110,6 +2264,8 @@ static const struct ethtool_ops bcm_sysport_ethtool_ops = {
21102264
.set_coalesce = bcm_sysport_set_coalesce,
21112265
.get_link_ksettings = phy_ethtool_get_link_ksettings,
21122266
.set_link_ksettings = phy_ethtool_set_link_ksettings,
2267+
.get_rxnfc = bcm_sysport_get_rxnfc,
2268+
.set_rxnfc = bcm_sysport_set_rxnfc,
21132269
};
21142270

21152271
static u16 bcm_sysport_select_queue(struct net_device *dev, struct sk_buff *skb,
@@ -2434,16 +2590,39 @@ static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv)
24342590
{
24352591
struct net_device *ndev = priv->netdev;
24362592
unsigned int timeout = 1000;
2593+
unsigned int index, i = 0;
24372594
u32 reg;
24382595

24392596
/* Password has already been programmed */
24402597
reg = umac_readl(priv, UMAC_MPD_CTRL);
2441-
reg |= MPD_EN;
2598+
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE))
2599+
reg |= MPD_EN;
24422600
reg &= ~PSW_EN;
24432601
if (priv->wolopts & WAKE_MAGICSECURE)
24442602
reg |= PSW_EN;
24452603
umac_writel(priv, reg, UMAC_MPD_CTRL);
24462604

2605+
if (priv->wolopts & WAKE_FILTER) {
2606+
/* Turn on ACPI matching to steal packets from RBUF */
2607+
reg = rbuf_readl(priv, RBUF_CONTROL);
2608+
if (priv->is_lite)
2609+
reg |= RBUF_ACPI_EN_LITE;
2610+
else
2611+
reg |= RBUF_ACPI_EN;
2612+
rbuf_writel(priv, reg, RBUF_CONTROL);
2613+
2614+
/* Enable RXCHK, active filters and Broadcom tag matching */
2615+
reg = rxchk_readl(priv, RXCHK_CONTROL);
2616+
reg &= ~(RXCHK_BRCM_TAG_MATCH_MASK <<
2617+
RXCHK_BRCM_TAG_MATCH_SHIFT);
2618+
for_each_set_bit(index, priv->filters, RXCHK_BRCM_TAG_MAX) {
2619+
reg |= BIT(RXCHK_BRCM_TAG_MATCH_SHIFT + i);
2620+
i++;
2621+
}
2622+
reg |= RXCHK_EN | RXCHK_BRCM_TAG_EN;
2623+
rxchk_writel(priv, reg, RXCHK_CONTROL);
2624+
}
2625+
24472626
/* Make sure RBUF entered WoL mode as result */
24482627
do {
24492628
reg = rbuf_readl(priv, RBUF_STATUS);
@@ -2464,7 +2643,7 @@ static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv)
24642643
umac_enable_set(priv, CMD_RX_EN, 1);
24652644

24662645
/* Enable the interrupt wake-up source */
2467-
intrl2_0_mask_clear(priv, INTRL2_0_MPD);
2646+
intrl2_0_mask_clear(priv, INTRL2_0_MPD | INTRL2_0_BRCM_MATCH_TAG);
24682647

24692648
netif_dbg(priv, wol, ndev, "entered WOL mode\n");
24702649

0 commit comments

Comments
 (0)