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