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 static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer,
14 					u32 *idx)
15 {
16 	if (parent == TC_H_ROOT) {
17 		*layer = 2;
18 		*idx = portno;
19 	} else {
20 		u32 queue = TC_H_MIN(parent) - 1;
21 		*layer = 0;
22 		*idx = SPX5_HSCH_L0_GET_IDX(portno, queue);
23 	}
24 }
25 
26 static int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev,
27 					struct tc_mqprio_qopt_offload *m)
28 {
29 	m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
30 
31 	if (m->qopt.num_tc == 0)
32 		return sparx5_tc_mqprio_del(ndev);
33 	else
34 		return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc);
35 }
36 
37 static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev,
38 				     struct tc_tbf_qopt_offload *qopt)
39 {
40 	struct sparx5_port *port = netdev_priv(ndev);
41 	u32 layer, se_idx;
42 
43 	sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer,
44 				    &se_idx);
45 
46 	switch (qopt->command) {
47 	case TC_TBF_REPLACE:
48 		return sparx5_tc_tbf_add(port, &qopt->replace_params, layer,
49 					 se_idx);
50 	case TC_TBF_DESTROY:
51 		return sparx5_tc_tbf_del(port, layer, se_idx);
52 	case TC_TBF_STATS:
53 		return -EOPNOTSUPP;
54 	default:
55 		return -EOPNOTSUPP;
56 	}
57 
58 	return -EOPNOTSUPP;
59 }
60 
61 static int sparx5_tc_setup_qdisc_ets(struct net_device *ndev,
62 				     struct tc_ets_qopt_offload *qopt)
63 {
64 	struct tc_ets_qopt_offload_replace_params *params =
65 		&qopt->replace_params;
66 	struct sparx5_port *port = netdev_priv(ndev);
67 	int i;
68 
69 	/* Only allow ets on ports  */
70 	if (qopt->parent != TC_H_ROOT)
71 		return -EOPNOTSUPP;
72 
73 	switch (qopt->command) {
74 	case TC_ETS_REPLACE:
75 
76 		/* We support eight priorities */
77 		if (params->bands != SPX5_PRIOS)
78 			return -EOPNOTSUPP;
79 
80 		/* Sanity checks */
81 		for (i = 0; i < SPX5_PRIOS; ++i) {
82 			/* Priority map is *always* reverse e.g: 7 6 5 .. 0 */
83 			if (params->priomap[i] != (7 - i))
84 				return -EOPNOTSUPP;
85 			/* Throw an error if we receive zero weights by tc */
86 			if (params->quanta[i] && params->weights[i] == 0) {
87 				pr_err("Invalid ets configuration; band %d has weight zero",
88 				       i);
89 				return -EINVAL;
90 			}
91 		}
92 
93 		sparx5_tc_ets_add(port, params);
94 		break;
95 	case TC_ETS_DESTROY:
96 
97 		sparx5_tc_ets_del(port);
98 
99 		break;
100 	case TC_ETS_GRAFT:
101 		return -EOPNOTSUPP;
102 
103 	default:
104 		return -EOPNOTSUPP;
105 	}
106 
107 	return -EOPNOTSUPP;
108 }
109 
110 int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
111 			 void *type_data)
112 {
113 	switch (type) {
114 	case TC_SETUP_QDISC_MQPRIO:
115 		return sparx5_tc_setup_qdisc_mqprio(ndev, type_data);
116 	case TC_SETUP_QDISC_TBF:
117 		return sparx5_tc_setup_qdisc_tbf(ndev, type_data);
118 	case TC_SETUP_QDISC_ETS:
119 		return sparx5_tc_setup_qdisc_ets(ndev, type_data);
120 	default:
121 		return -EOPNOTSUPP;
122 	}
123 
124 	return 0;
125 }
126