1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
3 * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
4 * stmmac TC Handling (HW only)
7 #include <net/pkt_cls.h>
8 #include <net/tc_act/tc_gact.h>
14 static void tc_fill_all_pass_entry(struct stmmac_tc_entry
*entry
)
16 memset(entry
, 0, sizeof(*entry
));
18 entry
->is_last
= true;
19 entry
->is_frag
= false;
22 entry
->val
.match_data
= 0x0;
23 entry
->val
.match_en
= 0x0;
25 entry
->val
.dma_ch_no
= 0x0;
28 static struct stmmac_tc_entry
*tc_find_entry(struct stmmac_priv
*priv
,
29 struct tc_cls_u32_offload
*cls
,
32 struct stmmac_tc_entry
*entry
, *first
= NULL
, *dup
= NULL
;
33 u32 loc
= cls
->knode
.handle
;
36 for (i
= 0; i
< priv
->tc_entries_max
; i
++) {
37 entry
= &priv
->tc_entries
[i
];
38 if (!entry
->in_use
&& !first
&& free
)
40 if ((entry
->handle
== loc
) && !free
&& !entry
->is_frag
)
51 memset(&first
->val
, 0, sizeof(first
->val
));
57 static int tc_fill_actions(struct stmmac_tc_entry
*entry
,
58 struct stmmac_tc_entry
*frag
,
59 struct tc_cls_u32_offload
*cls
)
61 struct stmmac_tc_entry
*action_entry
= entry
;
62 const struct tc_action
*act
;
63 struct tcf_exts
*exts
;
66 exts
= cls
->knode
.exts
;
67 if (!tcf_exts_has_actions(exts
))
72 tcf_exts_for_each_action(i
, act
, exts
) {
74 if (is_tcf_gact_ok(act
)) {
75 action_entry
->val
.af
= 1;
79 if (is_tcf_gact_shot(act
)) {
80 action_entry
->val
.rf
= 1;
91 static int tc_fill_entry(struct stmmac_priv
*priv
,
92 struct tc_cls_u32_offload
*cls
)
94 struct stmmac_tc_entry
*entry
, *frag
= NULL
;
95 struct tc_u32_sel
*sel
= cls
->knode
.sel
;
96 u32 off
, data
, mask
, real_off
, rem
;
97 u32 prio
= cls
->common
.prio
;
100 /* Only 1 match per entry */
101 if (sel
->nkeys
<= 0 || sel
->nkeys
> 1)
104 off
= sel
->keys
[0].off
<< sel
->offshift
;
105 data
= sel
->keys
[0].val
;
106 mask
= sel
->keys
[0].mask
;
108 switch (ntohs(cls
->common
.protocol
)) {
118 if (off
> priv
->tc_off_max
)
124 entry
= tc_find_entry(priv
, cls
, true);
129 frag
= tc_find_entry(priv
, cls
, true);
135 entry
->frag_ptr
= frag
;
136 entry
->val
.match_en
= (mask
<< (rem
* 8)) &
137 GENMASK(31, rem
* 8);
138 entry
->val
.match_data
= (data
<< (rem
* 8)) &
139 GENMASK(31, rem
* 8);
140 entry
->val
.frame_offset
= real_off
;
143 frag
->val
.match_en
= (mask
>> (rem
* 8)) &
144 GENMASK(rem
* 8 - 1, 0);
145 frag
->val
.match_data
= (data
>> (rem
* 8)) &
146 GENMASK(rem
* 8 - 1, 0);
147 frag
->val
.frame_offset
= real_off
+ 1;
149 frag
->is_frag
= true;
151 entry
->frag_ptr
= NULL
;
152 entry
->val
.match_en
= mask
;
153 entry
->val
.match_data
= data
;
154 entry
->val
.frame_offset
= real_off
;
158 ret
= tc_fill_actions(entry
, frag
, cls
);
166 frag
->in_use
= false;
167 entry
->in_use
= false;
171 static void tc_unfill_entry(struct stmmac_priv
*priv
,
172 struct tc_cls_u32_offload
*cls
)
174 struct stmmac_tc_entry
*entry
;
176 entry
= tc_find_entry(priv
, cls
, false);
180 entry
->in_use
= false;
181 if (entry
->frag_ptr
) {
182 entry
= entry
->frag_ptr
;
183 entry
->is_frag
= false;
184 entry
->in_use
= false;
188 static int tc_config_knode(struct stmmac_priv
*priv
,
189 struct tc_cls_u32_offload
*cls
)
193 ret
= tc_fill_entry(priv
, cls
);
197 ret
= stmmac_rxp_config(priv
, priv
->hw
->pcsr
, priv
->tc_entries
,
198 priv
->tc_entries_max
);
205 tc_unfill_entry(priv
, cls
);
209 static int tc_delete_knode(struct stmmac_priv
*priv
,
210 struct tc_cls_u32_offload
*cls
)
214 /* Set entry and fragments as not used */
215 tc_unfill_entry(priv
, cls
);
217 ret
= stmmac_rxp_config(priv
, priv
->hw
->pcsr
, priv
->tc_entries
,
218 priv
->tc_entries_max
);
225 static int tc_setup_cls_u32(struct stmmac_priv
*priv
,
226 struct tc_cls_u32_offload
*cls
)
228 switch (cls
->command
) {
229 case TC_CLSU32_REPLACE_KNODE
:
230 tc_unfill_entry(priv
, cls
);
232 case TC_CLSU32_NEW_KNODE
:
233 return tc_config_knode(priv
, cls
);
234 case TC_CLSU32_DELETE_KNODE
:
235 return tc_delete_knode(priv
, cls
);
241 static int tc_init(struct stmmac_priv
*priv
)
243 struct dma_features
*dma_cap
= &priv
->dma_cap
;
246 if (!dma_cap
->frpsel
)
249 switch (dma_cap
->frpbs
) {
251 priv
->tc_off_max
= 64;
254 priv
->tc_off_max
= 128;
257 priv
->tc_off_max
= 256;
263 switch (dma_cap
->frpes
) {
277 /* Reserve one last filter which lets all pass */
278 priv
->tc_entries_max
= count
;
279 priv
->tc_entries
= devm_kcalloc(priv
->device
,
280 count
, sizeof(*priv
->tc_entries
), GFP_KERNEL
);
281 if (!priv
->tc_entries
)
284 tc_fill_all_pass_entry(&priv
->tc_entries
[count
- 1]);
286 dev_info(priv
->device
, "Enabling HW TC (entries=%d, max_off=%d)\n",
287 priv
->tc_entries_max
, priv
->tc_off_max
);
291 static int tc_setup_cbs(struct stmmac_priv
*priv
,
292 struct tc_cbs_qopt_offload
*qopt
)
294 u32 tx_queues_count
= priv
->plat
->tx_queues_to_use
;
295 u32 queue
= qopt
->queue
;
301 /* Queue 0 is not AVB capable */
302 if (queue
<= 0 || queue
>= tx_queues_count
)
304 if (!priv
->dma_cap
.av
)
306 if (priv
->speed
!= SPEED_100
&& priv
->speed
!= SPEED_1000
)
309 mode_to_use
= priv
->plat
->tx_queues_cfg
[queue
].mode_to_use
;
310 if (mode_to_use
== MTL_QUEUE_DCB
&& qopt
->enable
) {
311 ret
= stmmac_dma_qmode(priv
, priv
->ioaddr
, queue
, MTL_QUEUE_AVB
);
315 priv
->plat
->tx_queues_cfg
[queue
].mode_to_use
= MTL_QUEUE_AVB
;
316 } else if (!qopt
->enable
) {
317 return stmmac_dma_qmode(priv
, priv
->ioaddr
, queue
, MTL_QUEUE_DCB
);
320 /* Port Transmit Rate and Speed Divider */
321 ptr
= (priv
->speed
== SPEED_100
) ? 4 : 8;
322 speed_div
= (priv
->speed
== SPEED_100
) ? 100000 : 1000000;
324 /* Final adjustments for HW */
325 value
= div_s64(qopt
->idleslope
* 1024ll * ptr
, speed_div
);
326 priv
->plat
->tx_queues_cfg
[queue
].idle_slope
= value
& GENMASK(31, 0);
328 value
= div_s64(-qopt
->sendslope
* 1024ll * ptr
, speed_div
);
329 priv
->plat
->tx_queues_cfg
[queue
].send_slope
= value
& GENMASK(31, 0);
331 value
= qopt
->hicredit
* 1024ll * 8;
332 priv
->plat
->tx_queues_cfg
[queue
].high_credit
= value
& GENMASK(31, 0);
334 value
= qopt
->locredit
* 1024ll * 8;
335 priv
->plat
->tx_queues_cfg
[queue
].low_credit
= value
& GENMASK(31, 0);
337 ret
= stmmac_config_cbs(priv
, priv
->hw
,
338 priv
->plat
->tx_queues_cfg
[queue
].send_slope
,
339 priv
->plat
->tx_queues_cfg
[queue
].idle_slope
,
340 priv
->plat
->tx_queues_cfg
[queue
].high_credit
,
341 priv
->plat
->tx_queues_cfg
[queue
].low_credit
,
346 dev_info(priv
->device
, "CBS queue %d: send %d, idle %d, hi %d, lo %d\n",
347 queue
, qopt
->sendslope
, qopt
->idleslope
,
348 qopt
->hicredit
, qopt
->locredit
);
352 const struct stmmac_tc_ops dwmac510_tc_ops
= {
354 .setup_cls_u32
= tc_setup_cls_u32
,
355 .setup_cbs
= tc_setup_cbs
,