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

source code of linux/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c