cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sparx5_calendar.c (15803B)


      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}