brintos

brintos / linux-shallow public Read only

0
0
Text · 15.4 KiB · 76a8bb5 Raw
597 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Microchip Sparx5 Switch driver3 *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               6419#define SPX5_DSM_CAL_EMPTY             0xFFFF20#define SPX5_DSM_CAL_MAX_DEVS_PER_TAXI 1321#define SPX5_DSM_CAL_TAXIS             822#define SPX5_DSM_CAL_BW_LOSS           55323 24#define SPX5_TAXI_PORT_MAX             7025 26#define SPEED_12500                    1250027 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 = 786};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		else188			/* 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 for220	 * core clock ppm deviations221	 */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_value309				(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		else348			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