mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-23 02:54:32 +08:00
net: bcmgenet: add support for ethtool rxnfc flows
This commit enables driver support for ethtool commands of this form: ethtool -N|-U|--config-nfc|--config-ntuple devname flow-type ether|ip4 [src xx:yy:zz:aa:bb:cc [m xx:yy:zz:aa:bb:cc]] [dst xx:yy:zz:aa:bb:cc [m xx:yy:zz:aa:bb:cc]] [proto N [m N]] [src-ip x.x.x.x [m x.x.x.x]] [dst-ip x.x.x.x [m x.x.x.x]] [tos N [m N]] [l4proto N [m N]] [src-port N [m N]] [dst-port N [m N]] [spi N [m N]] [l4data N [m N]] [vlan-etype N [m N]] [vlan N [m N]] [dst-mac xx:yy:zz:aa:bb:cc [m xx:yy:zz:aa:bb:cc]] [action 0] [loc N] | delete N Since there is only one Rx Ring in this implementation action 0 behaves no differently from not specifying a rule. The rules can be seen with ethtool commands of this form: ethtool -n|-u|--show-nfc|--show-ntuple devname [rule N] Signed-off-by: Doug Berger <opendmb@gmail.com> Acked-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
854295d03c
commit
3e37095228
@ -479,6 +479,30 @@ static void bcmgenet_hfb_enable_filter(struct bcmgenet_priv *priv, u32 f_index)
|
||||
reg = bcmgenet_hfb_reg_readl(priv, offset);
|
||||
reg |= (1 << (f_index % 32));
|
||||
bcmgenet_hfb_reg_writel(priv, reg, offset);
|
||||
reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
|
||||
reg |= RBUF_HFB_EN;
|
||||
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
|
||||
}
|
||||
|
||||
static void bcmgenet_hfb_disable_filter(struct bcmgenet_priv *priv, u32 f_index)
|
||||
{
|
||||
u32 offset, reg, reg1;
|
||||
|
||||
offset = HFB_FLT_ENABLE_V3PLUS;
|
||||
reg = bcmgenet_hfb_reg_readl(priv, offset);
|
||||
reg1 = bcmgenet_hfb_reg_readl(priv, offset + sizeof(u32));
|
||||
if (f_index < 32) {
|
||||
reg1 &= ~(1 << (f_index % 32));
|
||||
bcmgenet_hfb_reg_writel(priv, reg1, offset + sizeof(u32));
|
||||
} else {
|
||||
reg &= ~(1 << (f_index % 32));
|
||||
bcmgenet_hfb_reg_writel(priv, reg, offset);
|
||||
}
|
||||
if (!reg && !reg1) {
|
||||
reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
|
||||
reg &= ~RBUF_HFB_EN;
|
||||
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
|
||||
}
|
||||
}
|
||||
|
||||
static void bcmgenet_hfb_set_filter_rx_queue_mapping(struct bcmgenet_priv *priv,
|
||||
@ -513,13 +537,213 @@ static int bcmgenet_hfb_find_unused_filter(struct bcmgenet_priv *priv)
|
||||
{
|
||||
u32 f_index;
|
||||
|
||||
for (f_index = 0; f_index < priv->hw_params->hfb_filter_cnt; f_index++)
|
||||
/* First MAX_NUM_OF_FS_RULES are reserved for Rx NFC filters */
|
||||
for (f_index = MAX_NUM_OF_FS_RULES;
|
||||
f_index < priv->hw_params->hfb_filter_cnt; f_index++)
|
||||
if (!bcmgenet_hfb_is_filter_enabled(priv, f_index))
|
||||
return f_index;
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int bcmgenet_hfb_validate_mask(void *mask, size_t size)
|
||||
{
|
||||
while (size) {
|
||||
switch (*(unsigned char *)mask++) {
|
||||
case 0x00:
|
||||
case 0x0f:
|
||||
case 0xf0:
|
||||
case 0xff:
|
||||
size--;
|
||||
continue;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
int index;
|
||||
u32 tmp;
|
||||
|
||||
index = offset / 2;
|
||||
tmp = f[index];
|
||||
|
||||
while (size--) {
|
||||
if (offset++ & 1) {
|
||||
tmp &= ~0x300FF;
|
||||
tmp |= (*(unsigned char *)val++);
|
||||
switch ((*(unsigned char *)mask++)) {
|
||||
case 0xFF:
|
||||
tmp |= 0x30000;
|
||||
break;
|
||||
case 0xF0:
|
||||
tmp |= 0x20000;
|
||||
break;
|
||||
case 0x0F:
|
||||
tmp |= 0x10000;
|
||||
break;
|
||||
}
|
||||
f[index++] = tmp;
|
||||
if (size)
|
||||
tmp = f[index];
|
||||
} else {
|
||||
tmp &= ~0xCFF00;
|
||||
tmp |= (*(unsigned char *)val++) << 8;
|
||||
switch ((*(unsigned char *)mask++)) {
|
||||
case 0xFF:
|
||||
tmp |= 0xC0000;
|
||||
break;
|
||||
case 0xF0:
|
||||
tmp |= 0x80000;
|
||||
break;
|
||||
case 0x0F:
|
||||
tmp |= 0x40000;
|
||||
break;
|
||||
}
|
||||
if (!size)
|
||||
f[index] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct ethtool_rx_flow_spec *fs = &rule->fs;
|
||||
int err = 0, offset = 0, f_length = 0;
|
||||
u16 val_16, mask_16;
|
||||
u8 val_8, mask_8;
|
||||
size_t size;
|
||||
u32 *f_data;
|
||||
|
||||
f_data = kcalloc(priv->hw_params->hfb_filter_size, sizeof(u32),
|
||||
GFP_KERNEL);
|
||||
if (!f_data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (fs->flow_type & FLOW_MAC_EXT) {
|
||||
bcmgenet_hfb_insert_data(f_data, 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,
|
||||
&fs->h_ext.vlan_etype,
|
||||
&fs->m_ext.vlan_etype,
|
||||
sizeof(fs->h_ext.vlan_etype));
|
||||
bcmgenet_hfb_insert_data(f_data, 14,
|
||||
&fs->h_ext.vlan_tci,
|
||||
&fs->m_ext.vlan_tci,
|
||||
sizeof(fs->h_ext.vlan_tci));
|
||||
offset += VLAN_HLEN;
|
||||
f_length += DIV_ROUND_UP(VLAN_HLEN, 2);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
&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,
|
||||
&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,
|
||||
&fs->h_u.ether_spec.h_proto,
|
||||
&fs->m_u.ether_spec.h_proto,
|
||||
sizeof(fs->h_u.ether_spec.h_proto));
|
||||
break;
|
||||
case IP_USER_FLOW:
|
||||
f_length += DIV_ROUND_UP(ETH_HLEN + 20, 2);
|
||||
/* Specify IP Ether Type */
|
||||
val_16 = htons(ETH_P_IP);
|
||||
mask_16 = 0xFFFF;
|
||||
bcmgenet_hfb_insert_data(f_data, (2 * ETH_ALEN) + offset,
|
||||
&val_16, &mask_16, sizeof(val_16));
|
||||
bcmgenet_hfb_insert_data(f_data, 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,
|
||||
&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,
|
||||
&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,
|
||||
&fs->h_u.usr_ip4_spec.ip4dst,
|
||||
&fs->m_u.usr_ip4_spec.ip4dst,
|
||||
sizeof(fs->h_u.usr_ip4_spec.ip4dst));
|
||||
if (!fs->m_u.usr_ip4_spec.l4_4_bytes)
|
||||
break;
|
||||
|
||||
/* Only supports 20 byte IPv4 header */
|
||||
val_8 = 0x45;
|
||||
mask_8 = 0xFF;
|
||||
bcmgenet_hfb_insert_data(f_data, 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,
|
||||
ETH_HLEN + 20 + offset,
|
||||
&fs->h_u.usr_ip4_spec.l4_4_bytes,
|
||||
&fs->m_u.usr_ip4_spec.l4_4_bytes,
|
||||
size);
|
||||
f_length += DIV_ROUND_UP(size, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!fs->ring_cookie) {
|
||||
/* 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);
|
||||
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);
|
||||
rule->state = BCMGENET_RXNFC_STATE_ENABLED;
|
||||
}
|
||||
|
||||
kfree(f_data);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* bcmgenet_hfb_add_filter
|
||||
*
|
||||
* Add new filter to Hardware Filter Block to match and direct Rx traffic to
|
||||
@ -559,7 +783,6 @@ int bcmgenet_hfb_add_filter(struct bcmgenet_priv *priv, u32 *f_data,
|
||||
u32 f_length, u32 rx_queue)
|
||||
{
|
||||
int f_index;
|
||||
u32 i;
|
||||
|
||||
f_index = bcmgenet_hfb_find_unused_filter(priv);
|
||||
if (f_index < 0)
|
||||
@ -568,15 +791,8 @@ int bcmgenet_hfb_add_filter(struct bcmgenet_priv *priv, u32 *f_data,
|
||||
if (f_length > priv->hw_params->hfb_filter_size)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < f_length; i++)
|
||||
bcmgenet_hfb_writel(priv, f_data[i],
|
||||
(f_index * priv->hw_params->hfb_filter_size + 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);
|
||||
bcmgenet_hfb_set_filter(priv, f_data, f_length, rx_queue, f_index);
|
||||
bcmgenet_hfb_enable_filter(priv, f_index);
|
||||
bcmgenet_hfb_reg_writel(priv, 0x1, HFB_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -607,9 +823,17 @@ static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv)
|
||||
|
||||
static void bcmgenet_hfb_init(struct bcmgenet_priv *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bcmgenet_hfb_clear(priv);
|
||||
}
|
||||
|
||||
@ -1197,6 +1421,228 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e)
|
||||
return phy_ethtool_set_eee(dev->phydev, e);
|
||||
}
|
||||
|
||||
static int bcmgenet_validate_flow(struct net_device *dev,
|
||||
struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct ethtool_usrip4_spec *l4_mask;
|
||||
struct ethhdr *eth_mask;
|
||||
|
||||
if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) {
|
||||
netdev_err(dev, "rxnfc: Invalid location (%d)\n",
|
||||
cmd->fs.location);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
|
||||
case IP_USER_FLOW:
|
||||
l4_mask = &cmd->fs.m_u.usr_ip4_spec;
|
||||
/* don't allow mask which isn't valid */
|
||||
if (VALIDATE_MASK(l4_mask->ip4src) ||
|
||||
VALIDATE_MASK(l4_mask->ip4dst) ||
|
||||
VALIDATE_MASK(l4_mask->l4_4_bytes) ||
|
||||
VALIDATE_MASK(l4_mask->proto) ||
|
||||
VALIDATE_MASK(l4_mask->ip_ver) ||
|
||||
VALIDATE_MASK(l4_mask->tos)) {
|
||||
netdev_err(dev, "rxnfc: Unsupported mask\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case ETHER_FLOW:
|
||||
eth_mask = &cmd->fs.m_u.ether_spec;
|
||||
/* don't allow mask which isn't valid */
|
||||
if (VALIDATE_MASK(eth_mask->h_source) ||
|
||||
VALIDATE_MASK(eth_mask->h_source) ||
|
||||
VALIDATE_MASK(eth_mask->h_proto)) {
|
||||
netdev_err(dev, "rxnfc: Unsupported mask\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
netdev_err(dev, "rxnfc: Unsupported flow type (0x%x)\n",
|
||||
cmd->fs.flow_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((cmd->fs.flow_type & FLOW_EXT)) {
|
||||
/* don't allow mask which isn't valid */
|
||||
if (VALIDATE_MASK(cmd->fs.m_ext.vlan_etype) ||
|
||||
VALIDATE_MASK(cmd->fs.m_ext.vlan_tci)) {
|
||||
netdev_err(dev, "rxnfc: Unsupported mask\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (cmd->fs.m_ext.data[0] || cmd->fs.m_ext.data[1]) {
|
||||
netdev_err(dev, "rxnfc: user-def not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((cmd->fs.flow_type & FLOW_MAC_EXT)) {
|
||||
/* don't allow mask which isn't valid */
|
||||
if (VALIDATE_MASK(cmd->fs.m_ext.h_dest)) {
|
||||
netdev_err(dev, "rxnfc: Unsupported mask\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcmgenet_insert_flow(struct net_device *dev,
|
||||
struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct bcmgenet_priv *priv = netdev_priv(dev);
|
||||
struct bcmgenet_rxnfc_rule *loc_rule;
|
||||
int err;
|
||||
|
||||
if (priv->hw_params->hfb_filter_size < 128) {
|
||||
netdev_err(dev, "rxnfc: Not supported by this device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cmd->fs.ring_cookie > priv->hw_params->rx_queues) {
|
||||
netdev_err(dev, "rxnfc: Unsupported action (%llu)\n",
|
||||
cmd->fs.ring_cookie);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = bcmgenet_validate_flow(dev, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
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)
|
||||
list_del(&loc_rule->list);
|
||||
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;
|
||||
}
|
||||
|
||||
list_add_tail(&loc_rule->list, &priv->rxnfc_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcmgenet_delete_flow(struct net_device *dev,
|
||||
struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct bcmgenet_priv *priv = netdev_priv(dev);
|
||||
struct bcmgenet_rxnfc_rule *rule;
|
||||
int err = 0;
|
||||
|
||||
if (cmd->fs.location >= MAX_NUM_OF_FS_RULES)
|
||||
return -EINVAL;
|
||||
|
||||
rule = &priv->rxnfc_rules[cmd->fs.location];
|
||||
if (rule->state == BCMGENET_RXNFC_STATE_UNUSED) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rule->state == BCMGENET_RXNFC_STATE_ENABLED)
|
||||
bcmgenet_hfb_disable_filter(priv, cmd->fs.location);
|
||||
if (rule->state != BCMGENET_RXNFC_STATE_UNUSED)
|
||||
list_del(&rule->list);
|
||||
rule->state = BCMGENET_RXNFC_STATE_UNUSED;
|
||||
memset(&rule->fs, 0, sizeof(struct ethtool_rx_flow_spec));
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bcmgenet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct bcmgenet_priv *priv = netdev_priv(dev);
|
||||
int err = 0;
|
||||
|
||||
switch (cmd->cmd) {
|
||||
case ETHTOOL_SRXCLSRLINS:
|
||||
err = bcmgenet_insert_flow(dev, cmd);
|
||||
break;
|
||||
case ETHTOOL_SRXCLSRLDEL:
|
||||
err = bcmgenet_delete_flow(dev, cmd);
|
||||
break;
|
||||
default:
|
||||
netdev_warn(priv->dev, "Unsupported ethtool command. (%d)\n",
|
||||
cmd->cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bcmgenet_get_flow(struct net_device *dev, struct ethtool_rxnfc *cmd,
|
||||
int loc)
|
||||
{
|
||||
struct bcmgenet_priv *priv = netdev_priv(dev);
|
||||
struct bcmgenet_rxnfc_rule *rule;
|
||||
int err = 0;
|
||||
|
||||
if (loc < 0 || loc >= MAX_NUM_OF_FS_RULES)
|
||||
return -EINVAL;
|
||||
|
||||
rule = &priv->rxnfc_rules[loc];
|
||||
if (rule->state == BCMGENET_RXNFC_STATE_UNUSED)
|
||||
err = -ENOENT;
|
||||
else
|
||||
memcpy(&cmd->fs, &rule->fs,
|
||||
sizeof(struct ethtool_rx_flow_spec));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bcmgenet_get_num_flows(struct bcmgenet_priv *priv)
|
||||
{
|
||||
struct list_head *pos;
|
||||
int res = 0;
|
||||
|
||||
list_for_each(pos, &priv->rxnfc_list)
|
||||
res++;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int bcmgenet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
|
||||
u32 *rule_locs)
|
||||
{
|
||||
struct bcmgenet_priv *priv = netdev_priv(dev);
|
||||
struct bcmgenet_rxnfc_rule *rule;
|
||||
int err = 0;
|
||||
int i = 0;
|
||||
|
||||
switch (cmd->cmd) {
|
||||
case ETHTOOL_GRXRINGS:
|
||||
cmd->data = priv->hw_params->rx_queues ?: 1;
|
||||
break;
|
||||
case ETHTOOL_GRXCLSRLCNT:
|
||||
cmd->rule_cnt = bcmgenet_get_num_flows(priv);
|
||||
cmd->data = MAX_NUM_OF_FS_RULES;
|
||||
break;
|
||||
case ETHTOOL_GRXCLSRULE:
|
||||
err = bcmgenet_get_flow(dev, cmd, cmd->fs.location);
|
||||
break;
|
||||
case ETHTOOL_GRXCLSRLALL:
|
||||
list_for_each_entry(rule, &priv->rxnfc_list, list)
|
||||
if (i < cmd->rule_cnt)
|
||||
rule_locs[i++] = rule->fs.location;
|
||||
cmd->rule_cnt = i;
|
||||
cmd->data = MAX_NUM_OF_FS_RULES;
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* standard ethtool support functions. */
|
||||
static const struct ethtool_ops bcmgenet_ethtool_ops = {
|
||||
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
|
||||
@ -1221,6 +1667,8 @@ static const struct ethtool_ops bcmgenet_ethtool_ops = {
|
||||
.get_link_ksettings = bcmgenet_get_link_ksettings,
|
||||
.set_link_ksettings = bcmgenet_set_link_ksettings,
|
||||
.get_ts_info = ethtool_op_get_ts_info,
|
||||
.get_rxnfc = bcmgenet_get_rxnfc,
|
||||
.set_rxnfc = bcmgenet_set_rxnfc,
|
||||
};
|
||||
|
||||
/* Power down the unimac, based on mode. */
|
||||
@ -3730,8 +4178,8 @@ static int bcmgenet_resume(struct device *d)
|
||||
struct net_device *dev = dev_get_drvdata(d);
|
||||
struct bcmgenet_priv *priv = netdev_priv(dev);
|
||||
unsigned long dma_ctrl;
|
||||
u32 offset, reg;
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
if (!netif_running(dev))
|
||||
return 0;
|
||||
@ -3766,6 +4214,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);
|
||||
|
||||
if (priv->internal_phy) {
|
||||
reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
|
||||
reg |= EXT_ENERGY_DET_MASK;
|
||||
@ -3809,6 +4262,7 @@ static int bcmgenet_suspend(struct device *d)
|
||||
struct net_device *dev = dev_get_drvdata(d);
|
||||
struct bcmgenet_priv *priv = netdev_priv(dev);
|
||||
int ret = 0;
|
||||
u32 offset;
|
||||
|
||||
if (!netif_running(dev))
|
||||
return 0;
|
||||
@ -3820,6 +4274,13 @@ 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));
|
||||
bcmgenet_hfb_reg_writel(priv, 0, HFB_CTRL);
|
||||
|
||||
/* Prepare the device for Wake-on-LAN and switch to the slow clock */
|
||||
if (device_may_wakeup(d) && priv->wolopts)
|
||||
ret = bcmgenet_power_down(priv, GENET_POWER_WOL_MAGIC);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#define DMA_MAX_BURST_LENGTH 0x10
|
||||
|
||||
/* misc. configuration */
|
||||
#define MAX_NUM_OF_FS_RULES 16
|
||||
#define CLEAR_ALL_HFB 0xFF
|
||||
#define DMA_FC_THRESH_HI (TOTAL_DESC >> 4)
|
||||
#define DMA_FC_THRESH_LO 5
|
||||
@ -609,6 +610,18 @@ struct bcmgenet_rx_ring {
|
||||
struct bcmgenet_priv *priv;
|
||||
};
|
||||
|
||||
enum bcmgenet_rxnfc_state {
|
||||
BCMGENET_RXNFC_STATE_UNUSED = 0,
|
||||
BCMGENET_RXNFC_STATE_DISABLED,
|
||||
BCMGENET_RXNFC_STATE_ENABLED
|
||||
};
|
||||
|
||||
struct bcmgenet_rxnfc_rule {
|
||||
struct list_head list;
|
||||
struct ethtool_rx_flow_spec fs;
|
||||
enum bcmgenet_rxnfc_state state;
|
||||
};
|
||||
|
||||
/* device context */
|
||||
struct bcmgenet_priv {
|
||||
void __iomem *base;
|
||||
@ -627,6 +640,8 @@ struct bcmgenet_priv {
|
||||
struct enet_cb *rx_cbs;
|
||||
unsigned int num_rx_bds;
|
||||
unsigned int rx_buf_len;
|
||||
struct bcmgenet_rxnfc_rule rxnfc_rules[MAX_NUM_OF_FS_RULES];
|
||||
struct list_head rxnfc_list;
|
||||
|
||||
struct bcmgenet_rx_ring rx_rings[DESC_INDEX + 1];
|
||||
|
||||
@ -679,6 +694,7 @@ struct bcmgenet_priv {
|
||||
u32 wolopts;
|
||||
u8 sopass[SOPASS_MAX];
|
||||
bool wol_active;
|
||||
u32 hfb_en[3];
|
||||
|
||||
struct bcmgenet_mib_counters mib;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user