xref: /linux/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c (revision fcc79e1714e8c2b8e216dc3149812edd37884eef)
1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
3  *
4  * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
5  */
6 
7 #include <linux/module.h>
8 #include <linux/device.h>
9 
10 #include "sparx5_main_regs.h"
11 #include "sparx5_main.h"
12 
13 /* QSYS calendar information */
14 #define SPX5_PORTS_PER_CALREG          10  /* Ports mapped in a calendar register */
15 #define SPX5_CALBITS_PER_PORT          3   /* Bit per port in calendar register */
16 
17 /* DSM calendar information */
18 #define SPX5_DSM_CAL_TAXIS             8
19 #define SPX5_DSM_CAL_BW_LOSS           553
20 
21 #define SPX5_TAXI_PORT_MAX             70
22 
23 #define SPEED_12500                    12500
24 
25 /* Maps from taxis to port numbers */
26 static u32 sparx5_taxi_ports[SPX5_DSM_CAL_TAXIS][SPX5_DSM_CAL_MAX_DEVS_PER_TAXI] = {
27 	{57, 12, 0, 1, 2, 16, 17, 18, 19, 20, 21, 22, 23},
28 	{58, 13, 3, 4, 5, 24, 25, 26, 27, 28, 29, 30, 31},
29 	{59, 14, 6, 7, 8, 32, 33, 34, 35, 36, 37, 38, 39},
30 	{60, 15, 9, 10, 11, 40, 41, 42, 43, 44, 45, 46, 47},
31 	{61, 48, 49, 50, 99, 99, 99, 99, 99, 99, 99, 99, 99},
32 	{62, 51, 52, 53, 99, 99, 99, 99, 99, 99, 99, 99, 99},
33 	{56, 63, 54, 55, 99, 99, 99, 99, 99, 99, 99, 99, 99},
34 	{64, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
35 };
36 
37 static u32 sparx5_target_bandwidth(struct sparx5 *sparx5)
38 {
39 	switch (sparx5->target_ct) {
40 	case SPX5_TARGET_CT_7546:
41 	case SPX5_TARGET_CT_7546TSN:
42 		return 65000;
43 	case SPX5_TARGET_CT_7549:
44 	case SPX5_TARGET_CT_7549TSN:
45 		return 91000;
46 	case SPX5_TARGET_CT_7552:
47 	case SPX5_TARGET_CT_7552TSN:
48 		return 129000;
49 	case SPX5_TARGET_CT_7556:
50 	case SPX5_TARGET_CT_7556TSN:
51 		return 161000;
52 	case SPX5_TARGET_CT_7558:
53 	case SPX5_TARGET_CT_7558TSN:
54 		return 201000;
55 	case SPX5_TARGET_CT_LAN9691VAO:
56 		return 46000;
57 	case SPX5_TARGET_CT_LAN9694RED:
58 	case SPX5_TARGET_CT_LAN9694TSN:
59 	case SPX5_TARGET_CT_LAN9694:
60 		return 68000;
61 	case SPX5_TARGET_CT_LAN9696RED:
62 	case SPX5_TARGET_CT_LAN9696TSN:
63 	case SPX5_TARGET_CT_LAN9692VAO:
64 	case SPX5_TARGET_CT_LAN9696:
65 		return 88000;
66 	case SPX5_TARGET_CT_LAN9698RED:
67 	case SPX5_TARGET_CT_LAN9698TSN:
68 	case SPX5_TARGET_CT_LAN9693VAO:
69 	case SPX5_TARGET_CT_LAN9698:
70 		return 101000;
71 	default:
72 		return 0;
73 	}
74 }
75 
76 static u32 sparx5_clk_to_bandwidth(enum sparx5_core_clockfreq cclock)
77 {
78 	switch (cclock) {
79 	case SPX5_CORE_CLOCK_250MHZ: return 83000; /* 250000 / 3 */
80 	case SPX5_CORE_CLOCK_328MHZ: return 109375; /* 328000 / 3 */
81 	case SPX5_CORE_CLOCK_500MHZ: return 166000; /* 500000 / 3 */
82 	case SPX5_CORE_CLOCK_625MHZ: return  208000; /* 625000 / 3 */
83 	default: return 0;
84 	}
85 	return 0;
86 }
87 
88 u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
89 {
90 	switch (speed) {
91 	case SPX5_CAL_SPEED_1G:   return 1000;
92 	case SPX5_CAL_SPEED_2G5:  return 2500;
93 	case SPX5_CAL_SPEED_5G:   return 5000;
94 	case SPX5_CAL_SPEED_10G:  return 10000;
95 	case SPX5_CAL_SPEED_25G:  return 25000;
96 	case SPX5_CAL_SPEED_0G5:  return 500;
97 	case SPX5_CAL_SPEED_12G5: return 12500;
98 	default: return 0;
99 	}
100 }
101 EXPORT_SYMBOL_GPL(sparx5_cal_speed_to_value);
102 
103 static u32 sparx5_bandwidth_to_calendar(u32 bw)
104 {
105 	switch (bw) {
106 	case SPEED_10:      return SPX5_CAL_SPEED_0G5;
107 	case SPEED_100:     return SPX5_CAL_SPEED_0G5;
108 	case SPEED_1000:    return SPX5_CAL_SPEED_1G;
109 	case SPEED_2500:    return SPX5_CAL_SPEED_2G5;
110 	case SPEED_5000:    return SPX5_CAL_SPEED_5G;
111 	case SPEED_10000:   return SPX5_CAL_SPEED_10G;
112 	case SPEED_12500:   return SPX5_CAL_SPEED_12G5;
113 	case SPEED_25000:   return SPX5_CAL_SPEED_25G;
114 	case SPEED_UNKNOWN: return SPX5_CAL_SPEED_1G;
115 	default:            return SPX5_CAL_SPEED_NONE;
116 	}
117 }
118 
119 enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5, u32 portno)
120 {
121 	struct sparx5_port *port;
122 
123 	if (portno >= sparx5->data->consts->n_ports) {
124 		/* Internal ports */
125 		if (portno ==
126 			    sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_0) ||
127 		    portno ==
128 			    sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_1)) {
129 			/* Equals 1.25G */
130 			return SPX5_CAL_SPEED_2G5;
131 		} else if (portno ==
132 			   sparx5_get_internal_port(sparx5, SPX5_PORT_VD0)) {
133 			/* IPMC only idle BW */
134 			return SPX5_CAL_SPEED_NONE;
135 		} else if (portno ==
136 			   sparx5_get_internal_port(sparx5, SPX5_PORT_VD1)) {
137 			/* OAM only idle BW */
138 			return SPX5_CAL_SPEED_NONE;
139 		} else if (portno ==
140 			   sparx5_get_internal_port(sparx5, SPX5_PORT_VD2)) {
141 			/* IPinIP gets only idle BW */
142 			return SPX5_CAL_SPEED_NONE;
143 		}
144 		/* not in port map */
145 		return SPX5_CAL_SPEED_NONE;
146 	}
147 	/* Front ports - may be used */
148 	port = sparx5->ports[portno];
149 	if (!port)
150 		return SPX5_CAL_SPEED_NONE;
151 	return sparx5_bandwidth_to_calendar(port->conf.bandwidth);
152 }
153 EXPORT_SYMBOL_GPL(sparx5_get_port_cal_speed);
154 
155 /* Auto configure the QSYS calendar based on port configuration */
156 int sparx5_config_auto_calendar(struct sparx5 *sparx5)
157 {
158 	const struct sparx5_consts *consts = sparx5->data->consts;
159 	u32 cal[7], value, idx, portno;
160 	u32 max_core_bw;
161 	u32 total_bw = 0, used_port_bw = 0;
162 	int err = 0;
163 	enum sparx5_cal_bw spd;
164 
165 	memset(cal, 0, sizeof(cal));
166 
167 	max_core_bw = sparx5_clk_to_bandwidth(sparx5->coreclock);
168 	if (max_core_bw == 0) {
169 		dev_err(sparx5->dev, "Core clock not supported");
170 		return -EINVAL;
171 	}
172 
173 	/* Setup the calendar with the bandwidth to each port */
174 	for (portno = 0; portno < consts->n_ports_all; portno++) {
175 		u64 reg, offset, this_bw;
176 
177 		spd = sparx5_get_port_cal_speed(sparx5, portno);
178 		if (spd == SPX5_CAL_SPEED_NONE)
179 			continue;
180 
181 		this_bw = sparx5_cal_speed_to_value(spd);
182 		if (portno < consts->n_ports)
183 			used_port_bw += this_bw;
184 		else
185 			/* Internal ports are granted half the value */
186 			this_bw = this_bw / 2;
187 		total_bw += this_bw;
188 		reg = portno;
189 		offset = do_div(reg, SPX5_PORTS_PER_CALREG);
190 		cal[reg] |= spd << (offset * SPX5_CALBITS_PER_PORT);
191 	}
192 
193 	if (used_port_bw > sparx5_target_bandwidth(sparx5)) {
194 		dev_err(sparx5->dev,
195 			"Port BW %u above target BW %u\n",
196 			used_port_bw, sparx5_target_bandwidth(sparx5));
197 		return -EINVAL;
198 	}
199 
200 	if (total_bw > max_core_bw) {
201 		dev_err(sparx5->dev,
202 			"Total BW %u above switch core BW %u\n",
203 			total_bw, max_core_bw);
204 		return -EINVAL;
205 	}
206 
207 	/* Halt the calendar while changing it */
208 	if (is_sparx5(sparx5))
209 		spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(10),
210 			 QSYS_CAL_CTRL_CAL_MODE,
211 			 sparx5, QSYS_CAL_CTRL);
212 
213 	/* Assign port bandwidth to auto calendar */
214 	for (idx = 0; idx < consts->n_auto_cals; idx++)
215 		spx5_wr(cal[idx], sparx5, QSYS_CAL_AUTO(idx));
216 
217 	/* Increase grant rate of all ports to account for
218 	 * core clock ppm deviations
219 	 */
220 	spx5_rmw(QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE_SET(671), /* 672->671 */
221 		 QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE,
222 		 sparx5,
223 		 QSYS_CAL_CTRL);
224 
225 	/* Grant idle usage to VD 0-2 */
226 	for (idx = 2; idx < 5; idx++)
227 		spx5_wr(HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA_SET(12),
228 			sparx5,
229 			HSCH_OUTB_SHARE_ENA(idx));
230 
231 	/* Enable Auto mode */
232 	spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(8),
233 		 QSYS_CAL_CTRL_CAL_MODE,
234 		 sparx5, QSYS_CAL_CTRL);
235 
236 	/* Verify successful calendar config */
237 	value = spx5_rd(sparx5, QSYS_CAL_CTRL);
238 	if (QSYS_CAL_CTRL_CAL_AUTO_ERROR_GET(value)) {
239 		dev_err(sparx5->dev, "QSYS calendar error\n");
240 		err = -EINVAL;
241 	}
242 	return err;
243 }
244 
245 static u32 sparx5_dsm_exb_gcd(u32 a, u32 b)
246 {
247 	if (b == 0)
248 		return a;
249 	return sparx5_dsm_exb_gcd(b, a % b);
250 }
251 
252 static u32 sparx5_dsm_cal_len(u32 *cal)
253 {
254 	u32 idx = 0, len = 0;
255 
256 	while (idx < SPX5_DSM_CAL_LEN) {
257 		if (cal[idx] != SPX5_DSM_CAL_EMPTY)
258 			len++;
259 		idx++;
260 	}
261 	return len;
262 }
263 
264 static u32 sparx5_dsm_cp_cal(u32 *sched)
265 {
266 	u32 idx = 0, tmp;
267 
268 	while (idx < SPX5_DSM_CAL_LEN) {
269 		if (sched[idx] != SPX5_DSM_CAL_EMPTY) {
270 			tmp = sched[idx];
271 			sched[idx] = SPX5_DSM_CAL_EMPTY;
272 			return tmp;
273 		}
274 		idx++;
275 	}
276 	return SPX5_DSM_CAL_EMPTY;
277 }
278 
279 int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
280 			     struct sparx5_calendar_data *data)
281 {
282 	bool slow_mode;
283 	u32 gcd, idx, sum, min, factor;
284 	u32 num_of_slots, slot_spd, empty_slots;
285 	u32 taxi_bw, clk_period_ps;
286 
287 	clk_period_ps = sparx5_clk_period(sparx5->coreclock);
288 	taxi_bw = 128 * 1000000 / clk_period_ps;
289 	slow_mode = !!(clk_period_ps > 2000);
290 	memcpy(data->taxi_ports, &sparx5_taxi_ports[taxi],
291 	       sizeof(data->taxi_ports));
292 
293 	for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) {
294 		data->new_slots[idx] = SPX5_DSM_CAL_EMPTY;
295 		data->schedule[idx] = SPX5_DSM_CAL_EMPTY;
296 		data->temp_sched[idx] = SPX5_DSM_CAL_EMPTY;
297 	}
298 	/* Default empty calendar */
299 	data->schedule[0] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
300 
301 	/* Map ports to taxi positions */
302 	for (idx = 0; idx < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; idx++) {
303 		u32 portno = data->taxi_ports[idx];
304 
305 		if (portno < sparx5->data->consts->n_ports_all) {
306 			data->taxi_speeds[idx] = sparx5_cal_speed_to_value
307 				(sparx5_get_port_cal_speed(sparx5, portno));
308 		} else {
309 			data->taxi_speeds[idx] = 0;
310 		}
311 	}
312 
313 	sum = 0;
314 	min = 25000;
315 	for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) {
316 		u32 jdx;
317 
318 		sum += data->taxi_speeds[idx];
319 		if (data->taxi_speeds[idx] && data->taxi_speeds[idx] < min)
320 			min = data->taxi_speeds[idx];
321 		gcd = min;
322 		for (jdx = 0; jdx < ARRAY_SIZE(data->taxi_speeds); jdx++)
323 			gcd = sparx5_dsm_exb_gcd(gcd, data->taxi_speeds[jdx]);
324 	}
325 	if (sum == 0) /* Empty calendar */
326 		return 0;
327 	/* Make room for overhead traffic */
328 	factor = 100 * 100 * 1000 / (100 * 100 - SPX5_DSM_CAL_BW_LOSS);
329 
330 	if (sum * factor > (taxi_bw * 1000)) {
331 		dev_err(sparx5->dev,
332 			"Taxi %u, Requested BW %u above available BW %u\n",
333 			taxi, sum, taxi_bw);
334 		return -EINVAL;
335 	}
336 	for (idx = 0; idx < 4; idx++) {
337 		u32 raw_spd;
338 
339 		if (idx == 0)
340 			raw_spd = gcd / 5;
341 		else if (idx == 1)
342 			raw_spd = gcd / 2;
343 		else if (idx == 2)
344 			raw_spd = gcd;
345 		else
346 			raw_spd = min;
347 		slot_spd = raw_spd * factor / 1000;
348 		num_of_slots = taxi_bw / slot_spd;
349 		if (num_of_slots <= 64)
350 			break;
351 	}
352 
353 	num_of_slots = num_of_slots > 64 ? 64 : num_of_slots;
354 	slot_spd = taxi_bw / num_of_slots;
355 
356 	sum = 0;
357 	for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) {
358 		u32 spd = data->taxi_speeds[idx];
359 		u32 adjusted_speed = data->taxi_speeds[idx] * factor / 1000;
360 
361 		if (adjusted_speed > 0) {
362 			data->avg_dist[idx] = (128 * 1000000 * 10) /
363 				(adjusted_speed * clk_period_ps);
364 		} else {
365 			data->avg_dist[idx] = -1;
366 		}
367 		data->dev_slots[idx] = ((spd * factor / slot_spd) + 999) / 1000;
368 		if (spd != 25000 && (spd != 10000 || !slow_mode)) {
369 			if (num_of_slots < (5 * data->dev_slots[idx])) {
370 				dev_err(sparx5->dev,
371 					"Taxi %u, speed %u, Low slot sep.\n",
372 					taxi, spd);
373 				return -EINVAL;
374 			}
375 		}
376 		sum += data->dev_slots[idx];
377 		if (sum > num_of_slots) {
378 			dev_err(sparx5->dev,
379 				"Taxi %u with overhead factor %u\n",
380 				taxi, factor);
381 			return -EINVAL;
382 		}
383 	}
384 
385 	empty_slots = num_of_slots - sum;
386 
387 	for (idx = 0; idx < empty_slots; idx++)
388 		data->schedule[idx] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
389 
390 	for (idx = 1; idx < num_of_slots; idx++) {
391 		u32 indices_len = 0;
392 		u32 slot, jdx, kdx, ts;
393 		s32 cnt;
394 		u32 num_of_old_slots, num_of_new_slots, tgt_score;
395 
396 		for (slot = 0; slot < ARRAY_SIZE(data->dev_slots); slot++) {
397 			if (data->dev_slots[slot] == idx) {
398 				data->indices[indices_len] = slot;
399 				indices_len++;
400 			}
401 		}
402 		if (indices_len == 0)
403 			continue;
404 		kdx = 0;
405 		for (slot = 0; slot < idx; slot++) {
406 			for (jdx = 0; jdx < indices_len; jdx++, kdx++)
407 				data->new_slots[kdx] = data->indices[jdx];
408 		}
409 
410 		for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
411 			if (data->schedule[slot] == SPX5_DSM_CAL_EMPTY)
412 				break;
413 		}
414 
415 		num_of_old_slots =  slot;
416 		num_of_new_slots =  kdx;
417 		cnt = 0;
418 		ts = 0;
419 
420 		if (num_of_new_slots > num_of_old_slots) {
421 			memcpy(data->short_list, data->schedule,
422 			       sizeof(data->short_list));
423 			memcpy(data->long_list, data->new_slots,
424 			       sizeof(data->long_list));
425 			tgt_score = 100000 * num_of_old_slots /
426 				num_of_new_slots;
427 		} else {
428 			memcpy(data->short_list, data->new_slots,
429 			       sizeof(data->short_list));
430 			memcpy(data->long_list, data->schedule,
431 			       sizeof(data->long_list));
432 			tgt_score = 100000 * num_of_new_slots /
433 				num_of_old_slots;
434 		}
435 
436 		while (sparx5_dsm_cal_len(data->short_list) > 0 ||
437 		       sparx5_dsm_cal_len(data->long_list) > 0) {
438 			u32 act = 0;
439 
440 			if (sparx5_dsm_cal_len(data->short_list) > 0) {
441 				data->temp_sched[ts] =
442 					sparx5_dsm_cp_cal(data->short_list);
443 				ts++;
444 				cnt += 100000;
445 				act = 1;
446 			}
447 			while (sparx5_dsm_cal_len(data->long_list) > 0 &&
448 			       cnt > 0) {
449 				data->temp_sched[ts] =
450 					sparx5_dsm_cp_cal(data->long_list);
451 				ts++;
452 				cnt -= tgt_score;
453 				act = 1;
454 			}
455 			if (act == 0) {
456 				dev_err(sparx5->dev,
457 					"Error in DSM calendar calculation\n");
458 				return -EINVAL;
459 			}
460 		}
461 
462 		for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
463 			if (data->temp_sched[slot] == SPX5_DSM_CAL_EMPTY)
464 				break;
465 		}
466 		for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
467 			data->schedule[slot] = data->temp_sched[slot];
468 			data->temp_sched[slot] = SPX5_DSM_CAL_EMPTY;
469 			data->new_slots[slot] = SPX5_DSM_CAL_EMPTY;
470 		}
471 	}
472 	return 0;
473 }
474 
475 static int sparx5_dsm_calendar_check(struct sparx5 *sparx5,
476 				     struct sparx5_calendar_data *data)
477 {
478 	u32 num_of_slots, idx, port;
479 	int cnt, max_dist;
480 	u32 slot_indices[SPX5_DSM_CAL_LEN], distances[SPX5_DSM_CAL_LEN];
481 	u32 cal_length = sparx5_dsm_cal_len(data->schedule);
482 
483 	for (port = 0; port < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; port++) {
484 		num_of_slots = 0;
485 		max_dist = data->avg_dist[port];
486 		for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) {
487 			slot_indices[idx] = SPX5_DSM_CAL_EMPTY;
488 			distances[idx] = SPX5_DSM_CAL_EMPTY;
489 		}
490 
491 		for (idx = 0; idx < cal_length; idx++) {
492 			if (data->schedule[idx] == port) {
493 				slot_indices[num_of_slots] = idx;
494 				num_of_slots++;
495 			}
496 		}
497 
498 		slot_indices[num_of_slots] = slot_indices[0] + cal_length;
499 
500 		for (idx = 0; idx < num_of_slots; idx++) {
501 			distances[idx] = (slot_indices[idx + 1] -
502 					  slot_indices[idx]) * 10;
503 		}
504 
505 		for (idx = 0; idx < num_of_slots; idx++) {
506 			u32 jdx, kdx;
507 
508 			cnt = distances[idx] - max_dist;
509 			if (cnt < 0)
510 				cnt = -cnt;
511 			kdx = 0;
512 			for (jdx = (idx + 1) % num_of_slots;
513 			     jdx != idx;
514 			     jdx = (jdx + 1) % num_of_slots, kdx++) {
515 				cnt =  cnt + distances[jdx] - max_dist;
516 				if (cnt < 0)
517 					cnt = -cnt;
518 				if (cnt > max_dist)
519 					goto check_err;
520 			}
521 		}
522 	}
523 	return 0;
524 check_err:
525 	dev_err(sparx5->dev,
526 		"Port %u: distance %u above limit %d\n",
527 		port, cnt, max_dist);
528 	return -EINVAL;
529 }
530 
531 static int sparx5_dsm_calendar_update(struct sparx5 *sparx5, u32 taxi,
532 				      struct sparx5_calendar_data *data)
533 {
534 	u32 cal_len = sparx5_dsm_cal_len(data->schedule), len, idx;
535 
536 	if (!is_sparx5(sparx5)) {
537 		u32 val, act;
538 
539 		val = spx5_rd(sparx5, DSM_TAXI_CAL_CFG(taxi));
540 		act = DSM_TAXI_CAL_CFG_CAL_SEL_STAT_GET(val);
541 
542 		spx5_rmw(DSM_TAXI_CAL_CFG_CAL_PGM_SEL_SET(!act),
543 			 DSM_TAXI_CAL_CFG_CAL_PGM_SEL,
544 			 sparx5, DSM_TAXI_CAL_CFG(taxi));
545 	}
546 
547 	spx5_rmw(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(1),
548 		 DSM_TAXI_CAL_CFG_CAL_PGM_ENA,
549 		 sparx5,
550 		 DSM_TAXI_CAL_CFG(taxi));
551 	for (idx = 0; idx < cal_len; idx++) {
552 		spx5_rmw(DSM_TAXI_CAL_CFG_CAL_IDX_SET(idx),
553 			 DSM_TAXI_CAL_CFG_CAL_IDX,
554 			 sparx5,
555 			 DSM_TAXI_CAL_CFG(taxi));
556 		spx5_rmw(DSM_TAXI_CAL_CFG_CAL_PGM_VAL_SET(data->schedule[idx]),
557 			 DSM_TAXI_CAL_CFG_CAL_PGM_VAL,
558 			 sparx5,
559 			 DSM_TAXI_CAL_CFG(taxi));
560 	}
561 	spx5_rmw(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(0),
562 		 DSM_TAXI_CAL_CFG_CAL_PGM_ENA,
563 		 sparx5,
564 		 DSM_TAXI_CAL_CFG(taxi));
565 	len = DSM_TAXI_CAL_CFG_CAL_CUR_LEN_GET(spx5_rd(sparx5,
566 						       DSM_TAXI_CAL_CFG(taxi)));
567 	if (len != cal_len - 1)
568 		goto update_err;
569 
570 	if (!is_sparx5(sparx5)) {
571 		spx5_rmw(DSM_TAXI_CAL_CFG_CAL_SWITCH_SET(1),
572 			 DSM_TAXI_CAL_CFG_CAL_SWITCH,
573 			 sparx5, DSM_TAXI_CAL_CFG(taxi));
574 	}
575 
576 	return 0;
577 update_err:
578 	dev_err(sparx5->dev, "Incorrect calendar length: %u\n", len);
579 	return -EINVAL;
580 }
581 
582 /* Configure the DSM calendar based on port configuration */
583 int sparx5_config_dsm_calendar(struct sparx5 *sparx5)
584 {
585 	const struct sparx5_ops *ops = sparx5->data->ops;
586 	int taxi;
587 	struct sparx5_calendar_data *data;
588 	int err = 0;
589 
590 	data = kzalloc(sizeof(*data), GFP_KERNEL);
591 	if (!data)
592 		return -ENOMEM;
593 
594 	for (taxi = 0; taxi < sparx5->data->consts->n_dsm_cal_taxis; ++taxi) {
595 		err = ops->dsm_calendar_calc(sparx5, taxi, data);
596 		if (err) {
597 			dev_err(sparx5->dev, "DSM calendar calculation failed\n");
598 			goto cal_out;
599 		}
600 		err = sparx5_dsm_calendar_check(sparx5, data);
601 		if (err) {
602 			dev_err(sparx5->dev, "DSM calendar check failed\n");
603 			goto cal_out;
604 		}
605 		err = sparx5_dsm_calendar_update(sparx5, taxi, data);
606 		if (err) {
607 			dev_err(sparx5->dev, "DSM calendar update failed\n");
608 			goto cal_out;
609 		}
610 	}
611 cal_out:
612 	kfree(data);
613 	return err;
614 }
615