Loading...
1// SPDX-License-Identifier: GPL-2.0+
2/* Microchip Sparx5 Switch driver
3 *
4 * Copyright (c) 2023 Microchip Technology Inc. and its subsidiaries.
5 */
6
7#include "sparx5_main_regs.h"
8#include "sparx5_main.h"
9
10struct sparx5_sdlb_group sdlb_groups[SPX5_SDLB_GROUP_CNT] = {
11 { SPX5_SDLB_GROUP_RATE_MAX, 8192 / 1, 64 }, /* 25 G */
12 { 15000000000ULL, 8192 / 1, 64 }, /* 15 G */
13 { 10000000000ULL, 8192 / 1, 64 }, /* 10 G */
14 { 5000000000ULL, 8192 / 1, 64 }, /* 5 G */
15 { 2500000000ULL, 8192 / 1, 64 }, /* 2.5 G */
16 { 1000000000ULL, 8192 / 2, 64 }, /* 1 G */
17 { 500000000ULL, 8192 / 2, 64 }, /* 500 M */
18 { 100000000ULL, 8192 / 4, 64 }, /* 100 M */
19 { 50000000ULL, 8192 / 4, 64 }, /* 50 M */
20 { 5000000ULL, 8192 / 8, 64 } /* 5 M */
21};
22
23struct sparx5_sdlb_group *sparx5_get_sdlb_group(int idx)
24{
25 return &sdlb_groups[idx];
26}
27
28u64 sparx5_sdlb_clk_hz_get(struct sparx5 *sparx5)
29{
30 u64 clk_hz;
31
32 clk_hz = (10 * 1000 * 1000) /
33 (sparx5_clk_period(sparx5->coreclock) / 100);
34
35 return clk_hz *= 1000;
36}
37
38static int sparx5_sdlb_pup_interval_get(struct sparx5 *sparx5, u32 max_token,
39 u64 max_rate)
40{
41 u64 clk_hz;
42
43 clk_hz = sparx5_sdlb_clk_hz_get(sparx5);
44
45 return div64_u64((8 * clk_hz * max_token), max_rate);
46}
47
48int sparx5_sdlb_pup_token_get(struct sparx5 *sparx5, u32 pup_interval, u64 rate)
49{
50 u64 clk_hz;
51
52 if (!rate)
53 return SPX5_SDLB_PUP_TOKEN_DISABLE;
54
55 clk_hz = sparx5_sdlb_clk_hz_get(sparx5);
56
57 return DIV64_U64_ROUND_UP((rate * pup_interval), (clk_hz * 8));
58}
59
60static void sparx5_sdlb_group_disable(struct sparx5 *sparx5, u32 group)
61{
62 spx5_rmw(ANA_AC_SDLB_PUP_CTRL_PUP_ENA_SET(0),
63 ANA_AC_SDLB_PUP_CTRL_PUP_ENA, sparx5,
64 ANA_AC_SDLB_PUP_CTRL(group));
65}
66
67static void sparx5_sdlb_group_enable(struct sparx5 *sparx5, u32 group)
68{
69 spx5_rmw(ANA_AC_SDLB_PUP_CTRL_PUP_ENA_SET(1),
70 ANA_AC_SDLB_PUP_CTRL_PUP_ENA, sparx5,
71 ANA_AC_SDLB_PUP_CTRL(group));
72}
73
74static u32 sparx5_sdlb_group_get_first(struct sparx5 *sparx5, u32 group)
75{
76 u32 val;
77
78 val = spx5_rd(sparx5, ANA_AC_SDLB_XLB_START(group));
79
80 return ANA_AC_SDLB_XLB_START_LBSET_START_GET(val);
81}
82
83static u32 sparx5_sdlb_group_get_next(struct sparx5 *sparx5, u32 group,
84 u32 lb)
85{
86 u32 val;
87
88 val = spx5_rd(sparx5, ANA_AC_SDLB_XLB_NEXT(lb));
89
90 return ANA_AC_SDLB_XLB_NEXT_LBSET_NEXT_GET(val);
91}
92
93static bool sparx5_sdlb_group_is_first(struct sparx5 *sparx5, u32 group,
94 u32 lb)
95{
96 return lb == sparx5_sdlb_group_get_first(sparx5, group);
97}
98
99static bool sparx5_sdlb_group_is_last(struct sparx5 *sparx5, u32 group,
100 u32 lb)
101{
102 return lb == sparx5_sdlb_group_get_next(sparx5, group, lb);
103}
104
105static bool sparx5_sdlb_group_is_empty(struct sparx5 *sparx5, u32 group)
106{
107 u32 val;
108
109 val = spx5_rd(sparx5, ANA_AC_SDLB_PUP_CTRL(group));
110
111 return ANA_AC_SDLB_PUP_CTRL_PUP_ENA_GET(val) == 0;
112}
113
114static u32 sparx5_sdlb_group_get_last(struct sparx5 *sparx5, u32 group)
115{
116 u32 itr, next;
117
118 itr = sparx5_sdlb_group_get_first(sparx5, group);
119
120 for (;;) {
121 next = sparx5_sdlb_group_get_next(sparx5, group, itr);
122 if (itr == next)
123 return itr;
124
125 itr = next;
126 }
127}
128
129static bool sparx5_sdlb_group_is_singular(struct sparx5 *sparx5, u32 group)
130{
131 if (sparx5_sdlb_group_is_empty(sparx5, group))
132 return false;
133
134 return sparx5_sdlb_group_get_first(sparx5, group) ==
135 sparx5_sdlb_group_get_last(sparx5, group);
136}
137
138static int sparx5_sdlb_group_get_adjacent(struct sparx5 *sparx5, u32 group,
139 u32 idx, u32 *prev, u32 *next,
140 u32 *first)
141{
142 u32 itr;
143
144 *first = sparx5_sdlb_group_get_first(sparx5, group);
145 *prev = *first;
146 *next = *first;
147 itr = *first;
148
149 for (;;) {
150 *next = sparx5_sdlb_group_get_next(sparx5, group, itr);
151
152 if (itr == idx)
153 return 0; /* Found it */
154
155 if (itr == *next)
156 return -EINVAL; /* Was not found */
157
158 *prev = itr;
159 itr = *next;
160 }
161}
162
163static int sparx5_sdlb_group_get_count(struct sparx5 *sparx5, u32 group)
164{
165 u32 itr, next;
166 int count = 0;
167
168 itr = sparx5_sdlb_group_get_first(sparx5, group);
169
170 for (;;) {
171 next = sparx5_sdlb_group_get_next(sparx5, group, itr);
172 if (itr == next)
173 return count;
174
175 itr = next;
176 count++;
177 }
178}
179
180int sparx5_sdlb_group_get_by_rate(struct sparx5 *sparx5, u32 rate, u32 burst)
181{
182 const struct sparx5_ops *ops = sparx5->data->ops;
183 const struct sparx5_sdlb_group *group;
184 u64 rate_bps;
185 int i, count;
186
187 rate_bps = rate * 1000;
188
189 for (i = sparx5->data->consts->n_lb_groups - 1; i >= 0; i--) {
190 group = ops->get_sdlb_group(i);
191
192 count = sparx5_sdlb_group_get_count(sparx5, i);
193
194 /* Check that this group is not full.
195 * According to LB group configuration rules: the number of XLBs
196 * in a group must not exceed PUP_INTERVAL/4 - 1.
197 */
198 if (count > ((group->pup_interval / 4) - 1))
199 continue;
200
201 if (rate_bps < group->max_rate)
202 return i;
203 }
204
205 return -ENOSPC;
206}
207
208int sparx5_sdlb_group_get_by_index(struct sparx5 *sparx5, u32 idx, u32 *group)
209{
210 u32 itr, next;
211 int i;
212
213 for (i = 0; i < sparx5->data->consts->n_lb_groups; i++) {
214 if (sparx5_sdlb_group_is_empty(sparx5, i))
215 continue;
216
217 itr = sparx5_sdlb_group_get_first(sparx5, i);
218
219 for (;;) {
220 next = sparx5_sdlb_group_get_next(sparx5, i, itr);
221
222 if (itr == idx) {
223 *group = i;
224 return 0; /* Found it */
225 }
226 if (itr == next)
227 break; /* Was not found */
228
229 itr = next;
230 }
231 }
232
233 return -EINVAL;
234}
235
236static int sparx5_sdlb_group_link(struct sparx5 *sparx5, u32 group, u32 idx,
237 u32 first, u32 next, bool empty)
238{
239 /* Stop leaking */
240 sparx5_sdlb_group_disable(sparx5, group);
241
242 if (empty)
243 return 0;
244
245 /* Link insertion lb to next lb */
246 spx5_wr(ANA_AC_SDLB_XLB_NEXT_LBSET_NEXT_SET(next) |
247 ANA_AC_SDLB_XLB_NEXT_LBGRP_SET(group),
248 sparx5, ANA_AC_SDLB_XLB_NEXT(idx));
249
250 /* Set the first lb */
251 spx5_wr(ANA_AC_SDLB_XLB_START_LBSET_START_SET(first), sparx5,
252 ANA_AC_SDLB_XLB_START(group));
253
254 /* Start leaking */
255 sparx5_sdlb_group_enable(sparx5, group);
256
257 return 0;
258};
259
260int sparx5_sdlb_group_add(struct sparx5 *sparx5, u32 group, u32 idx)
261{
262 u32 first, next;
263
264 /* We always add to head of the list */
265 first = idx;
266
267 if (sparx5_sdlb_group_is_empty(sparx5, group))
268 next = idx;
269 else
270 next = sparx5_sdlb_group_get_first(sparx5, group);
271
272 return sparx5_sdlb_group_link(sparx5, group, idx, first, next, false);
273}
274
275int sparx5_sdlb_group_del(struct sparx5 *sparx5, u32 group, u32 idx)
276{
277 u32 first, next, prev;
278 bool empty = false;
279
280 if (sparx5_sdlb_group_get_adjacent(sparx5, group, idx, &prev, &next,
281 &first) < 0) {
282 pr_err("%s:%d Could not find idx: %d in group: %d", __func__,
283 __LINE__, idx, group);
284 return -EINVAL;
285 }
286
287 if (sparx5_sdlb_group_is_singular(sparx5, group)) {
288 empty = true;
289 } else if (sparx5_sdlb_group_is_last(sparx5, group, idx)) {
290 /* idx is removed, prev is now last */
291 idx = prev;
292 next = prev;
293 } else if (sparx5_sdlb_group_is_first(sparx5, group, idx)) {
294 /* idx is removed and points to itself, first is next */
295 first = next;
296 next = idx;
297 } else {
298 /* Next is not touched */
299 idx = prev;
300 }
301
302 return sparx5_sdlb_group_link(sparx5, group, idx, first, next, empty);
303}
304
305void sparx5_sdlb_group_init(struct sparx5 *sparx5, u64 max_rate, u32 min_burst,
306 u32 frame_size, u32 idx)
307{
308 const struct sparx5_ops *ops = sparx5->data->ops;
309 u32 thres_shift, mask = 0x01, power = 0;
310 struct sparx5_sdlb_group *group;
311 u64 max_token;
312
313 group = ops->get_sdlb_group(idx);
314
315 /* Number of positions to right-shift LB's threshold value. */
316 while ((min_burst & mask) == 0) {
317 power++;
318 mask <<= 1;
319 }
320 thres_shift = SPX5_SDLB_2CYCLES_TYPE2_THRES_OFFSET - power;
321
322 max_token = (min_burst > SPX5_SDLB_PUP_TOKEN_MAX) ?
323 SPX5_SDLB_PUP_TOKEN_MAX :
324 min_burst;
325 group->pup_interval =
326 sparx5_sdlb_pup_interval_get(sparx5, max_token, max_rate);
327
328 group->frame_size = frame_size;
329
330 spx5_wr(ANA_AC_SDLB_PUP_INTERVAL_PUP_INTERVAL_SET(group->pup_interval),
331 sparx5, ANA_AC_SDLB_PUP_INTERVAL(idx));
332
333 spx5_wr(ANA_AC_SDLB_FRM_RATE_TOKENS_FRM_RATE_TOKENS_SET(frame_size),
334 sparx5, ANA_AC_SDLB_FRM_RATE_TOKENS(idx));
335
336 spx5_wr(ANA_AC_SDLB_LBGRP_MISC_THRES_SHIFT_SET(thres_shift), sparx5,
337 ANA_AC_SDLB_LBGRP_MISC(idx));
338}
1// SPDX-License-Identifier: GPL-2.0+
2/* Microchip Sparx5 Switch driver
3 *
4 * Copyright (c) 2023 Microchip Technology Inc. and its subsidiaries.
5 */
6
7#include "sparx5_main_regs.h"
8#include "sparx5_main.h"
9
10struct sparx5_sdlb_group sdlb_groups[SPX5_SDLB_GROUP_CNT] = {
11 { SPX5_SDLB_GROUP_RATE_MAX, 8192 / 1, 64 }, /* 25 G */
12 { 15000000000ULL, 8192 / 1, 64 }, /* 15 G */
13 { 10000000000ULL, 8192 / 1, 64 }, /* 10 G */
14 { 5000000000ULL, 8192 / 1, 64 }, /* 5 G */
15 { 2500000000ULL, 8192 / 1, 64 }, /* 2.5 G */
16 { 1000000000ULL, 8192 / 2, 64 }, /* 1 G */
17 { 500000000ULL, 8192 / 2, 64 }, /* 500 M */
18 { 100000000ULL, 8192 / 4, 64 }, /* 100 M */
19 { 50000000ULL, 8192 / 4, 64 }, /* 50 M */
20 { 5000000ULL, 8192 / 8, 64 } /* 5 M */
21};
22
23int sparx5_sdlb_clk_hz_get(struct sparx5 *sparx5)
24{
25 u32 clk_per_100ps;
26 u64 clk_hz;
27
28 clk_per_100ps = HSCH_SYS_CLK_PER_100PS_GET(spx5_rd(sparx5,
29 HSCH_SYS_CLK_PER));
30 if (!clk_per_100ps)
31 clk_per_100ps = SPX5_CLK_PER_100PS_DEFAULT;
32
33 clk_hz = (10 * 1000 * 1000) / clk_per_100ps;
34 return clk_hz *= 1000;
35}
36
37static int sparx5_sdlb_pup_interval_get(struct sparx5 *sparx5, u32 max_token,
38 u64 max_rate)
39{
40 u64 clk_hz;
41
42 clk_hz = sparx5_sdlb_clk_hz_get(sparx5);
43
44 return div64_u64((8 * clk_hz * max_token), max_rate);
45}
46
47int sparx5_sdlb_pup_token_get(struct sparx5 *sparx5, u32 pup_interval, u64 rate)
48{
49 u64 clk_hz;
50
51 if (!rate)
52 return SPX5_SDLB_PUP_TOKEN_DISABLE;
53
54 clk_hz = sparx5_sdlb_clk_hz_get(sparx5);
55
56 return DIV64_U64_ROUND_UP((rate * pup_interval), (clk_hz * 8));
57}
58
59static void sparx5_sdlb_group_disable(struct sparx5 *sparx5, u32 group)
60{
61 spx5_rmw(ANA_AC_SDLB_PUP_CTRL_PUP_ENA_SET(0),
62 ANA_AC_SDLB_PUP_CTRL_PUP_ENA, sparx5,
63 ANA_AC_SDLB_PUP_CTRL(group));
64}
65
66static void sparx5_sdlb_group_enable(struct sparx5 *sparx5, u32 group)
67{
68 spx5_rmw(ANA_AC_SDLB_PUP_CTRL_PUP_ENA_SET(1),
69 ANA_AC_SDLB_PUP_CTRL_PUP_ENA, sparx5,
70 ANA_AC_SDLB_PUP_CTRL(group));
71}
72
73static u32 sparx5_sdlb_group_get_first(struct sparx5 *sparx5, u32 group)
74{
75 u32 val;
76
77 val = spx5_rd(sparx5, ANA_AC_SDLB_XLB_START(group));
78
79 return ANA_AC_SDLB_XLB_START_LBSET_START_GET(val);
80}
81
82static u32 sparx5_sdlb_group_get_next(struct sparx5 *sparx5, u32 group,
83 u32 lb)
84{
85 u32 val;
86
87 val = spx5_rd(sparx5, ANA_AC_SDLB_XLB_NEXT(lb));
88
89 return ANA_AC_SDLB_XLB_NEXT_LBSET_NEXT_GET(val);
90}
91
92static bool sparx5_sdlb_group_is_first(struct sparx5 *sparx5, u32 group,
93 u32 lb)
94{
95 return lb == sparx5_sdlb_group_get_first(sparx5, group);
96}
97
98static bool sparx5_sdlb_group_is_last(struct sparx5 *sparx5, u32 group,
99 u32 lb)
100{
101 return lb == sparx5_sdlb_group_get_next(sparx5, group, lb);
102}
103
104static bool sparx5_sdlb_group_is_empty(struct sparx5 *sparx5, u32 group)
105{
106 u32 val;
107
108 val = spx5_rd(sparx5, ANA_AC_SDLB_PUP_CTRL(group));
109
110 return ANA_AC_SDLB_PUP_CTRL_PUP_ENA_GET(val) == 0;
111}
112
113static u32 sparx5_sdlb_group_get_last(struct sparx5 *sparx5, u32 group)
114{
115 u32 itr, next;
116
117 itr = sparx5_sdlb_group_get_first(sparx5, group);
118
119 for (;;) {
120 next = sparx5_sdlb_group_get_next(sparx5, group, itr);
121 if (itr == next)
122 return itr;
123
124 itr = next;
125 }
126}
127
128static bool sparx5_sdlb_group_is_singular(struct sparx5 *sparx5, u32 group)
129{
130 if (sparx5_sdlb_group_is_empty(sparx5, group))
131 return false;
132
133 return sparx5_sdlb_group_get_first(sparx5, group) ==
134 sparx5_sdlb_group_get_last(sparx5, group);
135}
136
137static int sparx5_sdlb_group_get_adjacent(struct sparx5 *sparx5, u32 group,
138 u32 idx, u32 *prev, u32 *next,
139 u32 *first)
140{
141 u32 itr;
142
143 *first = sparx5_sdlb_group_get_first(sparx5, group);
144 *prev = *first;
145 *next = *first;
146 itr = *first;
147
148 for (;;) {
149 *next = sparx5_sdlb_group_get_next(sparx5, group, itr);
150
151 if (itr == idx)
152 return 0; /* Found it */
153
154 if (itr == *next)
155 return -EINVAL; /* Was not found */
156
157 *prev = itr;
158 itr = *next;
159 }
160}
161
162static int sparx5_sdlb_group_get_count(struct sparx5 *sparx5, u32 group)
163{
164 u32 itr, next;
165 int count = 0;
166
167 itr = sparx5_sdlb_group_get_first(sparx5, group);
168
169 for (;;) {
170 next = sparx5_sdlb_group_get_next(sparx5, group, itr);
171 if (itr == next)
172 return count;
173
174 itr = next;
175 count++;
176 }
177}
178
179int sparx5_sdlb_group_get_by_rate(struct sparx5 *sparx5, u32 rate, u32 burst)
180{
181 const struct sparx5_sdlb_group *group;
182 u64 rate_bps;
183 int i, count;
184
185 rate_bps = rate * 1000;
186
187 for (i = SPX5_SDLB_GROUP_CNT - 1; i >= 0; i--) {
188 group = &sdlb_groups[i];
189
190 count = sparx5_sdlb_group_get_count(sparx5, i);
191
192 /* Check that this group is not full.
193 * According to LB group configuration rules: the number of XLBs
194 * in a group must not exceed PUP_INTERVAL/4 - 1.
195 */
196 if (count > ((group->pup_interval / 4) - 1))
197 continue;
198
199 if (rate_bps < group->max_rate)
200 return i;
201 }
202
203 return -ENOSPC;
204}
205
206int sparx5_sdlb_group_get_by_index(struct sparx5 *sparx5, u32 idx, u32 *group)
207{
208 u32 itr, next;
209 int i;
210
211 for (i = 0; i < SPX5_SDLB_GROUP_CNT; i++) {
212 if (sparx5_sdlb_group_is_empty(sparx5, i))
213 continue;
214
215 itr = sparx5_sdlb_group_get_first(sparx5, i);
216
217 for (;;) {
218 next = sparx5_sdlb_group_get_next(sparx5, i, itr);
219
220 if (itr == idx) {
221 *group = i;
222 return 0; /* Found it */
223 }
224 if (itr == next)
225 break; /* Was not found */
226
227 itr = next;
228 }
229 }
230
231 return -EINVAL;
232}
233
234static int sparx5_sdlb_group_link(struct sparx5 *sparx5, u32 group, u32 idx,
235 u32 first, u32 next, bool empty)
236{
237 /* Stop leaking */
238 sparx5_sdlb_group_disable(sparx5, group);
239
240 if (empty)
241 return 0;
242
243 /* Link insertion lb to next lb */
244 spx5_wr(ANA_AC_SDLB_XLB_NEXT_LBSET_NEXT_SET(next) |
245 ANA_AC_SDLB_XLB_NEXT_LBGRP_SET(group),
246 sparx5, ANA_AC_SDLB_XLB_NEXT(idx));
247
248 /* Set the first lb */
249 spx5_wr(ANA_AC_SDLB_XLB_START_LBSET_START_SET(first), sparx5,
250 ANA_AC_SDLB_XLB_START(group));
251
252 /* Start leaking */
253 sparx5_sdlb_group_enable(sparx5, group);
254
255 return 0;
256};
257
258int sparx5_sdlb_group_add(struct sparx5 *sparx5, u32 group, u32 idx)
259{
260 u32 first, next;
261
262 /* We always add to head of the list */
263 first = idx;
264
265 if (sparx5_sdlb_group_is_empty(sparx5, group))
266 next = idx;
267 else
268 next = sparx5_sdlb_group_get_first(sparx5, group);
269
270 return sparx5_sdlb_group_link(sparx5, group, idx, first, next, false);
271}
272
273int sparx5_sdlb_group_del(struct sparx5 *sparx5, u32 group, u32 idx)
274{
275 u32 first, next, prev;
276 bool empty = false;
277
278 if (sparx5_sdlb_group_get_adjacent(sparx5, group, idx, &prev, &next,
279 &first) < 0) {
280 pr_err("%s:%d Could not find idx: %d in group: %d", __func__,
281 __LINE__, idx, group);
282 return -EINVAL;
283 }
284
285 if (sparx5_sdlb_group_is_singular(sparx5, group)) {
286 empty = true;
287 } else if (sparx5_sdlb_group_is_last(sparx5, group, idx)) {
288 /* idx is removed, prev is now last */
289 idx = prev;
290 next = prev;
291 } else if (sparx5_sdlb_group_is_first(sparx5, group, idx)) {
292 /* idx is removed and points to itself, first is next */
293 first = next;
294 next = idx;
295 } else {
296 /* Next is not touched */
297 idx = prev;
298 }
299
300 return sparx5_sdlb_group_link(sparx5, group, idx, first, next, empty);
301}
302
303void sparx5_sdlb_group_init(struct sparx5 *sparx5, u64 max_rate, u32 min_burst,
304 u32 frame_size, u32 idx)
305{
306 u32 thres_shift, mask = 0x01, power = 0;
307 struct sparx5_sdlb_group *group;
308 u64 max_token;
309
310 group = &sdlb_groups[idx];
311
312 /* Number of positions to right-shift LB's threshold value. */
313 while ((min_burst & mask) == 0) {
314 power++;
315 mask <<= 1;
316 }
317 thres_shift = SPX5_SDLB_2CYCLES_TYPE2_THRES_OFFSET - power;
318
319 max_token = (min_burst > SPX5_SDLB_PUP_TOKEN_MAX) ?
320 SPX5_SDLB_PUP_TOKEN_MAX :
321 min_burst;
322 group->pup_interval =
323 sparx5_sdlb_pup_interval_get(sparx5, max_token, max_rate);
324
325 group->frame_size = frame_size;
326
327 spx5_wr(ANA_AC_SDLB_PUP_INTERVAL_PUP_INTERVAL_SET(group->pup_interval),
328 sparx5, ANA_AC_SDLB_PUP_INTERVAL(idx));
329
330 spx5_wr(ANA_AC_SDLB_FRM_RATE_TOKENS_FRM_RATE_TOKENS_SET(frame_size),
331 sparx5, ANA_AC_SDLB_FRM_RATE_TOKENS(idx));
332
333 spx5_wr(ANA_AC_SDLB_LBGRP_MISC_THRES_SHIFT_SET(thres_shift), sparx5,
334 ANA_AC_SDLB_LBGRP_MISC(idx));
335}