14dbbe8ddSJose Abreu // SPDX-License-Identifier: (GPL-2.0 OR MIT)
24dbbe8ddSJose Abreu /*
34dbbe8ddSJose Abreu * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
44dbbe8ddSJose Abreu * stmmac TC Handling (HW only)
54dbbe8ddSJose Abreu */
64dbbe8ddSJose Abreu
74dbbe8ddSJose Abreu #include <net/pkt_cls.h>
84dbbe8ddSJose Abreu #include <net/tc_act/tc_gact.h>
94dbbe8ddSJose Abreu #include "common.h"
104dbbe8ddSJose Abreu #include "dwmac4.h"
114dbbe8ddSJose Abreu #include "dwmac5.h"
124dbbe8ddSJose Abreu #include "stmmac.h"
134dbbe8ddSJose Abreu
tc_fill_all_pass_entry(struct stmmac_tc_entry * entry)144dbbe8ddSJose Abreu static void tc_fill_all_pass_entry(struct stmmac_tc_entry *entry)
154dbbe8ddSJose Abreu {
164dbbe8ddSJose Abreu memset(entry, 0, sizeof(*entry));
174dbbe8ddSJose Abreu entry->in_use = true;
184dbbe8ddSJose Abreu entry->is_last = true;
194dbbe8ddSJose Abreu entry->is_frag = false;
204dbbe8ddSJose Abreu entry->prio = ~0x0;
214dbbe8ddSJose Abreu entry->handle = 0;
224dbbe8ddSJose Abreu entry->val.match_data = 0x0;
234dbbe8ddSJose Abreu entry->val.match_en = 0x0;
244dbbe8ddSJose Abreu entry->val.af = 1;
254dbbe8ddSJose Abreu entry->val.dma_ch_no = 0x0;
264dbbe8ddSJose Abreu }
274dbbe8ddSJose Abreu
tc_find_entry(struct stmmac_priv * priv,struct tc_cls_u32_offload * cls,bool free)284dbbe8ddSJose Abreu static struct stmmac_tc_entry *tc_find_entry(struct stmmac_priv *priv,
294dbbe8ddSJose Abreu struct tc_cls_u32_offload *cls,
304dbbe8ddSJose Abreu bool free)
314dbbe8ddSJose Abreu {
324dbbe8ddSJose Abreu struct stmmac_tc_entry *entry, *first = NULL, *dup = NULL;
334dbbe8ddSJose Abreu u32 loc = cls->knode.handle;
344dbbe8ddSJose Abreu int i;
354dbbe8ddSJose Abreu
364dbbe8ddSJose Abreu for (i = 0; i < priv->tc_entries_max; i++) {
374dbbe8ddSJose Abreu entry = &priv->tc_entries[i];
384dbbe8ddSJose Abreu if (!entry->in_use && !first && free)
394dbbe8ddSJose Abreu first = entry;
404a6a1385SJose Abreu if ((entry->handle == loc) && !free && !entry->is_frag)
414dbbe8ddSJose Abreu dup = entry;
424dbbe8ddSJose Abreu }
434dbbe8ddSJose Abreu
444dbbe8ddSJose Abreu if (dup)
454dbbe8ddSJose Abreu return dup;
464dbbe8ddSJose Abreu if (first) {
474dbbe8ddSJose Abreu first->handle = loc;
484dbbe8ddSJose Abreu first->in_use = true;
494dbbe8ddSJose Abreu
504dbbe8ddSJose Abreu /* Reset HW values */
514dbbe8ddSJose Abreu memset(&first->val, 0, sizeof(first->val));
524dbbe8ddSJose Abreu }
534dbbe8ddSJose Abreu
544dbbe8ddSJose Abreu return first;
554dbbe8ddSJose Abreu }
564dbbe8ddSJose Abreu
tc_fill_actions(struct stmmac_tc_entry * entry,struct stmmac_tc_entry * frag,struct tc_cls_u32_offload * cls)574dbbe8ddSJose Abreu static int tc_fill_actions(struct stmmac_tc_entry *entry,
584dbbe8ddSJose Abreu struct stmmac_tc_entry *frag,
594dbbe8ddSJose Abreu struct tc_cls_u32_offload *cls)
604dbbe8ddSJose Abreu {
614dbbe8ddSJose Abreu struct stmmac_tc_entry *action_entry = entry;
624dbbe8ddSJose Abreu const struct tc_action *act;
634dbbe8ddSJose Abreu struct tcf_exts *exts;
64244cd96aSCong Wang int i;
654dbbe8ddSJose Abreu
664dbbe8ddSJose Abreu exts = cls->knode.exts;
674dbbe8ddSJose Abreu if (!tcf_exts_has_actions(exts))
684dbbe8ddSJose Abreu return -EINVAL;
694dbbe8ddSJose Abreu if (frag)
704dbbe8ddSJose Abreu action_entry = frag;
714dbbe8ddSJose Abreu
72244cd96aSCong Wang tcf_exts_for_each_action(i, act, exts) {
734dbbe8ddSJose Abreu /* Accept */
744dbbe8ddSJose Abreu if (is_tcf_gact_ok(act)) {
754dbbe8ddSJose Abreu action_entry->val.af = 1;
764dbbe8ddSJose Abreu break;
774dbbe8ddSJose Abreu }
784dbbe8ddSJose Abreu /* Drop */
794dbbe8ddSJose Abreu if (is_tcf_gact_shot(act)) {
804dbbe8ddSJose Abreu action_entry->val.rf = 1;
814dbbe8ddSJose Abreu break;
824dbbe8ddSJose Abreu }
834dbbe8ddSJose Abreu
844dbbe8ddSJose Abreu /* Unsupported */
854dbbe8ddSJose Abreu return -EINVAL;
864dbbe8ddSJose Abreu }
874dbbe8ddSJose Abreu
884dbbe8ddSJose Abreu return 0;
894dbbe8ddSJose Abreu }
904dbbe8ddSJose Abreu
tc_fill_entry(struct stmmac_priv * priv,struct tc_cls_u32_offload * cls)914dbbe8ddSJose Abreu static int tc_fill_entry(struct stmmac_priv *priv,
924dbbe8ddSJose Abreu struct tc_cls_u32_offload *cls)
934dbbe8ddSJose Abreu {
944dbbe8ddSJose Abreu struct stmmac_tc_entry *entry, *frag = NULL;
954dbbe8ddSJose Abreu struct tc_u32_sel *sel = cls->knode.sel;
964dbbe8ddSJose Abreu u32 off, data, mask, real_off, rem;
97ef01adaeSPablo Neira Ayuso u32 prio = cls->common.prio << 16;
984dbbe8ddSJose Abreu int ret;
994dbbe8ddSJose Abreu
1004dbbe8ddSJose Abreu /* Only 1 match per entry */
1014dbbe8ddSJose Abreu if (sel->nkeys <= 0 || sel->nkeys > 1)
1024dbbe8ddSJose Abreu return -EINVAL;
1034dbbe8ddSJose Abreu
1044dbbe8ddSJose Abreu off = sel->keys[0].off << sel->offshift;
1054dbbe8ddSJose Abreu data = sel->keys[0].val;
1064dbbe8ddSJose Abreu mask = sel->keys[0].mask;
1074dbbe8ddSJose Abreu
1084dbbe8ddSJose Abreu switch (ntohs(cls->common.protocol)) {
1094dbbe8ddSJose Abreu case ETH_P_ALL:
1104dbbe8ddSJose Abreu break;
1114dbbe8ddSJose Abreu case ETH_P_IP:
1124dbbe8ddSJose Abreu off += ETH_HLEN;
1134dbbe8ddSJose Abreu break;
1144dbbe8ddSJose Abreu default:
1154dbbe8ddSJose Abreu return -EINVAL;
1164dbbe8ddSJose Abreu }
1174dbbe8ddSJose Abreu
1184dbbe8ddSJose Abreu if (off > priv->tc_off_max)
1194dbbe8ddSJose Abreu return -EINVAL;
1204dbbe8ddSJose Abreu
1214dbbe8ddSJose Abreu real_off = off / 4;
1224dbbe8ddSJose Abreu rem = off % 4;
1234dbbe8ddSJose Abreu
1244dbbe8ddSJose Abreu entry = tc_find_entry(priv, cls, true);
1254dbbe8ddSJose Abreu if (!entry)
1264dbbe8ddSJose Abreu return -EINVAL;
1274dbbe8ddSJose Abreu
1284dbbe8ddSJose Abreu if (rem) {
1294dbbe8ddSJose Abreu frag = tc_find_entry(priv, cls, true);
1304dbbe8ddSJose Abreu if (!frag) {
1314dbbe8ddSJose Abreu ret = -EINVAL;
1324dbbe8ddSJose Abreu goto err_unuse;
1334dbbe8ddSJose Abreu }
1344dbbe8ddSJose Abreu
1354dbbe8ddSJose Abreu entry->frag_ptr = frag;
1364dbbe8ddSJose Abreu entry->val.match_en = (mask << (rem * 8)) &
1374dbbe8ddSJose Abreu GENMASK(31, rem * 8);
1384dbbe8ddSJose Abreu entry->val.match_data = (data << (rem * 8)) &
1394dbbe8ddSJose Abreu GENMASK(31, rem * 8);
1404dbbe8ddSJose Abreu entry->val.frame_offset = real_off;
1414dbbe8ddSJose Abreu entry->prio = prio;
1424dbbe8ddSJose Abreu
1434dbbe8ddSJose Abreu frag->val.match_en = (mask >> (rem * 8)) &
1444dbbe8ddSJose Abreu GENMASK(rem * 8 - 1, 0);
1454dbbe8ddSJose Abreu frag->val.match_data = (data >> (rem * 8)) &
1464dbbe8ddSJose Abreu GENMASK(rem * 8 - 1, 0);
1474dbbe8ddSJose Abreu frag->val.frame_offset = real_off + 1;
1484dbbe8ddSJose Abreu frag->prio = prio;
1494dbbe8ddSJose Abreu frag->is_frag = true;
1504dbbe8ddSJose Abreu } else {
1514dbbe8ddSJose Abreu entry->frag_ptr = NULL;
1524dbbe8ddSJose Abreu entry->val.match_en = mask;
1534dbbe8ddSJose Abreu entry->val.match_data = data;
1544dbbe8ddSJose Abreu entry->val.frame_offset = real_off;
1554dbbe8ddSJose Abreu entry->prio = prio;
1564dbbe8ddSJose Abreu }
1574dbbe8ddSJose Abreu
1584dbbe8ddSJose Abreu ret = tc_fill_actions(entry, frag, cls);
1594dbbe8ddSJose Abreu if (ret)
1604dbbe8ddSJose Abreu goto err_unuse;
1614dbbe8ddSJose Abreu
1624dbbe8ddSJose Abreu return 0;
1634dbbe8ddSJose Abreu
1644dbbe8ddSJose Abreu err_unuse:
1654dbbe8ddSJose Abreu if (frag)
1664dbbe8ddSJose Abreu frag->in_use = false;
1674dbbe8ddSJose Abreu entry->in_use = false;
1684dbbe8ddSJose Abreu return ret;
1694dbbe8ddSJose Abreu }
1704dbbe8ddSJose Abreu
tc_unfill_entry(struct stmmac_priv * priv,struct tc_cls_u32_offload * cls)1714dbbe8ddSJose Abreu static void tc_unfill_entry(struct stmmac_priv *priv,
1724dbbe8ddSJose Abreu struct tc_cls_u32_offload *cls)
1734dbbe8ddSJose Abreu {
1744dbbe8ddSJose Abreu struct stmmac_tc_entry *entry;
1754dbbe8ddSJose Abreu
1764dbbe8ddSJose Abreu entry = tc_find_entry(priv, cls, false);
1774dbbe8ddSJose Abreu if (!entry)
1784dbbe8ddSJose Abreu return;
1794dbbe8ddSJose Abreu
1804dbbe8ddSJose Abreu entry->in_use = false;
1814dbbe8ddSJose Abreu if (entry->frag_ptr) {
1824dbbe8ddSJose Abreu entry = entry->frag_ptr;
1834dbbe8ddSJose Abreu entry->is_frag = false;
1844dbbe8ddSJose Abreu entry->in_use = false;
1854dbbe8ddSJose Abreu }
1864dbbe8ddSJose Abreu }
1874dbbe8ddSJose Abreu
tc_config_knode(struct stmmac_priv * priv,struct tc_cls_u32_offload * cls)1884dbbe8ddSJose Abreu static int tc_config_knode(struct stmmac_priv *priv,
1894dbbe8ddSJose Abreu struct tc_cls_u32_offload *cls)
1904dbbe8ddSJose Abreu {
1914dbbe8ddSJose Abreu int ret;
1924dbbe8ddSJose Abreu
1934dbbe8ddSJose Abreu ret = tc_fill_entry(priv, cls);
1944dbbe8ddSJose Abreu if (ret)
1954dbbe8ddSJose Abreu return ret;
1964dbbe8ddSJose Abreu
1974dbbe8ddSJose Abreu ret = stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
1984dbbe8ddSJose Abreu priv->tc_entries_max);
1994dbbe8ddSJose Abreu if (ret)
2004dbbe8ddSJose Abreu goto err_unfill;
2014dbbe8ddSJose Abreu
2024dbbe8ddSJose Abreu return 0;
2034dbbe8ddSJose Abreu
2044dbbe8ddSJose Abreu err_unfill:
2054dbbe8ddSJose Abreu tc_unfill_entry(priv, cls);
2064dbbe8ddSJose Abreu return ret;
2074dbbe8ddSJose Abreu }
2084dbbe8ddSJose Abreu
tc_delete_knode(struct stmmac_priv * priv,struct tc_cls_u32_offload * cls)2094dbbe8ddSJose Abreu static int tc_delete_knode(struct stmmac_priv *priv,
2104dbbe8ddSJose Abreu struct tc_cls_u32_offload *cls)
2114dbbe8ddSJose Abreu {
2124dbbe8ddSJose Abreu /* Set entry and fragments as not used */
2134dbbe8ddSJose Abreu tc_unfill_entry(priv, cls);
2144dbbe8ddSJose Abreu
215ec73c31dSZheng Yongjun return stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
2164dbbe8ddSJose Abreu priv->tc_entries_max);
2174dbbe8ddSJose Abreu }
2184dbbe8ddSJose Abreu
tc_setup_cls_u32(struct stmmac_priv * priv,struct tc_cls_u32_offload * cls)2194dbbe8ddSJose Abreu static int tc_setup_cls_u32(struct stmmac_priv *priv,
2204dbbe8ddSJose Abreu struct tc_cls_u32_offload *cls)
2214dbbe8ddSJose Abreu {
2224dbbe8ddSJose Abreu switch (cls->command) {
2234dbbe8ddSJose Abreu case TC_CLSU32_REPLACE_KNODE:
2244dbbe8ddSJose Abreu tc_unfill_entry(priv, cls);
225df561f66SGustavo A. R. Silva fallthrough;
2264dbbe8ddSJose Abreu case TC_CLSU32_NEW_KNODE:
2274dbbe8ddSJose Abreu return tc_config_knode(priv, cls);
2284dbbe8ddSJose Abreu case TC_CLSU32_DELETE_KNODE:
2294dbbe8ddSJose Abreu return tc_delete_knode(priv, cls);
2304dbbe8ddSJose Abreu default:
2314dbbe8ddSJose Abreu return -EOPNOTSUPP;
2324dbbe8ddSJose Abreu }
2334dbbe8ddSJose Abreu }
2344dbbe8ddSJose Abreu
tc_rfs_init(struct stmmac_priv * priv)235aeb7c75cSOng Boon Leong static int tc_rfs_init(struct stmmac_priv *priv)
236aeb7c75cSOng Boon Leong {
237aeb7c75cSOng Boon Leong int i;
238aeb7c75cSOng Boon Leong
239aeb7c75cSOng Boon Leong priv->rfs_entries_max[STMMAC_RFS_T_VLAN] = 8;
240e48cb313SOng Boon Leong priv->rfs_entries_max[STMMAC_RFS_T_LLDP] = 1;
241e48cb313SOng Boon Leong priv->rfs_entries_max[STMMAC_RFS_T_1588] = 1;
242aeb7c75cSOng Boon Leong
243aeb7c75cSOng Boon Leong for (i = 0; i < STMMAC_RFS_T_MAX; i++)
244aeb7c75cSOng Boon Leong priv->rfs_entries_total += priv->rfs_entries_max[i];
245aeb7c75cSOng Boon Leong
246aeb7c75cSOng Boon Leong priv->rfs_entries = devm_kcalloc(priv->device,
247aeb7c75cSOng Boon Leong priv->rfs_entries_total,
248aeb7c75cSOng Boon Leong sizeof(*priv->rfs_entries),
249aeb7c75cSOng Boon Leong GFP_KERNEL);
250aeb7c75cSOng Boon Leong if (!priv->rfs_entries)
251aeb7c75cSOng Boon Leong return -ENOMEM;
252aeb7c75cSOng Boon Leong
253aeb7c75cSOng Boon Leong dev_info(priv->device, "Enabled RFS Flow TC (entries=%d)\n",
254aeb7c75cSOng Boon Leong priv->rfs_entries_total);
255aeb7c75cSOng Boon Leong
256aeb7c75cSOng Boon Leong return 0;
257aeb7c75cSOng Boon Leong }
258aeb7c75cSOng Boon Leong
tc_init(struct stmmac_priv * priv)2594dbbe8ddSJose Abreu static int tc_init(struct stmmac_priv *priv)
2604dbbe8ddSJose Abreu {
2614dbbe8ddSJose Abreu struct dma_features *dma_cap = &priv->dma_cap;
2624dbbe8ddSJose Abreu unsigned int count;
263aeb7c75cSOng Boon Leong int ret, i;
264425eabddSJose Abreu
265425eabddSJose Abreu if (dma_cap->l3l4fnum) {
266425eabddSJose Abreu priv->flow_entries_max = dma_cap->l3l4fnum;
267425eabddSJose Abreu priv->flow_entries = devm_kcalloc(priv->device,
268425eabddSJose Abreu dma_cap->l3l4fnum,
269425eabddSJose Abreu sizeof(*priv->flow_entries),
270425eabddSJose Abreu GFP_KERNEL);
271425eabddSJose Abreu if (!priv->flow_entries)
272425eabddSJose Abreu return -ENOMEM;
273425eabddSJose Abreu
274425eabddSJose Abreu for (i = 0; i < priv->flow_entries_max; i++)
275425eabddSJose Abreu priv->flow_entries[i].idx = i;
276425eabddSJose Abreu
277aeb7c75cSOng Boon Leong dev_info(priv->device, "Enabled L3L4 Flow TC (entries=%d)\n",
278425eabddSJose Abreu priv->flow_entries_max);
279425eabddSJose Abreu }
2804dbbe8ddSJose Abreu
281aeb7c75cSOng Boon Leong ret = tc_rfs_init(priv);
282aeb7c75cSOng Boon Leong if (ret)
283aeb7c75cSOng Boon Leong return -ENOMEM;
284aeb7c75cSOng Boon Leong
28563c173ffSMohammad Athari Bin Ismail if (!priv->plat->fpe_cfg) {
28663c173ffSMohammad Athari Bin Ismail priv->plat->fpe_cfg = devm_kzalloc(priv->device,
28763c173ffSMohammad Athari Bin Ismail sizeof(*priv->plat->fpe_cfg),
28863c173ffSMohammad Athari Bin Ismail GFP_KERNEL);
28963c173ffSMohammad Athari Bin Ismail if (!priv->plat->fpe_cfg)
29063c173ffSMohammad Athari Bin Ismail return -ENOMEM;
29163c173ffSMohammad Athari Bin Ismail } else {
29263c173ffSMohammad Athari Bin Ismail memset(priv->plat->fpe_cfg, 0, sizeof(*priv->plat->fpe_cfg));
29363c173ffSMohammad Athari Bin Ismail }
29463c173ffSMohammad Athari Bin Ismail
295c104891cSJose Abreu /* Fail silently as we can still use remaining features, e.g. CBS */
2964dbbe8ddSJose Abreu if (!dma_cap->frpsel)
297c104891cSJose Abreu return 0;
2984dbbe8ddSJose Abreu
2994dbbe8ddSJose Abreu switch (dma_cap->frpbs) {
3004dbbe8ddSJose Abreu case 0x0:
3014dbbe8ddSJose Abreu priv->tc_off_max = 64;
3024dbbe8ddSJose Abreu break;
3034dbbe8ddSJose Abreu case 0x1:
3044dbbe8ddSJose Abreu priv->tc_off_max = 128;
3054dbbe8ddSJose Abreu break;
3064dbbe8ddSJose Abreu case 0x2:
3074dbbe8ddSJose Abreu priv->tc_off_max = 256;
3084dbbe8ddSJose Abreu break;
3094dbbe8ddSJose Abreu default:
3104dbbe8ddSJose Abreu return -EINVAL;
3114dbbe8ddSJose Abreu }
3124dbbe8ddSJose Abreu
3134dbbe8ddSJose Abreu switch (dma_cap->frpes) {
3144dbbe8ddSJose Abreu case 0x0:
3154dbbe8ddSJose Abreu count = 64;
3164dbbe8ddSJose Abreu break;
3174dbbe8ddSJose Abreu case 0x1:
3184dbbe8ddSJose Abreu count = 128;
3194dbbe8ddSJose Abreu break;
3204dbbe8ddSJose Abreu case 0x2:
3214dbbe8ddSJose Abreu count = 256;
3224dbbe8ddSJose Abreu break;
3234dbbe8ddSJose Abreu default:
3244dbbe8ddSJose Abreu return -EINVAL;
3254dbbe8ddSJose Abreu }
3264dbbe8ddSJose Abreu
3274dbbe8ddSJose Abreu /* Reserve one last filter which lets all pass */
3284dbbe8ddSJose Abreu priv->tc_entries_max = count;
329a86854d0SKees Cook priv->tc_entries = devm_kcalloc(priv->device,
330a86854d0SKees Cook count, sizeof(*priv->tc_entries), GFP_KERNEL);
3314dbbe8ddSJose Abreu if (!priv->tc_entries)
3324dbbe8ddSJose Abreu return -ENOMEM;
3334dbbe8ddSJose Abreu
3344dbbe8ddSJose Abreu tc_fill_all_pass_entry(&priv->tc_entries[count - 1]);
3354dbbe8ddSJose Abreu
3364dbbe8ddSJose Abreu dev_info(priv->device, "Enabling HW TC (entries=%d, max_off=%d)\n",
3374dbbe8ddSJose Abreu priv->tc_entries_max, priv->tc_off_max);
3385a558611SOng Boon Leong
3394dbbe8ddSJose Abreu return 0;
3404dbbe8ddSJose Abreu }
3414dbbe8ddSJose Abreu
tc_setup_cbs(struct stmmac_priv * priv,struct tc_cbs_qopt_offload * qopt)3421f705bc6SJose Abreu static int tc_setup_cbs(struct stmmac_priv *priv,
3431f705bc6SJose Abreu struct tc_cbs_qopt_offload *qopt)
3441f705bc6SJose Abreu {
3451f705bc6SJose Abreu u32 tx_queues_count = priv->plat->tx_queues_to_use;
34635119b11SXiaolei Wang s64 port_transmit_rate_kbps;
3471f705bc6SJose Abreu u32 queue = qopt->queue;
3481f705bc6SJose Abreu u32 mode_to_use;
3491f705bc6SJose Abreu u64 value;
35035119b11SXiaolei Wang u32 ptr;
3511f705bc6SJose Abreu int ret;
3521f705bc6SJose Abreu
3531f705bc6SJose Abreu /* Queue 0 is not AVB capable */
3541f705bc6SJose Abreu if (queue <= 0 || queue >= tx_queues_count)
3551f705bc6SJose Abreu return -EINVAL;
3560650d401SJose Abreu if (!priv->dma_cap.av)
3570650d401SJose Abreu return -EOPNOTSUPP;
3581f705bc6SJose Abreu
35935119b11SXiaolei Wang port_transmit_rate_kbps = qopt->idleslope - qopt->sendslope;
36035119b11SXiaolei Wang
361a71b6864SXiaolei Wang if (qopt->enable) {
36224877687SSong, Yoong Siang /* Port Transmit Rate and Speed Divider */
36335119b11SXiaolei Wang switch (div_s64(port_transmit_rate_kbps, 1000)) {
36424877687SSong, Yoong Siang case SPEED_10000:
36524877687SSong, Yoong Siang case SPEED_5000:
36624877687SSong, Yoong Siang ptr = 32;
36724877687SSong, Yoong Siang break;
36824877687SSong, Yoong Siang case SPEED_2500:
36924877687SSong, Yoong Siang case SPEED_1000:
37024877687SSong, Yoong Siang ptr = 8;
37124877687SSong, Yoong Siang break;
37224877687SSong, Yoong Siang case SPEED_100:
37324877687SSong, Yoong Siang ptr = 4;
37424877687SSong, Yoong Siang break;
37524877687SSong, Yoong Siang default:
37635119b11SXiaolei Wang netdev_err(priv->dev,
37735119b11SXiaolei Wang "Invalid portTransmitRate %lld (idleSlope - sendSlope)\n",
37835119b11SXiaolei Wang port_transmit_rate_kbps);
37935119b11SXiaolei Wang return -EINVAL;
38024877687SSong, Yoong Siang }
381a71b6864SXiaolei Wang } else {
382a71b6864SXiaolei Wang ptr = 0;
383a71b6864SXiaolei Wang }
38424877687SSong, Yoong Siang
3851f705bc6SJose Abreu mode_to_use = priv->plat->tx_queues_cfg[queue].mode_to_use;
3861f705bc6SJose Abreu if (mode_to_use == MTL_QUEUE_DCB && qopt->enable) {
3871f705bc6SJose Abreu ret = stmmac_dma_qmode(priv, priv->ioaddr, queue, MTL_QUEUE_AVB);
3881f705bc6SJose Abreu if (ret)
3891f705bc6SJose Abreu return ret;
3901f705bc6SJose Abreu
3911f705bc6SJose Abreu priv->plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_AVB;
3921f705bc6SJose Abreu } else if (!qopt->enable) {
393f317e2eaSMohammad Athari Bin Ismail ret = stmmac_dma_qmode(priv, priv->ioaddr, queue,
394f317e2eaSMohammad Athari Bin Ismail MTL_QUEUE_DCB);
395f317e2eaSMohammad Athari Bin Ismail if (ret)
396f317e2eaSMohammad Athari Bin Ismail return ret;
397f317e2eaSMohammad Athari Bin Ismail
398f317e2eaSMohammad Athari Bin Ismail priv->plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB;
39903582f47SKhaiWenTan return 0;
4001f705bc6SJose Abreu }
4011f705bc6SJose Abreu
4021f705bc6SJose Abreu /* Final adjustments for HW */
40335119b11SXiaolei Wang value = div_s64(qopt->idleslope * 1024ll * ptr, port_transmit_rate_kbps);
4041f705bc6SJose Abreu priv->plat->tx_queues_cfg[queue].idle_slope = value & GENMASK(31, 0);
4051f705bc6SJose Abreu
40635119b11SXiaolei Wang value = div_s64(-qopt->sendslope * 1024ll * ptr, port_transmit_rate_kbps);
4071f705bc6SJose Abreu priv->plat->tx_queues_cfg[queue].send_slope = value & GENMASK(31, 0);
4081f705bc6SJose Abreu
4098f704ef6SArnd Bergmann value = qopt->hicredit * 1024ll * 8;
4101f705bc6SJose Abreu priv->plat->tx_queues_cfg[queue].high_credit = value & GENMASK(31, 0);
4111f705bc6SJose Abreu
4128f704ef6SArnd Bergmann value = qopt->locredit * 1024ll * 8;
4131f705bc6SJose Abreu priv->plat->tx_queues_cfg[queue].low_credit = value & GENMASK(31, 0);
4141f705bc6SJose Abreu
4151f705bc6SJose Abreu ret = stmmac_config_cbs(priv, priv->hw,
4161f705bc6SJose Abreu priv->plat->tx_queues_cfg[queue].send_slope,
4171f705bc6SJose Abreu priv->plat->tx_queues_cfg[queue].idle_slope,
4181f705bc6SJose Abreu priv->plat->tx_queues_cfg[queue].high_credit,
4191f705bc6SJose Abreu priv->plat->tx_queues_cfg[queue].low_credit,
4201f705bc6SJose Abreu queue);
4211f705bc6SJose Abreu if (ret)
4221f705bc6SJose Abreu return ret;
4231f705bc6SJose Abreu
4241f705bc6SJose Abreu dev_info(priv->device, "CBS queue %d: send %d, idle %d, hi %d, lo %d\n",
4251f705bc6SJose Abreu queue, qopt->sendslope, qopt->idleslope,
4261f705bc6SJose Abreu qopt->hicredit, qopt->locredit);
4271f705bc6SJose Abreu return 0;
4281f705bc6SJose Abreu }
4291f705bc6SJose Abreu
tc_parse_flow_actions(struct stmmac_priv * priv,struct flow_action * action,struct stmmac_flow_entry * entry,struct netlink_ext_ack * extack)430425eabddSJose Abreu static int tc_parse_flow_actions(struct stmmac_priv *priv,
431425eabddSJose Abreu struct flow_action *action,
432319a1d19SJiri Pirko struct stmmac_flow_entry *entry,
433319a1d19SJiri Pirko struct netlink_ext_ack *extack)
434425eabddSJose Abreu {
435425eabddSJose Abreu struct flow_action_entry *act;
436425eabddSJose Abreu int i;
437425eabddSJose Abreu
438425eabddSJose Abreu if (!flow_action_has_entries(action))
439425eabddSJose Abreu return -EINVAL;
440425eabddSJose Abreu
44153eca1f3SJakub Kicinski if (!flow_action_basic_hw_stats_check(action, extack))
442319a1d19SJiri Pirko return -EOPNOTSUPP;
443319a1d19SJiri Pirko
444425eabddSJose Abreu flow_action_for_each(i, act, action) {
445425eabddSJose Abreu switch (act->id) {
446425eabddSJose Abreu case FLOW_ACTION_DROP:
447425eabddSJose Abreu entry->action |= STMMAC_FLOW_ACTION_DROP;
448425eabddSJose Abreu return 0;
449425eabddSJose Abreu default:
450425eabddSJose Abreu break;
451425eabddSJose Abreu }
452425eabddSJose Abreu }
453425eabddSJose Abreu
454425eabddSJose Abreu /* Nothing to do, maybe inverse filter ? */
455425eabddSJose Abreu return 0;
456425eabddSJose Abreu }
457425eabddSJose Abreu
458e48cb313SOng Boon Leong #define ETHER_TYPE_FULL_MASK cpu_to_be16(~0)
459e48cb313SOng Boon Leong
tc_add_basic_flow(struct stmmac_priv * priv,struct flow_cls_offload * cls,struct stmmac_flow_entry * entry)460425eabddSJose Abreu static int tc_add_basic_flow(struct stmmac_priv *priv,
461425eabddSJose Abreu struct flow_cls_offload *cls,
462425eabddSJose Abreu struct stmmac_flow_entry *entry)
463425eabddSJose Abreu {
464425eabddSJose Abreu struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
465425eabddSJose Abreu struct flow_dissector *dissector = rule->match.dissector;
466425eabddSJose Abreu struct flow_match_basic match;
467425eabddSJose Abreu
468425eabddSJose Abreu /* Nothing to do here */
469425eabddSJose Abreu if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_BASIC))
470425eabddSJose Abreu return -EINVAL;
471425eabddSJose Abreu
472425eabddSJose Abreu flow_rule_match_basic(rule, &match);
473e48cb313SOng Boon Leong
474425eabddSJose Abreu entry->ip_proto = match.key->ip_proto;
475425eabddSJose Abreu return 0;
476425eabddSJose Abreu }
477425eabddSJose Abreu
tc_add_ip4_flow(struct stmmac_priv * priv,struct flow_cls_offload * cls,struct stmmac_flow_entry * entry)478425eabddSJose Abreu static int tc_add_ip4_flow(struct stmmac_priv *priv,
479425eabddSJose Abreu struct flow_cls_offload *cls,
480425eabddSJose Abreu struct stmmac_flow_entry *entry)
481425eabddSJose Abreu {
482425eabddSJose Abreu struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
483425eabddSJose Abreu struct flow_dissector *dissector = rule->match.dissector;
484425eabddSJose Abreu bool inv = entry->action & STMMAC_FLOW_ACTION_DROP;
485425eabddSJose Abreu struct flow_match_ipv4_addrs match;
486425eabddSJose Abreu u32 hw_match;
487425eabddSJose Abreu int ret;
488425eabddSJose Abreu
489425eabddSJose Abreu /* Nothing to do here */
490425eabddSJose Abreu if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS))
491425eabddSJose Abreu return -EINVAL;
492425eabddSJose Abreu
493425eabddSJose Abreu flow_rule_match_ipv4_addrs(rule, &match);
494425eabddSJose Abreu hw_match = ntohl(match.key->src) & ntohl(match.mask->src);
495425eabddSJose Abreu if (hw_match) {
496425eabddSJose Abreu ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, true,
497425eabddSJose Abreu false, true, inv, hw_match);
498425eabddSJose Abreu if (ret)
499425eabddSJose Abreu return ret;
500425eabddSJose Abreu }
501425eabddSJose Abreu
502425eabddSJose Abreu hw_match = ntohl(match.key->dst) & ntohl(match.mask->dst);
503425eabddSJose Abreu if (hw_match) {
504425eabddSJose Abreu ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, true,
505425eabddSJose Abreu false, false, inv, hw_match);
506425eabddSJose Abreu if (ret)
507425eabddSJose Abreu return ret;
508425eabddSJose Abreu }
509425eabddSJose Abreu
510425eabddSJose Abreu return 0;
511425eabddSJose Abreu }
512425eabddSJose Abreu
tc_add_ports_flow(struct stmmac_priv * priv,struct flow_cls_offload * cls,struct stmmac_flow_entry * entry)513425eabddSJose Abreu static int tc_add_ports_flow(struct stmmac_priv *priv,
514425eabddSJose Abreu struct flow_cls_offload *cls,
515425eabddSJose Abreu struct stmmac_flow_entry *entry)
516425eabddSJose Abreu {
517425eabddSJose Abreu struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
518425eabddSJose Abreu struct flow_dissector *dissector = rule->match.dissector;
519425eabddSJose Abreu bool inv = entry->action & STMMAC_FLOW_ACTION_DROP;
520425eabddSJose Abreu struct flow_match_ports match;
521425eabddSJose Abreu u32 hw_match;
522425eabddSJose Abreu bool is_udp;
523425eabddSJose Abreu int ret;
524425eabddSJose Abreu
525425eabddSJose Abreu /* Nothing to do here */
526425eabddSJose Abreu if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_PORTS))
527425eabddSJose Abreu return -EINVAL;
528425eabddSJose Abreu
529425eabddSJose Abreu switch (entry->ip_proto) {
530425eabddSJose Abreu case IPPROTO_TCP:
531425eabddSJose Abreu is_udp = false;
532425eabddSJose Abreu break;
533425eabddSJose Abreu case IPPROTO_UDP:
534425eabddSJose Abreu is_udp = true;
535425eabddSJose Abreu break;
536425eabddSJose Abreu default:
537425eabddSJose Abreu return -EINVAL;
538425eabddSJose Abreu }
539425eabddSJose Abreu
540425eabddSJose Abreu flow_rule_match_ports(rule, &match);
541425eabddSJose Abreu
542425eabddSJose Abreu hw_match = ntohs(match.key->src) & ntohs(match.mask->src);
543425eabddSJose Abreu if (hw_match) {
544425eabddSJose Abreu ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, true,
545425eabddSJose Abreu is_udp, true, inv, hw_match);
546425eabddSJose Abreu if (ret)
547425eabddSJose Abreu return ret;
548425eabddSJose Abreu }
549425eabddSJose Abreu
550425eabddSJose Abreu hw_match = ntohs(match.key->dst) & ntohs(match.mask->dst);
551425eabddSJose Abreu if (hw_match) {
552425eabddSJose Abreu ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, true,
553425eabddSJose Abreu is_udp, false, inv, hw_match);
554425eabddSJose Abreu if (ret)
555425eabddSJose Abreu return ret;
556425eabddSJose Abreu }
557425eabddSJose Abreu
558425eabddSJose Abreu entry->is_l4 = true;
559425eabddSJose Abreu return 0;
560425eabddSJose Abreu }
561425eabddSJose Abreu
tc_find_flow(struct stmmac_priv * priv,struct flow_cls_offload * cls,bool get_free)562425eabddSJose Abreu static struct stmmac_flow_entry *tc_find_flow(struct stmmac_priv *priv,
563425eabddSJose Abreu struct flow_cls_offload *cls,
564425eabddSJose Abreu bool get_free)
565425eabddSJose Abreu {
566425eabddSJose Abreu int i;
567425eabddSJose Abreu
568425eabddSJose Abreu for (i = 0; i < priv->flow_entries_max; i++) {
569425eabddSJose Abreu struct stmmac_flow_entry *entry = &priv->flow_entries[i];
570425eabddSJose Abreu
571425eabddSJose Abreu if (entry->cookie == cls->cookie)
572425eabddSJose Abreu return entry;
573425eabddSJose Abreu if (get_free && (entry->in_use == false))
574425eabddSJose Abreu return entry;
575425eabddSJose Abreu }
576425eabddSJose Abreu
577425eabddSJose Abreu return NULL;
578425eabddSJose Abreu }
579425eabddSJose Abreu
580bad28d88SBen Dooks (Codethink) static struct {
581425eabddSJose Abreu int (*fn)(struct stmmac_priv *priv, struct flow_cls_offload *cls,
582425eabddSJose Abreu struct stmmac_flow_entry *entry);
583425eabddSJose Abreu } tc_flow_parsers[] = {
584425eabddSJose Abreu { .fn = tc_add_basic_flow },
585425eabddSJose Abreu { .fn = tc_add_ip4_flow },
586425eabddSJose Abreu { .fn = tc_add_ports_flow },
587425eabddSJose Abreu };
588425eabddSJose Abreu
tc_add_flow(struct stmmac_priv * priv,struct flow_cls_offload * cls)589425eabddSJose Abreu static int tc_add_flow(struct stmmac_priv *priv,
590425eabddSJose Abreu struct flow_cls_offload *cls)
591425eabddSJose Abreu {
592425eabddSJose Abreu struct stmmac_flow_entry *entry = tc_find_flow(priv, cls, false);
593425eabddSJose Abreu struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
594425eabddSJose Abreu int i, ret;
595425eabddSJose Abreu
596425eabddSJose Abreu if (!entry) {
597425eabddSJose Abreu entry = tc_find_flow(priv, cls, true);
598425eabddSJose Abreu if (!entry)
599425eabddSJose Abreu return -ENOENT;
600425eabddSJose Abreu }
601425eabddSJose Abreu
602319a1d19SJiri Pirko ret = tc_parse_flow_actions(priv, &rule->action, entry,
603319a1d19SJiri Pirko cls->common.extack);
604425eabddSJose Abreu if (ret)
605425eabddSJose Abreu return ret;
606425eabddSJose Abreu
607425eabddSJose Abreu for (i = 0; i < ARRAY_SIZE(tc_flow_parsers); i++) {
608425eabddSJose Abreu ret = tc_flow_parsers[i].fn(priv, cls, entry);
609c44924c5SColin Ian King if (!ret)
610425eabddSJose Abreu entry->in_use = true;
611425eabddSJose Abreu }
612425eabddSJose Abreu
613425eabddSJose Abreu if (!entry->in_use)
614425eabddSJose Abreu return -EINVAL;
615425eabddSJose Abreu
616425eabddSJose Abreu entry->cookie = cls->cookie;
617425eabddSJose Abreu return 0;
618425eabddSJose Abreu }
619425eabddSJose Abreu
tc_del_flow(struct stmmac_priv * priv,struct flow_cls_offload * cls)620425eabddSJose Abreu static int tc_del_flow(struct stmmac_priv *priv,
621425eabddSJose Abreu struct flow_cls_offload *cls)
622425eabddSJose Abreu {
623425eabddSJose Abreu struct stmmac_flow_entry *entry = tc_find_flow(priv, cls, false);
624425eabddSJose Abreu int ret;
625425eabddSJose Abreu
626425eabddSJose Abreu if (!entry || !entry->in_use)
627425eabddSJose Abreu return -ENOENT;
628425eabddSJose Abreu
629425eabddSJose Abreu if (entry->is_l4) {
630425eabddSJose Abreu ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, false,
631425eabddSJose Abreu false, false, false, 0);
632425eabddSJose Abreu } else {
633425eabddSJose Abreu ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, false,
634425eabddSJose Abreu false, false, false, 0);
635425eabddSJose Abreu }
636425eabddSJose Abreu
637425eabddSJose Abreu entry->in_use = false;
638425eabddSJose Abreu entry->cookie = 0;
639425eabddSJose Abreu entry->is_l4 = false;
640425eabddSJose Abreu return ret;
641425eabddSJose Abreu }
642425eabddSJose Abreu
tc_find_rfs(struct stmmac_priv * priv,struct flow_cls_offload * cls,bool get_free)643aeb7c75cSOng Boon Leong static struct stmmac_rfs_entry *tc_find_rfs(struct stmmac_priv *priv,
644aeb7c75cSOng Boon Leong struct flow_cls_offload *cls,
645aeb7c75cSOng Boon Leong bool get_free)
646aeb7c75cSOng Boon Leong {
647aeb7c75cSOng Boon Leong int i;
648aeb7c75cSOng Boon Leong
649aeb7c75cSOng Boon Leong for (i = 0; i < priv->rfs_entries_total; i++) {
650aeb7c75cSOng Boon Leong struct stmmac_rfs_entry *entry = &priv->rfs_entries[i];
651aeb7c75cSOng Boon Leong
652aeb7c75cSOng Boon Leong if (entry->cookie == cls->cookie)
653aeb7c75cSOng Boon Leong return entry;
654aeb7c75cSOng Boon Leong if (get_free && entry->in_use == false)
655aeb7c75cSOng Boon Leong return entry;
656aeb7c75cSOng Boon Leong }
657aeb7c75cSOng Boon Leong
658aeb7c75cSOng Boon Leong return NULL;
659aeb7c75cSOng Boon Leong }
660aeb7c75cSOng Boon Leong
6610e039f5cSOng Boon Leong #define VLAN_PRIO_FULL_MASK (0x07)
6620e039f5cSOng Boon Leong
tc_add_vlan_flow(struct stmmac_priv * priv,struct flow_cls_offload * cls)6630e039f5cSOng Boon Leong static int tc_add_vlan_flow(struct stmmac_priv *priv,
6640e039f5cSOng Boon Leong struct flow_cls_offload *cls)
6650e039f5cSOng Boon Leong {
666aeb7c75cSOng Boon Leong struct stmmac_rfs_entry *entry = tc_find_rfs(priv, cls, false);
6670e039f5cSOng Boon Leong struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
6680e039f5cSOng Boon Leong struct flow_dissector *dissector = rule->match.dissector;
6690e039f5cSOng Boon Leong int tc = tc_classid_to_hwtc(priv->dev, cls->classid);
6700e039f5cSOng Boon Leong struct flow_match_vlan match;
6710e039f5cSOng Boon Leong
672aeb7c75cSOng Boon Leong if (!entry) {
673aeb7c75cSOng Boon Leong entry = tc_find_rfs(priv, cls, true);
674aeb7c75cSOng Boon Leong if (!entry)
675aeb7c75cSOng Boon Leong return -ENOENT;
676aeb7c75cSOng Boon Leong }
677aeb7c75cSOng Boon Leong
678aeb7c75cSOng Boon Leong if (priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN] >=
679aeb7c75cSOng Boon Leong priv->rfs_entries_max[STMMAC_RFS_T_VLAN])
680aeb7c75cSOng Boon Leong return -ENOENT;
681aeb7c75cSOng Boon Leong
6820e039f5cSOng Boon Leong /* Nothing to do here */
6830e039f5cSOng Boon Leong if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_VLAN))
6840e039f5cSOng Boon Leong return -EINVAL;
6850e039f5cSOng Boon Leong
6860e039f5cSOng Boon Leong if (tc < 0) {
6870e039f5cSOng Boon Leong netdev_err(priv->dev, "Invalid traffic class\n");
6880e039f5cSOng Boon Leong return -EINVAL;
6890e039f5cSOng Boon Leong }
6900e039f5cSOng Boon Leong
6910e039f5cSOng Boon Leong flow_rule_match_vlan(rule, &match);
6920e039f5cSOng Boon Leong
6930e039f5cSOng Boon Leong if (match.mask->vlan_priority) {
6940e039f5cSOng Boon Leong u32 prio;
6950e039f5cSOng Boon Leong
6960e039f5cSOng Boon Leong if (match.mask->vlan_priority != VLAN_PRIO_FULL_MASK) {
6970e039f5cSOng Boon Leong netdev_err(priv->dev, "Only full mask is supported for VLAN priority");
6980e039f5cSOng Boon Leong return -EINVAL;
6990e039f5cSOng Boon Leong }
7000e039f5cSOng Boon Leong
7010e039f5cSOng Boon Leong prio = BIT(match.key->vlan_priority);
7020e039f5cSOng Boon Leong stmmac_rx_queue_prio(priv, priv->hw, prio, tc);
703aeb7c75cSOng Boon Leong
704aeb7c75cSOng Boon Leong entry->in_use = true;
705aeb7c75cSOng Boon Leong entry->cookie = cls->cookie;
706aeb7c75cSOng Boon Leong entry->tc = tc;
707aeb7c75cSOng Boon Leong entry->type = STMMAC_RFS_T_VLAN;
708aeb7c75cSOng Boon Leong priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN]++;
7090e039f5cSOng Boon Leong }
7100e039f5cSOng Boon Leong
7110e039f5cSOng Boon Leong return 0;
7120e039f5cSOng Boon Leong }
7130e039f5cSOng Boon Leong
tc_del_vlan_flow(struct stmmac_priv * priv,struct flow_cls_offload * cls)7140e039f5cSOng Boon Leong static int tc_del_vlan_flow(struct stmmac_priv *priv,
7150e039f5cSOng Boon Leong struct flow_cls_offload *cls)
7160e039f5cSOng Boon Leong {
717aeb7c75cSOng Boon Leong struct stmmac_rfs_entry *entry = tc_find_rfs(priv, cls, false);
7180e039f5cSOng Boon Leong
719aeb7c75cSOng Boon Leong if (!entry || !entry->in_use || entry->type != STMMAC_RFS_T_VLAN)
720aeb7c75cSOng Boon Leong return -ENOENT;
7210e039f5cSOng Boon Leong
722aeb7c75cSOng Boon Leong stmmac_rx_queue_prio(priv, priv->hw, 0, entry->tc);
7230e039f5cSOng Boon Leong
724aeb7c75cSOng Boon Leong entry->in_use = false;
725aeb7c75cSOng Boon Leong entry->cookie = 0;
726aeb7c75cSOng Boon Leong entry->tc = 0;
727aeb7c75cSOng Boon Leong entry->type = 0;
728aeb7c75cSOng Boon Leong
729aeb7c75cSOng Boon Leong priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN]--;
7300e039f5cSOng Boon Leong
7310e039f5cSOng Boon Leong return 0;
7320e039f5cSOng Boon Leong }
7330e039f5cSOng Boon Leong
tc_add_ethtype_flow(struct stmmac_priv * priv,struct flow_cls_offload * cls)734e48cb313SOng Boon Leong static int tc_add_ethtype_flow(struct stmmac_priv *priv,
735e48cb313SOng Boon Leong struct flow_cls_offload *cls)
736e48cb313SOng Boon Leong {
737e48cb313SOng Boon Leong struct stmmac_rfs_entry *entry = tc_find_rfs(priv, cls, false);
738e48cb313SOng Boon Leong struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
739e48cb313SOng Boon Leong struct flow_dissector *dissector = rule->match.dissector;
740e48cb313SOng Boon Leong int tc = tc_classid_to_hwtc(priv->dev, cls->classid);
741e48cb313SOng Boon Leong struct flow_match_basic match;
742e48cb313SOng Boon Leong
743e48cb313SOng Boon Leong if (!entry) {
744e48cb313SOng Boon Leong entry = tc_find_rfs(priv, cls, true);
745e48cb313SOng Boon Leong if (!entry)
746e48cb313SOng Boon Leong return -ENOENT;
747e48cb313SOng Boon Leong }
748e48cb313SOng Boon Leong
749e48cb313SOng Boon Leong /* Nothing to do here */
750e48cb313SOng Boon Leong if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_BASIC))
751e48cb313SOng Boon Leong return -EINVAL;
752e48cb313SOng Boon Leong
753e48cb313SOng Boon Leong if (tc < 0) {
754e48cb313SOng Boon Leong netdev_err(priv->dev, "Invalid traffic class\n");
755e48cb313SOng Boon Leong return -EINVAL;
756e48cb313SOng Boon Leong }
757e48cb313SOng Boon Leong
758e48cb313SOng Boon Leong flow_rule_match_basic(rule, &match);
759e48cb313SOng Boon Leong
760e48cb313SOng Boon Leong if (match.mask->n_proto) {
761e48cb313SOng Boon Leong u16 etype = ntohs(match.key->n_proto);
762e48cb313SOng Boon Leong
763e48cb313SOng Boon Leong if (match.mask->n_proto != ETHER_TYPE_FULL_MASK) {
764e48cb313SOng Boon Leong netdev_err(priv->dev, "Only full mask is supported for EthType filter");
765e48cb313SOng Boon Leong return -EINVAL;
766e48cb313SOng Boon Leong }
767e48cb313SOng Boon Leong switch (etype) {
768e48cb313SOng Boon Leong case ETH_P_LLDP:
769e48cb313SOng Boon Leong if (priv->rfs_entries_cnt[STMMAC_RFS_T_LLDP] >=
770e48cb313SOng Boon Leong priv->rfs_entries_max[STMMAC_RFS_T_LLDP])
771e48cb313SOng Boon Leong return -ENOENT;
772e48cb313SOng Boon Leong
773e48cb313SOng Boon Leong entry->type = STMMAC_RFS_T_LLDP;
774e48cb313SOng Boon Leong priv->rfs_entries_cnt[STMMAC_RFS_T_LLDP]++;
775e48cb313SOng Boon Leong
776e48cb313SOng Boon Leong stmmac_rx_queue_routing(priv, priv->hw,
777e48cb313SOng Boon Leong PACKET_DCBCPQ, tc);
778e48cb313SOng Boon Leong break;
779e48cb313SOng Boon Leong case ETH_P_1588:
780e48cb313SOng Boon Leong if (priv->rfs_entries_cnt[STMMAC_RFS_T_1588] >=
781e48cb313SOng Boon Leong priv->rfs_entries_max[STMMAC_RFS_T_1588])
782e48cb313SOng Boon Leong return -ENOENT;
783e48cb313SOng Boon Leong
784e48cb313SOng Boon Leong entry->type = STMMAC_RFS_T_1588;
785e48cb313SOng Boon Leong priv->rfs_entries_cnt[STMMAC_RFS_T_1588]++;
786e48cb313SOng Boon Leong
787e48cb313SOng Boon Leong stmmac_rx_queue_routing(priv, priv->hw,
788e48cb313SOng Boon Leong PACKET_PTPQ, tc);
789e48cb313SOng Boon Leong break;
790e48cb313SOng Boon Leong default:
791e48cb313SOng Boon Leong netdev_err(priv->dev, "EthType(0x%x) is not supported", etype);
792e48cb313SOng Boon Leong return -EINVAL;
793e48cb313SOng Boon Leong }
794e48cb313SOng Boon Leong
795e48cb313SOng Boon Leong entry->in_use = true;
796e48cb313SOng Boon Leong entry->cookie = cls->cookie;
797e48cb313SOng Boon Leong entry->tc = tc;
798e48cb313SOng Boon Leong entry->etype = etype;
799e48cb313SOng Boon Leong
800e48cb313SOng Boon Leong return 0;
801e48cb313SOng Boon Leong }
802e48cb313SOng Boon Leong
803e48cb313SOng Boon Leong return -EINVAL;
804e48cb313SOng Boon Leong }
805e48cb313SOng Boon Leong
tc_del_ethtype_flow(struct stmmac_priv * priv,struct flow_cls_offload * cls)806e48cb313SOng Boon Leong static int tc_del_ethtype_flow(struct stmmac_priv *priv,
807e48cb313SOng Boon Leong struct flow_cls_offload *cls)
808e48cb313SOng Boon Leong {
809e48cb313SOng Boon Leong struct stmmac_rfs_entry *entry = tc_find_rfs(priv, cls, false);
810e48cb313SOng Boon Leong
811e48cb313SOng Boon Leong if (!entry || !entry->in_use ||
812e48cb313SOng Boon Leong entry->type < STMMAC_RFS_T_LLDP ||
813e48cb313SOng Boon Leong entry->type > STMMAC_RFS_T_1588)
814e48cb313SOng Boon Leong return -ENOENT;
815e48cb313SOng Boon Leong
816e48cb313SOng Boon Leong switch (entry->etype) {
817e48cb313SOng Boon Leong case ETH_P_LLDP:
818e48cb313SOng Boon Leong stmmac_rx_queue_routing(priv, priv->hw,
819e48cb313SOng Boon Leong PACKET_DCBCPQ, 0);
820e48cb313SOng Boon Leong priv->rfs_entries_cnt[STMMAC_RFS_T_LLDP]--;
821e48cb313SOng Boon Leong break;
822e48cb313SOng Boon Leong case ETH_P_1588:
823e48cb313SOng Boon Leong stmmac_rx_queue_routing(priv, priv->hw,
824e48cb313SOng Boon Leong PACKET_PTPQ, 0);
825e48cb313SOng Boon Leong priv->rfs_entries_cnt[STMMAC_RFS_T_1588]--;
826e48cb313SOng Boon Leong break;
827e48cb313SOng Boon Leong default:
828e48cb313SOng Boon Leong netdev_err(priv->dev, "EthType(0x%x) is not supported",
829e48cb313SOng Boon Leong entry->etype);
830e48cb313SOng Boon Leong return -EINVAL;
831e48cb313SOng Boon Leong }
832e48cb313SOng Boon Leong
833e48cb313SOng Boon Leong entry->in_use = false;
834e48cb313SOng Boon Leong entry->cookie = 0;
835e48cb313SOng Boon Leong entry->tc = 0;
836e48cb313SOng Boon Leong entry->etype = 0;
837e48cb313SOng Boon Leong entry->type = 0;
838e48cb313SOng Boon Leong
839e48cb313SOng Boon Leong return 0;
840e48cb313SOng Boon Leong }
841e48cb313SOng Boon Leong
tc_add_flow_cls(struct stmmac_priv * priv,struct flow_cls_offload * cls)842bd0f670eSOng Boon Leong static int tc_add_flow_cls(struct stmmac_priv *priv,
843bd0f670eSOng Boon Leong struct flow_cls_offload *cls)
844bd0f670eSOng Boon Leong {
845bd0f670eSOng Boon Leong int ret;
846bd0f670eSOng Boon Leong
847bd0f670eSOng Boon Leong ret = tc_add_flow(priv, cls);
8480e039f5cSOng Boon Leong if (!ret)
849bd0f670eSOng Boon Leong return ret;
8500e039f5cSOng Boon Leong
851e48cb313SOng Boon Leong ret = tc_add_ethtype_flow(priv, cls);
852e48cb313SOng Boon Leong if (!ret)
853e48cb313SOng Boon Leong return ret;
854e48cb313SOng Boon Leong
8550e039f5cSOng Boon Leong return tc_add_vlan_flow(priv, cls);
856bd0f670eSOng Boon Leong }
857bd0f670eSOng Boon Leong
tc_del_flow_cls(struct stmmac_priv * priv,struct flow_cls_offload * cls)858bd0f670eSOng Boon Leong static int tc_del_flow_cls(struct stmmac_priv *priv,
859bd0f670eSOng Boon Leong struct flow_cls_offload *cls)
860bd0f670eSOng Boon Leong {
861bd0f670eSOng Boon Leong int ret;
862bd0f670eSOng Boon Leong
863bd0f670eSOng Boon Leong ret = tc_del_flow(priv, cls);
8640e039f5cSOng Boon Leong if (!ret)
865bd0f670eSOng Boon Leong return ret;
8660e039f5cSOng Boon Leong
867e48cb313SOng Boon Leong ret = tc_del_ethtype_flow(priv, cls);
868e48cb313SOng Boon Leong if (!ret)
869e48cb313SOng Boon Leong return ret;
870e48cb313SOng Boon Leong
8710e039f5cSOng Boon Leong return tc_del_vlan_flow(priv, cls);
872bd0f670eSOng Boon Leong }
873bd0f670eSOng Boon Leong
tc_setup_cls(struct stmmac_priv * priv,struct flow_cls_offload * cls)874425eabddSJose Abreu static int tc_setup_cls(struct stmmac_priv *priv,
875425eabddSJose Abreu struct flow_cls_offload *cls)
876425eabddSJose Abreu {
877425eabddSJose Abreu int ret = 0;
878425eabddSJose Abreu
8797bd754c4SJose Abreu /* When RSS is enabled, the filtering will be bypassed */
8807bd754c4SJose Abreu if (priv->rss.enable)
8817bd754c4SJose Abreu return -EBUSY;
8827bd754c4SJose Abreu
883425eabddSJose Abreu switch (cls->command) {
884425eabddSJose Abreu case FLOW_CLS_REPLACE:
885bd0f670eSOng Boon Leong ret = tc_add_flow_cls(priv, cls);
886425eabddSJose Abreu break;
887425eabddSJose Abreu case FLOW_CLS_DESTROY:
888bd0f670eSOng Boon Leong ret = tc_del_flow_cls(priv, cls);
889425eabddSJose Abreu break;
890425eabddSJose Abreu default:
891425eabddSJose Abreu return -EOPNOTSUPP;
892425eabddSJose Abreu }
893425eabddSJose Abreu
894425eabddSJose Abreu return ret;
895425eabddSJose Abreu }
896425eabddSJose Abreu
stmmac_calc_tas_basetime(ktime_t old_base_time,ktime_t current_time,u64 cycle_time)89781c52c42SXiaoliang Yang struct timespec64 stmmac_calc_tas_basetime(ktime_t old_base_time,
89881c52c42SXiaoliang Yang ktime_t current_time,
89981c52c42SXiaoliang Yang u64 cycle_time)
90081c52c42SXiaoliang Yang {
90181c52c42SXiaoliang Yang struct timespec64 time;
90281c52c42SXiaoliang Yang
90381c52c42SXiaoliang Yang if (ktime_after(old_base_time, current_time)) {
90481c52c42SXiaoliang Yang time = ktime_to_timespec64(old_base_time);
90581c52c42SXiaoliang Yang } else {
90681c52c42SXiaoliang Yang s64 n;
90781c52c42SXiaoliang Yang ktime_t base_time;
90881c52c42SXiaoliang Yang
90981c52c42SXiaoliang Yang n = div64_s64(ktime_sub_ns(current_time, old_base_time),
91081c52c42SXiaoliang Yang cycle_time);
91181c52c42SXiaoliang Yang base_time = ktime_add_ns(old_base_time,
91281c52c42SXiaoliang Yang (n + 1) * cycle_time);
91381c52c42SXiaoliang Yang
91481c52c42SXiaoliang Yang time = ktime_to_timespec64(base_time);
91581c52c42SXiaoliang Yang }
91681c52c42SXiaoliang Yang
91781c52c42SXiaoliang Yang return time;
91881c52c42SXiaoliang Yang }
91981c52c42SXiaoliang Yang
tc_setup_taprio(struct stmmac_priv * priv,struct tc_taprio_qopt_offload * qopt)920b60189e0SJose Abreu static int tc_setup_taprio(struct stmmac_priv *priv,
921b60189e0SJose Abreu struct tc_taprio_qopt_offload *qopt)
922b60189e0SJose Abreu {
923b60189e0SJose Abreu u32 size, wid = priv->dma_cap.estwid, dep = priv->dma_cap.estdep;
924b60189e0SJose Abreu struct plat_stmmacenet_data *plat = priv->plat;
925e9e37200SXiaoliang Yang struct timespec64 time, current_time, qopt_time;
926fe28c53eSYannick Vignon ktime_t current_time_ns;
9271ac14241SJose Abreu bool fpe = false;
928b60189e0SJose Abreu int i, ret = 0;
929a1ec57c0SJose Abreu u64 ctr;
930b60189e0SJose Abreu
9316d534ee0SMichael Sit Wei Hong if (qopt->base_time < 0)
9326d534ee0SMichael Sit Wei Hong return -ERANGE;
9336d534ee0SMichael Sit Wei Hong
934b60189e0SJose Abreu if (!priv->dma_cap.estsel)
935b60189e0SJose Abreu return -EOPNOTSUPP;
936b60189e0SJose Abreu
937b60189e0SJose Abreu switch (wid) {
938b60189e0SJose Abreu case 0x1:
939b60189e0SJose Abreu wid = 16;
940b60189e0SJose Abreu break;
941b60189e0SJose Abreu case 0x2:
942b60189e0SJose Abreu wid = 20;
943b60189e0SJose Abreu break;
944b60189e0SJose Abreu case 0x3:
945b60189e0SJose Abreu wid = 24;
946b60189e0SJose Abreu break;
947b60189e0SJose Abreu default:
948b60189e0SJose Abreu return -EOPNOTSUPP;
949b60189e0SJose Abreu }
950b60189e0SJose Abreu
951b60189e0SJose Abreu switch (dep) {
952b60189e0SJose Abreu case 0x1:
953b60189e0SJose Abreu dep = 64;
954b60189e0SJose Abreu break;
955b60189e0SJose Abreu case 0x2:
956b60189e0SJose Abreu dep = 128;
957b60189e0SJose Abreu break;
958b60189e0SJose Abreu case 0x3:
959b60189e0SJose Abreu dep = 256;
960b60189e0SJose Abreu break;
961b60189e0SJose Abreu case 0x4:
962b60189e0SJose Abreu dep = 512;
963b60189e0SJose Abreu break;
964b60189e0SJose Abreu case 0x5:
965b60189e0SJose Abreu dep = 1024;
966b60189e0SJose Abreu break;
967b60189e0SJose Abreu default:
968b60189e0SJose Abreu return -EOPNOTSUPP;
969b60189e0SJose Abreu }
970b60189e0SJose Abreu
9712d800bc5SVladimir Oltean if (qopt->cmd == TAPRIO_CMD_DESTROY)
972b60189e0SJose Abreu goto disable;
9732d800bc5SVladimir Oltean else if (qopt->cmd != TAPRIO_CMD_REPLACE)
9742d800bc5SVladimir Oltean return -EOPNOTSUPP;
9752d800bc5SVladimir Oltean
976b60189e0SJose Abreu if (qopt->num_entries >= dep)
977b60189e0SJose Abreu return -EINVAL;
978b60189e0SJose Abreu if (!qopt->cycle_time)
979b60189e0SJose Abreu return -ERANGE;
980b60189e0SJose Abreu
981b60189e0SJose Abreu if (!plat->est) {
982b60189e0SJose Abreu plat->est = devm_kzalloc(priv->device, sizeof(*plat->est),
983b60189e0SJose Abreu GFP_KERNEL);
984b60189e0SJose Abreu if (!plat->est)
985b60189e0SJose Abreu return -ENOMEM;
986b2aae654SXiaoliang Yang
987*b538fefeSXiaolei Wang mutex_init(&priv->est_lock);
988b60189e0SJose Abreu } else {
989*b538fefeSXiaolei Wang mutex_lock(&priv->est_lock);
990b60189e0SJose Abreu memset(plat->est, 0, sizeof(*plat->est));
991*b538fefeSXiaolei Wang mutex_unlock(&priv->est_lock);
992b60189e0SJose Abreu }
993b60189e0SJose Abreu
994b60189e0SJose Abreu size = qopt->num_entries;
995b60189e0SJose Abreu
996*b538fefeSXiaolei Wang mutex_lock(&priv->est_lock);
997b60189e0SJose Abreu priv->plat->est->gcl_size = size;
9982d800bc5SVladimir Oltean priv->plat->est->enable = qopt->cmd == TAPRIO_CMD_REPLACE;
999*b538fefeSXiaolei Wang mutex_unlock(&priv->est_lock);
1000b60189e0SJose Abreu
1001b60189e0SJose Abreu for (i = 0; i < size; i++) {
1002b60189e0SJose Abreu s64 delta_ns = qopt->entries[i].interval;
1003b60189e0SJose Abreu u32 gates = qopt->entries[i].gate_mask;
1004b60189e0SJose Abreu
1005b60189e0SJose Abreu if (delta_ns > GENMASK(wid, 0))
1006b60189e0SJose Abreu return -ERANGE;
1007b60189e0SJose Abreu if (gates > GENMASK(31 - wid, 0))
1008b60189e0SJose Abreu return -ERANGE;
10091ac14241SJose Abreu
10101ac14241SJose Abreu switch (qopt->entries[i].command) {
10111ac14241SJose Abreu case TC_TAPRIO_CMD_SET_GATES:
10121ac14241SJose Abreu if (fpe)
10131ac14241SJose Abreu return -EINVAL;
10141ac14241SJose Abreu break;
10151ac14241SJose Abreu case TC_TAPRIO_CMD_SET_AND_HOLD:
10161ac14241SJose Abreu gates |= BIT(0);
10171ac14241SJose Abreu fpe = true;
10181ac14241SJose Abreu break;
10191ac14241SJose Abreu case TC_TAPRIO_CMD_SET_AND_RELEASE:
10201ac14241SJose Abreu gates &= ~BIT(0);
10211ac14241SJose Abreu fpe = true;
10221ac14241SJose Abreu break;
10231ac14241SJose Abreu default:
1024b60189e0SJose Abreu return -EOPNOTSUPP;
10251ac14241SJose Abreu }
1026b60189e0SJose Abreu
1027b60189e0SJose Abreu priv->plat->est->gcl[i] = delta_ns | (gates << wid);
1028b60189e0SJose Abreu }
1029b60189e0SJose Abreu
1030*b538fefeSXiaolei Wang mutex_lock(&priv->est_lock);
1031b60189e0SJose Abreu /* Adjust for real system time */
1032fe28c53eSYannick Vignon priv->ptp_clock_ops.gettime64(&priv->ptp_clock_ops, ¤t_time);
1033fe28c53eSYannick Vignon current_time_ns = timespec64_to_ktime(current_time);
103481c52c42SXiaoliang Yang time = stmmac_calc_tas_basetime(qopt->base_time, current_time_ns,
1035fe28c53eSYannick Vignon qopt->cycle_time);
1036fe28c53eSYannick Vignon
1037b60189e0SJose Abreu priv->plat->est->btr[0] = (u32)time.tv_nsec;
1038b60189e0SJose Abreu priv->plat->est->btr[1] = (u32)time.tv_sec;
1039b60189e0SJose Abreu
1040e9e37200SXiaoliang Yang qopt_time = ktime_to_timespec64(qopt->base_time);
1041e9e37200SXiaoliang Yang priv->plat->est->btr_reserve[0] = (u32)qopt_time.tv_nsec;
1042e9e37200SXiaoliang Yang priv->plat->est->btr_reserve[1] = (u32)qopt_time.tv_sec;
1043e9e37200SXiaoliang Yang
1044a1ec57c0SJose Abreu ctr = qopt->cycle_time;
1045a1ec57c0SJose Abreu priv->plat->est->ctr[0] = do_div(ctr, NSEC_PER_SEC);
1046a1ec57c0SJose Abreu priv->plat->est->ctr[1] = (u32)ctr;
1047b60189e0SJose Abreu
1048b2aae654SXiaoliang Yang if (fpe && !priv->dma_cap.fpesel) {
1049*b538fefeSXiaolei Wang mutex_unlock(&priv->est_lock);
10501ac14241SJose Abreu return -EOPNOTSUPP;
1051b2aae654SXiaoliang Yang }
10521ac14241SJose Abreu
10535a558611SOng Boon Leong /* Actual FPE register configuration will be done after FPE handshake
10545a558611SOng Boon Leong * is success.
10555a558611SOng Boon Leong */
10565a558611SOng Boon Leong priv->plat->fpe_cfg->enable = fpe;
10571ac14241SJose Abreu
1058b60189e0SJose Abreu ret = stmmac_est_configure(priv, priv->ioaddr, priv->plat->est,
1059b60189e0SJose Abreu priv->plat->clk_ptp_rate);
1060*b538fefeSXiaolei Wang mutex_unlock(&priv->est_lock);
1061b60189e0SJose Abreu if (ret) {
1062b60189e0SJose Abreu netdev_err(priv->dev, "failed to configure EST\n");
1063b60189e0SJose Abreu goto disable;
1064b60189e0SJose Abreu }
1065b60189e0SJose Abreu
1066b60189e0SJose Abreu netdev_info(priv->dev, "configured EST\n");
10675a558611SOng Boon Leong
10685a558611SOng Boon Leong if (fpe) {
10695a558611SOng Boon Leong stmmac_fpe_handshake(priv, true);
10705a558611SOng Boon Leong netdev_info(priv->dev, "start FPE handshake\n");
10715a558611SOng Boon Leong }
10725a558611SOng Boon Leong
1073b60189e0SJose Abreu return 0;
1074b60189e0SJose Abreu
1075b60189e0SJose Abreu disable:
107682a44ae1SWong Vee Khee if (priv->plat->est) {
1077*b538fefeSXiaolei Wang mutex_lock(&priv->est_lock);
1078b60189e0SJose Abreu priv->plat->est->enable = false;
1079b60189e0SJose Abreu stmmac_est_configure(priv, priv->ioaddr, priv->plat->est,
1080b60189e0SJose Abreu priv->plat->clk_ptp_rate);
1081*b538fefeSXiaolei Wang mutex_unlock(&priv->est_lock);
108282a44ae1SWong Vee Khee }
10835a558611SOng Boon Leong
10845a558611SOng Boon Leong priv->plat->fpe_cfg->enable = false;
10855a558611SOng Boon Leong stmmac_fpe_configure(priv, priv->ioaddr,
1086e1fbdef9SJianheng Zhang priv->plat->fpe_cfg,
10875a558611SOng Boon Leong priv->plat->tx_queues_to_use,
10885a558611SOng Boon Leong priv->plat->rx_queues_to_use,
10895a558611SOng Boon Leong false);
10905a558611SOng Boon Leong netdev_info(priv->dev, "disabled FPE\n");
10915a558611SOng Boon Leong
10925a558611SOng Boon Leong stmmac_fpe_handshake(priv, false);
10935a558611SOng Boon Leong netdev_info(priv->dev, "stop FPE handshake\n");
10945a558611SOng Boon Leong
1095b60189e0SJose Abreu return ret;
1096b60189e0SJose Abreu }
1097b60189e0SJose Abreu
tc_setup_etf(struct stmmac_priv * priv,struct tc_etf_qopt_offload * qopt)1098430b383cSJose Abreu static int tc_setup_etf(struct stmmac_priv *priv,
1099430b383cSJose Abreu struct tc_etf_qopt_offload *qopt)
1100430b383cSJose Abreu {
1101430b383cSJose Abreu if (!priv->dma_cap.tbssel)
1102430b383cSJose Abreu return -EOPNOTSUPP;
1103430b383cSJose Abreu if (qopt->queue >= priv->plat->tx_queues_to_use)
1104430b383cSJose Abreu return -EINVAL;
11058531c808SChristian Marangi if (!(priv->dma_conf.tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL))
1106430b383cSJose Abreu return -EINVAL;
1107430b383cSJose Abreu
1108430b383cSJose Abreu if (qopt->enable)
11098531c808SChristian Marangi priv->dma_conf.tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN;
1110430b383cSJose Abreu else
11118531c808SChristian Marangi priv->dma_conf.tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN;
1112430b383cSJose Abreu
1113430b383cSJose Abreu netdev_info(priv->dev, "%s ETF for Queue %d\n",
1114430b383cSJose Abreu qopt->enable ? "enabled" : "disabled", qopt->queue);
1115430b383cSJose Abreu return 0;
1116430b383cSJose Abreu }
1117430b383cSJose Abreu
tc_query_caps(struct stmmac_priv * priv,struct tc_query_caps_base * base)1118522d15eaSVladimir Oltean static int tc_query_caps(struct stmmac_priv *priv,
1119522d15eaSVladimir Oltean struct tc_query_caps_base *base)
1120522d15eaSVladimir Oltean {
1121522d15eaSVladimir Oltean switch (base->type) {
1122522d15eaSVladimir Oltean case TC_SETUP_QDISC_TAPRIO: {
1123522d15eaSVladimir Oltean struct tc_taprio_caps *caps = base->caps;
1124522d15eaSVladimir Oltean
1125522d15eaSVladimir Oltean if (!priv->dma_cap.estsel)
1126522d15eaSVladimir Oltean return -EOPNOTSUPP;
1127522d15eaSVladimir Oltean
1128522d15eaSVladimir Oltean caps->gate_mask_per_txq = true;
1129522d15eaSVladimir Oltean
1130522d15eaSVladimir Oltean return 0;
1131522d15eaSVladimir Oltean }
1132522d15eaSVladimir Oltean default:
1133522d15eaSVladimir Oltean return -EOPNOTSUPP;
1134522d15eaSVladimir Oltean }
1135522d15eaSVladimir Oltean }
1136522d15eaSVladimir Oltean
11374dbbe8ddSJose Abreu const struct stmmac_tc_ops dwmac510_tc_ops = {
11384dbbe8ddSJose Abreu .init = tc_init,
11394dbbe8ddSJose Abreu .setup_cls_u32 = tc_setup_cls_u32,
11401f705bc6SJose Abreu .setup_cbs = tc_setup_cbs,
1141425eabddSJose Abreu .setup_cls = tc_setup_cls,
1142b60189e0SJose Abreu .setup_taprio = tc_setup_taprio,
1143430b383cSJose Abreu .setup_etf = tc_setup_etf,
1144522d15eaSVladimir Oltean .query_caps = tc_query_caps,
11454dbbe8ddSJose Abreu };
1146