Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /* Microchip Sparx5 Switch driver
0003  *
0004  * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
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 /* QSYS calendar information */
0014 #define SPX5_PORTS_PER_CALREG          10  /* Ports mapped in a calendar register */
0015 #define SPX5_CALBITS_PER_PORT          3   /* Bit per port in calendar register */
0016 
0017 /* DSM calendar information */
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 /* Maps from taxis to port numbers */
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 /* This is used in calendar configuration */
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; /* 250000 / 3 */
0092     case SPX5_CORE_CLOCK_500MHZ: return 166000; /* 500000 / 3 */
0093     case SPX5_CORE_CLOCK_625MHZ: return  208000; /* 625000 / 3 */
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         /* Internal ports */
0136         if (portno == SPX5_PORT_CPU_0 || portno == SPX5_PORT_CPU_1) {
0137             /* Equals 1.25G */
0138             return SPX5_CAL_SPEED_2G5;
0139         } else if (portno == SPX5_PORT_VD0) {
0140             /* IPMC only idle BW */
0141             return SPX5_CAL_SPEED_NONE;
0142         } else if (portno == SPX5_PORT_VD1) {
0143             /* OAM only idle BW */
0144             return SPX5_CAL_SPEED_NONE;
0145         } else if (portno == SPX5_PORT_VD2) {
0146             /* IPinIP gets only idle BW */
0147             return SPX5_CAL_SPEED_NONE;
0148         }
0149         /* not in port map */
0150         return SPX5_CAL_SPEED_NONE;
0151     }
0152     /* Front ports - may be used */
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 /* Auto configure the QSYS calendar based on port configuration */
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     /* Setup the calendar with the bandwidth to each port */
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             /* Internal ports are granted half the value */
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     /* Halt the calendar while changing it */
0211     spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(10),
0212          QSYS_CAL_CTRL_CAL_MODE,
0213          sparx5, QSYS_CAL_CTRL);
0214 
0215     /* Assign port bandwidth to auto calendar */
0216     for (idx = 0; idx < ARRAY_SIZE(cal); idx++)
0217         spx5_wr(cal[idx], sparx5, QSYS_CAL_AUTO(idx));
0218 
0219     /* Increase grant rate of all ports to account for
0220      * core clock ppm deviations
0221      */
0222     spx5_rmw(QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE_SET(671), /* 672->671 */
0223          QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE,
0224          sparx5,
0225          QSYS_CAL_CTRL);
0226 
0227     /* Grant idle usage to VD 0-2 */
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     /* Enable Auto mode */
0234     spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(8),
0235          QSYS_CAL_CTRL_CAL_MODE,
0236          sparx5, QSYS_CAL_CTRL);
0237 
0238     /* Verify successful calendar config */
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     /* Default empty calendar */
0301     data->schedule[0] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
0302 
0303     /* Map ports to taxi positions */
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) /* Empty calendar */
0328         return 0;
0329     /* Make room for overhead traffic */
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 /* Configure the DSM calendar based on port configuration */
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 }