diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/common.h | 5 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac5.c | 195 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac5.h | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/hwif.c | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/hwif.h | 25 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac.h | 29 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 59 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 295 |
12 files changed, 636 insertions, 3 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index e3b578b4f7cb..68e9e2640c62 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -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 \ 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 \ - $(stmmac-y) + stmmac_tc.o $(stmmac-y) # Ordering matters. Generic driver must be last. obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 627e905b6d76..a679cb729d1d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -353,6 +353,10 @@ struct dma_features { unsigned int rx_fifo_size; /* Automotive Safety Package */ unsigned int asp; + /* RX Parser */ + unsigned int frpsel; + unsigned int frpbs; + unsigned int frpes; }; /* GMAC TX FIFO is 8K, Rx FIFO is 16K */ @@ -412,6 +416,7 @@ struct mac_device_info { const struct stmmac_dma_ops *dma; const struct stmmac_mode_ops *mode; const struct stmmac_hwtimestamp *ptp; + const struct stmmac_tc_ops *tc; struct mii_regs mii; /* MII register Addresses */ struct mac_link link; void __iomem *pcsr; /* vpointer to device CSRs */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 03eab9077c1c..6330a55953df 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -194,6 +194,9 @@ enum power_event { /* MAC HW features3 bitmap */ #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 */ #define GMAC_HI_DCS GENMASK(18, 16) @@ -202,6 +205,7 @@ enum power_event { /* MTL registers */ #define MTL_OPERATION_MODE 0x00000c00 +#define MTL_FRPE BIT(15) #define MTL_OPERATION_SCHALG_MASK GENMASK(6, 5) #define MTL_OPERATION_SCHALG_WRR (0x0 << 5) #define MTL_OPERATION_SCHALG_WFQ (0x1 << 5) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 7289b3b47d8e..a7121a7d9391 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -795,6 +795,7 @@ const struct stmmac_ops dwmac510_ops = { .safety_feat_config = dwmac5_safety_feat_config, .safety_feat_irq_status = dwmac5_safety_feat_irq_status, .safety_feat_dump = dwmac5_safety_feat_dump, + .rxp_config = dwmac5_rxp_config, }; int dwmac4_setup(struct stmmac_priv *priv) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index d37d457306d1..117c3a5288f0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -379,6 +379,9 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr, /* 5.10 Features */ 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 */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c index 2978550bb7f6..b2becb80a697 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c @@ -7,6 +7,7 @@ #include "common.h" #include "dwmac4.h" #include "dwmac5.h" +#include "stmmac.h" struct dwmac5_error_desc { bool valid; @@ -299,3 +300,197 @@ int dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats, *desc = dwmac5_all_errors[module].desc[offset].desc; 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; +} diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h index bd4c466d303e..cc810aff7100 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h @@ -11,6 +11,17 @@ #define PRTYEN BIT(1) #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 TSOEE BIT(4) #define MRXPEE BIT(3) @@ -48,5 +59,7 @@ int dwmac5_safety_feat_irq_status(struct net_device *ndev, struct stmmac_safety_stats *stats); int dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats, 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__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.c b/drivers/net/ethernet/stmicro/stmmac/hwif.c index 2b0a7e79de00..9acc8d2f1039 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.c +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.c @@ -77,6 +77,7 @@ static const struct stmmac_hwif_entry { const void *mac; const void *hwtimestamp; const void *mode; + const void *tc; int (*setup)(struct stmmac_priv *priv); int (*quirks)(struct stmmac_priv *priv); } stmmac_hw[] = { @@ -90,6 +91,7 @@ static const struct stmmac_hwif_entry { .mac = &dwmac100_ops, .hwtimestamp = &stmmac_ptp, .mode = NULL, + .tc = NULL, .setup = dwmac100_setup, .quirks = stmmac_dwmac1_quirks, }, { @@ -101,6 +103,7 @@ static const struct stmmac_hwif_entry { .mac = &dwmac1000_ops, .hwtimestamp = &stmmac_ptp, .mode = NULL, + .tc = NULL, .setup = dwmac1000_setup, .quirks = stmmac_dwmac1_quirks, }, { @@ -112,6 +115,7 @@ static const struct stmmac_hwif_entry { .mac = &dwmac4_ops, .hwtimestamp = &stmmac_ptp, .mode = NULL, + .tc = NULL, .setup = dwmac4_setup, .quirks = stmmac_dwmac4_quirks, }, { @@ -123,6 +127,7 @@ static const struct stmmac_hwif_entry { .mac = &dwmac410_ops, .hwtimestamp = &stmmac_ptp, .mode = &dwmac4_ring_mode_ops, + .tc = NULL, .setup = dwmac4_setup, .quirks = NULL, }, { @@ -134,6 +139,7 @@ static const struct stmmac_hwif_entry { .mac = &dwmac410_ops, .hwtimestamp = &stmmac_ptp, .mode = &dwmac4_ring_mode_ops, + .tc = NULL, .setup = dwmac4_setup, .quirks = NULL, }, { @@ -145,6 +151,7 @@ static const struct stmmac_hwif_entry { .mac = &dwmac510_ops, .hwtimestamp = &stmmac_ptp, .mode = &dwmac4_ring_mode_ops, + .tc = &dwmac510_tc_ops, .setup = dwmac4_setup, .quirks = NULL, } @@ -196,6 +203,7 @@ int stmmac_hwif_init(struct stmmac_priv *priv) mac->mac = entry->mac; mac->ptp = entry->hwtimestamp; mac->mode = entry->mode; + mac->tc = entry->tc; priv->hw = mac; diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index bfad61607f07..b7539a14e6ad 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -5,10 +5,12 @@ #ifndef __STMMAC_HWIF_H__ #define __STMMAC_HWIF_H__ +#include <linux/netdevice.h> + #define stmmac_do_void_callback(__priv, __module, __cname, __arg0, __args...) \ ({ \ int __result = -EINVAL; \ - if ((__priv)->hw->__module->__cname) { \ + if ((__priv)->hw->__module && (__priv)->hw->__module->__cname) { \ (__priv)->hw->__module->__cname((__arg0), ##__args); \ __result = 0; \ } \ @@ -17,7 +19,7 @@ #define stmmac_do_callback(__priv, __module, __cname, __arg0, __args...) \ ({ \ int __result = -EINVAL; \ - if ((__priv)->hw->__module->__cname) \ + if ((__priv)->hw->__module && (__priv)->hw->__module->__cname) \ __result = (__priv)->hw->__module->__cname((__arg0), ##__args); \ __result; \ }) @@ -232,6 +234,7 @@ struct mac_device_info; struct net_device; struct rgmii_adv; struct stmmac_safety_stats; +struct stmmac_tc_entry; /* Helpers to program the MAC core */ struct stmmac_ops { @@ -301,6 +304,9 @@ struct stmmac_ops { struct stmmac_safety_stats *stats); int (*safety_feat_dump)(struct stmmac_safety_stats *stats, 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...) \ @@ -365,6 +371,8 @@ struct stmmac_ops { stmmac_do_callback(__priv, mac, safety_feat_irq_status, __args) #define stmmac_safety_feat_dump(__priv, __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 */ struct stmmac_hwtimestamp { @@ -419,6 +427,18 @@ struct stmmac_mode_ops { stmmac_do_void_callback(__priv, mode, clean_desc3, __args) 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_dma_ops dwmac100_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_dma_ops dwmac410_dma_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 GMAC4_VERSION 0x00000110 /* GMAC4+ CORE Version */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 2443f20e07bf..42fc76e76bf9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -76,6 +76,30 @@ struct stmmac_rx_queue { 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 { /* Frequently used values are kept adjacent for cache effect */ u32 tx_count_frames; @@ -151,6 +175,11 @@ struct stmmac_priv { unsigned long state; struct workqueue_struct *wq; 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 { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b935478df55f..d9dbe1355896 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -45,6 +45,7 @@ #include <linux/seq_file.h> #endif /* CONFIG_DEBUG_FS */ #include <linux/net_tstamp.h> +#include <net/pkt_cls.h> #include "stmmac_ptp.h" #include "stmmac.h" #include <linux/reset.h> @@ -3790,6 +3791,58 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 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) { struct stmmac_priv *priv = netdev_priv(ndev); @@ -4028,6 +4081,7 @@ static const struct net_device_ops stmmac_netdev_ops = { .ndo_set_rx_mode = stmmac_set_rx_mode, .ndo_tx_timeout = stmmac_tx_timeout, .ndo_do_ioctl = stmmac_ioctl, + .ndo_setup_tc = stmmac_setup_tc, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = stmmac_poll_controller, #endif @@ -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 | 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)) { ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6; priv->tso = true; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c new file mode 100644 index 000000000000..881c94b73e2f --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -0,0 +1,295 @@ +// 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, +}; |