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