xref: /openbmc/linux/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c (revision fac59652993f075d57860769c99045b3ca18780d)
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, &current_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