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