net: macb: Added support for RX filtering
authorRafal Ozieblo <rafalo@cadence.com>
Thu, 30 Nov 2017 18:20:44 +0000 (18:20 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 30 Nov 2017 19:12:46 +0000 (14:12 -0500)
This patch allows filtering received packets to different
hardware queues (aka ntuple).

Signed-off-by: Rafal Ozieblo <rafalo@cadence.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/cadence/macb.h
drivers/net/ethernet/cadence/macb_main.c

index a26e7633f7e86693ee9a757ba17e061fcacb1688..3165c2ba58f97744e4ea88b32de1705875d92e05 100644 (file)
 #define GEM_DCFG5              0x0290 /* Design Config 5 */
 #define GEM_DCFG6              0x0294 /* Design Config 6 */
 #define GEM_DCFG7              0x0298 /* Design Config 7 */
+#define GEM_DCFG8              0x029C /* Design Config 8 */
 
 #define GEM_TXBDCTRL   0x04cc /* TX Buffer Descriptor control register */
 #define GEM_RXBDCTRL   0x04d0 /* RX Buffer Descriptor control register */
 
+/* Screener Type 2 match registers */
+#define GEM_SCRT2              0x540
+
+/* EtherType registers */
+#define GEM_ETHT               0x06E0
+
+/* Type 2 compare registers */
+#define GEM_T2CMPW0            0x0700
+#define GEM_T2CMPW1            0x0704
+#define T2CMP_OFST(t2idx)      (t2idx * 2)
+
+/* type 2 compare registers
+ * each location requires 3 compare regs
+ */
+#define GEM_IP4SRC_CMP(idx)            (idx * 3)
+#define GEM_IP4DST_CMP(idx)            (idx * 3 + 1)
+#define GEM_PORT_CMP(idx)              (idx * 3 + 2)
+
+/* Which screening type 2 EtherType register will be used (0 - 7) */
+#define SCRT2_ETHT             0
+
 #define GEM_ISR(hw_q)          (0x0400 + ((hw_q) << 2))
 #define GEM_TBQP(hw_q)         (0x0440 + ((hw_q) << 2))
 #define GEM_TBQPH(hw_q)                (0x04C8)
 #define GEM_DAW64_OFFSET                       23
 #define GEM_DAW64_SIZE                         1
 
+/* Bitfields in DCFG8. */
+#define GEM_T1SCR_OFFSET                       24
+#define GEM_T1SCR_SIZE                         8
+#define GEM_T2SCR_OFFSET                       16
+#define GEM_T2SCR_SIZE                         8
+#define GEM_SCR2ETH_OFFSET                     8
+#define GEM_SCR2ETH_SIZE                       8
+#define GEM_SCR2CMP_OFFSET                     0
+#define GEM_SCR2CMP_SIZE                       8
+
 /* Bitfields in TISUBN */
 #define GEM_SUBNSINCR_OFFSET                   0
 #define GEM_SUBNSINCR_SIZE                     16
 #define GEM_RXTSMODE_OFFSET                    4 /* RX Descriptor Timestamp Insertion mode */
 #define GEM_RXTSMODE_SIZE                      2
 
+/* Bitfields in SCRT2 */
+#define GEM_QUEUE_OFFSET                       0 /* Queue Number */
+#define GEM_QUEUE_SIZE                         4
+#define GEM_VLANPR_OFFSET                      4 /* VLAN Priority */
+#define GEM_VLANPR_SIZE                                3
+#define GEM_VLANEN_OFFSET                      8 /* VLAN Enable */
+#define GEM_VLANEN_SIZE                                1
+#define GEM_ETHT2IDX_OFFSET                    9 /* Index to screener type 2 EtherType register */
+#define GEM_ETHT2IDX_SIZE                      3
+#define GEM_ETHTEN_OFFSET                      12 /* EtherType Enable */
+#define GEM_ETHTEN_SIZE                                1
+#define GEM_CMPA_OFFSET                                13 /* Compare A - Index to screener type 2 Compare register */
+#define GEM_CMPA_SIZE                          5
+#define GEM_CMPAEN_OFFSET                      18 /* Compare A Enable */
+#define GEM_CMPAEN_SIZE                                1
+#define GEM_CMPB_OFFSET                                19 /* Compare B - Index to screener type 2 Compare register */
+#define GEM_CMPB_SIZE                          5
+#define GEM_CMPBEN_OFFSET                      24 /* Compare B Enable */
+#define GEM_CMPBEN_SIZE                                1
+#define GEM_CMPC_OFFSET                                25 /* Compare C - Index to screener type 2 Compare register */
+#define GEM_CMPC_SIZE                          5
+#define GEM_CMPCEN_OFFSET                      30 /* Compare C Enable */
+#define GEM_CMPCEN_SIZE                                1
+
+/* Bitfields in ETHT */
+#define GEM_ETHTCMP_OFFSET                     0 /* EtherType compare value */
+#define GEM_ETHTCMP_SIZE                       16
+
+/* Bitfields in T2CMPW0 */
+#define GEM_T2CMP_OFFSET                       16 /* 0xFFFF0000 compare value */
+#define GEM_T2CMP_SIZE                         16
+#define GEM_T2MASK_OFFSET                      0 /* 0x0000FFFF compare value or mask */
+#define GEM_T2MASK_SIZE                                16
+
+/* Bitfields in T2CMPW1 */
+#define GEM_T2DISMSK_OFFSET                    9 /* disable mask */
+#define GEM_T2DISMSK_SIZE                      1
+#define GEM_T2CMPOFST_OFFSET                   7 /* compare offset */
+#define GEM_T2CMPOFST_SIZE                     2
+#define GEM_T2OFST_OFFSET                      0 /* offset value */
+#define GEM_T2OFST_SIZE                                7
+
+/* Offset for screener type 2 compare values (T2CMPOFST).
+ * Note the offset is applied after the specified point,
+ * e.g. GEM_T2COMPOFST_ETYPE denotes the EtherType field, so an offset
+ * of 12 bytes from this would be the source IP address in an IP header
+ */
+#define GEM_T2COMPOFST_SOF             0
+#define GEM_T2COMPOFST_ETYPE   1
+#define GEM_T2COMPOFST_IPHDR   2
+#define GEM_T2COMPOFST_TCPUDP  3
+
+/* offset from EtherType to IP address */
+#define ETYPE_SRCIP_OFFSET                     12
+#define ETYPE_DSTIP_OFFSET                     16
+
+/* offset from IP header to port */
+#define IPHDR_SRCPORT_OFFSET           0
+#define IPHDR_DSTPORT_OFFSET           2
+
 /* Transmit DMA buffer descriptor Word 1 */
 #define GEM_DMA_TXVALID_OFFSET         23 /* timestamp has been captured in the Buffer Descriptor */
 #define GEM_DMA_TXVALID_SIZE           1
 #define gem_writel(port, reg, value)   (port)->macb_reg_writel((port), GEM_##reg, (value))
 #define queue_readl(queue, reg)                (queue)->bp->macb_reg_readl((queue)->bp, (queue)->reg)
 #define queue_writel(queue, reg, value)        (queue)->bp->macb_reg_writel((queue)->bp, (queue)->reg, (value))
+#define gem_readl_n(port, reg, idx)            (port)->macb_reg_readl((port), GEM_##reg + idx * 4)
+#define gem_writel_n(port, reg, idx, value)    (port)->macb_reg_writel((port), GEM_##reg + idx * 4, (value))
 
 #define PTP_TS_BUFFER_SIZE             128 /* must be power of 2 */
 
@@ -1026,6 +1120,16 @@ struct macb_queue {
 #endif
 };
 
+struct ethtool_rx_fs_item {
+       struct ethtool_rx_flow_spec fs;
+       struct list_head list;
+};
+
+struct ethtool_rx_fs_list {
+       struct list_head list;
+       unsigned int count;
+};
+
 struct macb {
        void __iomem            *regs;
        bool                    native_io;
@@ -1092,6 +1196,11 @@ struct macb {
        struct ptp_clock_info ptp_clock_info;
        struct tsu_incr tsu_incr;
        struct hwtstamp_config tstamp_config;
+
+       /* RX queue filer rule set*/
+       struct ethtool_rx_fs_list rx_fs_list;
+       spinlock_t rx_fs_lock;
+       unsigned int max_tuples;
 };
 
 #ifdef CONFIG_MACB_USE_HWSTAMP
index d91a87e05382e4673bd2852bb8f987f26865a488..c5fa87cdc6c401a790522c1232026c15c70ba296 100644 (file)
@@ -2668,6 +2668,308 @@ static int macb_get_ts_info(struct net_device *netdev,
        return ethtool_op_get_ts_info(netdev, info);
 }
 
+static void gem_enable_flow_filters(struct macb *bp, bool enable)
+{
+       struct ethtool_rx_fs_item *item;
+       u32 t2_scr;
+       int num_t2_scr;
+
+       num_t2_scr = GEM_BFEXT(T2SCR, gem_readl(bp, DCFG8));
+
+       list_for_each_entry(item, &bp->rx_fs_list.list, list) {
+               struct ethtool_rx_flow_spec *fs = &item->fs;
+               struct ethtool_tcpip4_spec *tp4sp_m;
+
+               if (fs->location >= num_t2_scr)
+                       continue;
+
+               t2_scr = gem_readl_n(bp, SCRT2, fs->location);
+
+               /* enable/disable screener regs for the flow entry */
+               t2_scr = GEM_BFINS(ETHTEN, enable, t2_scr);
+
+               /* only enable fields with no masking */
+               tp4sp_m = &(fs->m_u.tcp_ip4_spec);
+
+               if (enable && (tp4sp_m->ip4src == 0xFFFFFFFF))
+                       t2_scr = GEM_BFINS(CMPAEN, 1, t2_scr);
+               else
+                       t2_scr = GEM_BFINS(CMPAEN, 0, t2_scr);
+
+               if (enable && (tp4sp_m->ip4dst == 0xFFFFFFFF))
+                       t2_scr = GEM_BFINS(CMPBEN, 1, t2_scr);
+               else
+                       t2_scr = GEM_BFINS(CMPBEN, 0, t2_scr);
+
+               if (enable && ((tp4sp_m->psrc == 0xFFFF) || (tp4sp_m->pdst == 0xFFFF)))
+                       t2_scr = GEM_BFINS(CMPCEN, 1, t2_scr);
+               else
+                       t2_scr = GEM_BFINS(CMPCEN, 0, t2_scr);
+
+               gem_writel_n(bp, SCRT2, fs->location, t2_scr);
+       }
+}
+
+static void gem_prog_cmp_regs(struct macb *bp, struct ethtool_rx_flow_spec *fs)
+{
+       struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m;
+       uint16_t index = fs->location;
+       u32 w0, w1, t2_scr;
+       bool cmp_a = false;
+       bool cmp_b = false;
+       bool cmp_c = false;
+
+       tp4sp_v = &(fs->h_u.tcp_ip4_spec);
+       tp4sp_m = &(fs->m_u.tcp_ip4_spec);
+
+       /* ignore field if any masking set */
+       if (tp4sp_m->ip4src == 0xFFFFFFFF) {
+               /* 1st compare reg - IP source address */
+               w0 = 0;
+               w1 = 0;
+               w0 = tp4sp_v->ip4src;
+               w1 = GEM_BFINS(T2DISMSK, 1, w1); /* 32-bit compare */
+               w1 = GEM_BFINS(T2CMPOFST, GEM_T2COMPOFST_ETYPE, w1);
+               w1 = GEM_BFINS(T2OFST, ETYPE_SRCIP_OFFSET, w1);
+               gem_writel_n(bp, T2CMPW0, T2CMP_OFST(GEM_IP4SRC_CMP(index)), w0);
+               gem_writel_n(bp, T2CMPW1, T2CMP_OFST(GEM_IP4SRC_CMP(index)), w1);
+               cmp_a = true;
+       }
+
+       /* ignore field if any masking set */
+       if (tp4sp_m->ip4dst == 0xFFFFFFFF) {
+               /* 2nd compare reg - IP destination address */
+               w0 = 0;
+               w1 = 0;
+               w0 = tp4sp_v->ip4dst;
+               w1 = GEM_BFINS(T2DISMSK, 1, w1); /* 32-bit compare */
+               w1 = GEM_BFINS(T2CMPOFST, GEM_T2COMPOFST_ETYPE, w1);
+               w1 = GEM_BFINS(T2OFST, ETYPE_DSTIP_OFFSET, w1);
+               gem_writel_n(bp, T2CMPW0, T2CMP_OFST(GEM_IP4DST_CMP(index)), w0);
+               gem_writel_n(bp, T2CMPW1, T2CMP_OFST(GEM_IP4DST_CMP(index)), w1);
+               cmp_b = true;
+       }
+
+       /* ignore both port fields if masking set in both */
+       if ((tp4sp_m->psrc == 0xFFFF) || (tp4sp_m->pdst == 0xFFFF)) {
+               /* 3rd compare reg - source port, destination port */
+               w0 = 0;
+               w1 = 0;
+               w1 = GEM_BFINS(T2CMPOFST, GEM_T2COMPOFST_IPHDR, w1);
+               if (tp4sp_m->psrc == tp4sp_m->pdst) {
+                       w0 = GEM_BFINS(T2MASK, tp4sp_v->psrc, w0);
+                       w0 = GEM_BFINS(T2CMP, tp4sp_v->pdst, w0);
+                       w1 = GEM_BFINS(T2DISMSK, 1, w1); /* 32-bit compare */
+                       w1 = GEM_BFINS(T2OFST, IPHDR_SRCPORT_OFFSET, w1);
+               } else {
+                       /* only one port definition */
+                       w1 = GEM_BFINS(T2DISMSK, 0, w1); /* 16-bit compare */
+                       w0 = GEM_BFINS(T2MASK, 0xFFFF, w0);
+                       if (tp4sp_m->psrc == 0xFFFF) { /* src port */
+                               w0 = GEM_BFINS(T2CMP, tp4sp_v->psrc, w0);
+                               w1 = GEM_BFINS(T2OFST, IPHDR_SRCPORT_OFFSET, w1);
+                       } else { /* dst port */
+                               w0 = GEM_BFINS(T2CMP, tp4sp_v->pdst, w0);
+                               w1 = GEM_BFINS(T2OFST, IPHDR_DSTPORT_OFFSET, w1);
+                       }
+               }
+               gem_writel_n(bp, T2CMPW0, T2CMP_OFST(GEM_PORT_CMP(index)), w0);
+               gem_writel_n(bp, T2CMPW1, T2CMP_OFST(GEM_PORT_CMP(index)), w1);
+               cmp_c = true;
+       }
+
+       t2_scr = 0;
+       t2_scr = GEM_BFINS(QUEUE, (fs->ring_cookie) & 0xFF, t2_scr);
+       t2_scr = GEM_BFINS(ETHT2IDX, SCRT2_ETHT, t2_scr);
+       if (cmp_a)
+               t2_scr = GEM_BFINS(CMPA, GEM_IP4SRC_CMP(index), t2_scr);
+       if (cmp_b)
+               t2_scr = GEM_BFINS(CMPB, GEM_IP4DST_CMP(index), t2_scr);
+       if (cmp_c)
+               t2_scr = GEM_BFINS(CMPC, GEM_PORT_CMP(index), t2_scr);
+       gem_writel_n(bp, SCRT2, index, t2_scr);
+}
+
+static int gem_add_flow_filter(struct net_device *netdev,
+               struct ethtool_rxnfc *cmd)
+{
+       struct macb *bp = netdev_priv(netdev);
+       struct ethtool_rx_flow_spec *fs = &cmd->fs;
+       struct ethtool_rx_fs_item *item, *newfs;
+       int ret = -EINVAL;
+       bool added = false;
+
+       newfs = kmalloc(sizeof(*newfs), GFP_KERNEL);
+       if (newfs == NULL)
+               return -ENOMEM;
+       memcpy(&newfs->fs, fs, sizeof(newfs->fs));
+
+       netdev_dbg(netdev,
+                       "Adding flow filter entry,type=%u,queue=%u,loc=%u,src=%08X,dst=%08X,ps=%u,pd=%u\n",
+                       fs->flow_type, (int)fs->ring_cookie, fs->location,
+                       htonl(fs->h_u.tcp_ip4_spec.ip4src),
+                       htonl(fs->h_u.tcp_ip4_spec.ip4dst),
+                       htons(fs->h_u.tcp_ip4_spec.psrc), htons(fs->h_u.tcp_ip4_spec.pdst));
+
+       /* find correct place to add in list */
+       if (list_empty(&bp->rx_fs_list.list))
+               list_add(&newfs->list, &bp->rx_fs_list.list);
+       else {
+               list_for_each_entry(item, &bp->rx_fs_list.list, list) {
+                       if (item->fs.location > newfs->fs.location) {
+                               list_add_tail(&newfs->list, &item->list);
+                               added = true;
+                               break;
+                       } else if (item->fs.location == fs->location) {
+                               netdev_err(netdev, "Rule not added: location %d not free!\n",
+                                               fs->location);
+                               ret = -EBUSY;
+                               goto err;
+                       }
+               }
+               if (!added)
+                       list_add_tail(&newfs->list, &bp->rx_fs_list.list);
+       }
+
+       gem_prog_cmp_regs(bp, fs);
+       bp->rx_fs_list.count++;
+       /* enable filtering if NTUPLE on */
+       if (netdev->features & NETIF_F_NTUPLE)
+               gem_enable_flow_filters(bp, 1);
+
+       return 0;
+
+err:
+       kfree(newfs);
+       return ret;
+}
+
+static int gem_del_flow_filter(struct net_device *netdev,
+               struct ethtool_rxnfc *cmd)
+{
+       struct macb *bp = netdev_priv(netdev);
+       struct ethtool_rx_fs_item *item;
+       struct ethtool_rx_flow_spec *fs;
+
+       if (list_empty(&bp->rx_fs_list.list))
+               return -EINVAL;
+
+       list_for_each_entry(item, &bp->rx_fs_list.list, list) {
+               if (item->fs.location == cmd->fs.location) {
+                       /* disable screener regs for the flow entry */
+                       fs = &(item->fs);
+                       netdev_dbg(netdev,
+                                       "Deleting flow filter entry,type=%u,queue=%u,loc=%u,src=%08X,dst=%08X,ps=%u,pd=%u\n",
+                                       fs->flow_type, (int)fs->ring_cookie, fs->location,
+                                       htonl(fs->h_u.tcp_ip4_spec.ip4src),
+                                       htonl(fs->h_u.tcp_ip4_spec.ip4dst),
+                                       htons(fs->h_u.tcp_ip4_spec.psrc),
+                                       htons(fs->h_u.tcp_ip4_spec.pdst));
+
+                       gem_writel_n(bp, SCRT2, fs->location, 0);
+
+                       list_del(&item->list);
+                       kfree(item);
+                       bp->rx_fs_list.count--;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int gem_get_flow_entry(struct net_device *netdev,
+               struct ethtool_rxnfc *cmd)
+{
+       struct macb *bp = netdev_priv(netdev);
+       struct ethtool_rx_fs_item *item;
+
+       list_for_each_entry(item, &bp->rx_fs_list.list, list) {
+               if (item->fs.location == cmd->fs.location) {
+                       memcpy(&cmd->fs, &item->fs, sizeof(cmd->fs));
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
+static int gem_get_all_flow_entries(struct net_device *netdev,
+               struct ethtool_rxnfc *cmd, u32 *rule_locs)
+{
+       struct macb *bp = netdev_priv(netdev);
+       struct ethtool_rx_fs_item *item;
+       uint32_t cnt = 0;
+
+       list_for_each_entry(item, &bp->rx_fs_list.list, list) {
+               if (cnt == cmd->rule_cnt)
+                       return -EMSGSIZE;
+               rule_locs[cnt] = item->fs.location;
+               cnt++;
+       }
+       cmd->data = bp->max_tuples;
+       cmd->rule_cnt = cnt;
+
+       return 0;
+}
+
+static int gem_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
+               u32 *rule_locs)
+{
+       struct macb *bp = netdev_priv(netdev);
+       int ret = 0;
+
+       switch (cmd->cmd) {
+       case ETHTOOL_GRXRINGS:
+               cmd->data = bp->num_queues;
+               break;
+       case ETHTOOL_GRXCLSRLCNT:
+               cmd->rule_cnt = bp->rx_fs_list.count;
+               break;
+       case ETHTOOL_GRXCLSRULE:
+               ret = gem_get_flow_entry(netdev, cmd);
+               break;
+       case ETHTOOL_GRXCLSRLALL:
+               ret = gem_get_all_flow_entries(netdev, cmd, rule_locs);
+               break;
+       default:
+               netdev_err(netdev,
+                         "Command parameter %d is not supported\n", cmd->cmd);
+               ret = -EOPNOTSUPP;
+       }
+
+       return ret;
+}
+
+static int gem_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
+{
+       struct macb *bp = netdev_priv(netdev);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&bp->rx_fs_lock, flags);
+
+       switch (cmd->cmd) {
+       case ETHTOOL_SRXCLSRLINS:
+               if ((cmd->fs.location >= bp->max_tuples)
+                               || (cmd->fs.ring_cookie >= bp->num_queues)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = gem_add_flow_filter(netdev, cmd);
+               break;
+       case ETHTOOL_SRXCLSRLDEL:
+               ret = gem_del_flow_filter(netdev, cmd);
+               break;
+       default:
+               netdev_err(netdev,
+                         "Command parameter %d is not supported\n", cmd->cmd);
+               ret = -EOPNOTSUPP;
+       }
+
+       spin_unlock_irqrestore(&bp->rx_fs_lock, flags);
+       return ret;
+}
+
 static const struct ethtool_ops macb_ethtool_ops = {
        .get_regs_len           = macb_get_regs_len,
        .get_regs               = macb_get_regs,
@@ -2693,6 +2995,8 @@ static const struct ethtool_ops gem_ethtool_ops = {
        .set_link_ksettings     = phy_ethtool_set_link_ksettings,
        .get_ringparam          = macb_get_ringparam,
        .set_ringparam          = macb_set_ringparam,
+       .get_rxnfc                      = gem_get_rxnfc,
+       .set_rxnfc                      = gem_set_rxnfc,
 };
 
 static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
@@ -2750,6 +3054,12 @@ static int macb_set_features(struct net_device *netdev,
                gem_writel(bp, NCFGR, netcfg);
        }
 
+       /* RX Flow Filters */
+       if ((changed & NETIF_F_NTUPLE) && macb_is_gem(bp)) {
+               bool turn_on = features & NETIF_F_NTUPLE;
+
+               gem_enable_flow_filters(bp, turn_on);
+       }
        return 0;
 }
 
@@ -2915,7 +3225,7 @@ static int macb_init(struct platform_device *pdev)
        struct macb *bp = netdev_priv(dev);
        struct macb_queue *queue;
        int err;
-       u32 val;
+       u32 val, reg;
 
        bp->tx_ring_size = DEFAULT_TX_RING_SIZE;
        bp->rx_ring_size = DEFAULT_RX_RING_SIZE;
@@ -3013,6 +3323,30 @@ static int macb_init(struct platform_device *pdev)
                dev->hw_features &= ~NETIF_F_SG;
        dev->features = dev->hw_features;
 
+       /* Check RX Flow Filters support.
+        * Max Rx flows set by availability of screeners & compare regs:
+        * each 4-tuple define requires 1 T2 screener reg + 3 compare regs
+        */
+       reg = gem_readl(bp, DCFG8);
+       bp->max_tuples = min((GEM_BFEXT(SCR2CMP, reg) / 3),
+                       GEM_BFEXT(T2SCR, reg));
+       if (bp->max_tuples > 0) {
+               /* also needs one ethtype match to check IPv4 */
+               if (GEM_BFEXT(SCR2ETH, reg) > 0) {
+                       /* program this reg now */
+                       reg = 0;
+                       reg = GEM_BFINS(ETHTCMP, (uint16_t)ETH_P_IP, reg);
+                       gem_writel_n(bp, ETHT, SCRT2_ETHT, reg);
+                       /* Filtering is supported in hw but don't enable it in kernel now */
+                       dev->hw_features |= NETIF_F_NTUPLE;
+                       /* init Rx flow definitions */
+                       INIT_LIST_HEAD(&bp->rx_fs_list.list);
+                       bp->rx_fs_list.count = 0;
+                       spin_lock_init(&bp->rx_fs_lock);
+               } else
+                       bp->max_tuples = 0;
+       }
+
        if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) {
                val = 0;
                if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII)