Commit a8c64542 authored by Doug Berger's avatar Doug Berger Committed by David S. Miller

net: bcmgenet: restore HFB filters on resume

The Hardware Filter Block RAM may not be preserved when the GENET
block is reset during a deep sleep, so it is not sufficient to
only backup and restore the enables.

This commit clears out the HFB block and reprograms the rxnfc
rules when the system resumes from a suspended state. To support
this the bcmgenet_hfb_create_rxnfc_filter() function is modified
to access the register space directly so that it can't fail due
to memory allocation issues.

Fixes: f50932cc ("net: bcmgenet: add WAKE_FILTER support")
Signed-off-by: default avatarDoug Berger <opendmb@gmail.com>
Acked-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3d653adb
...@@ -543,14 +543,14 @@ static int bcmgenet_hfb_validate_mask(void *mask, size_t size) ...@@ -543,14 +543,14 @@ static int bcmgenet_hfb_validate_mask(void *mask, size_t size)
#define VALIDATE_MASK(x) \ #define VALIDATE_MASK(x) \
bcmgenet_hfb_validate_mask(&(x), sizeof(x)) bcmgenet_hfb_validate_mask(&(x), sizeof(x))
static int bcmgenet_hfb_insert_data(u32 *f, int offset, static int bcmgenet_hfb_insert_data(struct bcmgenet_priv *priv, u32 f_index,
void *val, void *mask, size_t size) u32 offset, void *val, void *mask,
size_t size)
{ {
int index; u32 index, tmp;
u32 tmp;
index = offset / 2; index = f_index * priv->hw_params->hfb_filter_size + offset / 2;
tmp = f[index]; tmp = bcmgenet_hfb_readl(priv, index * sizeof(u32));
while (size--) { while (size--) {
if (offset++ & 1) { if (offset++ & 1) {
...@@ -567,9 +567,10 @@ static int bcmgenet_hfb_insert_data(u32 *f, int offset, ...@@ -567,9 +567,10 @@ static int bcmgenet_hfb_insert_data(u32 *f, int offset,
tmp |= 0x10000; tmp |= 0x10000;
break; break;
} }
f[index++] = tmp; bcmgenet_hfb_writel(priv, tmp, index++ * sizeof(u32));
if (size) if (size)
tmp = f[index]; tmp = bcmgenet_hfb_readl(priv,
index * sizeof(u32));
} else { } else {
tmp &= ~0xCFF00; tmp &= ~0xCFF00;
tmp |= (*(unsigned char *)val++) << 8; tmp |= (*(unsigned char *)val++) << 8;
...@@ -585,44 +586,26 @@ static int bcmgenet_hfb_insert_data(u32 *f, int offset, ...@@ -585,44 +586,26 @@ static int bcmgenet_hfb_insert_data(u32 *f, int offset,
break; break;
} }
if (!size) if (!size)
f[index] = tmp; bcmgenet_hfb_writel(priv, tmp, index * sizeof(u32));
} }
} }
return 0; return 0;
} }
static void bcmgenet_hfb_set_filter(struct bcmgenet_priv *priv, u32 *f_data, static void bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
u32 f_length, u32 rx_queue, int f_index)
{
u32 base = f_index * priv->hw_params->hfb_filter_size;
int i;
for (i = 0; i < f_length; i++)
bcmgenet_hfb_writel(priv, f_data[i], (base + i) * sizeof(u32));
bcmgenet_hfb_set_filter_length(priv, f_index, 2 * f_length);
bcmgenet_hfb_set_filter_rx_queue_mapping(priv, f_index, rx_queue);
}
static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
struct bcmgenet_rxnfc_rule *rule) struct bcmgenet_rxnfc_rule *rule)
{ {
struct ethtool_rx_flow_spec *fs = &rule->fs; struct ethtool_rx_flow_spec *fs = &rule->fs;
int err = 0, offset = 0, f_length = 0; u32 offset = 0, f_length = 0, f;
u8 val_8, mask_8; u8 val_8, mask_8;
__be16 val_16; __be16 val_16;
u16 mask_16; u16 mask_16;
size_t size; size_t size;
u32 *f_data;
f_data = kcalloc(priv->hw_params->hfb_filter_size, sizeof(u32),
GFP_KERNEL);
if (!f_data)
return -ENOMEM;
f = fs->location;
if (fs->flow_type & FLOW_MAC_EXT) { if (fs->flow_type & FLOW_MAC_EXT) {
bcmgenet_hfb_insert_data(f_data, 0, bcmgenet_hfb_insert_data(priv, f, 0,
&fs->h_ext.h_dest, &fs->m_ext.h_dest, &fs->h_ext.h_dest, &fs->m_ext.h_dest,
sizeof(fs->h_ext.h_dest)); sizeof(fs->h_ext.h_dest));
} }
...@@ -630,11 +613,11 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv, ...@@ -630,11 +613,11 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
if (fs->flow_type & FLOW_EXT) { if (fs->flow_type & FLOW_EXT) {
if (fs->m_ext.vlan_etype || if (fs->m_ext.vlan_etype ||
fs->m_ext.vlan_tci) { fs->m_ext.vlan_tci) {
bcmgenet_hfb_insert_data(f_data, 12, bcmgenet_hfb_insert_data(priv, f, 12,
&fs->h_ext.vlan_etype, &fs->h_ext.vlan_etype,
&fs->m_ext.vlan_etype, &fs->m_ext.vlan_etype,
sizeof(fs->h_ext.vlan_etype)); sizeof(fs->h_ext.vlan_etype));
bcmgenet_hfb_insert_data(f_data, 14, bcmgenet_hfb_insert_data(priv, f, 14,
&fs->h_ext.vlan_tci, &fs->h_ext.vlan_tci,
&fs->m_ext.vlan_tci, &fs->m_ext.vlan_tci,
sizeof(fs->h_ext.vlan_tci)); sizeof(fs->h_ext.vlan_tci));
...@@ -646,15 +629,15 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv, ...@@ -646,15 +629,15 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
case ETHER_FLOW: case ETHER_FLOW:
f_length += DIV_ROUND_UP(ETH_HLEN, 2); f_length += DIV_ROUND_UP(ETH_HLEN, 2);
bcmgenet_hfb_insert_data(f_data, 0, bcmgenet_hfb_insert_data(priv, f, 0,
&fs->h_u.ether_spec.h_dest, &fs->h_u.ether_spec.h_dest,
&fs->m_u.ether_spec.h_dest, &fs->m_u.ether_spec.h_dest,
sizeof(fs->h_u.ether_spec.h_dest)); sizeof(fs->h_u.ether_spec.h_dest));
bcmgenet_hfb_insert_data(f_data, ETH_ALEN, bcmgenet_hfb_insert_data(priv, f, ETH_ALEN,
&fs->h_u.ether_spec.h_source, &fs->h_u.ether_spec.h_source,
&fs->m_u.ether_spec.h_source, &fs->m_u.ether_spec.h_source,
sizeof(fs->h_u.ether_spec.h_source)); sizeof(fs->h_u.ether_spec.h_source));
bcmgenet_hfb_insert_data(f_data, (2 * ETH_ALEN) + offset, bcmgenet_hfb_insert_data(priv, f, (2 * ETH_ALEN) + offset,
&fs->h_u.ether_spec.h_proto, &fs->h_u.ether_spec.h_proto,
&fs->m_u.ether_spec.h_proto, &fs->m_u.ether_spec.h_proto,
sizeof(fs->h_u.ether_spec.h_proto)); sizeof(fs->h_u.ether_spec.h_proto));
...@@ -664,21 +647,21 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv, ...@@ -664,21 +647,21 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
/* Specify IP Ether Type */ /* Specify IP Ether Type */
val_16 = htons(ETH_P_IP); val_16 = htons(ETH_P_IP);
mask_16 = 0xFFFF; mask_16 = 0xFFFF;
bcmgenet_hfb_insert_data(f_data, (2 * ETH_ALEN) + offset, bcmgenet_hfb_insert_data(priv, f, (2 * ETH_ALEN) + offset,
&val_16, &mask_16, sizeof(val_16)); &val_16, &mask_16, sizeof(val_16));
bcmgenet_hfb_insert_data(f_data, 15 + offset, bcmgenet_hfb_insert_data(priv, f, 15 + offset,
&fs->h_u.usr_ip4_spec.tos, &fs->h_u.usr_ip4_spec.tos,
&fs->m_u.usr_ip4_spec.tos, &fs->m_u.usr_ip4_spec.tos,
sizeof(fs->h_u.usr_ip4_spec.tos)); sizeof(fs->h_u.usr_ip4_spec.tos));
bcmgenet_hfb_insert_data(f_data, 23 + offset, bcmgenet_hfb_insert_data(priv, f, 23 + offset,
&fs->h_u.usr_ip4_spec.proto, &fs->h_u.usr_ip4_spec.proto,
&fs->m_u.usr_ip4_spec.proto, &fs->m_u.usr_ip4_spec.proto,
sizeof(fs->h_u.usr_ip4_spec.proto)); sizeof(fs->h_u.usr_ip4_spec.proto));
bcmgenet_hfb_insert_data(f_data, 26 + offset, bcmgenet_hfb_insert_data(priv, f, 26 + offset,
&fs->h_u.usr_ip4_spec.ip4src, &fs->h_u.usr_ip4_spec.ip4src,
&fs->m_u.usr_ip4_spec.ip4src, &fs->m_u.usr_ip4_spec.ip4src,
sizeof(fs->h_u.usr_ip4_spec.ip4src)); sizeof(fs->h_u.usr_ip4_spec.ip4src));
bcmgenet_hfb_insert_data(f_data, 30 + offset, bcmgenet_hfb_insert_data(priv, f, 30 + offset,
&fs->h_u.usr_ip4_spec.ip4dst, &fs->h_u.usr_ip4_spec.ip4dst,
&fs->m_u.usr_ip4_spec.ip4dst, &fs->m_u.usr_ip4_spec.ip4dst,
sizeof(fs->h_u.usr_ip4_spec.ip4dst)); sizeof(fs->h_u.usr_ip4_spec.ip4dst));
...@@ -688,11 +671,11 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv, ...@@ -688,11 +671,11 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
/* Only supports 20 byte IPv4 header */ /* Only supports 20 byte IPv4 header */
val_8 = 0x45; val_8 = 0x45;
mask_8 = 0xFF; mask_8 = 0xFF;
bcmgenet_hfb_insert_data(f_data, ETH_HLEN + offset, bcmgenet_hfb_insert_data(priv, f, ETH_HLEN + offset,
&val_8, &mask_8, &val_8, &mask_8,
sizeof(val_8)); sizeof(val_8));
size = sizeof(fs->h_u.usr_ip4_spec.l4_4_bytes); size = sizeof(fs->h_u.usr_ip4_spec.l4_4_bytes);
bcmgenet_hfb_insert_data(f_data, bcmgenet_hfb_insert_data(priv, f,
ETH_HLEN + 20 + offset, ETH_HLEN + 20 + offset,
&fs->h_u.usr_ip4_spec.l4_4_bytes, &fs->h_u.usr_ip4_spec.l4_4_bytes,
&fs->m_u.usr_ip4_spec.l4_4_bytes, &fs->m_u.usr_ip4_spec.l4_4_bytes,
...@@ -701,34 +684,42 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv, ...@@ -701,34 +684,42 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
break; break;
} }
bcmgenet_hfb_set_filter_length(priv, f, 2 * f_length);
if (!fs->ring_cookie || fs->ring_cookie == RX_CLS_FLOW_WAKE) { if (!fs->ring_cookie || fs->ring_cookie == RX_CLS_FLOW_WAKE) {
/* Ring 0 flows can be handled by the default Descriptor Ring /* Ring 0 flows can be handled by the default Descriptor Ring
* We'll map them to ring 0, but don't enable the filter * We'll map them to ring 0, but don't enable the filter
*/ */
bcmgenet_hfb_set_filter(priv, f_data, f_length, 0, bcmgenet_hfb_set_filter_rx_queue_mapping(priv, f, 0);
fs->location);
rule->state = BCMGENET_RXNFC_STATE_DISABLED; rule->state = BCMGENET_RXNFC_STATE_DISABLED;
} else { } else {
/* Other Rx rings are direct mapped here */ /* Other Rx rings are direct mapped here */
bcmgenet_hfb_set_filter(priv, f_data, f_length, bcmgenet_hfb_set_filter_rx_queue_mapping(priv, f,
fs->ring_cookie, fs->location); fs->ring_cookie);
bcmgenet_hfb_enable_filter(priv, fs->location); bcmgenet_hfb_enable_filter(priv, f);
rule->state = BCMGENET_RXNFC_STATE_ENABLED; rule->state = BCMGENET_RXNFC_STATE_ENABLED;
} }
kfree(f_data);
return err;
} }
/* bcmgenet_hfb_clear /* bcmgenet_hfb_clear
* *
* Clear Hardware Filter Block and disable all filtering. * Clear Hardware Filter Block and disable all filtering.
*/ */
static void bcmgenet_hfb_clear_filter(struct bcmgenet_priv *priv, u32 f_index)
{
u32 base, i;
base = f_index * priv->hw_params->hfb_filter_size;
for (i = 0; i < priv->hw_params->hfb_filter_size; i++)
bcmgenet_hfb_writel(priv, 0x0, (base + i) * sizeof(u32));
}
static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv) static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv)
{ {
u32 i; u32 i;
if (GENET_IS_V1(priv) || GENET_IS_V2(priv))
return;
bcmgenet_hfb_reg_writel(priv, 0x0, HFB_CTRL); bcmgenet_hfb_reg_writel(priv, 0x0, HFB_CTRL);
bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS); bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS);
bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS + 4); bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS + 4);
...@@ -740,19 +731,18 @@ static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv) ...@@ -740,19 +731,18 @@ static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv)
bcmgenet_hfb_reg_writel(priv, 0x0, bcmgenet_hfb_reg_writel(priv, 0x0,
HFB_FLT_LEN_V3PLUS + i * sizeof(u32)); HFB_FLT_LEN_V3PLUS + i * sizeof(u32));
for (i = 0; i < priv->hw_params->hfb_filter_cnt * for (i = 0; i < priv->hw_params->hfb_filter_cnt; i++)
priv->hw_params->hfb_filter_size; i++) bcmgenet_hfb_clear_filter(priv, i);
bcmgenet_hfb_writel(priv, 0x0, i * sizeof(u32));
} }
static void bcmgenet_hfb_init(struct bcmgenet_priv *priv) static void bcmgenet_hfb_init(struct bcmgenet_priv *priv)
{ {
int i; int i;
INIT_LIST_HEAD(&priv->rxnfc_list);
if (GENET_IS_V1(priv) || GENET_IS_V2(priv)) if (GENET_IS_V1(priv) || GENET_IS_V2(priv))
return; return;
INIT_LIST_HEAD(&priv->rxnfc_list);
for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) { for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) {
INIT_LIST_HEAD(&priv->rxnfc_rules[i].list); INIT_LIST_HEAD(&priv->rxnfc_rules[i].list);
priv->rxnfc_rules[i].state = BCMGENET_RXNFC_STATE_UNUSED; priv->rxnfc_rules[i].state = BCMGENET_RXNFC_STATE_UNUSED;
...@@ -1437,18 +1427,15 @@ static int bcmgenet_insert_flow(struct net_device *dev, ...@@ -1437,18 +1427,15 @@ static int bcmgenet_insert_flow(struct net_device *dev,
loc_rule = &priv->rxnfc_rules[cmd->fs.location]; loc_rule = &priv->rxnfc_rules[cmd->fs.location];
if (loc_rule->state == BCMGENET_RXNFC_STATE_ENABLED) if (loc_rule->state == BCMGENET_RXNFC_STATE_ENABLED)
bcmgenet_hfb_disable_filter(priv, cmd->fs.location); bcmgenet_hfb_disable_filter(priv, cmd->fs.location);
if (loc_rule->state != BCMGENET_RXNFC_STATE_UNUSED) if (loc_rule->state != BCMGENET_RXNFC_STATE_UNUSED) {
list_del(&loc_rule->list); list_del(&loc_rule->list);
bcmgenet_hfb_clear_filter(priv, cmd->fs.location);
}
loc_rule->state = BCMGENET_RXNFC_STATE_UNUSED; loc_rule->state = BCMGENET_RXNFC_STATE_UNUSED;
memcpy(&loc_rule->fs, &cmd->fs, memcpy(&loc_rule->fs, &cmd->fs,
sizeof(struct ethtool_rx_flow_spec)); sizeof(struct ethtool_rx_flow_spec));
err = bcmgenet_hfb_create_rxnfc_filter(priv, loc_rule); bcmgenet_hfb_create_rxnfc_filter(priv, loc_rule);
if (err) {
netdev_err(dev, "rxnfc: Could not install rule (%d)\n",
err);
return err;
}
list_add_tail(&loc_rule->list, &priv->rxnfc_list); list_add_tail(&loc_rule->list, &priv->rxnfc_list);
...@@ -1473,8 +1460,10 @@ static int bcmgenet_delete_flow(struct net_device *dev, ...@@ -1473,8 +1460,10 @@ static int bcmgenet_delete_flow(struct net_device *dev,
if (rule->state == BCMGENET_RXNFC_STATE_ENABLED) if (rule->state == BCMGENET_RXNFC_STATE_ENABLED)
bcmgenet_hfb_disable_filter(priv, cmd->fs.location); bcmgenet_hfb_disable_filter(priv, cmd->fs.location);
if (rule->state != BCMGENET_RXNFC_STATE_UNUSED) if (rule->state != BCMGENET_RXNFC_STATE_UNUSED) {
list_del(&rule->list); list_del(&rule->list);
bcmgenet_hfb_clear_filter(priv, cmd->fs.location);
}
rule->state = BCMGENET_RXNFC_STATE_UNUSED; rule->state = BCMGENET_RXNFC_STATE_UNUSED;
memset(&rule->fs, 0, sizeof(struct ethtool_rx_flow_spec)); memset(&rule->fs, 0, sizeof(struct ethtool_rx_flow_spec));
...@@ -4129,8 +4118,9 @@ static int bcmgenet_resume(struct device *d) ...@@ -4129,8 +4118,9 @@ static int bcmgenet_resume(struct device *d)
{ {
struct net_device *dev = dev_get_drvdata(d); struct net_device *dev = dev_get_drvdata(d);
struct bcmgenet_priv *priv = netdev_priv(dev); struct bcmgenet_priv *priv = netdev_priv(dev);
struct bcmgenet_rxnfc_rule *rule;
unsigned long dma_ctrl; unsigned long dma_ctrl;
u32 offset, reg; u32 reg;
int ret; int ret;
if (!netif_running(dev)) if (!netif_running(dev))
...@@ -4161,10 +4151,11 @@ static int bcmgenet_resume(struct device *d) ...@@ -4161,10 +4151,11 @@ static int bcmgenet_resume(struct device *d)
bcmgenet_set_hw_addr(priv, dev->dev_addr); bcmgenet_set_hw_addr(priv, dev->dev_addr);
offset = HFB_FLT_ENABLE_V3PLUS; /* Restore hardware filters */
bcmgenet_hfb_reg_writel(priv, priv->hfb_en[1], offset); bcmgenet_hfb_clear(priv);
bcmgenet_hfb_reg_writel(priv, priv->hfb_en[2], offset + sizeof(u32)); list_for_each_entry(rule, &priv->rxnfc_list, list)
bcmgenet_hfb_reg_writel(priv, priv->hfb_en[0], HFB_CTRL); if (rule->state != BCMGENET_RXNFC_STATE_UNUSED)
bcmgenet_hfb_create_rxnfc_filter(priv, rule);
if (priv->internal_phy) { if (priv->internal_phy) {
reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
...@@ -4208,7 +4199,6 @@ static int bcmgenet_suspend(struct device *d) ...@@ -4208,7 +4199,6 @@ static int bcmgenet_suspend(struct device *d)
{ {
struct net_device *dev = dev_get_drvdata(d); struct net_device *dev = dev_get_drvdata(d);
struct bcmgenet_priv *priv = netdev_priv(dev); struct bcmgenet_priv *priv = netdev_priv(dev);
u32 offset;
if (!netif_running(dev)) if (!netif_running(dev))
return 0; return 0;
...@@ -4220,11 +4210,7 @@ static int bcmgenet_suspend(struct device *d) ...@@ -4220,11 +4210,7 @@ static int bcmgenet_suspend(struct device *d)
if (!device_may_wakeup(d)) if (!device_may_wakeup(d))
phy_suspend(dev->phydev); phy_suspend(dev->phydev);
/* Preserve filter state and disable filtering */ /* Disable filtering */
priv->hfb_en[0] = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
offset = HFB_FLT_ENABLE_V3PLUS;
priv->hfb_en[1] = bcmgenet_hfb_reg_readl(priv, offset);
priv->hfb_en[2] = bcmgenet_hfb_reg_readl(priv, offset + sizeof(u32));
bcmgenet_hfb_reg_writel(priv, 0, HFB_CTRL); bcmgenet_hfb_reg_writel(priv, 0, HFB_CTRL);
return 0; return 0;
......
...@@ -696,7 +696,6 @@ struct bcmgenet_priv { ...@@ -696,7 +696,6 @@ struct bcmgenet_priv {
u32 wolopts; u32 wolopts;
u8 sopass[SOPASS_MAX]; u8 sopass[SOPASS_MAX];
bool wol_active; bool wol_active;
u32 hfb_en[3];
struct bcmgenet_mib_counters mib; struct bcmgenet_mib_counters mib;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment