Commit 4dbbe8dd authored by Jose Abreu's avatar Jose Abreu Committed by David S. Miller

net: stmmac: Add support for U32 TC filter using Flexible RX Parser

This adds support for U32 filter by using an HW only feature called
Flexible RX Parser. This allow us to match any given packet field with a
pattern and accept/reject or even route the packet to a specific DMA
channel.

Right now we only support acception or rejection of frame and we only
support simple rules. Though, the Parser has the flexibility of jumping to
specific rules as an if condition so complex rules can be established.

This is only supported in GMAC5.10+.

The following commands can be used to test this code:

	1) Setup an ingress qdisk:
	# tc qdisc add dev eth0 handle ffff: ingress

	2) Setup a filter (e.g. filter by IP):
	# tc filter add dev eth0 parent ffff: protocol ip u32 match ip \
		src 192.168.0.3 skip_sw action drop

In every tests performed we always used the "skip_sw" flag to make sure
only the RX Parser was involved.
Signed-off-by: default avatarJose Abreu <joabreu@synopsys.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: Vitor Soares <soares@synopsys.com>
Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Cc: Alexandre Torgue <alexandre.torgue@st.com>
Cc: Jakub Kicinski <kubakici@wp.pl>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b2641e2a
...@@ -5,7 +5,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ ...@@ -5,7 +5,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \ dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o dwmac4_descs.o \ mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o dwmac4_descs.o \
dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5.o hwif.o \ dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5.o hwif.o \
$(stmmac-y) stmmac_tc.o $(stmmac-y)
# Ordering matters. Generic driver must be last. # Ordering matters. Generic driver must be last.
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
......
...@@ -353,6 +353,10 @@ struct dma_features { ...@@ -353,6 +353,10 @@ struct dma_features {
unsigned int rx_fifo_size; unsigned int rx_fifo_size;
/* Automotive Safety Package */ /* Automotive Safety Package */
unsigned int asp; unsigned int asp;
/* RX Parser */
unsigned int frpsel;
unsigned int frpbs;
unsigned int frpes;
}; };
/* GMAC TX FIFO is 8K, Rx FIFO is 16K */ /* GMAC TX FIFO is 8K, Rx FIFO is 16K */
...@@ -412,6 +416,7 @@ struct mac_device_info { ...@@ -412,6 +416,7 @@ struct mac_device_info {
const struct stmmac_dma_ops *dma; const struct stmmac_dma_ops *dma;
const struct stmmac_mode_ops *mode; const struct stmmac_mode_ops *mode;
const struct stmmac_hwtimestamp *ptp; const struct stmmac_hwtimestamp *ptp;
const struct stmmac_tc_ops *tc;
struct mii_regs mii; /* MII register Addresses */ struct mii_regs mii; /* MII register Addresses */
struct mac_link link; struct mac_link link;
void __iomem *pcsr; /* vpointer to device CSRs */ void __iomem *pcsr; /* vpointer to device CSRs */
......
...@@ -194,6 +194,9 @@ enum power_event { ...@@ -194,6 +194,9 @@ enum power_event {
/* MAC HW features3 bitmap */ /* MAC HW features3 bitmap */
#define GMAC_HW_FEAT_ASP GENMASK(29, 28) #define GMAC_HW_FEAT_ASP GENMASK(29, 28)
#define GMAC_HW_FEAT_FRPES GENMASK(14, 13)
#define GMAC_HW_FEAT_FRPBS GENMASK(12, 11)
#define GMAC_HW_FEAT_FRPSEL BIT(10)
/* MAC HW ADDR regs */ /* MAC HW ADDR regs */
#define GMAC_HI_DCS GENMASK(18, 16) #define GMAC_HI_DCS GENMASK(18, 16)
...@@ -202,6 +205,7 @@ enum power_event { ...@@ -202,6 +205,7 @@ enum power_event {
/* MTL registers */ /* MTL registers */
#define MTL_OPERATION_MODE 0x00000c00 #define MTL_OPERATION_MODE 0x00000c00
#define MTL_FRPE BIT(15)
#define MTL_OPERATION_SCHALG_MASK GENMASK(6, 5) #define MTL_OPERATION_SCHALG_MASK GENMASK(6, 5)
#define MTL_OPERATION_SCHALG_WRR (0x0 << 5) #define MTL_OPERATION_SCHALG_WRR (0x0 << 5)
#define MTL_OPERATION_SCHALG_WFQ (0x1 << 5) #define MTL_OPERATION_SCHALG_WFQ (0x1 << 5)
......
...@@ -795,6 +795,7 @@ const struct stmmac_ops dwmac510_ops = { ...@@ -795,6 +795,7 @@ const struct stmmac_ops dwmac510_ops = {
.safety_feat_config = dwmac5_safety_feat_config, .safety_feat_config = dwmac5_safety_feat_config,
.safety_feat_irq_status = dwmac5_safety_feat_irq_status, .safety_feat_irq_status = dwmac5_safety_feat_irq_status,
.safety_feat_dump = dwmac5_safety_feat_dump, .safety_feat_dump = dwmac5_safety_feat_dump,
.rxp_config = dwmac5_rxp_config,
}; };
int dwmac4_setup(struct stmmac_priv *priv) int dwmac4_setup(struct stmmac_priv *priv)
......
...@@ -379,6 +379,9 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr, ...@@ -379,6 +379,9 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr,
/* 5.10 Features */ /* 5.10 Features */
dma_cap->asp = (hw_cap & GMAC_HW_FEAT_ASP) >> 28; dma_cap->asp = (hw_cap & GMAC_HW_FEAT_ASP) >> 28;
dma_cap->frpes = (hw_cap & GMAC_HW_FEAT_FRPES) >> 13;
dma_cap->frpbs = (hw_cap & GMAC_HW_FEAT_FRPBS) >> 11;
dma_cap->frpsel = (hw_cap & GMAC_HW_FEAT_FRPSEL) >> 10;
} }
/* Enable/disable TSO feature and set MSS */ /* Enable/disable TSO feature and set MSS */
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "common.h" #include "common.h"
#include "dwmac4.h" #include "dwmac4.h"
#include "dwmac5.h" #include "dwmac5.h"
#include "stmmac.h"
struct dwmac5_error_desc { struct dwmac5_error_desc {
bool valid; bool valid;
...@@ -299,3 +300,197 @@ int dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats, ...@@ -299,3 +300,197 @@ int dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats,
*desc = dwmac5_all_errors[module].desc[offset].desc; *desc = dwmac5_all_errors[module].desc[offset].desc;
return 0; return 0;
} }
static int dwmac5_rxp_disable(void __iomem *ioaddr)
{
u32 val;
int ret;
val = readl(ioaddr + MTL_OPERATION_MODE);
val &= ~MTL_FRPE;
writel(val, ioaddr + MTL_OPERATION_MODE);
ret = readl_poll_timeout(ioaddr + MTL_RXP_CONTROL_STATUS, val,
val & RXPI, 1, 10000);
if (ret)
return ret;
return 0;
}
static void dwmac5_rxp_enable(void __iomem *ioaddr)
{
u32 val;
val = readl(ioaddr + MTL_OPERATION_MODE);
val |= MTL_FRPE;
writel(val, ioaddr + MTL_OPERATION_MODE);
}
static int dwmac5_rxp_update_single_entry(void __iomem *ioaddr,
struct stmmac_tc_entry *entry,
int pos)
{
int ret, i;
for (i = 0; i < (sizeof(entry->val) / sizeof(u32)); i++) {
int real_pos = pos * (sizeof(entry->val) / sizeof(u32)) + i;
u32 val;
/* Wait for ready */
ret = readl_poll_timeout(ioaddr + MTL_RXP_IACC_CTRL_STATUS,
val, !(val & STARTBUSY), 1, 10000);
if (ret)
return ret;
/* Write data */
val = *((u32 *)&entry->val + i);
writel(val, ioaddr + MTL_RXP_IACC_DATA);
/* Write pos */
val = real_pos & ADDR;
writel(val, ioaddr + MTL_RXP_IACC_CTRL_STATUS);
/* Write OP */
val |= WRRDN;
writel(val, ioaddr + MTL_RXP_IACC_CTRL_STATUS);
/* Start Write */
val |= STARTBUSY;
writel(val, ioaddr + MTL_RXP_IACC_CTRL_STATUS);
/* Wait for done */
ret = readl_poll_timeout(ioaddr + MTL_RXP_IACC_CTRL_STATUS,
val, !(val & STARTBUSY), 1, 10000);
if (ret)
return ret;
}
return 0;
}
static struct stmmac_tc_entry *
dwmac5_rxp_get_next_entry(struct stmmac_tc_entry *entries, unsigned int count,
u32 curr_prio)
{
struct stmmac_tc_entry *entry;
u32 min_prio = ~0x0;
int i, min_prio_idx;
bool found = false;
for (i = count - 1; i >= 0; i--) {
entry = &entries[i];
/* Do not update unused entries */
if (!entry->in_use)
continue;
/* Do not update already updated entries (i.e. fragments) */
if (entry->in_hw)
continue;
/* Let last entry be updated last */
if (entry->is_last)
continue;
/* Do not return fragments */
if (entry->is_frag)
continue;
/* Check if we already checked this prio */
if (entry->prio < curr_prio)
continue;
/* Check if this is the minimum prio */
if (entry->prio < min_prio) {
min_prio = entry->prio;
min_prio_idx = i;
found = true;
}
}
if (found)
return &entries[min_prio_idx];
return NULL;
}
int dwmac5_rxp_config(void __iomem *ioaddr, struct stmmac_tc_entry *entries,
unsigned int count)
{
struct stmmac_tc_entry *entry, *frag;
int i, ret, nve = 0;
u32 curr_prio = 0;
u32 old_val, val;
/* Force disable RX */
old_val = readl(ioaddr + GMAC_CONFIG);
val = old_val & ~GMAC_CONFIG_RE;
writel(val, ioaddr + GMAC_CONFIG);
/* Disable RX Parser */
ret = dwmac5_rxp_disable(ioaddr);
if (ret)
goto re_enable;
/* Set all entries as NOT in HW */
for (i = 0; i < count; i++) {
entry = &entries[i];
entry->in_hw = false;
}
/* Update entries by reverse order */
while (1) {
entry = dwmac5_rxp_get_next_entry(entries, count, curr_prio);
if (!entry)
break;
curr_prio = entry->prio;
frag = entry->frag_ptr;
/* Set special fragment requirements */
if (frag) {
entry->val.af = 0;
entry->val.rf = 0;
entry->val.nc = 1;
entry->val.ok_index = nve + 2;
}
ret = dwmac5_rxp_update_single_entry(ioaddr, entry, nve);
if (ret)
goto re_enable;
entry->table_pos = nve++;
entry->in_hw = true;
if (frag && !frag->in_hw) {
ret = dwmac5_rxp_update_single_entry(ioaddr, frag, nve);
if (ret)
goto re_enable;
frag->table_pos = nve++;
frag->in_hw = true;
}
}
if (!nve)
goto re_enable;
/* Update all pass entry */
for (i = 0; i < count; i++) {
entry = &entries[i];
if (!entry->is_last)
continue;
ret = dwmac5_rxp_update_single_entry(ioaddr, entry, nve);
if (ret)
goto re_enable;
entry->table_pos = nve++;
}
/* Assume n. of parsable entries == n. of valid entries */
val = (nve << 16) & NPE;
val |= nve & NVE;
writel(val, ioaddr + MTL_RXP_CONTROL_STATUS);
/* Enable RX Parser */
dwmac5_rxp_enable(ioaddr);
re_enable:
/* Re-enable RX */
writel(old_val, ioaddr + GMAC_CONFIG);
return ret;
}
...@@ -11,6 +11,17 @@ ...@@ -11,6 +11,17 @@
#define PRTYEN BIT(1) #define PRTYEN BIT(1)
#define TMOUTEN BIT(0) #define TMOUTEN BIT(0)
#define MTL_RXP_CONTROL_STATUS 0x00000ca0
#define RXPI BIT(31)
#define NPE GENMASK(23, 16)
#define NVE GENMASK(7, 0)
#define MTL_RXP_IACC_CTRL_STATUS 0x00000cb0
#define STARTBUSY BIT(31)
#define RXPEIEC GENMASK(22, 21)
#define RXPEIEE BIT(20)
#define WRRDN BIT(16)
#define ADDR GENMASK(15, 0)
#define MTL_RXP_IACC_DATA 0x00000cb4
#define MTL_ECC_CONTROL 0x00000cc0 #define MTL_ECC_CONTROL 0x00000cc0
#define TSOEE BIT(4) #define TSOEE BIT(4)
#define MRXPEE BIT(3) #define MRXPEE BIT(3)
...@@ -48,5 +59,7 @@ int dwmac5_safety_feat_irq_status(struct net_device *ndev, ...@@ -48,5 +59,7 @@ int dwmac5_safety_feat_irq_status(struct net_device *ndev,
struct stmmac_safety_stats *stats); struct stmmac_safety_stats *stats);
int dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats, int dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats,
int index, unsigned long *count, const char **desc); int index, unsigned long *count, const char **desc);
int dwmac5_rxp_config(void __iomem *ioaddr, struct stmmac_tc_entry *entries,
unsigned int count);
#endif /* __DWMAC5_H__ */ #endif /* __DWMAC5_H__ */
...@@ -77,6 +77,7 @@ static const struct stmmac_hwif_entry { ...@@ -77,6 +77,7 @@ static const struct stmmac_hwif_entry {
const void *mac; const void *mac;
const void *hwtimestamp; const void *hwtimestamp;
const void *mode; const void *mode;
const void *tc;
int (*setup)(struct stmmac_priv *priv); int (*setup)(struct stmmac_priv *priv);
int (*quirks)(struct stmmac_priv *priv); int (*quirks)(struct stmmac_priv *priv);
} stmmac_hw[] = { } stmmac_hw[] = {
...@@ -90,6 +91,7 @@ static const struct stmmac_hwif_entry { ...@@ -90,6 +91,7 @@ static const struct stmmac_hwif_entry {
.mac = &dwmac100_ops, .mac = &dwmac100_ops,
.hwtimestamp = &stmmac_ptp, .hwtimestamp = &stmmac_ptp,
.mode = NULL, .mode = NULL,
.tc = NULL,
.setup = dwmac100_setup, .setup = dwmac100_setup,
.quirks = stmmac_dwmac1_quirks, .quirks = stmmac_dwmac1_quirks,
}, { }, {
...@@ -101,6 +103,7 @@ static const struct stmmac_hwif_entry { ...@@ -101,6 +103,7 @@ static const struct stmmac_hwif_entry {
.mac = &dwmac1000_ops, .mac = &dwmac1000_ops,
.hwtimestamp = &stmmac_ptp, .hwtimestamp = &stmmac_ptp,
.mode = NULL, .mode = NULL,
.tc = NULL,
.setup = dwmac1000_setup, .setup = dwmac1000_setup,
.quirks = stmmac_dwmac1_quirks, .quirks = stmmac_dwmac1_quirks,
}, { }, {
...@@ -112,6 +115,7 @@ static const struct stmmac_hwif_entry { ...@@ -112,6 +115,7 @@ static const struct stmmac_hwif_entry {
.mac = &dwmac4_ops, .mac = &dwmac4_ops,
.hwtimestamp = &stmmac_ptp, .hwtimestamp = &stmmac_ptp,
.mode = NULL, .mode = NULL,
.tc = NULL,
.setup = dwmac4_setup, .setup = dwmac4_setup,
.quirks = stmmac_dwmac4_quirks, .quirks = stmmac_dwmac4_quirks,
}, { }, {
...@@ -123,6 +127,7 @@ static const struct stmmac_hwif_entry { ...@@ -123,6 +127,7 @@ static const struct stmmac_hwif_entry {
.mac = &dwmac410_ops, .mac = &dwmac410_ops,
.hwtimestamp = &stmmac_ptp, .hwtimestamp = &stmmac_ptp,
.mode = &dwmac4_ring_mode_ops, .mode = &dwmac4_ring_mode_ops,
.tc = NULL,
.setup = dwmac4_setup, .setup = dwmac4_setup,
.quirks = NULL, .quirks = NULL,
}, { }, {
...@@ -134,6 +139,7 @@ static const struct stmmac_hwif_entry { ...@@ -134,6 +139,7 @@ static const struct stmmac_hwif_entry {
.mac = &dwmac410_ops, .mac = &dwmac410_ops,
.hwtimestamp = &stmmac_ptp, .hwtimestamp = &stmmac_ptp,
.mode = &dwmac4_ring_mode_ops, .mode = &dwmac4_ring_mode_ops,
.tc = NULL,
.setup = dwmac4_setup, .setup = dwmac4_setup,
.quirks = NULL, .quirks = NULL,
}, { }, {
...@@ -145,6 +151,7 @@ static const struct stmmac_hwif_entry { ...@@ -145,6 +151,7 @@ static const struct stmmac_hwif_entry {
.mac = &dwmac510_ops, .mac = &dwmac510_ops,
.hwtimestamp = &stmmac_ptp, .hwtimestamp = &stmmac_ptp,
.mode = &dwmac4_ring_mode_ops, .mode = &dwmac4_ring_mode_ops,
.tc = &dwmac510_tc_ops,
.setup = dwmac4_setup, .setup = dwmac4_setup,
.quirks = NULL, .quirks = NULL,
} }
...@@ -196,6 +203,7 @@ int stmmac_hwif_init(struct stmmac_priv *priv) ...@@ -196,6 +203,7 @@ int stmmac_hwif_init(struct stmmac_priv *priv)
mac->mac = entry->mac; mac->mac = entry->mac;
mac->ptp = entry->hwtimestamp; mac->ptp = entry->hwtimestamp;
mac->mode = entry->mode; mac->mode = entry->mode;
mac->tc = entry->tc;
priv->hw = mac; priv->hw = mac;
......
...@@ -5,10 +5,12 @@ ...@@ -5,10 +5,12 @@
#ifndef __STMMAC_HWIF_H__ #ifndef __STMMAC_HWIF_H__
#define __STMMAC_HWIF_H__ #define __STMMAC_HWIF_H__
#include <linux/netdevice.h>
#define stmmac_do_void_callback(__priv, __module, __cname, __arg0, __args...) \ #define stmmac_do_void_callback(__priv, __module, __cname, __arg0, __args...) \
({ \ ({ \
int __result = -EINVAL; \ int __result = -EINVAL; \
if ((__priv)->hw->__module->__cname) { \ if ((__priv)->hw->__module && (__priv)->hw->__module->__cname) { \
(__priv)->hw->__module->__cname((__arg0), ##__args); \ (__priv)->hw->__module->__cname((__arg0), ##__args); \
__result = 0; \ __result = 0; \
} \ } \
...@@ -17,7 +19,7 @@ ...@@ -17,7 +19,7 @@
#define stmmac_do_callback(__priv, __module, __cname, __arg0, __args...) \ #define stmmac_do_callback(__priv, __module, __cname, __arg0, __args...) \
({ \ ({ \
int __result = -EINVAL; \ int __result = -EINVAL; \
if ((__priv)->hw->__module->__cname) \ if ((__priv)->hw->__module && (__priv)->hw->__module->__cname) \
__result = (__priv)->hw->__module->__cname((__arg0), ##__args); \ __result = (__priv)->hw->__module->__cname((__arg0), ##__args); \
__result; \ __result; \
}) })
...@@ -232,6 +234,7 @@ struct mac_device_info; ...@@ -232,6 +234,7 @@ struct mac_device_info;
struct net_device; struct net_device;
struct rgmii_adv; struct rgmii_adv;
struct stmmac_safety_stats; struct stmmac_safety_stats;
struct stmmac_tc_entry;
/* Helpers to program the MAC core */ /* Helpers to program the MAC core */
struct stmmac_ops { struct stmmac_ops {
...@@ -301,6 +304,9 @@ struct stmmac_ops { ...@@ -301,6 +304,9 @@ struct stmmac_ops {
struct stmmac_safety_stats *stats); struct stmmac_safety_stats *stats);
int (*safety_feat_dump)(struct stmmac_safety_stats *stats, int (*safety_feat_dump)(struct stmmac_safety_stats *stats,
int index, unsigned long *count, const char **desc); int index, unsigned long *count, const char **desc);
/* Flexible RX Parser */
int (*rxp_config)(void __iomem *ioaddr, struct stmmac_tc_entry *entries,
unsigned int count);
}; };
#define stmmac_core_init(__priv, __args...) \ #define stmmac_core_init(__priv, __args...) \
...@@ -365,6 +371,8 @@ struct stmmac_ops { ...@@ -365,6 +371,8 @@ struct stmmac_ops {
stmmac_do_callback(__priv, mac, safety_feat_irq_status, __args) stmmac_do_callback(__priv, mac, safety_feat_irq_status, __args)
#define stmmac_safety_feat_dump(__priv, __args...) \ #define stmmac_safety_feat_dump(__priv, __args...) \
stmmac_do_callback(__priv, mac, safety_feat_dump, __args) stmmac_do_callback(__priv, mac, safety_feat_dump, __args)
#define stmmac_rxp_config(__priv, __args...) \
stmmac_do_callback(__priv, mac, rxp_config, __args)
/* PTP and HW Timer helpers */ /* PTP and HW Timer helpers */
struct stmmac_hwtimestamp { struct stmmac_hwtimestamp {
...@@ -419,6 +427,18 @@ struct stmmac_mode_ops { ...@@ -419,6 +427,18 @@ struct stmmac_mode_ops {
stmmac_do_void_callback(__priv, mode, clean_desc3, __args) stmmac_do_void_callback(__priv, mode, clean_desc3, __args)
struct stmmac_priv; struct stmmac_priv;
struct tc_cls_u32_offload;
struct stmmac_tc_ops {
int (*init)(struct stmmac_priv *priv);
int (*setup_cls_u32)(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls);
};
#define stmmac_tc_init(__priv, __args...) \
stmmac_do_callback(__priv, tc, init, __args)
#define stmmac_tc_setup_cls_u32(__priv, __args...) \
stmmac_do_callback(__priv, tc, setup_cls_u32, __args)
extern const struct stmmac_ops dwmac100_ops; extern const struct stmmac_ops dwmac100_ops;
extern const struct stmmac_dma_ops dwmac100_dma_ops; extern const struct stmmac_dma_ops dwmac100_dma_ops;
...@@ -429,6 +449,7 @@ extern const struct stmmac_dma_ops dwmac4_dma_ops; ...@@ -429,6 +449,7 @@ extern const struct stmmac_dma_ops dwmac4_dma_ops;
extern const struct stmmac_ops dwmac410_ops; extern const struct stmmac_ops dwmac410_ops;
extern const struct stmmac_dma_ops dwmac410_dma_ops; extern const struct stmmac_dma_ops dwmac410_dma_ops;
extern const struct stmmac_ops dwmac510_ops; extern const struct stmmac_ops dwmac510_ops;
extern const struct stmmac_tc_ops dwmac510_tc_ops;
#define GMAC_VERSION 0x00000020 /* GMAC CORE Version */ #define GMAC_VERSION 0x00000020 /* GMAC CORE Version */
#define GMAC4_VERSION 0x00000110 /* GMAC4+ CORE Version */ #define GMAC4_VERSION 0x00000110 /* GMAC4+ CORE Version */
......
...@@ -76,6 +76,30 @@ struct stmmac_rx_queue { ...@@ -76,6 +76,30 @@ struct stmmac_rx_queue {
struct napi_struct napi ____cacheline_aligned_in_smp; struct napi_struct napi ____cacheline_aligned_in_smp;
}; };
struct stmmac_tc_entry {
bool in_use;
bool in_hw;
bool is_last;
bool is_frag;
void *frag_ptr;
unsigned int table_pos;
u32 handle;
u32 prio;
struct {
u32 match_data;
u32 match_en;
u8 af:1;
u8 rf:1;
u8 im:1;
u8 nc:1;
u8 res1:4;
u8 frame_offset;
u8 ok_index;
u8 dma_ch_no;
u32 res2;
} __packed val;
};
struct stmmac_priv { struct stmmac_priv {
/* Frequently used values are kept adjacent for cache effect */ /* Frequently used values are kept adjacent for cache effect */
u32 tx_count_frames; u32 tx_count_frames;
...@@ -151,6 +175,11 @@ struct stmmac_priv { ...@@ -151,6 +175,11 @@ struct stmmac_priv {
unsigned long state; unsigned long state;
struct workqueue_struct *wq; struct workqueue_struct *wq;
struct work_struct service_task; struct work_struct service_task;
/* TC Handling */
unsigned int tc_entries_max;
unsigned int tc_off_max;
struct stmmac_tc_entry *tc_entries;
}; };
enum stmmac_state { enum stmmac_state {
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_DEBUG_FS */
#include <linux/net_tstamp.h> #include <linux/net_tstamp.h>
#include <net/pkt_cls.h>
#include "stmmac_ptp.h" #include "stmmac_ptp.h"
#include "stmmac.h" #include "stmmac.h"
#include <linux/reset.h> #include <linux/reset.h>
...@@ -3790,6 +3791,58 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ...@@ -3790,6 +3791,58 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return ret; return ret;
} }
static int stmmac_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
void *cb_priv)
{
struct stmmac_priv *priv = cb_priv;
int ret = -EOPNOTSUPP;
stmmac_disable_all_queues(priv);
switch (type) {
case TC_SETUP_CLSU32:
if (tc_cls_can_offload_and_chain0(priv->dev, type_data))
ret = stmmac_tc_setup_cls_u32(priv, priv, type_data);
break;
default:
break;
}
stmmac_enable_all_queues(priv);
return ret;
}
static int stmmac_setup_tc_block(struct stmmac_priv *priv,
struct tc_block_offload *f)
{
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block, stmmac_setup_tc_block_cb,
priv, priv);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block, stmmac_setup_tc_block_cb, priv);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int stmmac_setup_tc(struct net_device *ndev, enum tc_setup_type type,
void *type_data)
{
struct stmmac_priv *priv = netdev_priv(ndev);
switch (type) {
case TC_SETUP_BLOCK:
return stmmac_setup_tc_block(priv, type_data);
default:
return -EOPNOTSUPP;
}
}
static int stmmac_set_mac_address(struct net_device *ndev, void *addr) static int stmmac_set_mac_address(struct net_device *ndev, void *addr)
{ {
struct stmmac_priv *priv = netdev_priv(ndev); struct stmmac_priv *priv = netdev_priv(ndev);
...@@ -4028,6 +4081,7 @@ static const struct net_device_ops stmmac_netdev_ops = { ...@@ -4028,6 +4081,7 @@ static const struct net_device_ops stmmac_netdev_ops = {
.ndo_set_rx_mode = stmmac_set_rx_mode, .ndo_set_rx_mode = stmmac_set_rx_mode,
.ndo_tx_timeout = stmmac_tx_timeout, .ndo_tx_timeout = stmmac_tx_timeout,
.ndo_do_ioctl = stmmac_ioctl, .ndo_do_ioctl = stmmac_ioctl,
.ndo_setup_tc = stmmac_setup_tc,
#ifdef CONFIG_NET_POLL_CONTROLLER #ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = stmmac_poll_controller, .ndo_poll_controller = stmmac_poll_controller,
#endif #endif
...@@ -4227,6 +4281,11 @@ int stmmac_dvr_probe(struct device *device, ...@@ -4227,6 +4281,11 @@ int stmmac_dvr_probe(struct device *device,
ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_RXCSUM; NETIF_F_RXCSUM;
ret = stmmac_tc_init(priv, priv);
if (!ret) {
ndev->hw_features |= NETIF_F_HW_TC;
}
if ((priv->plat->tso_en) && (priv->dma_cap.tsoen)) { if ((priv->plat->tso_en) && (priv->dma_cap.tsoen)) {
ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6; ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
priv->tso = true; priv->tso = true;
......
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
* stmmac TC Handling (HW only)
*/
#include <net/pkt_cls.h>
#include <net/tc_act/tc_gact.h>
#include "common.h"
#include "dwmac4.h"
#include "dwmac5.h"
#include "stmmac.h"
static void tc_fill_all_pass_entry(struct stmmac_tc_entry *entry)
{
memset(entry, 0, sizeof(*entry));
entry->in_use = true;
entry->is_last = true;
entry->is_frag = false;
entry->prio = ~0x0;
entry->handle = 0;
entry->val.match_data = 0x0;
entry->val.match_en = 0x0;
entry->val.af = 1;
entry->val.dma_ch_no = 0x0;
}
static struct stmmac_tc_entry *tc_find_entry(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls,
bool free)
{
struct stmmac_tc_entry *entry, *first = NULL, *dup = NULL;
u32 loc = cls->knode.handle;
int i;
for (i = 0; i < priv->tc_entries_max; i++) {
entry = &priv->tc_entries[i];
if (!entry->in_use && !first && free)
first = entry;
if (entry->handle == loc && !free)
dup = entry;
}
if (dup)
return dup;
if (first) {
first->handle = loc;
first->in_use = true;
/* Reset HW values */
memset(&first->val, 0, sizeof(first->val));
}
return first;
}
static int tc_fill_actions(struct stmmac_tc_entry *entry,
struct stmmac_tc_entry *frag,
struct tc_cls_u32_offload *cls)
{
struct stmmac_tc_entry *action_entry = entry;
const struct tc_action *act;
struct tcf_exts *exts;
LIST_HEAD(actions);
exts = cls->knode.exts;
if (!tcf_exts_has_actions(exts))
return -EINVAL;
if (frag)
action_entry = frag;
tcf_exts_to_list(exts, &actions);
list_for_each_entry(act, &actions, list) {
/* Accept */
if (is_tcf_gact_ok(act)) {
action_entry->val.af = 1;
break;
}
/* Drop */
if (is_tcf_gact_shot(act)) {
action_entry->val.rf = 1;
break;
}
/* Unsupported */
return -EINVAL;
}
return 0;
}
static int tc_fill_entry(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
struct stmmac_tc_entry *entry, *frag = NULL;
struct tc_u32_sel *sel = cls->knode.sel;
u32 off, data, mask, real_off, rem;
u32 prio = cls->common.prio;
int ret;
/* Only 1 match per entry */
if (sel->nkeys <= 0 || sel->nkeys > 1)
return -EINVAL;
off = sel->keys[0].off << sel->offshift;
data = sel->keys[0].val;
mask = sel->keys[0].mask;
switch (ntohs(cls->common.protocol)) {
case ETH_P_ALL:
break;
case ETH_P_IP:
off += ETH_HLEN;
break;
default:
return -EINVAL;
}
if (off > priv->tc_off_max)
return -EINVAL;
real_off = off / 4;
rem = off % 4;
entry = tc_find_entry(priv, cls, true);
if (!entry)
return -EINVAL;
if (rem) {
frag = tc_find_entry(priv, cls, true);
if (!frag) {
ret = -EINVAL;
goto err_unuse;
}
entry->frag_ptr = frag;
entry->val.match_en = (mask << (rem * 8)) &
GENMASK(31, rem * 8);
entry->val.match_data = (data << (rem * 8)) &
GENMASK(31, rem * 8);
entry->val.frame_offset = real_off;
entry->prio = prio;
frag->val.match_en = (mask >> (rem * 8)) &
GENMASK(rem * 8 - 1, 0);
frag->val.match_data = (data >> (rem * 8)) &
GENMASK(rem * 8 - 1, 0);
frag->val.frame_offset = real_off + 1;
frag->prio = prio;
frag->is_frag = true;
} else {
entry->frag_ptr = NULL;
entry->val.match_en = mask;
entry->val.match_data = data;
entry->val.frame_offset = real_off;
entry->prio = prio;
}
ret = tc_fill_actions(entry, frag, cls);
if (ret)
goto err_unuse;
return 0;
err_unuse:
if (frag)
frag->in_use = false;
entry->in_use = false;
return ret;
}
static void tc_unfill_entry(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
struct stmmac_tc_entry *entry;
entry = tc_find_entry(priv, cls, false);
if (!entry)
return;
entry->in_use = false;
if (entry->frag_ptr) {
entry = entry->frag_ptr;
entry->is_frag = false;
entry->in_use = false;
}
}
static int tc_config_knode(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
int ret;
ret = tc_fill_entry(priv, cls);
if (ret)
return ret;
ret = stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
priv->tc_entries_max);
if (ret)
goto err_unfill;
return 0;
err_unfill:
tc_unfill_entry(priv, cls);
return ret;
}
static int tc_delete_knode(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
int ret;
/* Set entry and fragments as not used */
tc_unfill_entry(priv, cls);
ret = stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
priv->tc_entries_max);
if (ret)
return ret;
return 0;
}
static int tc_setup_cls_u32(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
switch (cls->command) {
case TC_CLSU32_REPLACE_KNODE:
tc_unfill_entry(priv, cls);
/* Fall through */
case TC_CLSU32_NEW_KNODE:
return tc_config_knode(priv, cls);
case TC_CLSU32_DELETE_KNODE:
return tc_delete_knode(priv, cls);
default:
return -EOPNOTSUPP;
}
}
static int tc_init(struct stmmac_priv *priv)
{
struct dma_features *dma_cap = &priv->dma_cap;
unsigned int count;
if (!dma_cap->frpsel)
return -EINVAL;
switch (dma_cap->frpbs) {
case 0x0:
priv->tc_off_max = 64;
break;
case 0x1:
priv->tc_off_max = 128;
break;
case 0x2:
priv->tc_off_max = 256;
break;
default:
return -EINVAL;
}
switch (dma_cap->frpes) {
case 0x0:
count = 64;
break;
case 0x1:
count = 128;
break;
case 0x2:
count = 256;
break;
default:
return -EINVAL;
}
/* Reserve one last filter which lets all pass */
priv->tc_entries_max = count;
priv->tc_entries = devm_kzalloc(priv->device,
sizeof(*priv->tc_entries) * count, GFP_KERNEL);
if (!priv->tc_entries)
return -ENOMEM;
tc_fill_all_pass_entry(&priv->tc_entries[count - 1]);
dev_info(priv->device, "Enabling HW TC (entries=%d, max_off=%d)\n",
priv->tc_entries_max, priv->tc_off_max);
return 0;
}
const struct stmmac_tc_ops dwmac510_tc_ops = {
.init = tc_init,
.setup_cls_u32 = tc_setup_cls_u32,
};
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