0001
0002
0003
0004
0005
0006
0007 #include <linux/module.h>
0008 #include <linux/device.h>
0009
0010 #include "sparx5_main_regs.h"
0011 #include "sparx5_main.h"
0012
0013
0014 #define SPX5_PORTS_PER_CALREG 10
0015 #define SPX5_CALBITS_PER_PORT 3
0016
0017
0018 #define SPX5_DSM_CAL_LEN 64
0019 #define SPX5_DSM_CAL_EMPTY 0xFFFF
0020 #define SPX5_DSM_CAL_MAX_DEVS_PER_TAXI 13
0021 #define SPX5_DSM_CAL_TAXIS 8
0022 #define SPX5_DSM_CAL_BW_LOSS 553
0023
0024 #define SPX5_TAXI_PORT_MAX 70
0025
0026 #define SPEED_12500 12500
0027
0028
0029 static u32 sparx5_taxi_ports[SPX5_DSM_CAL_TAXIS][SPX5_DSM_CAL_MAX_DEVS_PER_TAXI] = {
0030 {57, 12, 0, 1, 2, 16, 17, 18, 19, 20, 21, 22, 23},
0031 {58, 13, 3, 4, 5, 24, 25, 26, 27, 28, 29, 30, 31},
0032 {59, 14, 6, 7, 8, 32, 33, 34, 35, 36, 37, 38, 39},
0033 {60, 15, 9, 10, 11, 40, 41, 42, 43, 44, 45, 46, 47},
0034 {61, 48, 49, 50, 99, 99, 99, 99, 99, 99, 99, 99, 99},
0035 {62, 51, 52, 53, 99, 99, 99, 99, 99, 99, 99, 99, 99},
0036 {56, 63, 54, 55, 99, 99, 99, 99, 99, 99, 99, 99, 99},
0037 {64, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
0038 };
0039
0040 struct sparx5_calendar_data {
0041 u32 schedule[SPX5_DSM_CAL_LEN];
0042 u32 avg_dist[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
0043 u32 taxi_ports[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
0044 u32 taxi_speeds[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
0045 u32 dev_slots[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
0046 u32 new_slots[SPX5_DSM_CAL_LEN];
0047 u32 temp_sched[SPX5_DSM_CAL_LEN];
0048 u32 indices[SPX5_DSM_CAL_LEN];
0049 u32 short_list[SPX5_DSM_CAL_LEN];
0050 u32 long_list[SPX5_DSM_CAL_LEN];
0051 };
0052
0053 static u32 sparx5_target_bandwidth(struct sparx5 *sparx5)
0054 {
0055 switch (sparx5->target_ct) {
0056 case SPX5_TARGET_CT_7546:
0057 case SPX5_TARGET_CT_7546TSN:
0058 return 65000;
0059 case SPX5_TARGET_CT_7549:
0060 case SPX5_TARGET_CT_7549TSN:
0061 return 91000;
0062 case SPX5_TARGET_CT_7552:
0063 case SPX5_TARGET_CT_7552TSN:
0064 return 129000;
0065 case SPX5_TARGET_CT_7556:
0066 case SPX5_TARGET_CT_7556TSN:
0067 return 161000;
0068 case SPX5_TARGET_CT_7558:
0069 case SPX5_TARGET_CT_7558TSN:
0070 return 201000;
0071 default:
0072 return 0;
0073 }
0074 }
0075
0076
0077 enum sparx5_cal_bw {
0078 SPX5_CAL_SPEED_NONE = 0,
0079 SPX5_CAL_SPEED_1G = 1,
0080 SPX5_CAL_SPEED_2G5 = 2,
0081 SPX5_CAL_SPEED_5G = 3,
0082 SPX5_CAL_SPEED_10G = 4,
0083 SPX5_CAL_SPEED_25G = 5,
0084 SPX5_CAL_SPEED_0G5 = 6,
0085 SPX5_CAL_SPEED_12G5 = 7
0086 };
0087
0088 static u32 sparx5_clk_to_bandwidth(enum sparx5_core_clockfreq cclock)
0089 {
0090 switch (cclock) {
0091 case SPX5_CORE_CLOCK_250MHZ: return 83000;
0092 case SPX5_CORE_CLOCK_500MHZ: return 166000;
0093 case SPX5_CORE_CLOCK_625MHZ: return 208000;
0094 default: return 0;
0095 }
0096 return 0;
0097 }
0098
0099 static u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
0100 {
0101 switch (speed) {
0102 case SPX5_CAL_SPEED_1G: return 1000;
0103 case SPX5_CAL_SPEED_2G5: return 2500;
0104 case SPX5_CAL_SPEED_5G: return 5000;
0105 case SPX5_CAL_SPEED_10G: return 10000;
0106 case SPX5_CAL_SPEED_25G: return 25000;
0107 case SPX5_CAL_SPEED_0G5: return 500;
0108 case SPX5_CAL_SPEED_12G5: return 12500;
0109 default: return 0;
0110 }
0111 }
0112
0113 static u32 sparx5_bandwidth_to_calendar(u32 bw)
0114 {
0115 switch (bw) {
0116 case SPEED_10: return SPX5_CAL_SPEED_0G5;
0117 case SPEED_100: return SPX5_CAL_SPEED_0G5;
0118 case SPEED_1000: return SPX5_CAL_SPEED_1G;
0119 case SPEED_2500: return SPX5_CAL_SPEED_2G5;
0120 case SPEED_5000: return SPX5_CAL_SPEED_5G;
0121 case SPEED_10000: return SPX5_CAL_SPEED_10G;
0122 case SPEED_12500: return SPX5_CAL_SPEED_12G5;
0123 case SPEED_25000: return SPX5_CAL_SPEED_25G;
0124 case SPEED_UNKNOWN: return SPX5_CAL_SPEED_1G;
0125 default: return SPX5_CAL_SPEED_NONE;
0126 }
0127 }
0128
0129 static enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5,
0130 u32 portno)
0131 {
0132 struct sparx5_port *port;
0133
0134 if (portno >= SPX5_PORTS) {
0135
0136 if (portno == SPX5_PORT_CPU_0 || portno == SPX5_PORT_CPU_1) {
0137
0138 return SPX5_CAL_SPEED_2G5;
0139 } else if (portno == SPX5_PORT_VD0) {
0140
0141 return SPX5_CAL_SPEED_NONE;
0142 } else if (portno == SPX5_PORT_VD1) {
0143
0144 return SPX5_CAL_SPEED_NONE;
0145 } else if (portno == SPX5_PORT_VD2) {
0146
0147 return SPX5_CAL_SPEED_NONE;
0148 }
0149
0150 return SPX5_CAL_SPEED_NONE;
0151 }
0152
0153 port = sparx5->ports[portno];
0154 if (!port)
0155 return SPX5_CAL_SPEED_NONE;
0156 return sparx5_bandwidth_to_calendar(port->conf.bandwidth);
0157 }
0158
0159
0160 int sparx5_config_auto_calendar(struct sparx5 *sparx5)
0161 {
0162 u32 cal[7], value, idx, portno;
0163 u32 max_core_bw;
0164 u32 total_bw = 0, used_port_bw = 0;
0165 int err = 0;
0166 enum sparx5_cal_bw spd;
0167
0168 memset(cal, 0, sizeof(cal));
0169
0170 max_core_bw = sparx5_clk_to_bandwidth(sparx5->coreclock);
0171 if (max_core_bw == 0) {
0172 dev_err(sparx5->dev, "Core clock not supported");
0173 return -EINVAL;
0174 }
0175
0176
0177 for (portno = 0; portno < SPX5_PORTS_ALL; portno++) {
0178 u64 reg, offset, this_bw;
0179
0180 spd = sparx5_get_port_cal_speed(sparx5, portno);
0181 if (spd == SPX5_CAL_SPEED_NONE)
0182 continue;
0183
0184 this_bw = sparx5_cal_speed_to_value(spd);
0185 if (portno < SPX5_PORTS)
0186 used_port_bw += this_bw;
0187 else
0188
0189 this_bw = this_bw / 2;
0190 total_bw += this_bw;
0191 reg = portno;
0192 offset = do_div(reg, SPX5_PORTS_PER_CALREG);
0193 cal[reg] |= spd << (offset * SPX5_CALBITS_PER_PORT);
0194 }
0195
0196 if (used_port_bw > sparx5_target_bandwidth(sparx5)) {
0197 dev_err(sparx5->dev,
0198 "Port BW %u above target BW %u\n",
0199 used_port_bw, sparx5_target_bandwidth(sparx5));
0200 return -EINVAL;
0201 }
0202
0203 if (total_bw > max_core_bw) {
0204 dev_err(sparx5->dev,
0205 "Total BW %u above switch core BW %u\n",
0206 total_bw, max_core_bw);
0207 return -EINVAL;
0208 }
0209
0210
0211 spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(10),
0212 QSYS_CAL_CTRL_CAL_MODE,
0213 sparx5, QSYS_CAL_CTRL);
0214
0215
0216 for (idx = 0; idx < ARRAY_SIZE(cal); idx++)
0217 spx5_wr(cal[idx], sparx5, QSYS_CAL_AUTO(idx));
0218
0219
0220
0221
0222 spx5_rmw(QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE_SET(671),
0223 QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE,
0224 sparx5,
0225 QSYS_CAL_CTRL);
0226
0227
0228 for (idx = 2; idx < 5; idx++)
0229 spx5_wr(HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA_SET(12),
0230 sparx5,
0231 HSCH_OUTB_SHARE_ENA(idx));
0232
0233
0234 spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(8),
0235 QSYS_CAL_CTRL_CAL_MODE,
0236 sparx5, QSYS_CAL_CTRL);
0237
0238
0239 value = spx5_rd(sparx5, QSYS_CAL_CTRL);
0240 if (QSYS_CAL_CTRL_CAL_AUTO_ERROR_GET(value)) {
0241 dev_err(sparx5->dev, "QSYS calendar error\n");
0242 err = -EINVAL;
0243 }
0244 return err;
0245 }
0246
0247 static u32 sparx5_dsm_exb_gcd(u32 a, u32 b)
0248 {
0249 if (b == 0)
0250 return a;
0251 return sparx5_dsm_exb_gcd(b, a % b);
0252 }
0253
0254 static u32 sparx5_dsm_cal_len(u32 *cal)
0255 {
0256 u32 idx = 0, len = 0;
0257
0258 while (idx < SPX5_DSM_CAL_LEN) {
0259 if (cal[idx] != SPX5_DSM_CAL_EMPTY)
0260 len++;
0261 idx++;
0262 }
0263 return len;
0264 }
0265
0266 static u32 sparx5_dsm_cp_cal(u32 *sched)
0267 {
0268 u32 idx = 0, tmp;
0269
0270 while (idx < SPX5_DSM_CAL_LEN) {
0271 if (sched[idx] != SPX5_DSM_CAL_EMPTY) {
0272 tmp = sched[idx];
0273 sched[idx] = SPX5_DSM_CAL_EMPTY;
0274 return tmp;
0275 }
0276 idx++;
0277 }
0278 return SPX5_DSM_CAL_EMPTY;
0279 }
0280
0281 static int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
0282 struct sparx5_calendar_data *data)
0283 {
0284 bool slow_mode;
0285 u32 gcd, idx, sum, min, factor;
0286 u32 num_of_slots, slot_spd, empty_slots;
0287 u32 taxi_bw, clk_period_ps;
0288
0289 clk_period_ps = sparx5_clk_period(sparx5->coreclock);
0290 taxi_bw = 128 * 1000000 / clk_period_ps;
0291 slow_mode = !!(clk_period_ps > 2000);
0292 memcpy(data->taxi_ports, &sparx5_taxi_ports[taxi],
0293 sizeof(data->taxi_ports));
0294
0295 for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) {
0296 data->new_slots[idx] = SPX5_DSM_CAL_EMPTY;
0297 data->schedule[idx] = SPX5_DSM_CAL_EMPTY;
0298 data->temp_sched[idx] = SPX5_DSM_CAL_EMPTY;
0299 }
0300
0301 data->schedule[0] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
0302
0303
0304 for (idx = 0; idx < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; idx++) {
0305 u32 portno = data->taxi_ports[idx];
0306
0307 if (portno < SPX5_TAXI_PORT_MAX) {
0308 data->taxi_speeds[idx] = sparx5_cal_speed_to_value
0309 (sparx5_get_port_cal_speed(sparx5, portno));
0310 } else {
0311 data->taxi_speeds[idx] = 0;
0312 }
0313 }
0314
0315 sum = 0;
0316 min = 25000;
0317 for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) {
0318 u32 jdx;
0319
0320 sum += data->taxi_speeds[idx];
0321 if (data->taxi_speeds[idx] && data->taxi_speeds[idx] < min)
0322 min = data->taxi_speeds[idx];
0323 gcd = min;
0324 for (jdx = 0; jdx < ARRAY_SIZE(data->taxi_speeds); jdx++)
0325 gcd = sparx5_dsm_exb_gcd(gcd, data->taxi_speeds[jdx]);
0326 }
0327 if (sum == 0)
0328 return 0;
0329
0330 factor = 100 * 100 * 1000 / (100 * 100 - SPX5_DSM_CAL_BW_LOSS);
0331
0332 if (sum * factor > (taxi_bw * 1000)) {
0333 dev_err(sparx5->dev,
0334 "Taxi %u, Requested BW %u above available BW %u\n",
0335 taxi, sum, taxi_bw);
0336 return -EINVAL;
0337 }
0338 for (idx = 0; idx < 4; idx++) {
0339 u32 raw_spd;
0340
0341 if (idx == 0)
0342 raw_spd = gcd / 5;
0343 else if (idx == 1)
0344 raw_spd = gcd / 2;
0345 else if (idx == 2)
0346 raw_spd = gcd;
0347 else
0348 raw_spd = min;
0349 slot_spd = raw_spd * factor / 1000;
0350 num_of_slots = taxi_bw / slot_spd;
0351 if (num_of_slots <= 64)
0352 break;
0353 }
0354
0355 num_of_slots = num_of_slots > 64 ? 64 : num_of_slots;
0356 slot_spd = taxi_bw / num_of_slots;
0357
0358 sum = 0;
0359 for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) {
0360 u32 spd = data->taxi_speeds[idx];
0361 u32 adjusted_speed = data->taxi_speeds[idx] * factor / 1000;
0362
0363 if (adjusted_speed > 0) {
0364 data->avg_dist[idx] = (128 * 1000000 * 10) /
0365 (adjusted_speed * clk_period_ps);
0366 } else {
0367 data->avg_dist[idx] = -1;
0368 }
0369 data->dev_slots[idx] = ((spd * factor / slot_spd) + 999) / 1000;
0370 if (spd != 25000 && (spd != 10000 || !slow_mode)) {
0371 if (num_of_slots < (5 * data->dev_slots[idx])) {
0372 dev_err(sparx5->dev,
0373 "Taxi %u, speed %u, Low slot sep.\n",
0374 taxi, spd);
0375 return -EINVAL;
0376 }
0377 }
0378 sum += data->dev_slots[idx];
0379 if (sum > num_of_slots) {
0380 dev_err(sparx5->dev,
0381 "Taxi %u with overhead factor %u\n",
0382 taxi, factor);
0383 return -EINVAL;
0384 }
0385 }
0386
0387 empty_slots = num_of_slots - sum;
0388
0389 for (idx = 0; idx < empty_slots; idx++)
0390 data->schedule[idx] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
0391
0392 for (idx = 1; idx < num_of_slots; idx++) {
0393 u32 indices_len = 0;
0394 u32 slot, jdx, kdx, ts;
0395 s32 cnt;
0396 u32 num_of_old_slots, num_of_new_slots, tgt_score;
0397
0398 for (slot = 0; slot < ARRAY_SIZE(data->dev_slots); slot++) {
0399 if (data->dev_slots[slot] == idx) {
0400 data->indices[indices_len] = slot;
0401 indices_len++;
0402 }
0403 }
0404 if (indices_len == 0)
0405 continue;
0406 kdx = 0;
0407 for (slot = 0; slot < idx; slot++) {
0408 for (jdx = 0; jdx < indices_len; jdx++, kdx++)
0409 data->new_slots[kdx] = data->indices[jdx];
0410 }
0411
0412 for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
0413 if (data->schedule[slot] == SPX5_DSM_CAL_EMPTY)
0414 break;
0415 }
0416
0417 num_of_old_slots = slot;
0418 num_of_new_slots = kdx;
0419 cnt = 0;
0420 ts = 0;
0421
0422 if (num_of_new_slots > num_of_old_slots) {
0423 memcpy(data->short_list, data->schedule,
0424 sizeof(data->short_list));
0425 memcpy(data->long_list, data->new_slots,
0426 sizeof(data->long_list));
0427 tgt_score = 100000 * num_of_old_slots /
0428 num_of_new_slots;
0429 } else {
0430 memcpy(data->short_list, data->new_slots,
0431 sizeof(data->short_list));
0432 memcpy(data->long_list, data->schedule,
0433 sizeof(data->long_list));
0434 tgt_score = 100000 * num_of_new_slots /
0435 num_of_old_slots;
0436 }
0437
0438 while (sparx5_dsm_cal_len(data->short_list) > 0 ||
0439 sparx5_dsm_cal_len(data->long_list) > 0) {
0440 u32 act = 0;
0441
0442 if (sparx5_dsm_cal_len(data->short_list) > 0) {
0443 data->temp_sched[ts] =
0444 sparx5_dsm_cp_cal(data->short_list);
0445 ts++;
0446 cnt += 100000;
0447 act = 1;
0448 }
0449 while (sparx5_dsm_cal_len(data->long_list) > 0 &&
0450 cnt > 0) {
0451 data->temp_sched[ts] =
0452 sparx5_dsm_cp_cal(data->long_list);
0453 ts++;
0454 cnt -= tgt_score;
0455 act = 1;
0456 }
0457 if (act == 0) {
0458 dev_err(sparx5->dev,
0459 "Error in DSM calendar calculation\n");
0460 return -EINVAL;
0461 }
0462 }
0463
0464 for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
0465 if (data->temp_sched[slot] == SPX5_DSM_CAL_EMPTY)
0466 break;
0467 }
0468 for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
0469 data->schedule[slot] = data->temp_sched[slot];
0470 data->temp_sched[slot] = SPX5_DSM_CAL_EMPTY;
0471 data->new_slots[slot] = SPX5_DSM_CAL_EMPTY;
0472 }
0473 }
0474 return 0;
0475 }
0476
0477 static int sparx5_dsm_calendar_check(struct sparx5 *sparx5,
0478 struct sparx5_calendar_data *data)
0479 {
0480 u32 num_of_slots, idx, port;
0481 int cnt, max_dist;
0482 u32 slot_indices[SPX5_DSM_CAL_LEN], distances[SPX5_DSM_CAL_LEN];
0483 u32 cal_length = sparx5_dsm_cal_len(data->schedule);
0484
0485 for (port = 0; port < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; port++) {
0486 num_of_slots = 0;
0487 max_dist = data->avg_dist[port];
0488 for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) {
0489 slot_indices[idx] = SPX5_DSM_CAL_EMPTY;
0490 distances[idx] = SPX5_DSM_CAL_EMPTY;
0491 }
0492
0493 for (idx = 0; idx < cal_length; idx++) {
0494 if (data->schedule[idx] == port) {
0495 slot_indices[num_of_slots] = idx;
0496 num_of_slots++;
0497 }
0498 }
0499
0500 slot_indices[num_of_slots] = slot_indices[0] + cal_length;
0501
0502 for (idx = 0; idx < num_of_slots; idx++) {
0503 distances[idx] = (slot_indices[idx + 1] -
0504 slot_indices[idx]) * 10;
0505 }
0506
0507 for (idx = 0; idx < num_of_slots; idx++) {
0508 u32 jdx, kdx;
0509
0510 cnt = distances[idx] - max_dist;
0511 if (cnt < 0)
0512 cnt = -cnt;
0513 kdx = 0;
0514 for (jdx = (idx + 1) % num_of_slots;
0515 jdx != idx;
0516 jdx = (jdx + 1) % num_of_slots, kdx++) {
0517 cnt = cnt + distances[jdx] - max_dist;
0518 if (cnt < 0)
0519 cnt = -cnt;
0520 if (cnt > max_dist)
0521 goto check_err;
0522 }
0523 }
0524 }
0525 return 0;
0526 check_err:
0527 dev_err(sparx5->dev,
0528 "Port %u: distance %u above limit %d\n",
0529 port, cnt, max_dist);
0530 return -EINVAL;
0531 }
0532
0533 static int sparx5_dsm_calendar_update(struct sparx5 *sparx5, u32 taxi,
0534 struct sparx5_calendar_data *data)
0535 {
0536 u32 idx;
0537 u32 cal_len = sparx5_dsm_cal_len(data->schedule), len;
0538
0539 spx5_wr(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(1),
0540 sparx5,
0541 DSM_TAXI_CAL_CFG(taxi));
0542 for (idx = 0; idx < cal_len; idx++) {
0543 spx5_rmw(DSM_TAXI_CAL_CFG_CAL_IDX_SET(idx),
0544 DSM_TAXI_CAL_CFG_CAL_IDX,
0545 sparx5,
0546 DSM_TAXI_CAL_CFG(taxi));
0547 spx5_rmw(DSM_TAXI_CAL_CFG_CAL_PGM_VAL_SET(data->schedule[idx]),
0548 DSM_TAXI_CAL_CFG_CAL_PGM_VAL,
0549 sparx5,
0550 DSM_TAXI_CAL_CFG(taxi));
0551 }
0552 spx5_wr(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(0),
0553 sparx5,
0554 DSM_TAXI_CAL_CFG(taxi));
0555 len = DSM_TAXI_CAL_CFG_CAL_CUR_LEN_GET(spx5_rd(sparx5,
0556 DSM_TAXI_CAL_CFG(taxi)));
0557 if (len != cal_len - 1)
0558 goto update_err;
0559 return 0;
0560 update_err:
0561 dev_err(sparx5->dev, "Incorrect calendar length: %u\n", len);
0562 return -EINVAL;
0563 }
0564
0565
0566 int sparx5_config_dsm_calendar(struct sparx5 *sparx5)
0567 {
0568 int taxi;
0569 struct sparx5_calendar_data *data;
0570 int err = 0;
0571
0572 data = kzalloc(sizeof(*data), GFP_KERNEL);
0573 if (!data)
0574 return -ENOMEM;
0575
0576 for (taxi = 0; taxi < SPX5_DSM_CAL_TAXIS; ++taxi) {
0577 err = sparx5_dsm_calendar_calc(sparx5, taxi, data);
0578 if (err) {
0579 dev_err(sparx5->dev, "DSM calendar calculation failed\n");
0580 goto cal_out;
0581 }
0582 err = sparx5_dsm_calendar_check(sparx5, data);
0583 if (err) {
0584 dev_err(sparx5->dev, "DSM calendar check failed\n");
0585 goto cal_out;
0586 }
0587 err = sparx5_dsm_calendar_update(sparx5, taxi, data);
0588 if (err) {
0589 dev_err(sparx5->dev, "DSM calendar update failed\n");
0590 goto cal_out;
0591 }
0592 }
0593 cal_out:
0594 kfree(data);
0595 return err;
0596 }