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_main.h"
10 #include "sparx5_qos.h"
11
12 enum sparx5_tas_link_speed {
13 TAS_SPEED_NO_GB,
14 TAS_SPEED_10,
15 TAS_SPEED_100,
16 TAS_SPEED_1000,
17 TAS_SPEED_2500,
18 TAS_SPEED_5000,
19 TAS_SPEED_10000,
20 TAS_SPEED_25000,
21 };
22
23 /* Calculate new base_time based on cycle_time.
24 *
25 * The hardware requires a base_time that is always in the future.
26 * We define threshold_time as current_time + (2 * cycle_time).
27 * If base_time is below threshold_time this function recalculates it to be in
28 * the interval:
29 * threshold_time <= base_time < (threshold_time + cycle_time)
30 *
31 * A very simple algorithm could be like this:
32 * new_base_time = org_base_time + N * cycle_time
33 * using the lowest N so (new_base_time >= threshold_time
34 */
sparx5_new_base_time(struct sparx5 * sparx5,const u32 cycle_time,const ktime_t org_base_time,ktime_t * new_base_time)35 void sparx5_new_base_time(struct sparx5 *sparx5, const u32 cycle_time,
36 const ktime_t org_base_time, ktime_t *new_base_time)
37 {
38 ktime_t current_time, threshold_time, new_time;
39 struct timespec64 ts;
40 u64 nr_of_cycles_p2;
41 u64 nr_of_cycles;
42 u64 diff_time;
43
44 new_time = org_base_time;
45
46 sparx5_ptp_gettime64(&sparx5->phc[SPARX5_PHC_PORT].info, &ts);
47 current_time = timespec64_to_ktime(ts);
48 threshold_time = current_time + (2 * cycle_time);
49 diff_time = threshold_time - new_time;
50 nr_of_cycles = div_u64(diff_time, cycle_time);
51 nr_of_cycles_p2 = 1; /* Use 2^0 as start value */
52
53 if (new_time >= threshold_time) {
54 *new_base_time = new_time;
55 return;
56 }
57
58 /* Calculate the smallest power of 2 (nr_of_cycles_p2)
59 * that is larger than nr_of_cycles.
60 */
61 while (nr_of_cycles_p2 < nr_of_cycles)
62 nr_of_cycles_p2 <<= 1; /* Next (higher) power of 2 */
63
64 /* Add as big chunks (power of 2 * cycle_time)
65 * as possible for each power of 2
66 */
67 while (nr_of_cycles_p2) {
68 if (new_time < threshold_time) {
69 new_time += cycle_time * nr_of_cycles_p2;
70 while (new_time < threshold_time)
71 new_time += cycle_time * nr_of_cycles_p2;
72 new_time -= cycle_time * nr_of_cycles_p2;
73 }
74 nr_of_cycles_p2 >>= 1; /* Next (lower) power of 2 */
75 }
76 new_time += cycle_time;
77 *new_base_time = new_time;
78 }
79
80 /* Max rates for leak groups */
81 static const u32 spx5_hsch_max_group_rate[SPX5_HSCH_LEAK_GRP_CNT] = {
82 1048568, /* 1.049 Gbps */
83 2621420, /* 2.621 Gbps */
84 10485680, /* 10.486 Gbps */
85 26214200 /* 26.214 Gbps */
86 };
87
sparx5_get_hsch_max_group_rate(int grp)88 u32 sparx5_get_hsch_max_group_rate(int grp)
89 {
90 return spx5_hsch_max_group_rate[grp];
91 }
92
93 static struct sparx5_layer layers[SPX5_HSCH_LAYER_CNT];
94
sparx5_lg_get_leak_time(struct sparx5 * sparx5,u32 layer,u32 group)95 static u32 sparx5_lg_get_leak_time(struct sparx5 *sparx5, u32 layer, u32 group)
96 {
97 u32 value;
98
99 value = spx5_rd(sparx5, HSCH_HSCH_TIMER_CFG(layer, group));
100 return HSCH_HSCH_TIMER_CFG_LEAK_TIME_GET(value);
101 }
102
sparx5_lg_set_leak_time(struct sparx5 * sparx5,u32 layer,u32 group,u32 leak_time)103 static void sparx5_lg_set_leak_time(struct sparx5 *sparx5, u32 layer, u32 group,
104 u32 leak_time)
105 {
106 spx5_wr(HSCH_HSCH_TIMER_CFG_LEAK_TIME_SET(leak_time), sparx5,
107 HSCH_HSCH_TIMER_CFG(layer, group));
108 }
109
sparx5_lg_get_first(struct sparx5 * sparx5,u32 layer,u32 group)110 static u32 sparx5_lg_get_first(struct sparx5 *sparx5, u32 layer, u32 group)
111 {
112 u32 value;
113
114 value = spx5_rd(sparx5, HSCH_HSCH_LEAK_CFG(layer, group));
115 return HSCH_HSCH_LEAK_CFG_LEAK_FIRST_GET(value);
116 }
117
sparx5_lg_get_next(struct sparx5 * sparx5,u32 layer,u32 group,u32 idx)118 static u32 sparx5_lg_get_next(struct sparx5 *sparx5, u32 layer, u32 group,
119 u32 idx)
120
121 {
122 u32 value;
123
124 value = spx5_rd(sparx5, HSCH_SE_CONNECT(idx));
125 return HSCH_SE_CONNECT_SE_LEAK_LINK_GET(value);
126 }
127
sparx5_lg_get_last(struct sparx5 * sparx5,u32 layer,u32 group)128 static u32 sparx5_lg_get_last(struct sparx5 *sparx5, u32 layer, u32 group)
129 {
130 u32 itr, next;
131
132 itr = sparx5_lg_get_first(sparx5, layer, group);
133
134 for (;;) {
135 next = sparx5_lg_get_next(sparx5, layer, group, itr);
136 if (itr == next)
137 return itr;
138
139 itr = next;
140 }
141 }
142
sparx5_lg_is_last(struct sparx5 * sparx5,u32 layer,u32 group,u32 idx)143 static bool sparx5_lg_is_last(struct sparx5 *sparx5, u32 layer, u32 group,
144 u32 idx)
145 {
146 return idx == sparx5_lg_get_next(sparx5, layer, group, idx);
147 }
148
sparx5_lg_is_first(struct sparx5 * sparx5,u32 layer,u32 group,u32 idx)149 static bool sparx5_lg_is_first(struct sparx5 *sparx5, u32 layer, u32 group,
150 u32 idx)
151 {
152 return idx == sparx5_lg_get_first(sparx5, layer, group);
153 }
154
sparx5_lg_is_empty(struct sparx5 * sparx5,u32 layer,u32 group)155 static bool sparx5_lg_is_empty(struct sparx5 *sparx5, u32 layer, u32 group)
156 {
157 return sparx5_lg_get_leak_time(sparx5, layer, group) == 0;
158 }
159
sparx5_lg_is_singular(struct sparx5 * sparx5,u32 layer,u32 group)160 static bool sparx5_lg_is_singular(struct sparx5 *sparx5, u32 layer, u32 group)
161 {
162 if (sparx5_lg_is_empty(sparx5, layer, group))
163 return false;
164
165 return sparx5_lg_get_first(sparx5, layer, group) ==
166 sparx5_lg_get_last(sparx5, layer, group);
167 }
168
sparx5_lg_enable(struct sparx5 * sparx5,u32 layer,u32 group,u32 leak_time)169 static void sparx5_lg_enable(struct sparx5 *sparx5, u32 layer, u32 group,
170 u32 leak_time)
171 {
172 sparx5_lg_set_leak_time(sparx5, layer, group, leak_time);
173 }
174
sparx5_lg_disable(struct sparx5 * sparx5,u32 layer,u32 group)175 static void sparx5_lg_disable(struct sparx5 *sparx5, u32 layer, u32 group)
176 {
177 sparx5_lg_set_leak_time(sparx5, layer, group, 0);
178 }
179
sparx5_lg_get_group_by_index(struct sparx5 * sparx5,u32 layer,u32 idx,u32 * group)180 static int sparx5_lg_get_group_by_index(struct sparx5 *sparx5, u32 layer,
181 u32 idx, u32 *group)
182 {
183 u32 itr, next;
184 int i;
185
186 for (i = 0; i < SPX5_HSCH_LEAK_GRP_CNT; i++) {
187 if (sparx5_lg_is_empty(sparx5, layer, i))
188 continue;
189
190 itr = sparx5_lg_get_first(sparx5, layer, i);
191
192 for (;;) {
193 next = sparx5_lg_get_next(sparx5, layer, i, itr);
194
195 if (itr == idx) {
196 *group = i;
197 return 0; /* Found it */
198 }
199 if (itr == next)
200 break; /* Was not found */
201
202 itr = next;
203 }
204 }
205
206 return -1;
207 }
208
sparx5_lg_get_group_by_rate(u32 layer,u32 rate,u32 * group)209 static int sparx5_lg_get_group_by_rate(u32 layer, u32 rate, u32 *group)
210 {
211 struct sparx5_layer *l = &layers[layer];
212 struct sparx5_lg *lg;
213 u32 i;
214
215 for (i = 0; i < SPX5_HSCH_LEAK_GRP_CNT; i++) {
216 lg = &l->leak_groups[i];
217 if (rate <= lg->max_rate) {
218 *group = i;
219 return 0;
220 }
221 }
222
223 return -1;
224 }
225
sparx5_lg_get_adjacent(struct sparx5 * sparx5,u32 layer,u32 group,u32 idx,u32 * prev,u32 * next,u32 * first)226 static int sparx5_lg_get_adjacent(struct sparx5 *sparx5, u32 layer, u32 group,
227 u32 idx, u32 *prev, u32 *next, u32 *first)
228 {
229 u32 itr;
230
231 *first = sparx5_lg_get_first(sparx5, layer, group);
232 *prev = *first;
233 *next = *first;
234 itr = *first;
235
236 for (;;) {
237 *next = sparx5_lg_get_next(sparx5, layer, group, itr);
238
239 if (itr == idx)
240 return 0; /* Found it */
241
242 if (itr == *next)
243 return -1; /* Was not found */
244
245 *prev = itr;
246 itr = *next;
247 }
248
249 return -1;
250 }
251
sparx5_lg_conf_set(struct sparx5 * sparx5,u32 layer,u32 group,u32 se_first,u32 idx,u32 idx_next,bool empty)252 static int sparx5_lg_conf_set(struct sparx5 *sparx5, u32 layer, u32 group,
253 u32 se_first, u32 idx, u32 idx_next, bool empty)
254 {
255 u32 leak_time = layers[layer].leak_groups[group].leak_time;
256
257 /* Stop leaking */
258 sparx5_lg_disable(sparx5, layer, group);
259
260 if (empty)
261 return 0;
262
263 /* Select layer */
264 spx5_rmw(HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(layer),
265 HSCH_HSCH_CFG_CFG_HSCH_LAYER, sparx5, HSCH_HSCH_CFG_CFG);
266
267 /* Link elements */
268 spx5_wr(HSCH_SE_CONNECT_SE_LEAK_LINK_SET(idx_next), sparx5,
269 HSCH_SE_CONNECT(idx));
270
271 /* Set the first element. */
272 spx5_rmw(HSCH_HSCH_LEAK_CFG_LEAK_FIRST_SET(se_first),
273 HSCH_HSCH_LEAK_CFG_LEAK_FIRST, sparx5,
274 HSCH_HSCH_LEAK_CFG(layer, group));
275
276 /* Start leaking */
277 sparx5_lg_enable(sparx5, layer, group, leak_time);
278
279 return 0;
280 }
281
sparx5_lg_del(struct sparx5 * sparx5,u32 layer,u32 group,u32 idx)282 static int sparx5_lg_del(struct sparx5 *sparx5, u32 layer, u32 group, u32 idx)
283 {
284 u32 first, next, prev;
285 bool empty = false;
286
287 /* idx *must* be present in the leak group */
288 WARN_ON(sparx5_lg_get_adjacent(sparx5, layer, group, idx, &prev, &next,
289 &first) < 0);
290
291 if (sparx5_lg_is_singular(sparx5, layer, group)) {
292 empty = true;
293 } else if (sparx5_lg_is_last(sparx5, layer, group, idx)) {
294 /* idx is removed, prev is now last */
295 idx = prev;
296 next = prev;
297 } else if (sparx5_lg_is_first(sparx5, layer, group, idx)) {
298 /* idx is removed and points to itself, first is next */
299 first = next;
300 next = idx;
301 } else {
302 /* Next is not touched */
303 idx = prev;
304 }
305
306 return sparx5_lg_conf_set(sparx5, layer, group, first, idx, next,
307 empty);
308 }
309
sparx5_lg_add(struct sparx5 * sparx5,u32 layer,u32 new_group,u32 idx)310 static int sparx5_lg_add(struct sparx5 *sparx5, u32 layer, u32 new_group,
311 u32 idx)
312 {
313 u32 first, next, old_group;
314
315 pr_debug("ADD: layer: %d, new_group: %d, idx: %d", layer, new_group,
316 idx);
317
318 /* Is this SE already shaping ? */
319 if (sparx5_lg_get_group_by_index(sparx5, layer, idx, &old_group) >= 0) {
320 if (old_group != new_group) {
321 /* Delete from old group */
322 sparx5_lg_del(sparx5, layer, old_group, idx);
323 } else {
324 /* Nothing to do here */
325 return 0;
326 }
327 }
328
329 /* We always add to head of the list */
330 first = idx;
331
332 if (sparx5_lg_is_empty(sparx5, layer, new_group))
333 next = idx;
334 else
335 next = sparx5_lg_get_first(sparx5, layer, new_group);
336
337 return sparx5_lg_conf_set(sparx5, layer, new_group, first, idx, next,
338 false);
339 }
340
sparx5_shaper_conf_set(struct sparx5_port * port,const struct sparx5_shaper * sh,u32 layer,u32 idx,u32 group)341 static int sparx5_shaper_conf_set(struct sparx5_port *port,
342 const struct sparx5_shaper *sh, u32 layer,
343 u32 idx, u32 group)
344 {
345 int (*sparx5_lg_action)(struct sparx5 *, u32, u32, u32);
346 struct sparx5 *sparx5 = port->sparx5;
347
348 if (!sh->rate && !sh->burst)
349 sparx5_lg_action = &sparx5_lg_del;
350 else
351 sparx5_lg_action = &sparx5_lg_add;
352
353 /* Select layer */
354 spx5_rmw(HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(layer),
355 HSCH_HSCH_CFG_CFG_HSCH_LAYER, sparx5, HSCH_HSCH_CFG_CFG);
356
357 /* Set frame mode */
358 spx5_rmw(HSCH_SE_CFG_SE_FRM_MODE_SET(sh->mode), HSCH_SE_CFG_SE_FRM_MODE,
359 sparx5, HSCH_SE_CFG(idx));
360
361 /* Set committed rate and burst */
362 spx5_wr(HSCH_CIR_CFG_CIR_RATE_SET(sh->rate) |
363 HSCH_CIR_CFG_CIR_BURST_SET(sh->burst),
364 sparx5, HSCH_CIR_CFG(idx));
365
366 /* This has to be done after the shaper configuration has been set */
367 sparx5_lg_action(sparx5, layer, group, idx);
368
369 return 0;
370 }
371
sparx5_weight_to_hw_cost(u32 weight_min,u32 weight)372 static u32 sparx5_weight_to_hw_cost(u32 weight_min, u32 weight)
373 {
374 return ((((SPX5_DWRR_COST_MAX << 4) * weight_min / weight) + 8) >> 4) -
375 1;
376 }
377
sparx5_dwrr_conf_set(struct sparx5_port * port,struct sparx5_dwrr * dwrr)378 static int sparx5_dwrr_conf_set(struct sparx5_port *port,
379 struct sparx5_dwrr *dwrr)
380 {
381 u32 layer = is_sparx5(port->sparx5) ? 2 : 1;
382 int i;
383
384 spx5_rmw(HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(layer) |
385 HSCH_HSCH_CFG_CFG_CFG_SE_IDX_SET(port->portno),
386 HSCH_HSCH_CFG_CFG_HSCH_LAYER | HSCH_HSCH_CFG_CFG_CFG_SE_IDX,
387 port->sparx5, HSCH_HSCH_CFG_CFG);
388
389 /* Number of *lower* indexes that are arbitrated dwrr */
390 spx5_rmw(HSCH_SE_CFG_SE_DWRR_CNT_SET(dwrr->count),
391 HSCH_SE_CFG_SE_DWRR_CNT, port->sparx5,
392 HSCH_SE_CFG(port->portno));
393
394 for (i = 0; i < dwrr->count; i++) {
395 spx5_rmw(HSCH_DWRR_ENTRY_DWRR_COST_SET(dwrr->cost[i]),
396 HSCH_DWRR_ENTRY_DWRR_COST, port->sparx5,
397 HSCH_DWRR_ENTRY(i));
398 }
399
400 return 0;
401 }
402
sparx5_leak_groups_init(struct sparx5 * sparx5)403 static int sparx5_leak_groups_init(struct sparx5 *sparx5)
404 {
405 const struct sparx5_ops *ops = sparx5->data->ops;
406 struct sparx5_layer *layer;
407 u32 sys_clk_per_100ps;
408 struct sparx5_lg *lg;
409 u32 leak_time_us;
410 int i, ii;
411
412 sys_clk_per_100ps = spx5_rd(sparx5, HSCH_SYS_CLK_PER);
413
414 for (i = 0; i < SPX5_HSCH_LAYER_CNT; i++) {
415 layer = &layers[i];
416 for (ii = 0; ii < SPX5_HSCH_LEAK_GRP_CNT; ii++) {
417 lg = &layer->leak_groups[ii];
418 lg->max_rate = ops->get_hsch_max_group_rate(i);
419
420 /* Calculate the leak time in us, to serve a maximum
421 * rate of 'max_rate' for this group
422 */
423 leak_time_us = (SPX5_SE_RATE_MAX * 1000) / lg->max_rate;
424
425 /* Hardware wants leak time in ns */
426 lg->leak_time = 1000 * leak_time_us;
427
428 /* Calculate resolution */
429 lg->resolution = 1000 / leak_time_us;
430
431 /* Maximum number of shapers that can be served by
432 * this leak group
433 */
434 lg->max_ses = (1000 * leak_time_us) / sys_clk_per_100ps;
435
436 /* Example:
437 * Wanted bandwidth is 100Mbit:
438 *
439 * 100 mbps can be served by leak group zero.
440 *
441 * leak_time is 125000 ns.
442 * resolution is: 8
443 *
444 * cir = 100000 / 8 = 12500
445 * leaks_pr_sec = 125000 / 10^9 = 8000
446 * bw = 12500 * 8000 = 10^8 (100 Mbit)
447 */
448
449 /* Disable by default - this also indicates an empty
450 * leak group
451 */
452 sparx5_lg_disable(sparx5, i, ii);
453 }
454 }
455
456 return 0;
457 }
458
sparx5_qos_init(struct sparx5 * sparx5)459 int sparx5_qos_init(struct sparx5 *sparx5)
460 {
461 int ret;
462
463 ret = sparx5_leak_groups_init(sparx5);
464 if (ret < 0)
465 return ret;
466
467 ret = sparx5_dcb_init(sparx5);
468 if (ret < 0)
469 return ret;
470
471 sparx5_psfp_init(sparx5);
472
473 return 0;
474 }
475
sparx5_tc_mqprio_add(struct net_device * ndev,u8 num_tc)476 int sparx5_tc_mqprio_add(struct net_device *ndev, u8 num_tc)
477 {
478 int i;
479
480 if (num_tc != SPX5_PRIOS) {
481 netdev_err(ndev, "Only %d traffic classes supported\n",
482 SPX5_PRIOS);
483 return -EINVAL;
484 }
485
486 netdev_set_num_tc(ndev, num_tc);
487
488 for (i = 0; i < num_tc; i++)
489 netdev_set_tc_queue(ndev, i, 1, i);
490
491 netdev_dbg(ndev, "dev->num_tc %u dev->real_num_tx_queues %u\n",
492 ndev->num_tc, ndev->real_num_tx_queues);
493
494 return 0;
495 }
496
sparx5_tc_mqprio_del(struct net_device * ndev)497 int sparx5_tc_mqprio_del(struct net_device *ndev)
498 {
499 netdev_reset_tc(ndev);
500
501 netdev_dbg(ndev, "dev->num_tc %u dev->real_num_tx_queues %u\n",
502 ndev->num_tc, ndev->real_num_tx_queues);
503
504 return 0;
505 }
506
sparx5_tc_tbf_add(struct sparx5_port * port,struct tc_tbf_qopt_offload_replace_params * params,u32 layer,u32 idx)507 int sparx5_tc_tbf_add(struct sparx5_port *port,
508 struct tc_tbf_qopt_offload_replace_params *params,
509 u32 layer, u32 idx)
510 {
511 struct sparx5_shaper sh = {
512 .mode = SPX5_SE_MODE_DATARATE,
513 .rate = div_u64(params->rate.rate_bytes_ps, 1000) * 8,
514 .burst = params->max_size,
515 };
516 struct sparx5_lg *lg;
517 u32 group;
518
519 /* Find suitable group for this se */
520 if (sparx5_lg_get_group_by_rate(layer, sh.rate, &group) < 0) {
521 pr_debug("Could not find leak group for se with rate: %d",
522 sh.rate);
523 return -EINVAL;
524 }
525
526 lg = &layers[layer].leak_groups[group];
527
528 pr_debug("Found matching group (speed: %d)\n", lg->max_rate);
529
530 if (sh.rate < SPX5_SE_RATE_MIN || sh.burst < SPX5_SE_BURST_MIN)
531 return -EINVAL;
532
533 /* Calculate committed rate and burst */
534 sh.rate = DIV_ROUND_UP(sh.rate, lg->resolution);
535 sh.burst = DIV_ROUND_UP(sh.burst, SPX5_SE_BURST_UNIT);
536
537 if (sh.rate > SPX5_SE_RATE_MAX || sh.burst > SPX5_SE_BURST_MAX)
538 return -EINVAL;
539
540 return sparx5_shaper_conf_set(port, &sh, layer, idx, group);
541 }
542
sparx5_tc_tbf_del(struct sparx5_port * port,u32 layer,u32 idx)543 int sparx5_tc_tbf_del(struct sparx5_port *port, u32 layer, u32 idx)
544 {
545 struct sparx5_shaper sh = {0};
546 u32 group;
547
548 sparx5_lg_get_group_by_index(port->sparx5, layer, idx, &group);
549
550 return sparx5_shaper_conf_set(port, &sh, layer, idx, group);
551 }
552
sparx5_tc_ets_add(struct sparx5_port * port,struct tc_ets_qopt_offload_replace_params * params)553 int sparx5_tc_ets_add(struct sparx5_port *port,
554 struct tc_ets_qopt_offload_replace_params *params)
555 {
556 struct sparx5_dwrr dwrr = {0};
557 /* Minimum weight for each iteration */
558 unsigned int w_min = 100;
559 int i;
560
561 /* Find minimum weight for all dwrr bands */
562 for (i = 0; i < SPX5_PRIOS; i++) {
563 if (params->quanta[i] == 0)
564 continue;
565 w_min = min(w_min, params->weights[i]);
566 }
567
568 for (i = 0; i < SPX5_PRIOS; i++) {
569 /* Strict band; skip */
570 if (params->quanta[i] == 0)
571 continue;
572
573 dwrr.count++;
574
575 /* On the sparx5, bands with higher indexes are preferred and
576 * arbitrated strict. Strict bands are put in the lower indexes,
577 * by tc, so we reverse the bands here.
578 *
579 * Also convert the weight to something the hardware
580 * understands.
581 */
582 dwrr.cost[SPX5_PRIOS - i - 1] =
583 sparx5_weight_to_hw_cost(w_min, params->weights[i]);
584 }
585
586 return sparx5_dwrr_conf_set(port, &dwrr);
587 }
588
sparx5_tc_ets_del(struct sparx5_port * port)589 int sparx5_tc_ets_del(struct sparx5_port *port)
590 {
591 struct sparx5_dwrr dwrr = {0};
592
593 return sparx5_dwrr_conf_set(port, &dwrr);
594 }
595
sparx5_tas_speed(struct sparx5_port * port,int speed)596 void sparx5_tas_speed(struct sparx5_port *port, int speed)
597 {
598 struct sparx5 *sparx5 = port->sparx5;
599 u8 spd;
600
601 switch (speed) {
602 case SPEED_10:
603 spd = TAS_SPEED_10;
604 break;
605 case SPEED_100:
606 spd = TAS_SPEED_100;
607 break;
608 case SPEED_1000:
609 spd = TAS_SPEED_1000;
610 break;
611 case SPEED_2500:
612 spd = TAS_SPEED_2500;
613 break;
614 case SPEED_5000:
615 spd = TAS_SPEED_5000;
616 break;
617 case SPEED_10000:
618 spd = TAS_SPEED_10000;
619 break;
620 case SPEED_25000:
621 spd = TAS_SPEED_25000;
622 break;
623 default:
624 netdev_err(port->ndev, "TAS: Unsupported speed: %d\n", speed);
625 return;
626 }
627
628 spx5_rmw(HSCH_TAS_PROFILE_CONFIG_LINK_SPEED_SET(spd),
629 HSCH_TAS_PROFILE_CONFIG_LINK_SPEED,
630 sparx5,
631 HSCH_TAS_PROFILE_CONFIG(port->portno));
632 }
633