net: systemport: Add support for WAKE_FILTER
The SYSTEMPORT MAC allows up to 8 filters to be programmed to wake-up from LAN. Verify that we have up to 8 filters and program them to the appropriate RXCHK entries to be matched (along with their masks). We need to update the entry and exit to Wake-on-LAN mode to keep the RXCHK engine running to match during suspend, but this is otherwise fairly similar to Magic Packet detection. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
8a75f4f2ac
commit
bb9051a2b2
@ -521,7 +521,7 @@ static void bcm_sysport_get_wol(struct net_device *dev,
|
|||||||
struct bcm_sysport_priv *priv = netdev_priv(dev);
|
struct bcm_sysport_priv *priv = netdev_priv(dev);
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE;
|
wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
|
||||||
wol->wolopts = priv->wolopts;
|
wol->wolopts = priv->wolopts;
|
||||||
|
|
||||||
if (!(priv->wolopts & WAKE_MAGICSECURE))
|
if (!(priv->wolopts & WAKE_MAGICSECURE))
|
||||||
@ -539,7 +539,7 @@ static int bcm_sysport_set_wol(struct net_device *dev,
|
|||||||
{
|
{
|
||||||
struct bcm_sysport_priv *priv = netdev_priv(dev);
|
struct bcm_sysport_priv *priv = netdev_priv(dev);
|
||||||
struct device *kdev = &priv->pdev->dev;
|
struct device *kdev = &priv->pdev->dev;
|
||||||
u32 supported = WAKE_MAGIC | WAKE_MAGICSECURE;
|
u32 supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
|
||||||
|
|
||||||
if (!device_can_wakeup(kdev))
|
if (!device_can_wakeup(kdev))
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
@ -1043,7 +1043,7 @@ static int bcm_sysport_poll(struct napi_struct *napi, int budget)
|
|||||||
|
|
||||||
static void mpd_enable_set(struct bcm_sysport_priv *priv, bool enable)
|
static void mpd_enable_set(struct bcm_sysport_priv *priv, bool enable)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg, bit;
|
||||||
|
|
||||||
reg = umac_readl(priv, UMAC_MPD_CTRL);
|
reg = umac_readl(priv, UMAC_MPD_CTRL);
|
||||||
if (enable)
|
if (enable)
|
||||||
@ -1051,12 +1051,32 @@ static void mpd_enable_set(struct bcm_sysport_priv *priv, bool enable)
|
|||||||
else
|
else
|
||||||
reg &= ~MPD_EN;
|
reg &= ~MPD_EN;
|
||||||
umac_writel(priv, reg, UMAC_MPD_CTRL);
|
umac_writel(priv, reg, UMAC_MPD_CTRL);
|
||||||
|
|
||||||
|
if (priv->is_lite)
|
||||||
|
bit = RBUF_ACPI_EN_LITE;
|
||||||
|
else
|
||||||
|
bit = RBUF_ACPI_EN;
|
||||||
|
|
||||||
|
reg = rbuf_readl(priv, RBUF_CONTROL);
|
||||||
|
if (enable)
|
||||||
|
reg |= bit;
|
||||||
|
else
|
||||||
|
reg &= ~bit;
|
||||||
|
rbuf_writel(priv, reg, RBUF_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv)
|
static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv)
|
||||||
{
|
{
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
/* Stop monitoring MPD interrupt */
|
/* Stop monitoring MPD interrupt */
|
||||||
intrl2_0_mask_set(priv, INTRL2_0_MPD);
|
intrl2_0_mask_set(priv, INTRL2_0_MPD | INTRL2_0_BRCM_MATCH_TAG);
|
||||||
|
|
||||||
|
/* Disable RXCHK, active filters and Broadcom tag matching */
|
||||||
|
reg = rxchk_readl(priv, RXCHK_CONTROL);
|
||||||
|
reg &= ~(RXCHK_BRCM_TAG_MATCH_MASK <<
|
||||||
|
RXCHK_BRCM_TAG_MATCH_SHIFT | RXCHK_EN | RXCHK_BRCM_TAG_EN);
|
||||||
|
rxchk_writel(priv, reg, RXCHK_CONTROL);
|
||||||
|
|
||||||
/* Clear the MagicPacket detection logic */
|
/* Clear the MagicPacket detection logic */
|
||||||
mpd_enable_set(priv, false);
|
mpd_enable_set(priv, false);
|
||||||
@ -1085,6 +1105,7 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
|
|||||||
struct bcm_sysport_priv *priv = netdev_priv(dev);
|
struct bcm_sysport_priv *priv = netdev_priv(dev);
|
||||||
struct bcm_sysport_tx_ring *txr;
|
struct bcm_sysport_tx_ring *txr;
|
||||||
unsigned int ring, ring_bit;
|
unsigned int ring, ring_bit;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
|
priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
|
||||||
~intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
|
~intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
|
||||||
@ -1111,7 +1132,14 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
|
|||||||
bcm_sysport_tx_reclaim_all(priv);
|
bcm_sysport_tx_reclaim_all(priv);
|
||||||
|
|
||||||
if (priv->irq0_stat & INTRL2_0_MPD)
|
if (priv->irq0_stat & INTRL2_0_MPD)
|
||||||
netdev_info(priv->netdev, "Wake-on-LAN interrupt!\n");
|
netdev_info(priv->netdev, "Wake-on-LAN (MPD) interrupt!\n");
|
||||||
|
|
||||||
|
if (priv->irq0_stat & INTRL2_0_BRCM_MATCH_TAG) {
|
||||||
|
reg = rxchk_readl(priv, RXCHK_BRCM_TAG_MATCH_STATUS) &
|
||||||
|
RXCHK_BRCM_TAG_MATCH_MASK;
|
||||||
|
netdev_info(priv->netdev,
|
||||||
|
"Wake-on-LAN (filters 0x%02x) interrupt!\n", reg);
|
||||||
|
}
|
||||||
|
|
||||||
if (!priv->is_lite)
|
if (!priv->is_lite)
|
||||||
goto out;
|
goto out;
|
||||||
@ -2096,6 +2124,132 @@ static int bcm_sysport_stop(struct net_device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bcm_sysport_rule_find(struct bcm_sysport_priv *priv,
|
||||||
|
u64 location)
|
||||||
|
{
|
||||||
|
unsigned int index;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
for_each_set_bit(index, priv->filters, RXCHK_BRCM_TAG_MAX) {
|
||||||
|
reg = rxchk_readl(priv, RXCHK_BRCM_TAG(index));
|
||||||
|
reg >>= RXCHK_BRCM_TAG_CID_SHIFT;
|
||||||
|
reg &= RXCHK_BRCM_TAG_CID_MASK;
|
||||||
|
if (reg == location)
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sysport_rule_get(struct bcm_sysport_priv *priv,
|
||||||
|
struct ethtool_rxnfc *nfc)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
|
||||||
|
/* This is not a rule that we know about */
|
||||||
|
index = bcm_sysport_rule_find(priv, nfc->fs.location);
|
||||||
|
if (index < 0)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
nfc->fs.ring_cookie = RX_CLS_FLOW_WAKE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sysport_rule_set(struct bcm_sysport_priv *priv,
|
||||||
|
struct ethtool_rxnfc *nfc)
|
||||||
|
{
|
||||||
|
unsigned int index;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/* We cannot match locations greater than what the classification ID
|
||||||
|
* permits (256 entries)
|
||||||
|
*/
|
||||||
|
if (nfc->fs.location > RXCHK_BRCM_TAG_CID_MASK)
|
||||||
|
return -E2BIG;
|
||||||
|
|
||||||
|
/* We cannot support flows that are not destined for a wake-up */
|
||||||
|
if (nfc->fs.ring_cookie != RX_CLS_FLOW_WAKE)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/* All filters are already in use, we cannot match more rules */
|
||||||
|
if (bitmap_weight(priv->filters, RXCHK_BRCM_TAG_MAX) ==
|
||||||
|
RXCHK_BRCM_TAG_MAX)
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
|
index = find_first_zero_bit(priv->filters, RXCHK_BRCM_TAG_MAX);
|
||||||
|
if (index > RXCHK_BRCM_TAG_MAX)
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
|
/* Location is the classification ID, and index is the position
|
||||||
|
* within one of our 8 possible filters to be programmed
|
||||||
|
*/
|
||||||
|
reg = rxchk_readl(priv, RXCHK_BRCM_TAG(index));
|
||||||
|
reg &= ~(RXCHK_BRCM_TAG_CID_MASK << RXCHK_BRCM_TAG_CID_SHIFT);
|
||||||
|
reg |= nfc->fs.location << RXCHK_BRCM_TAG_CID_SHIFT;
|
||||||
|
rxchk_writel(priv, reg, RXCHK_BRCM_TAG(index));
|
||||||
|
rxchk_writel(priv, 0xff00ffff, RXCHK_BRCM_TAG_MASK(index));
|
||||||
|
|
||||||
|
set_bit(index, priv->filters);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sysport_rule_del(struct bcm_sysport_priv *priv,
|
||||||
|
u64 location)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
|
||||||
|
/* This is not a rule that we know about */
|
||||||
|
index = bcm_sysport_rule_find(priv, location);
|
||||||
|
if (index < 0)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/* No need to disable this filter if it was enabled, this will
|
||||||
|
* be taken care of during suspend time by bcm_sysport_suspend_to_wol
|
||||||
|
*/
|
||||||
|
clear_bit(index, priv->filters);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sysport_get_rxnfc(struct net_device *dev,
|
||||||
|
struct ethtool_rxnfc *nfc, u32 *rule_locs)
|
||||||
|
{
|
||||||
|
struct bcm_sysport_priv *priv = netdev_priv(dev);
|
||||||
|
int ret = -EOPNOTSUPP;
|
||||||
|
|
||||||
|
switch (nfc->cmd) {
|
||||||
|
case ETHTOOL_GRXCLSRULE:
|
||||||
|
ret = bcm_sysport_rule_get(priv, nfc);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sysport_set_rxnfc(struct net_device *dev,
|
||||||
|
struct ethtool_rxnfc *nfc)
|
||||||
|
{
|
||||||
|
struct bcm_sysport_priv *priv = netdev_priv(dev);
|
||||||
|
int ret = -EOPNOTSUPP;
|
||||||
|
|
||||||
|
switch (nfc->cmd) {
|
||||||
|
case ETHTOOL_SRXCLSRLINS:
|
||||||
|
ret = bcm_sysport_rule_set(priv, nfc);
|
||||||
|
break;
|
||||||
|
case ETHTOOL_SRXCLSRLDEL:
|
||||||
|
ret = bcm_sysport_rule_del(priv, nfc->fs.location);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct ethtool_ops bcm_sysport_ethtool_ops = {
|
static const struct ethtool_ops bcm_sysport_ethtool_ops = {
|
||||||
.get_drvinfo = bcm_sysport_get_drvinfo,
|
.get_drvinfo = bcm_sysport_get_drvinfo,
|
||||||
.get_msglevel = bcm_sysport_get_msglvl,
|
.get_msglevel = bcm_sysport_get_msglvl,
|
||||||
@ -2110,6 +2264,8 @@ static const struct ethtool_ops bcm_sysport_ethtool_ops = {
|
|||||||
.set_coalesce = bcm_sysport_set_coalesce,
|
.set_coalesce = bcm_sysport_set_coalesce,
|
||||||
.get_link_ksettings = phy_ethtool_get_link_ksettings,
|
.get_link_ksettings = phy_ethtool_get_link_ksettings,
|
||||||
.set_link_ksettings = phy_ethtool_set_link_ksettings,
|
.set_link_ksettings = phy_ethtool_set_link_ksettings,
|
||||||
|
.get_rxnfc = bcm_sysport_get_rxnfc,
|
||||||
|
.set_rxnfc = bcm_sysport_set_rxnfc,
|
||||||
};
|
};
|
||||||
|
|
||||||
static u16 bcm_sysport_select_queue(struct net_device *dev, struct sk_buff *skb,
|
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)
|
|||||||
{
|
{
|
||||||
struct net_device *ndev = priv->netdev;
|
struct net_device *ndev = priv->netdev;
|
||||||
unsigned int timeout = 1000;
|
unsigned int timeout = 1000;
|
||||||
|
unsigned int index, i = 0;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
/* Password has already been programmed */
|
/* Password has already been programmed */
|
||||||
reg = umac_readl(priv, UMAC_MPD_CTRL);
|
reg = umac_readl(priv, UMAC_MPD_CTRL);
|
||||||
reg |= MPD_EN;
|
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE))
|
||||||
|
reg |= MPD_EN;
|
||||||
reg &= ~PSW_EN;
|
reg &= ~PSW_EN;
|
||||||
if (priv->wolopts & WAKE_MAGICSECURE)
|
if (priv->wolopts & WAKE_MAGICSECURE)
|
||||||
reg |= PSW_EN;
|
reg |= PSW_EN;
|
||||||
umac_writel(priv, reg, UMAC_MPD_CTRL);
|
umac_writel(priv, reg, UMAC_MPD_CTRL);
|
||||||
|
|
||||||
|
if (priv->wolopts & WAKE_FILTER) {
|
||||||
|
/* Turn on ACPI matching to steal packets from RBUF */
|
||||||
|
reg = rbuf_readl(priv, RBUF_CONTROL);
|
||||||
|
if (priv->is_lite)
|
||||||
|
reg |= RBUF_ACPI_EN_LITE;
|
||||||
|
else
|
||||||
|
reg |= RBUF_ACPI_EN;
|
||||||
|
rbuf_writel(priv, reg, RBUF_CONTROL);
|
||||||
|
|
||||||
|
/* Enable RXCHK, active filters and Broadcom tag matching */
|
||||||
|
reg = rxchk_readl(priv, RXCHK_CONTROL);
|
||||||
|
reg &= ~(RXCHK_BRCM_TAG_MATCH_MASK <<
|
||||||
|
RXCHK_BRCM_TAG_MATCH_SHIFT);
|
||||||
|
for_each_set_bit(index, priv->filters, RXCHK_BRCM_TAG_MAX) {
|
||||||
|
reg |= BIT(RXCHK_BRCM_TAG_MATCH_SHIFT + i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
reg |= RXCHK_EN | RXCHK_BRCM_TAG_EN;
|
||||||
|
rxchk_writel(priv, reg, RXCHK_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Make sure RBUF entered WoL mode as result */
|
/* Make sure RBUF entered WoL mode as result */
|
||||||
do {
|
do {
|
||||||
reg = rbuf_readl(priv, RBUF_STATUS);
|
reg = rbuf_readl(priv, RBUF_STATUS);
|
||||||
@ -2464,7 +2643,7 @@ static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv)
|
|||||||
umac_enable_set(priv, CMD_RX_EN, 1);
|
umac_enable_set(priv, CMD_RX_EN, 1);
|
||||||
|
|
||||||
/* Enable the interrupt wake-up source */
|
/* Enable the interrupt wake-up source */
|
||||||
intrl2_0_mask_clear(priv, INTRL2_0_MPD);
|
intrl2_0_mask_clear(priv, INTRL2_0_MPD | INTRL2_0_BRCM_MATCH_TAG);
|
||||||
|
|
||||||
netif_dbg(priv, wol, ndev, "entered WOL mode\n");
|
netif_dbg(priv, wol, ndev, "entered WOL mode\n");
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#ifndef __BCM_SYSPORT_H
|
#ifndef __BCM_SYSPORT_H
|
||||||
#define __BCM_SYSPORT_H
|
#define __BCM_SYSPORT_H
|
||||||
|
|
||||||
|
#include <linux/bitmap.h>
|
||||||
#include <linux/if_vlan.h>
|
#include <linux/if_vlan.h>
|
||||||
#include <linux/net_dim.h>
|
#include <linux/net_dim.h>
|
||||||
|
|
||||||
@ -155,14 +156,18 @@ struct bcm_rsb {
|
|||||||
#define RXCHK_PARSE_AUTH (1 << 22)
|
#define RXCHK_PARSE_AUTH (1 << 22)
|
||||||
|
|
||||||
#define RXCHK_BRCM_TAG0 0x04
|
#define RXCHK_BRCM_TAG0 0x04
|
||||||
#define RXCHK_BRCM_TAG(i) ((i) * RXCHK_BRCM_TAG0)
|
#define RXCHK_BRCM_TAG(i) ((i) * 0x4 + RXCHK_BRCM_TAG0)
|
||||||
#define RXCHK_BRCM_TAG0_MASK 0x24
|
#define RXCHK_BRCM_TAG0_MASK 0x24
|
||||||
#define RXCHK_BRCM_TAG_MASK(i) ((i) * RXCHK_BRCM_TAG0_MASK)
|
#define RXCHK_BRCM_TAG_MASK(i) ((i) * 0x4 + RXCHK_BRCM_TAG0_MASK)
|
||||||
#define RXCHK_BRCM_TAG_MATCH_STATUS 0x44
|
#define RXCHK_BRCM_TAG_MATCH_STATUS 0x44
|
||||||
#define RXCHK_ETHERTYPE 0x48
|
#define RXCHK_ETHERTYPE 0x48
|
||||||
#define RXCHK_BAD_CSUM_CNTR 0x4C
|
#define RXCHK_BAD_CSUM_CNTR 0x4C
|
||||||
#define RXCHK_OTHER_DISC_CNTR 0x50
|
#define RXCHK_OTHER_DISC_CNTR 0x50
|
||||||
|
|
||||||
|
#define RXCHK_BRCM_TAG_MAX 8
|
||||||
|
#define RXCHK_BRCM_TAG_CID_SHIFT 16
|
||||||
|
#define RXCHK_BRCM_TAG_CID_MASK 0xff
|
||||||
|
|
||||||
/* TXCHCK offsets and defines */
|
/* TXCHCK offsets and defines */
|
||||||
#define SYS_PORT_TXCHK_OFFSET 0x380
|
#define SYS_PORT_TXCHK_OFFSET 0x380
|
||||||
#define TXCHK_PKT_RDY_THRESH 0x00
|
#define TXCHK_PKT_RDY_THRESH 0x00
|
||||||
@ -185,6 +190,7 @@ struct bcm_rsb {
|
|||||||
#define RBUF_RSB_SWAP0 (1 << 22)
|
#define RBUF_RSB_SWAP0 (1 << 22)
|
||||||
#define RBUF_RSB_SWAP1 (1 << 23)
|
#define RBUF_RSB_SWAP1 (1 << 23)
|
||||||
#define RBUF_ACPI_EN (1 << 23)
|
#define RBUF_ACPI_EN (1 << 23)
|
||||||
|
#define RBUF_ACPI_EN_LITE (1 << 24)
|
||||||
|
|
||||||
#define RBUF_PKT_RDY_THRESH 0x04
|
#define RBUF_PKT_RDY_THRESH 0x04
|
||||||
|
|
||||||
@ -777,6 +783,7 @@ struct bcm_sysport_priv {
|
|||||||
|
|
||||||
/* Ethtool */
|
/* Ethtool */
|
||||||
u32 msg_enable;
|
u32 msg_enable;
|
||||||
|
DECLARE_BITMAP(filters, RXCHK_BRCM_TAG_MAX);
|
||||||
|
|
||||||
struct bcm_sysport_stats64 stats64;
|
struct bcm_sysport_stats64 stats64;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user