xref: /linux/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
165ec1bbeSDaniel Machon // SPDX-License-Identifier: GPL-2.0+
265ec1bbeSDaniel Machon /* Microchip Sparx5 Switch driver
365ec1bbeSDaniel Machon  *
465ec1bbeSDaniel Machon  * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
565ec1bbeSDaniel Machon  */
665ec1bbeSDaniel Machon 
7ab0e493eSDaniel Machon #include <net/pkt_cls.h>
8*9adafe2bSVladimir Oltean #include <net/pkt_sched.h>
9ab0e493eSDaniel Machon 
1065ec1bbeSDaniel Machon #include "sparx5_tc.h"
1165ec1bbeSDaniel Machon #include "sparx5_main.h"
12ab0e493eSDaniel Machon #include "sparx5_qos.h"
13ab0e493eSDaniel Machon 
14c9da1ac1SSteen Hegelund /* tc block handling */
15c9da1ac1SSteen Hegelund static LIST_HEAD(sparx5_block_cb_list);
16c9da1ac1SSteen Hegelund 
sparx5_tc_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv,bool ingress)17c9da1ac1SSteen Hegelund static int sparx5_tc_block_cb(enum tc_setup_type type,
18c9da1ac1SSteen Hegelund 			      void *type_data,
19c9da1ac1SSteen Hegelund 			      void *cb_priv, bool ingress)
20c9da1ac1SSteen Hegelund {
21c9da1ac1SSteen Hegelund 	struct net_device *ndev = cb_priv;
22c9da1ac1SSteen Hegelund 
2367456717SSteen Hegelund 	switch (type) {
2467456717SSteen Hegelund 	case TC_SETUP_CLSMATCHALL:
2567456717SSteen Hegelund 		return sparx5_tc_matchall(ndev, type_data, ingress);
2667456717SSteen Hegelund 	case TC_SETUP_CLSFLOWER:
27c9da1ac1SSteen Hegelund 		return sparx5_tc_flower(ndev, type_data, ingress);
2867456717SSteen Hegelund 	default:
29c9da1ac1SSteen Hegelund 		return -EOPNOTSUPP;
30c9da1ac1SSteen Hegelund 	}
3167456717SSteen Hegelund }
32c9da1ac1SSteen Hegelund 
sparx5_tc_block_cb_ingress(enum tc_setup_type type,void * type_data,void * cb_priv)33c9da1ac1SSteen Hegelund static int sparx5_tc_block_cb_ingress(enum tc_setup_type type,
34c9da1ac1SSteen Hegelund 				      void *type_data,
35c9da1ac1SSteen Hegelund 				      void *cb_priv)
36c9da1ac1SSteen Hegelund {
37c9da1ac1SSteen Hegelund 	return sparx5_tc_block_cb(type, type_data, cb_priv, true);
38c9da1ac1SSteen Hegelund }
39c9da1ac1SSteen Hegelund 
sparx5_tc_block_cb_egress(enum tc_setup_type type,void * type_data,void * cb_priv)40c9da1ac1SSteen Hegelund static int sparx5_tc_block_cb_egress(enum tc_setup_type type,
41c9da1ac1SSteen Hegelund 				     void *type_data,
42c9da1ac1SSteen Hegelund 				     void *cb_priv)
43c9da1ac1SSteen Hegelund {
44c9da1ac1SSteen Hegelund 	return sparx5_tc_block_cb(type, type_data, cb_priv, false);
45c9da1ac1SSteen Hegelund }
46c9da1ac1SSteen Hegelund 
sparx5_tc_setup_block(struct net_device * ndev,struct flow_block_offload * fbo)47c9da1ac1SSteen Hegelund static int sparx5_tc_setup_block(struct net_device *ndev,
48c9da1ac1SSteen Hegelund 				 struct flow_block_offload *fbo)
49c9da1ac1SSteen Hegelund {
50c9da1ac1SSteen Hegelund 	flow_setup_cb_t *cb;
51c9da1ac1SSteen Hegelund 
52c9da1ac1SSteen Hegelund 	if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
53c9da1ac1SSteen Hegelund 		cb = sparx5_tc_block_cb_ingress;
54c9da1ac1SSteen Hegelund 	else if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
55c9da1ac1SSteen Hegelund 		cb = sparx5_tc_block_cb_egress;
56c9da1ac1SSteen Hegelund 	else
57c9da1ac1SSteen Hegelund 		return -EOPNOTSUPP;
58c9da1ac1SSteen Hegelund 
59c9da1ac1SSteen Hegelund 	return flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list,
60c9da1ac1SSteen Hegelund 					  cb, ndev, ndev, false);
61c9da1ac1SSteen Hegelund }
62c9da1ac1SSteen Hegelund 
sparx5_tc_get_layer_and_idx(u32 parent,u32 portno,u32 * layer,u32 * idx)63e02a5ac6SDaniel Machon static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer,
64e02a5ac6SDaniel Machon 					u32 *idx)
65e02a5ac6SDaniel Machon {
66e02a5ac6SDaniel Machon 	if (parent == TC_H_ROOT) {
67e02a5ac6SDaniel Machon 		*layer = 2;
68e02a5ac6SDaniel Machon 		*idx = portno;
69e02a5ac6SDaniel Machon 	} else {
70e02a5ac6SDaniel Machon 		u32 queue = TC_H_MIN(parent) - 1;
71e02a5ac6SDaniel Machon 		*layer = 0;
72e02a5ac6SDaniel Machon 		*idx = SPX5_HSCH_L0_GET_IDX(portno, queue);
73e02a5ac6SDaniel Machon 	}
74e02a5ac6SDaniel Machon }
75e02a5ac6SDaniel Machon 
sparx5_tc_setup_qdisc_mqprio(struct net_device * ndev,struct tc_mqprio_qopt_offload * m)76ab0e493eSDaniel Machon static int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev,
77ab0e493eSDaniel Machon 					struct tc_mqprio_qopt_offload *m)
78ab0e493eSDaniel Machon {
79ab0e493eSDaniel Machon 	m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
80ab0e493eSDaniel Machon 
81ab0e493eSDaniel Machon 	if (m->qopt.num_tc == 0)
82ab0e493eSDaniel Machon 		return sparx5_tc_mqprio_del(ndev);
83ab0e493eSDaniel Machon 	else
84ab0e493eSDaniel Machon 		return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc);
85ab0e493eSDaniel Machon }
8665ec1bbeSDaniel Machon 
sparx5_tc_setup_qdisc_tbf(struct net_device * ndev,struct tc_tbf_qopt_offload * qopt)87e02a5ac6SDaniel Machon static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev,
88e02a5ac6SDaniel Machon 				     struct tc_tbf_qopt_offload *qopt)
89e02a5ac6SDaniel Machon {
90e02a5ac6SDaniel Machon 	struct sparx5_port *port = netdev_priv(ndev);
91e02a5ac6SDaniel Machon 	u32 layer, se_idx;
92e02a5ac6SDaniel Machon 
93e02a5ac6SDaniel Machon 	sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer,
94e02a5ac6SDaniel Machon 				    &se_idx);
95e02a5ac6SDaniel Machon 
96e02a5ac6SDaniel Machon 	switch (qopt->command) {
97e02a5ac6SDaniel Machon 	case TC_TBF_REPLACE:
98e02a5ac6SDaniel Machon 		return sparx5_tc_tbf_add(port, &qopt->replace_params, layer,
99e02a5ac6SDaniel Machon 					 se_idx);
100e02a5ac6SDaniel Machon 	case TC_TBF_DESTROY:
101e02a5ac6SDaniel Machon 		return sparx5_tc_tbf_del(port, layer, se_idx);
102e02a5ac6SDaniel Machon 	case TC_TBF_STATS:
103e02a5ac6SDaniel Machon 		return -EOPNOTSUPP;
104e02a5ac6SDaniel Machon 	default:
105e02a5ac6SDaniel Machon 		return -EOPNOTSUPP;
106e02a5ac6SDaniel Machon 	}
107e02a5ac6SDaniel Machon 
108e02a5ac6SDaniel Machon 	return -EOPNOTSUPP;
109e02a5ac6SDaniel Machon }
110e02a5ac6SDaniel Machon 
sparx5_tc_setup_qdisc_ets(struct net_device * ndev,struct tc_ets_qopt_offload * qopt)11121122542SDaniel Machon static int sparx5_tc_setup_qdisc_ets(struct net_device *ndev,
11221122542SDaniel Machon 				     struct tc_ets_qopt_offload *qopt)
11321122542SDaniel Machon {
11421122542SDaniel Machon 	struct tc_ets_qopt_offload_replace_params *params =
11521122542SDaniel Machon 		&qopt->replace_params;
11621122542SDaniel Machon 	struct sparx5_port *port = netdev_priv(ndev);
11721122542SDaniel Machon 	int i;
11821122542SDaniel Machon 
11921122542SDaniel Machon 	/* Only allow ets on ports  */
12021122542SDaniel Machon 	if (qopt->parent != TC_H_ROOT)
12121122542SDaniel Machon 		return -EOPNOTSUPP;
12221122542SDaniel Machon 
12321122542SDaniel Machon 	switch (qopt->command) {
12421122542SDaniel Machon 	case TC_ETS_REPLACE:
12521122542SDaniel Machon 
12621122542SDaniel Machon 		/* We support eight priorities */
12721122542SDaniel Machon 		if (params->bands != SPX5_PRIOS)
12821122542SDaniel Machon 			return -EOPNOTSUPP;
12921122542SDaniel Machon 
13021122542SDaniel Machon 		/* Sanity checks */
13121122542SDaniel Machon 		for (i = 0; i < SPX5_PRIOS; ++i) {
13221122542SDaniel Machon 			/* Priority map is *always* reverse e.g: 7 6 5 .. 0 */
13321122542SDaniel Machon 			if (params->priomap[i] != (7 - i))
13421122542SDaniel Machon 				return -EOPNOTSUPP;
13521122542SDaniel Machon 			/* Throw an error if we receive zero weights by tc */
13621122542SDaniel Machon 			if (params->quanta[i] && params->weights[i] == 0) {
13721122542SDaniel Machon 				pr_err("Invalid ets configuration; band %d has weight zero",
13821122542SDaniel Machon 				       i);
13921122542SDaniel Machon 				return -EINVAL;
14021122542SDaniel Machon 			}
14121122542SDaniel Machon 		}
14221122542SDaniel Machon 
14319d04a94SLu Wei 		return sparx5_tc_ets_add(port, params);
14421122542SDaniel Machon 	case TC_ETS_DESTROY:
14521122542SDaniel Machon 
14619d04a94SLu Wei 		return sparx5_tc_ets_del(port);
14721122542SDaniel Machon 	case TC_ETS_GRAFT:
14821122542SDaniel Machon 		return -EOPNOTSUPP;
14921122542SDaniel Machon 
15021122542SDaniel Machon 	default:
15121122542SDaniel Machon 		return -EOPNOTSUPP;
15221122542SDaniel Machon 	}
15321122542SDaniel Machon 
15421122542SDaniel Machon 	return -EOPNOTSUPP;
15521122542SDaniel Machon }
15621122542SDaniel Machon 
sparx5_port_setup_tc(struct net_device * ndev,enum tc_setup_type type,void * type_data)15765ec1bbeSDaniel Machon int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
15865ec1bbeSDaniel Machon 			 void *type_data)
15965ec1bbeSDaniel Machon {
16065ec1bbeSDaniel Machon 	switch (type) {
161c9da1ac1SSteen Hegelund 	case TC_SETUP_BLOCK:
162c9da1ac1SSteen Hegelund 		return sparx5_tc_setup_block(ndev, type_data);
163ab0e493eSDaniel Machon 	case TC_SETUP_QDISC_MQPRIO:
164ab0e493eSDaniel Machon 		return sparx5_tc_setup_qdisc_mqprio(ndev, type_data);
165e02a5ac6SDaniel Machon 	case TC_SETUP_QDISC_TBF:
166e02a5ac6SDaniel Machon 		return sparx5_tc_setup_qdisc_tbf(ndev, type_data);
16721122542SDaniel Machon 	case TC_SETUP_QDISC_ETS:
16821122542SDaniel Machon 		return sparx5_tc_setup_qdisc_ets(ndev, type_data);
16965ec1bbeSDaniel Machon 	default:
17065ec1bbeSDaniel Machon 		return -EOPNOTSUPP;
17165ec1bbeSDaniel Machon 	}
17265ec1bbeSDaniel Machon 
17365ec1bbeSDaniel Machon 	return 0;
17465ec1bbeSDaniel Machon }
175