1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
4 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
7 #include <net/pkt_cls.h>
8 #include <net/pkt_sched.h>
10 #include "sparx5_tc.h"
11 #include "sparx5_main.h"
12 #include "sparx5_qos.h"
14 /* tc block handling */
15 static LIST_HEAD(sparx5_block_cb_list
);
17 static int sparx5_tc_block_cb(enum tc_setup_type type
,
19 void *cb_priv
, bool ingress
)
21 struct net_device
*ndev
= cb_priv
;
24 case TC_SETUP_CLSMATCHALL
:
25 return sparx5_tc_matchall(ndev
, type_data
, ingress
);
26 case TC_SETUP_CLSFLOWER
:
27 return sparx5_tc_flower(ndev
, type_data
, ingress
);
33 static int sparx5_tc_block_cb_ingress(enum tc_setup_type type
,
37 return sparx5_tc_block_cb(type
, type_data
, cb_priv
, true);
40 static int sparx5_tc_block_cb_egress(enum tc_setup_type type
,
44 return sparx5_tc_block_cb(type
, type_data
, cb_priv
, false);
47 static int sparx5_tc_setup_block(struct net_device
*ndev
,
48 struct flow_block_offload
*fbo
)
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
;
59 return flow_block_cb_setup_simple(fbo
, &sparx5_block_cb_list
,
60 cb
, ndev
, ndev
, false);
63 static void sparx5_tc_get_layer_and_idx(struct sparx5
*sparx5
, u32 parent
,
64 u32 portno
, u32
*layer
, u32
*idx
)
66 if (parent
== TC_H_ROOT
) {
70 u32 queue
= TC_H_MIN(parent
) - 1;
72 *idx
= SPX5_HSCH_L0_GET_IDX(portno
, queue
);
76 static int sparx5_tc_setup_qdisc_mqprio(struct net_device
*ndev
,
77 struct tc_mqprio_qopt_offload
*m
)
79 m
->qopt
.hw
= TC_MQPRIO_HW_OFFLOAD_TCS
;
81 if (m
->qopt
.num_tc
== 0)
82 return sparx5_tc_mqprio_del(ndev
);
84 return sparx5_tc_mqprio_add(ndev
, m
->qopt
.num_tc
);
87 static int sparx5_tc_setup_qdisc_tbf(struct net_device
*ndev
,
88 struct tc_tbf_qopt_offload
*qopt
)
90 struct sparx5_port
*port
= netdev_priv(ndev
);
93 sparx5_tc_get_layer_and_idx(port
->sparx5
, qopt
->parent
, port
->portno
,
96 switch (qopt
->command
) {
98 return sparx5_tc_tbf_add(port
, &qopt
->replace_params
, layer
,
101 return sparx5_tc_tbf_del(port
, layer
, se_idx
);
111 static int sparx5_tc_setup_qdisc_ets(struct net_device
*ndev
,
112 struct tc_ets_qopt_offload
*qopt
)
114 struct tc_ets_qopt_offload_replace_params
*params
=
115 &qopt
->replace_params
;
116 struct sparx5_port
*port
= netdev_priv(ndev
);
119 /* Only allow ets on ports */
120 if (qopt
->parent
!= TC_H_ROOT
)
123 switch (qopt
->command
) {
126 /* We support eight priorities */
127 if (params
->bands
!= SPX5_PRIOS
)
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
))
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",
143 return sparx5_tc_ets_add(port
, params
);
146 return sparx5_tc_ets_del(port
);
157 int sparx5_port_setup_tc(struct net_device
*ndev
, enum tc_setup_type type
,
162 return sparx5_tc_setup_block(ndev
, type_data
);
163 case TC_SETUP_QDISC_MQPRIO
:
164 return sparx5_tc_setup_qdisc_mqprio(ndev
, type_data
);
165 case TC_SETUP_QDISC_TBF
:
166 return sparx5_tc_setup_qdisc_tbf(ndev
, type_data
);
167 case TC_SETUP_QDISC_ETS
:
168 return sparx5_tc_setup_qdisc_ets(ndev
, type_data
);