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(dividend: (8 * clk_hz * max_token), divisor: 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, lb: 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, lb: 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, lb: 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, group: 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, group: i)) |
213 | continue; |
214 | |
215 | itr = sparx5_sdlb_group_get_first(sparx5, group: i); |
216 | |
217 | for (;;) { |
218 | next = sparx5_sdlb_group_get_next(sparx5, group: i, lb: 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, empty: 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: &prev, next: &next, |
279 | first: &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, lb: 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, lb: 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 | } |
336 | |