xref: /linux/drivers/net/ethernet/microchip/sparx5/sparx5_sdlb.c (revision fcc79e1714e8c2b8e216dc3149812edd37884eef)
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 u64 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 
38 static 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 
48 int 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 
60 static 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 
67 static 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 
74 static 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 
83 static 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 
93 static 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 
99 static 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 
105 static 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 
114 static 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 
129 static 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 
138 static 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 
163 static 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 
180 int 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 
208 int 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 
236 static 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 
260 int 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 
275 int 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 
305 void 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 }
339