xref: /linux/drivers/net/ethernet/microchip/sparx5/sparx5_sdlb.c (revision 8a922b7728a93d837954315c98b84f6b78de0c4f)
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 }
336