162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* Microchip Sparx5 Switch driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "sparx5_main_regs.h" 1162306a36Sopenharmony_ci#include "sparx5_main.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* QSYS calendar information */ 1462306a36Sopenharmony_ci#define SPX5_PORTS_PER_CALREG 10 /* Ports mapped in a calendar register */ 1562306a36Sopenharmony_ci#define SPX5_CALBITS_PER_PORT 3 /* Bit per port in calendar register */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* DSM calendar information */ 1862306a36Sopenharmony_ci#define SPX5_DSM_CAL_LEN 64 1962306a36Sopenharmony_ci#define SPX5_DSM_CAL_EMPTY 0xFFFF 2062306a36Sopenharmony_ci#define SPX5_DSM_CAL_MAX_DEVS_PER_TAXI 13 2162306a36Sopenharmony_ci#define SPX5_DSM_CAL_TAXIS 8 2262306a36Sopenharmony_ci#define SPX5_DSM_CAL_BW_LOSS 553 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define SPX5_TAXI_PORT_MAX 70 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define SPEED_12500 12500 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Maps from taxis to port numbers */ 2962306a36Sopenharmony_cistatic u32 sparx5_taxi_ports[SPX5_DSM_CAL_TAXIS][SPX5_DSM_CAL_MAX_DEVS_PER_TAXI] = { 3062306a36Sopenharmony_ci {57, 12, 0, 1, 2, 16, 17, 18, 19, 20, 21, 22, 23}, 3162306a36Sopenharmony_ci {58, 13, 3, 4, 5, 24, 25, 26, 27, 28, 29, 30, 31}, 3262306a36Sopenharmony_ci {59, 14, 6, 7, 8, 32, 33, 34, 35, 36, 37, 38, 39}, 3362306a36Sopenharmony_ci {60, 15, 9, 10, 11, 40, 41, 42, 43, 44, 45, 46, 47}, 3462306a36Sopenharmony_ci {61, 48, 49, 50, 99, 99, 99, 99, 99, 99, 99, 99, 99}, 3562306a36Sopenharmony_ci {62, 51, 52, 53, 99, 99, 99, 99, 99, 99, 99, 99, 99}, 3662306a36Sopenharmony_ci {56, 63, 54, 55, 99, 99, 99, 99, 99, 99, 99, 99, 99}, 3762306a36Sopenharmony_ci {64, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct sparx5_calendar_data { 4162306a36Sopenharmony_ci u32 schedule[SPX5_DSM_CAL_LEN]; 4262306a36Sopenharmony_ci u32 avg_dist[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI]; 4362306a36Sopenharmony_ci u32 taxi_ports[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI]; 4462306a36Sopenharmony_ci u32 taxi_speeds[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI]; 4562306a36Sopenharmony_ci u32 dev_slots[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI]; 4662306a36Sopenharmony_ci u32 new_slots[SPX5_DSM_CAL_LEN]; 4762306a36Sopenharmony_ci u32 temp_sched[SPX5_DSM_CAL_LEN]; 4862306a36Sopenharmony_ci u32 indices[SPX5_DSM_CAL_LEN]; 4962306a36Sopenharmony_ci u32 short_list[SPX5_DSM_CAL_LEN]; 5062306a36Sopenharmony_ci u32 long_list[SPX5_DSM_CAL_LEN]; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic u32 sparx5_target_bandwidth(struct sparx5 *sparx5) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci switch (sparx5->target_ct) { 5662306a36Sopenharmony_ci case SPX5_TARGET_CT_7546: 5762306a36Sopenharmony_ci case SPX5_TARGET_CT_7546TSN: 5862306a36Sopenharmony_ci return 65000; 5962306a36Sopenharmony_ci case SPX5_TARGET_CT_7549: 6062306a36Sopenharmony_ci case SPX5_TARGET_CT_7549TSN: 6162306a36Sopenharmony_ci return 91000; 6262306a36Sopenharmony_ci case SPX5_TARGET_CT_7552: 6362306a36Sopenharmony_ci case SPX5_TARGET_CT_7552TSN: 6462306a36Sopenharmony_ci return 129000; 6562306a36Sopenharmony_ci case SPX5_TARGET_CT_7556: 6662306a36Sopenharmony_ci case SPX5_TARGET_CT_7556TSN: 6762306a36Sopenharmony_ci return 161000; 6862306a36Sopenharmony_ci case SPX5_TARGET_CT_7558: 6962306a36Sopenharmony_ci case SPX5_TARGET_CT_7558TSN: 7062306a36Sopenharmony_ci return 201000; 7162306a36Sopenharmony_ci default: 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* This is used in calendar configuration */ 7762306a36Sopenharmony_cienum sparx5_cal_bw { 7862306a36Sopenharmony_ci SPX5_CAL_SPEED_NONE = 0, 7962306a36Sopenharmony_ci SPX5_CAL_SPEED_1G = 1, 8062306a36Sopenharmony_ci SPX5_CAL_SPEED_2G5 = 2, 8162306a36Sopenharmony_ci SPX5_CAL_SPEED_5G = 3, 8262306a36Sopenharmony_ci SPX5_CAL_SPEED_10G = 4, 8362306a36Sopenharmony_ci SPX5_CAL_SPEED_25G = 5, 8462306a36Sopenharmony_ci SPX5_CAL_SPEED_0G5 = 6, 8562306a36Sopenharmony_ci SPX5_CAL_SPEED_12G5 = 7 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic u32 sparx5_clk_to_bandwidth(enum sparx5_core_clockfreq cclock) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci switch (cclock) { 9162306a36Sopenharmony_ci case SPX5_CORE_CLOCK_250MHZ: return 83000; /* 250000 / 3 */ 9262306a36Sopenharmony_ci case SPX5_CORE_CLOCK_500MHZ: return 166000; /* 500000 / 3 */ 9362306a36Sopenharmony_ci case SPX5_CORE_CLOCK_625MHZ: return 208000; /* 625000 / 3 */ 9462306a36Sopenharmony_ci default: return 0; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci switch (speed) { 10262306a36Sopenharmony_ci case SPX5_CAL_SPEED_1G: return 1000; 10362306a36Sopenharmony_ci case SPX5_CAL_SPEED_2G5: return 2500; 10462306a36Sopenharmony_ci case SPX5_CAL_SPEED_5G: return 5000; 10562306a36Sopenharmony_ci case SPX5_CAL_SPEED_10G: return 10000; 10662306a36Sopenharmony_ci case SPX5_CAL_SPEED_25G: return 25000; 10762306a36Sopenharmony_ci case SPX5_CAL_SPEED_0G5: return 500; 10862306a36Sopenharmony_ci case SPX5_CAL_SPEED_12G5: return 12500; 10962306a36Sopenharmony_ci default: return 0; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic u32 sparx5_bandwidth_to_calendar(u32 bw) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci switch (bw) { 11662306a36Sopenharmony_ci case SPEED_10: return SPX5_CAL_SPEED_0G5; 11762306a36Sopenharmony_ci case SPEED_100: return SPX5_CAL_SPEED_0G5; 11862306a36Sopenharmony_ci case SPEED_1000: return SPX5_CAL_SPEED_1G; 11962306a36Sopenharmony_ci case SPEED_2500: return SPX5_CAL_SPEED_2G5; 12062306a36Sopenharmony_ci case SPEED_5000: return SPX5_CAL_SPEED_5G; 12162306a36Sopenharmony_ci case SPEED_10000: return SPX5_CAL_SPEED_10G; 12262306a36Sopenharmony_ci case SPEED_12500: return SPX5_CAL_SPEED_12G5; 12362306a36Sopenharmony_ci case SPEED_25000: return SPX5_CAL_SPEED_25G; 12462306a36Sopenharmony_ci case SPEED_UNKNOWN: return SPX5_CAL_SPEED_1G; 12562306a36Sopenharmony_ci default: return SPX5_CAL_SPEED_NONE; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5, 13062306a36Sopenharmony_ci u32 portno) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct sparx5_port *port; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (portno >= SPX5_PORTS) { 13562306a36Sopenharmony_ci /* Internal ports */ 13662306a36Sopenharmony_ci if (portno == SPX5_PORT_CPU_0 || portno == SPX5_PORT_CPU_1) { 13762306a36Sopenharmony_ci /* Equals 1.25G */ 13862306a36Sopenharmony_ci return SPX5_CAL_SPEED_2G5; 13962306a36Sopenharmony_ci } else if (portno == SPX5_PORT_VD0) { 14062306a36Sopenharmony_ci /* IPMC only idle BW */ 14162306a36Sopenharmony_ci return SPX5_CAL_SPEED_NONE; 14262306a36Sopenharmony_ci } else if (portno == SPX5_PORT_VD1) { 14362306a36Sopenharmony_ci /* OAM only idle BW */ 14462306a36Sopenharmony_ci return SPX5_CAL_SPEED_NONE; 14562306a36Sopenharmony_ci } else if (portno == SPX5_PORT_VD2) { 14662306a36Sopenharmony_ci /* IPinIP gets only idle BW */ 14762306a36Sopenharmony_ci return SPX5_CAL_SPEED_NONE; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci /* not in port map */ 15062306a36Sopenharmony_ci return SPX5_CAL_SPEED_NONE; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci /* Front ports - may be used */ 15362306a36Sopenharmony_ci port = sparx5->ports[portno]; 15462306a36Sopenharmony_ci if (!port) 15562306a36Sopenharmony_ci return SPX5_CAL_SPEED_NONE; 15662306a36Sopenharmony_ci return sparx5_bandwidth_to_calendar(port->conf.bandwidth); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* Auto configure the QSYS calendar based on port configuration */ 16062306a36Sopenharmony_ciint sparx5_config_auto_calendar(struct sparx5 *sparx5) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci u32 cal[7], value, idx, portno; 16362306a36Sopenharmony_ci u32 max_core_bw; 16462306a36Sopenharmony_ci u32 total_bw = 0, used_port_bw = 0; 16562306a36Sopenharmony_ci int err = 0; 16662306a36Sopenharmony_ci enum sparx5_cal_bw spd; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci memset(cal, 0, sizeof(cal)); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci max_core_bw = sparx5_clk_to_bandwidth(sparx5->coreclock); 17162306a36Sopenharmony_ci if (max_core_bw == 0) { 17262306a36Sopenharmony_ci dev_err(sparx5->dev, "Core clock not supported"); 17362306a36Sopenharmony_ci return -EINVAL; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Setup the calendar with the bandwidth to each port */ 17762306a36Sopenharmony_ci for (portno = 0; portno < SPX5_PORTS_ALL; portno++) { 17862306a36Sopenharmony_ci u64 reg, offset, this_bw; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci spd = sparx5_get_port_cal_speed(sparx5, portno); 18162306a36Sopenharmony_ci if (spd == SPX5_CAL_SPEED_NONE) 18262306a36Sopenharmony_ci continue; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci this_bw = sparx5_cal_speed_to_value(spd); 18562306a36Sopenharmony_ci if (portno < SPX5_PORTS) 18662306a36Sopenharmony_ci used_port_bw += this_bw; 18762306a36Sopenharmony_ci else 18862306a36Sopenharmony_ci /* Internal ports are granted half the value */ 18962306a36Sopenharmony_ci this_bw = this_bw / 2; 19062306a36Sopenharmony_ci total_bw += this_bw; 19162306a36Sopenharmony_ci reg = portno; 19262306a36Sopenharmony_ci offset = do_div(reg, SPX5_PORTS_PER_CALREG); 19362306a36Sopenharmony_ci cal[reg] |= spd << (offset * SPX5_CALBITS_PER_PORT); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (used_port_bw > sparx5_target_bandwidth(sparx5)) { 19762306a36Sopenharmony_ci dev_err(sparx5->dev, 19862306a36Sopenharmony_ci "Port BW %u above target BW %u\n", 19962306a36Sopenharmony_ci used_port_bw, sparx5_target_bandwidth(sparx5)); 20062306a36Sopenharmony_ci return -EINVAL; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (total_bw > max_core_bw) { 20462306a36Sopenharmony_ci dev_err(sparx5->dev, 20562306a36Sopenharmony_ci "Total BW %u above switch core BW %u\n", 20662306a36Sopenharmony_ci total_bw, max_core_bw); 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Halt the calendar while changing it */ 21162306a36Sopenharmony_ci spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(10), 21262306a36Sopenharmony_ci QSYS_CAL_CTRL_CAL_MODE, 21362306a36Sopenharmony_ci sparx5, QSYS_CAL_CTRL); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Assign port bandwidth to auto calendar */ 21662306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(cal); idx++) 21762306a36Sopenharmony_ci spx5_wr(cal[idx], sparx5, QSYS_CAL_AUTO(idx)); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Increase grant rate of all ports to account for 22062306a36Sopenharmony_ci * core clock ppm deviations 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci spx5_rmw(QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE_SET(671), /* 672->671 */ 22362306a36Sopenharmony_ci QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE, 22462306a36Sopenharmony_ci sparx5, 22562306a36Sopenharmony_ci QSYS_CAL_CTRL); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Grant idle usage to VD 0-2 */ 22862306a36Sopenharmony_ci for (idx = 2; idx < 5; idx++) 22962306a36Sopenharmony_ci spx5_wr(HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA_SET(12), 23062306a36Sopenharmony_ci sparx5, 23162306a36Sopenharmony_ci HSCH_OUTB_SHARE_ENA(idx)); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Enable Auto mode */ 23462306a36Sopenharmony_ci spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(8), 23562306a36Sopenharmony_ci QSYS_CAL_CTRL_CAL_MODE, 23662306a36Sopenharmony_ci sparx5, QSYS_CAL_CTRL); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Verify successful calendar config */ 23962306a36Sopenharmony_ci value = spx5_rd(sparx5, QSYS_CAL_CTRL); 24062306a36Sopenharmony_ci if (QSYS_CAL_CTRL_CAL_AUTO_ERROR_GET(value)) { 24162306a36Sopenharmony_ci dev_err(sparx5->dev, "QSYS calendar error\n"); 24262306a36Sopenharmony_ci err = -EINVAL; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci return err; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic u32 sparx5_dsm_exb_gcd(u32 a, u32 b) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci if (b == 0) 25062306a36Sopenharmony_ci return a; 25162306a36Sopenharmony_ci return sparx5_dsm_exb_gcd(b, a % b); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic u32 sparx5_dsm_cal_len(u32 *cal) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci u32 idx = 0, len = 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci while (idx < SPX5_DSM_CAL_LEN) { 25962306a36Sopenharmony_ci if (cal[idx] != SPX5_DSM_CAL_EMPTY) 26062306a36Sopenharmony_ci len++; 26162306a36Sopenharmony_ci idx++; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci return len; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic u32 sparx5_dsm_cp_cal(u32 *sched) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci u32 idx = 0, tmp; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci while (idx < SPX5_DSM_CAL_LEN) { 27162306a36Sopenharmony_ci if (sched[idx] != SPX5_DSM_CAL_EMPTY) { 27262306a36Sopenharmony_ci tmp = sched[idx]; 27362306a36Sopenharmony_ci sched[idx] = SPX5_DSM_CAL_EMPTY; 27462306a36Sopenharmony_ci return tmp; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci idx++; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci return SPX5_DSM_CAL_EMPTY; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi, 28262306a36Sopenharmony_ci struct sparx5_calendar_data *data) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci bool slow_mode; 28562306a36Sopenharmony_ci u32 gcd, idx, sum, min, factor; 28662306a36Sopenharmony_ci u32 num_of_slots, slot_spd, empty_slots; 28762306a36Sopenharmony_ci u32 taxi_bw, clk_period_ps; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci clk_period_ps = sparx5_clk_period(sparx5->coreclock); 29062306a36Sopenharmony_ci taxi_bw = 128 * 1000000 / clk_period_ps; 29162306a36Sopenharmony_ci slow_mode = !!(clk_period_ps > 2000); 29262306a36Sopenharmony_ci memcpy(data->taxi_ports, &sparx5_taxi_ports[taxi], 29362306a36Sopenharmony_ci sizeof(data->taxi_ports)); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) { 29662306a36Sopenharmony_ci data->new_slots[idx] = SPX5_DSM_CAL_EMPTY; 29762306a36Sopenharmony_ci data->schedule[idx] = SPX5_DSM_CAL_EMPTY; 29862306a36Sopenharmony_ci data->temp_sched[idx] = SPX5_DSM_CAL_EMPTY; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci /* Default empty calendar */ 30162306a36Sopenharmony_ci data->schedule[0] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Map ports to taxi positions */ 30462306a36Sopenharmony_ci for (idx = 0; idx < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; idx++) { 30562306a36Sopenharmony_ci u32 portno = data->taxi_ports[idx]; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (portno < SPX5_TAXI_PORT_MAX) { 30862306a36Sopenharmony_ci data->taxi_speeds[idx] = sparx5_cal_speed_to_value 30962306a36Sopenharmony_ci (sparx5_get_port_cal_speed(sparx5, portno)); 31062306a36Sopenharmony_ci } else { 31162306a36Sopenharmony_ci data->taxi_speeds[idx] = 0; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci sum = 0; 31662306a36Sopenharmony_ci min = 25000; 31762306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) { 31862306a36Sopenharmony_ci u32 jdx; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci sum += data->taxi_speeds[idx]; 32162306a36Sopenharmony_ci if (data->taxi_speeds[idx] && data->taxi_speeds[idx] < min) 32262306a36Sopenharmony_ci min = data->taxi_speeds[idx]; 32362306a36Sopenharmony_ci gcd = min; 32462306a36Sopenharmony_ci for (jdx = 0; jdx < ARRAY_SIZE(data->taxi_speeds); jdx++) 32562306a36Sopenharmony_ci gcd = sparx5_dsm_exb_gcd(gcd, data->taxi_speeds[jdx]); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci if (sum == 0) /* Empty calendar */ 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci /* Make room for overhead traffic */ 33062306a36Sopenharmony_ci factor = 100 * 100 * 1000 / (100 * 100 - SPX5_DSM_CAL_BW_LOSS); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (sum * factor > (taxi_bw * 1000)) { 33362306a36Sopenharmony_ci dev_err(sparx5->dev, 33462306a36Sopenharmony_ci "Taxi %u, Requested BW %u above available BW %u\n", 33562306a36Sopenharmony_ci taxi, sum, taxi_bw); 33662306a36Sopenharmony_ci return -EINVAL; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci for (idx = 0; idx < 4; idx++) { 33962306a36Sopenharmony_ci u32 raw_spd; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (idx == 0) 34262306a36Sopenharmony_ci raw_spd = gcd / 5; 34362306a36Sopenharmony_ci else if (idx == 1) 34462306a36Sopenharmony_ci raw_spd = gcd / 2; 34562306a36Sopenharmony_ci else if (idx == 2) 34662306a36Sopenharmony_ci raw_spd = gcd; 34762306a36Sopenharmony_ci else 34862306a36Sopenharmony_ci raw_spd = min; 34962306a36Sopenharmony_ci slot_spd = raw_spd * factor / 1000; 35062306a36Sopenharmony_ci num_of_slots = taxi_bw / slot_spd; 35162306a36Sopenharmony_ci if (num_of_slots <= 64) 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci num_of_slots = num_of_slots > 64 ? 64 : num_of_slots; 35662306a36Sopenharmony_ci slot_spd = taxi_bw / num_of_slots; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci sum = 0; 35962306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) { 36062306a36Sopenharmony_ci u32 spd = data->taxi_speeds[idx]; 36162306a36Sopenharmony_ci u32 adjusted_speed = data->taxi_speeds[idx] * factor / 1000; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (adjusted_speed > 0) { 36462306a36Sopenharmony_ci data->avg_dist[idx] = (128 * 1000000 * 10) / 36562306a36Sopenharmony_ci (adjusted_speed * clk_period_ps); 36662306a36Sopenharmony_ci } else { 36762306a36Sopenharmony_ci data->avg_dist[idx] = -1; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci data->dev_slots[idx] = ((spd * factor / slot_spd) + 999) / 1000; 37062306a36Sopenharmony_ci if (spd != 25000 && (spd != 10000 || !slow_mode)) { 37162306a36Sopenharmony_ci if (num_of_slots < (5 * data->dev_slots[idx])) { 37262306a36Sopenharmony_ci dev_err(sparx5->dev, 37362306a36Sopenharmony_ci "Taxi %u, speed %u, Low slot sep.\n", 37462306a36Sopenharmony_ci taxi, spd); 37562306a36Sopenharmony_ci return -EINVAL; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci sum += data->dev_slots[idx]; 37962306a36Sopenharmony_ci if (sum > num_of_slots) { 38062306a36Sopenharmony_ci dev_err(sparx5->dev, 38162306a36Sopenharmony_ci "Taxi %u with overhead factor %u\n", 38262306a36Sopenharmony_ci taxi, factor); 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci empty_slots = num_of_slots - sum; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci for (idx = 0; idx < empty_slots; idx++) 39062306a36Sopenharmony_ci data->schedule[idx] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci for (idx = 1; idx < num_of_slots; idx++) { 39362306a36Sopenharmony_ci u32 indices_len = 0; 39462306a36Sopenharmony_ci u32 slot, jdx, kdx, ts; 39562306a36Sopenharmony_ci s32 cnt; 39662306a36Sopenharmony_ci u32 num_of_old_slots, num_of_new_slots, tgt_score; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci for (slot = 0; slot < ARRAY_SIZE(data->dev_slots); slot++) { 39962306a36Sopenharmony_ci if (data->dev_slots[slot] == idx) { 40062306a36Sopenharmony_ci data->indices[indices_len] = slot; 40162306a36Sopenharmony_ci indices_len++; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci if (indices_len == 0) 40562306a36Sopenharmony_ci continue; 40662306a36Sopenharmony_ci kdx = 0; 40762306a36Sopenharmony_ci for (slot = 0; slot < idx; slot++) { 40862306a36Sopenharmony_ci for (jdx = 0; jdx < indices_len; jdx++, kdx++) 40962306a36Sopenharmony_ci data->new_slots[kdx] = data->indices[jdx]; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) { 41362306a36Sopenharmony_ci if (data->schedule[slot] == SPX5_DSM_CAL_EMPTY) 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci num_of_old_slots = slot; 41862306a36Sopenharmony_ci num_of_new_slots = kdx; 41962306a36Sopenharmony_ci cnt = 0; 42062306a36Sopenharmony_ci ts = 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (num_of_new_slots > num_of_old_slots) { 42362306a36Sopenharmony_ci memcpy(data->short_list, data->schedule, 42462306a36Sopenharmony_ci sizeof(data->short_list)); 42562306a36Sopenharmony_ci memcpy(data->long_list, data->new_slots, 42662306a36Sopenharmony_ci sizeof(data->long_list)); 42762306a36Sopenharmony_ci tgt_score = 100000 * num_of_old_slots / 42862306a36Sopenharmony_ci num_of_new_slots; 42962306a36Sopenharmony_ci } else { 43062306a36Sopenharmony_ci memcpy(data->short_list, data->new_slots, 43162306a36Sopenharmony_ci sizeof(data->short_list)); 43262306a36Sopenharmony_ci memcpy(data->long_list, data->schedule, 43362306a36Sopenharmony_ci sizeof(data->long_list)); 43462306a36Sopenharmony_ci tgt_score = 100000 * num_of_new_slots / 43562306a36Sopenharmony_ci num_of_old_slots; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci while (sparx5_dsm_cal_len(data->short_list) > 0 || 43962306a36Sopenharmony_ci sparx5_dsm_cal_len(data->long_list) > 0) { 44062306a36Sopenharmony_ci u32 act = 0; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (sparx5_dsm_cal_len(data->short_list) > 0) { 44362306a36Sopenharmony_ci data->temp_sched[ts] = 44462306a36Sopenharmony_ci sparx5_dsm_cp_cal(data->short_list); 44562306a36Sopenharmony_ci ts++; 44662306a36Sopenharmony_ci cnt += 100000; 44762306a36Sopenharmony_ci act = 1; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci while (sparx5_dsm_cal_len(data->long_list) > 0 && 45062306a36Sopenharmony_ci cnt > 0) { 45162306a36Sopenharmony_ci data->temp_sched[ts] = 45262306a36Sopenharmony_ci sparx5_dsm_cp_cal(data->long_list); 45362306a36Sopenharmony_ci ts++; 45462306a36Sopenharmony_ci cnt -= tgt_score; 45562306a36Sopenharmony_ci act = 1; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci if (act == 0) { 45862306a36Sopenharmony_ci dev_err(sparx5->dev, 45962306a36Sopenharmony_ci "Error in DSM calendar calculation\n"); 46062306a36Sopenharmony_ci return -EINVAL; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) { 46562306a36Sopenharmony_ci if (data->temp_sched[slot] == SPX5_DSM_CAL_EMPTY) 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) { 46962306a36Sopenharmony_ci data->schedule[slot] = data->temp_sched[slot]; 47062306a36Sopenharmony_ci data->temp_sched[slot] = SPX5_DSM_CAL_EMPTY; 47162306a36Sopenharmony_ci data->new_slots[slot] = SPX5_DSM_CAL_EMPTY; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci return 0; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int sparx5_dsm_calendar_check(struct sparx5 *sparx5, 47862306a36Sopenharmony_ci struct sparx5_calendar_data *data) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci u32 num_of_slots, idx, port; 48162306a36Sopenharmony_ci int cnt, max_dist; 48262306a36Sopenharmony_ci u32 slot_indices[SPX5_DSM_CAL_LEN], distances[SPX5_DSM_CAL_LEN]; 48362306a36Sopenharmony_ci u32 cal_length = sparx5_dsm_cal_len(data->schedule); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci for (port = 0; port < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; port++) { 48662306a36Sopenharmony_ci num_of_slots = 0; 48762306a36Sopenharmony_ci max_dist = data->avg_dist[port]; 48862306a36Sopenharmony_ci for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) { 48962306a36Sopenharmony_ci slot_indices[idx] = SPX5_DSM_CAL_EMPTY; 49062306a36Sopenharmony_ci distances[idx] = SPX5_DSM_CAL_EMPTY; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci for (idx = 0; idx < cal_length; idx++) { 49462306a36Sopenharmony_ci if (data->schedule[idx] == port) { 49562306a36Sopenharmony_ci slot_indices[num_of_slots] = idx; 49662306a36Sopenharmony_ci num_of_slots++; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci slot_indices[num_of_slots] = slot_indices[0] + cal_length; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci for (idx = 0; idx < num_of_slots; idx++) { 50362306a36Sopenharmony_ci distances[idx] = (slot_indices[idx + 1] - 50462306a36Sopenharmony_ci slot_indices[idx]) * 10; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci for (idx = 0; idx < num_of_slots; idx++) { 50862306a36Sopenharmony_ci u32 jdx, kdx; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci cnt = distances[idx] - max_dist; 51162306a36Sopenharmony_ci if (cnt < 0) 51262306a36Sopenharmony_ci cnt = -cnt; 51362306a36Sopenharmony_ci kdx = 0; 51462306a36Sopenharmony_ci for (jdx = (idx + 1) % num_of_slots; 51562306a36Sopenharmony_ci jdx != idx; 51662306a36Sopenharmony_ci jdx = (jdx + 1) % num_of_slots, kdx++) { 51762306a36Sopenharmony_ci cnt = cnt + distances[jdx] - max_dist; 51862306a36Sopenharmony_ci if (cnt < 0) 51962306a36Sopenharmony_ci cnt = -cnt; 52062306a36Sopenharmony_ci if (cnt > max_dist) 52162306a36Sopenharmony_ci goto check_err; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_cicheck_err: 52762306a36Sopenharmony_ci dev_err(sparx5->dev, 52862306a36Sopenharmony_ci "Port %u: distance %u above limit %d\n", 52962306a36Sopenharmony_ci port, cnt, max_dist); 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int sparx5_dsm_calendar_update(struct sparx5 *sparx5, u32 taxi, 53462306a36Sopenharmony_ci struct sparx5_calendar_data *data) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci u32 idx; 53762306a36Sopenharmony_ci u32 cal_len = sparx5_dsm_cal_len(data->schedule), len; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci spx5_wr(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(1), 54062306a36Sopenharmony_ci sparx5, 54162306a36Sopenharmony_ci DSM_TAXI_CAL_CFG(taxi)); 54262306a36Sopenharmony_ci for (idx = 0; idx < cal_len; idx++) { 54362306a36Sopenharmony_ci spx5_rmw(DSM_TAXI_CAL_CFG_CAL_IDX_SET(idx), 54462306a36Sopenharmony_ci DSM_TAXI_CAL_CFG_CAL_IDX, 54562306a36Sopenharmony_ci sparx5, 54662306a36Sopenharmony_ci DSM_TAXI_CAL_CFG(taxi)); 54762306a36Sopenharmony_ci spx5_rmw(DSM_TAXI_CAL_CFG_CAL_PGM_VAL_SET(data->schedule[idx]), 54862306a36Sopenharmony_ci DSM_TAXI_CAL_CFG_CAL_PGM_VAL, 54962306a36Sopenharmony_ci sparx5, 55062306a36Sopenharmony_ci DSM_TAXI_CAL_CFG(taxi)); 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci spx5_wr(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(0), 55362306a36Sopenharmony_ci sparx5, 55462306a36Sopenharmony_ci DSM_TAXI_CAL_CFG(taxi)); 55562306a36Sopenharmony_ci len = DSM_TAXI_CAL_CFG_CAL_CUR_LEN_GET(spx5_rd(sparx5, 55662306a36Sopenharmony_ci DSM_TAXI_CAL_CFG(taxi))); 55762306a36Sopenharmony_ci if (len != cal_len - 1) 55862306a36Sopenharmony_ci goto update_err; 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ciupdate_err: 56162306a36Sopenharmony_ci dev_err(sparx5->dev, "Incorrect calendar length: %u\n", len); 56262306a36Sopenharmony_ci return -EINVAL; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/* Configure the DSM calendar based on port configuration */ 56662306a36Sopenharmony_ciint sparx5_config_dsm_calendar(struct sparx5 *sparx5) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci int taxi; 56962306a36Sopenharmony_ci struct sparx5_calendar_data *data; 57062306a36Sopenharmony_ci int err = 0; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 57362306a36Sopenharmony_ci if (!data) 57462306a36Sopenharmony_ci return -ENOMEM; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci for (taxi = 0; taxi < SPX5_DSM_CAL_TAXIS; ++taxi) { 57762306a36Sopenharmony_ci err = sparx5_dsm_calendar_calc(sparx5, taxi, data); 57862306a36Sopenharmony_ci if (err) { 57962306a36Sopenharmony_ci dev_err(sparx5->dev, "DSM calendar calculation failed\n"); 58062306a36Sopenharmony_ci goto cal_out; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci err = sparx5_dsm_calendar_check(sparx5, data); 58362306a36Sopenharmony_ci if (err) { 58462306a36Sopenharmony_ci dev_err(sparx5->dev, "DSM calendar check failed\n"); 58562306a36Sopenharmony_ci goto cal_out; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci err = sparx5_dsm_calendar_update(sparx5, taxi, data); 58862306a36Sopenharmony_ci if (err) { 58962306a36Sopenharmony_ci dev_err(sparx5->dev, "DSM calendar update failed\n"); 59062306a36Sopenharmony_ci goto cal_out; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_cical_out: 59462306a36Sopenharmony_ci kfree(data); 59562306a36Sopenharmony_ci return err; 59662306a36Sopenharmony_ci} 597