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
sparx5_tc_get_layer_and_idx(u32 parent,u32 portno,u32 * layer,u32 * idx)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
sparx5_tc_setup_qdisc_mqprio(struct net_device * ndev,struct tc_mqprio_qopt_offload * m)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
sparx5_tc_setup_qdisc_tbf(struct net_device * ndev,struct tc_tbf_qopt_offload * qopt)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
sparx5_tc_setup_qdisc_ets(struct net_device * ndev,struct tc_ets_qopt_offload * qopt)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 return sparx5_tc_ets_add(port, params);
94 case TC_ETS_DESTROY:
95
96 return sparx5_tc_ets_del(port);
97 case TC_ETS_GRAFT:
98 return -EOPNOTSUPP;
99
100 default:
101 return -EOPNOTSUPP;
102 }
103
104 return -EOPNOTSUPP;
105 }
106
sparx5_port_setup_tc(struct net_device * ndev,enum tc_setup_type type,void * type_data)107 int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
108 void *type_data)
109 {
110 switch (type) {
111 case TC_SETUP_QDISC_MQPRIO:
112 return sparx5_tc_setup_qdisc_mqprio(ndev, type_data);
113 case TC_SETUP_QDISC_TBF:
114 return sparx5_tc_setup_qdisc_tbf(ndev, type_data);
115 case TC_SETUP_QDISC_ETS:
116 return sparx5_tc_setup_qdisc_ets(ndev, type_data);
117 default:
118 return -EOPNOTSUPP;
119 }
120
121 return 0;
122 }
123