]> Git Repo - J-linux.git/blob - drivers/net/ethernet/microchip/sparx5/sparx5_sdlb.c
Merge tag 'kbuild-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy...
[J-linux.git] / drivers / net / ethernet / microchip / sparx5 / sparx5_sdlb.c
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
10 struct 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
23 int 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
37 static 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
47 int 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
59 static 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
66 static 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
73 static 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
82 static 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
92 static 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
98 static 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
104 static 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
113 static 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
128 static 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
137 static 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
162 static 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
179 int 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
206 int 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
234 static 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
258 int 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
273 int 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
303 void 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 }
This page took 0.04696 seconds and 4 git commands to generate.