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
10struct 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
23int 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
37static 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
47int 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
59static 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
66static 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
73static 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
82static 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
92static 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
98static 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
104static 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
113static 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
128static 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
137static 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
162static 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
179int 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
206int 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
234static 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
258int 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
273int 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
303void 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