1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
3  *
4  * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
5  */
6 
7 #include <net/pkt_cls.h>
8 
9 #include "sparx5_tc.h"
10 #include "sparx5_main.h"
11 #include "sparx5_qos.h"
12 
13 /* tc block handling */
14 static LIST_HEAD(sparx5_block_cb_list);
15 
16 static int sparx5_tc_block_cb(enum tc_setup_type type,
17 			      void *type_data,
18 			      void *cb_priv, bool ingress)
19 {
20 	struct net_device *ndev = cb_priv;
21 
22 	switch (type) {
23 	case TC_SETUP_CLSMATCHALL:
24 		return sparx5_tc_matchall(ndev, type_data, ingress);
25 	case TC_SETUP_CLSFLOWER:
26 		return sparx5_tc_flower(ndev, type_data, ingress);
27 	default:
28 		return -EOPNOTSUPP;
29 	}
30 }
31 
32 static int sparx5_tc_block_cb_ingress(enum tc_setup_type type,
33 				      void *type_data,
34 				      void *cb_priv)
35 {
36 	return sparx5_tc_block_cb(type, type_data, cb_priv, true);
37 }
38 
39 static int sparx5_tc_block_cb_egress(enum tc_setup_type type,
40 				     void *type_data,
41 				     void *cb_priv)
42 {
43 	return sparx5_tc_block_cb(type, type_data, cb_priv, false);
44 }
45 
46 static int sparx5_tc_setup_block(struct net_device *ndev,
47 				 struct flow_block_offload *fbo)
48 {
49 	flow_setup_cb_t *cb;
50 
51 	if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
52 		cb = sparx5_tc_block_cb_ingress;
53 	else if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
54 		cb = sparx5_tc_block_cb_egress;
55 	else
56 		return -EOPNOTSUPP;
57 
58 	return flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list,
59 					  cb, ndev, ndev, false);
60 }
61 
62 static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer,
63 					u32 *idx)
64 {
65 	if (parent == TC_H_ROOT) {
66 		*layer = 2;
67 		*idx = portno;
68 	} else {
69 		u32 queue = TC_H_MIN(parent) - 1;
70 		*layer = 0;
71 		*idx = SPX5_HSCH_L0_GET_IDX(portno, queue);
72 	}
73 }
74 
75 static int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev,
76 					struct tc_mqprio_qopt_offload *m)
77 {
78 	m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
79 
80 	if (m->qopt.num_tc == 0)
81 		return sparx5_tc_mqprio_del(ndev);
82 	else
83 		return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc);
84 }
85 
86 static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev,
87 				     struct tc_tbf_qopt_offload *qopt)
88 {
89 	struct sparx5_port *port = netdev_priv(ndev);
90 	u32 layer, se_idx;
91 
92 	sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer,
93 				    &se_idx);
94 
95 	switch (qopt->command) {
96 	case TC_TBF_REPLACE:
97 		return sparx5_tc_tbf_add(port, &qopt->replace_params, layer,
98 					 se_idx);
99 	case TC_TBF_DESTROY:
100 		return sparx5_tc_tbf_del(port, layer, se_idx);
101 	case TC_TBF_STATS:
102 		return -EOPNOTSUPP;
103 	default:
104 		return -EOPNOTSUPP;
105 	}
106 
107 	return -EOPNOTSUPP;
108 }
109 
110 static int sparx5_tc_setup_qdisc_ets(struct net_device *ndev,
111 				     struct tc_ets_qopt_offload *qopt)
112 {
113 	struct tc_ets_qopt_offload_replace_params *params =
114 		&qopt->replace_params;
115 	struct sparx5_port *port = netdev_priv(ndev);
116 	int i;
117 
118 	/* Only allow ets on ports  */
119 	if (qopt->parent != TC_H_ROOT)
120 		return -EOPNOTSUPP;
121 
122 	switch (qopt->command) {
123 	case TC_ETS_REPLACE:
124 
125 		/* We support eight priorities */
126 		if (params->bands != SPX5_PRIOS)
127 			return -EOPNOTSUPP;
128 
129 		/* Sanity checks */
130 		for (i = 0; i < SPX5_PRIOS; ++i) {
131 			/* Priority map is *always* reverse e.g: 7 6 5 .. 0 */
132 			if (params->priomap[i] != (7 - i))
133 				return -EOPNOTSUPP;
134 			/* Throw an error if we receive zero weights by tc */
135 			if (params->quanta[i] && params->weights[i] == 0) {
136 				pr_err("Invalid ets configuration; band %d has weight zero",
137 				       i);
138 				return -EINVAL;
139 			}
140 		}
141 
142 		return sparx5_tc_ets_add(port, params);
143 	case TC_ETS_DESTROY:
144 
145 		return sparx5_tc_ets_del(port);
146 	case TC_ETS_GRAFT:
147 		return -EOPNOTSUPP;
148 
149 	default:
150 		return -EOPNOTSUPP;
151 	}
152 
153 	return -EOPNOTSUPP;
154 }
155 
156 int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
157 			 void *type_data)
158 {
159 	switch (type) {
160 	case TC_SETUP_BLOCK:
161 		return sparx5_tc_setup_block(ndev, type_data);
162 	case TC_SETUP_QDISC_MQPRIO:
163 		return sparx5_tc_setup_qdisc_mqprio(ndev, type_data);
164 	case TC_SETUP_QDISC_TBF:
165 		return sparx5_tc_setup_qdisc_tbf(ndev, type_data);
166 	case TC_SETUP_QDISC_ETS:
167 		return sparx5_tc_setup_qdisc_ets(ndev, type_data);
168 	default:
169 		return -EOPNOTSUPP;
170 	}
171 
172 	return 0;
173 }
174