Skip to content

Commit

Permalink
Merge branch 'bcmgenet-WAKE_FILTER'
Browse files Browse the repository at this point in the history
Doug Berger says:

====================
net: bcmgenet: fix WAKE_FILTER resume from deep sleep

The WAKE_FILTER logic can only wake the system from the standby
power state. However, some systems that include the GENET IP
support deeper power saving states and the driver should suspend
and resume correctly from those states as well.

This commit set squashes a few issues uncovered while testing
suspend and resume from these deep sleep states.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jul 17, 2020
2 parents aba69d4 + a8c6454 commit 1291517
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 84 deletions.
138 changes: 62 additions & 76 deletions drivers/net/ethernet/broadcom/genet/bcmgenet.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,14 +543,14 @@ static int bcmgenet_hfb_validate_mask(void *mask, size_t size)
#define VALIDATE_MASK(x) \
bcmgenet_hfb_validate_mask(&(x), sizeof(x))

static int bcmgenet_hfb_insert_data(u32 *f, int offset,
void *val, void *mask, size_t size)
static int bcmgenet_hfb_insert_data(struct bcmgenet_priv *priv, u32 f_index,
u32 offset, void *val, void *mask,
size_t size)
{
int index;
u32 tmp;
u32 index, tmp;

index = offset / 2;
tmp = f[index];
index = f_index * priv->hw_params->hfb_filter_size + offset / 2;
tmp = bcmgenet_hfb_readl(priv, index * sizeof(u32));

while (size--) {
if (offset++ & 1) {
Expand All @@ -567,9 +567,10 @@ static int bcmgenet_hfb_insert_data(u32 *f, int offset,
tmp |= 0x10000;
break;
}
f[index++] = tmp;
bcmgenet_hfb_writel(priv, tmp, index++ * sizeof(u32));
if (size)
tmp = f[index];
tmp = bcmgenet_hfb_readl(priv,
index * sizeof(u32));
} else {
tmp &= ~0xCFF00;
tmp |= (*(unsigned char *)val++) << 8;
Expand All @@ -585,56 +586,38 @@ static int bcmgenet_hfb_insert_data(u32 *f, int offset,
break;
}
if (!size)
f[index] = tmp;
bcmgenet_hfb_writel(priv, tmp, index * sizeof(u32));
}
}

return 0;
}

static void bcmgenet_hfb_set_filter(struct bcmgenet_priv *priv, u32 *f_data,
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)
static void bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
struct bcmgenet_rxnfc_rule *rule)
{
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;
__be16 val_16;
u16 mask_16;
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) {
bcmgenet_hfb_insert_data(f_data, 0,
bcmgenet_hfb_insert_data(priv, f, 0,
&fs->h_ext.h_dest, &fs->m_ext.h_dest,
sizeof(fs->h_ext.h_dest));
}

if (fs->flow_type & FLOW_EXT) {
if (fs->m_ext.vlan_etype ||
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->m_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->m_ext.vlan_tci,
sizeof(fs->h_ext.vlan_tci));
Expand All @@ -646,15 +629,15 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
case ETHER_FLOW:
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->m_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->m_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->m_u.ether_spec.h_proto,
sizeof(fs->h_u.ether_spec.h_proto));
Expand All @@ -664,21 +647,21 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
/* Specify IP Ether Type */
val_16 = htons(ETH_P_IP);
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));
bcmgenet_hfb_insert_data(f_data, 15 + offset,
bcmgenet_hfb_insert_data(priv, f, 15 + offset,
&fs->h_u.usr_ip4_spec.tos,
&fs->m_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->m_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->m_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->m_u.usr_ip4_spec.ip4dst,
sizeof(fs->h_u.usr_ip4_spec.ip4dst));
Expand All @@ -688,11 +671,11 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
/* Only supports 20 byte IPv4 header */
val_8 = 0x45;
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,
sizeof(val_8));
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,
&fs->h_u.usr_ip4_spec.l4_4_bytes,
&fs->m_u.usr_ip4_spec.l4_4_bytes,
Expand All @@ -701,34 +684,42 @@ static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
break;
}

bcmgenet_hfb_set_filter_length(priv, f, 2 * f_length);
if (!fs->ring_cookie || fs->ring_cookie == RX_CLS_FLOW_WAKE) {
/* Ring 0 flows can be handled by the default Descriptor Ring
* We'll map them to ring 0, but don't enable the filter
*/
bcmgenet_hfb_set_filter(priv, f_data, f_length, 0,
fs->location);
bcmgenet_hfb_set_filter_rx_queue_mapping(priv, f, 0);
rule->state = BCMGENET_RXNFC_STATE_DISABLED;
} else {
/* Other Rx rings are direct mapped here */
bcmgenet_hfb_set_filter(priv, f_data, f_length,
fs->ring_cookie, fs->location);
bcmgenet_hfb_enable_filter(priv, fs->location);
bcmgenet_hfb_set_filter_rx_queue_mapping(priv, f,
fs->ring_cookie);
bcmgenet_hfb_enable_filter(priv, f);
rule->state = BCMGENET_RXNFC_STATE_ENABLED;
}

kfree(f_data);

return err;
}

/* bcmgenet_hfb_clear
*
* 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)
{
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_FLT_ENABLE_V3PLUS);
bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS + 4);
Expand All @@ -740,19 +731,18 @@ static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv)
bcmgenet_hfb_reg_writel(priv, 0x0,
HFB_FLT_LEN_V3PLUS + i * sizeof(u32));

for (i = 0; i < priv->hw_params->hfb_filter_cnt *
priv->hw_params->hfb_filter_size; i++)
bcmgenet_hfb_writel(priv, 0x0, i * sizeof(u32));
for (i = 0; i < priv->hw_params->hfb_filter_cnt; i++)
bcmgenet_hfb_clear_filter(priv, i);
}

static void bcmgenet_hfb_init(struct bcmgenet_priv *priv)
{
int i;

INIT_LIST_HEAD(&priv->rxnfc_list);
if (GENET_IS_V1(priv) || GENET_IS_V2(priv))
return;

INIT_LIST_HEAD(&priv->rxnfc_list);
for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) {
INIT_LIST_HEAD(&priv->rxnfc_rules[i].list);
priv->rxnfc_rules[i].state = BCMGENET_RXNFC_STATE_UNUSED;
Expand Down Expand Up @@ -1437,18 +1427,15 @@ static int bcmgenet_insert_flow(struct net_device *dev,
loc_rule = &priv->rxnfc_rules[cmd->fs.location];
if (loc_rule->state == BCMGENET_RXNFC_STATE_ENABLED)
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);
bcmgenet_hfb_clear_filter(priv, cmd->fs.location);
}
loc_rule->state = BCMGENET_RXNFC_STATE_UNUSED;
memcpy(&loc_rule->fs, &cmd->fs,
sizeof(struct ethtool_rx_flow_spec));

err = bcmgenet_hfb_create_rxnfc_filter(priv, loc_rule);
if (err) {
netdev_err(dev, "rxnfc: Could not install rule (%d)\n",
err);
return err;
}
bcmgenet_hfb_create_rxnfc_filter(priv, loc_rule);

list_add_tail(&loc_rule->list, &priv->rxnfc_list);

Expand All @@ -1473,8 +1460,10 @@ static int bcmgenet_delete_flow(struct net_device *dev,

if (rule->state == BCMGENET_RXNFC_STATE_ENABLED)
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);
bcmgenet_hfb_clear_filter(priv, cmd->fs.location);
}
rule->state = BCMGENET_RXNFC_STATE_UNUSED;
memset(&rule->fs, 0, sizeof(struct ethtool_rx_flow_spec));

Expand Down Expand Up @@ -4129,8 +4118,9 @@ static int bcmgenet_resume(struct device *d)
{
struct net_device *dev = dev_get_drvdata(d);
struct bcmgenet_priv *priv = netdev_priv(dev);
struct bcmgenet_rxnfc_rule *rule;
unsigned long dma_ctrl;
u32 offset, reg;
u32 reg;
int ret;

if (!netif_running(dev))
Expand Down Expand Up @@ -4161,10 +4151,11 @@ static int bcmgenet_resume(struct device *d)

bcmgenet_set_hw_addr(priv, dev->dev_addr);

offset = HFB_FLT_ENABLE_V3PLUS;
bcmgenet_hfb_reg_writel(priv, priv->hfb_en[1], offset);
bcmgenet_hfb_reg_writel(priv, priv->hfb_en[2], offset + sizeof(u32));
bcmgenet_hfb_reg_writel(priv, priv->hfb_en[0], HFB_CTRL);
/* Restore hardware filters */
bcmgenet_hfb_clear(priv);
list_for_each_entry(rule, &priv->rxnfc_list, list)
if (rule->state != BCMGENET_RXNFC_STATE_UNUSED)
bcmgenet_hfb_create_rxnfc_filter(priv, rule);

if (priv->internal_phy) {
reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
Expand Down Expand Up @@ -4208,7 +4199,6 @@ static int bcmgenet_suspend(struct device *d)
{
struct net_device *dev = dev_get_drvdata(d);
struct bcmgenet_priv *priv = netdev_priv(dev);
u32 offset;

if (!netif_running(dev))
return 0;
Expand All @@ -4220,11 +4210,7 @@ static int bcmgenet_suspend(struct device *d)
if (!device_may_wakeup(d))
phy_suspend(dev->phydev);

/* Preserve filter state and 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));
/* Disable filtering */
bcmgenet_hfb_reg_writel(priv, 0, HFB_CTRL);

return 0;
Expand Down
1 change: 0 additions & 1 deletion drivers/net/ethernet/broadcom/genet/bcmgenet.h
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,6 @@ struct bcmgenet_priv {
u32 wolopts;
u8 sopass[SOPASS_MAX];
bool wol_active;
u32 hfb_en[3];

struct bcmgenet_mib_counters mib;

Expand Down
22 changes: 15 additions & 7 deletions drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,20 +217,28 @@ void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,

priv->wol_active = 0;
clk_disable_unprepare(priv->clk_wol);
priv->crc_fwd_en = 0;

/* Disable Magic Packet Detection */
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
reg &= ~(MPD_EN | MPD_PW_EN);
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
if (!(reg & MPD_EN))
return; /* already reset so skip the rest */
reg &= ~(MPD_EN | MPD_PW_EN);
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
}

/* Disable WAKE_FILTER Detection */
reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
if (priv->wolopts & WAKE_FILTER) {
reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
if (!(reg & RBUF_ACPI_EN))
return; /* already reset so skip the rest */
reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
}

/* Disable CRC Forward */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~CMD_CRC_FWD;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
priv->crc_fwd_en = 0;
}

0 comments on commit 1291517

Please sign in to comment.