162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* Microchip Sparx5 Switch driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <net/pkt_cls.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "sparx5_main.h" 1062306a36Sopenharmony_ci#include "sparx5_qos.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* Calculate new base_time based on cycle_time. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The hardware requires a base_time that is always in the future. 1562306a36Sopenharmony_ci * We define threshold_time as current_time + (2 * cycle_time). 1662306a36Sopenharmony_ci * If base_time is below threshold_time this function recalculates it to be in 1762306a36Sopenharmony_ci * the interval: 1862306a36Sopenharmony_ci * threshold_time <= base_time < (threshold_time + cycle_time) 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * A very simple algorithm could be like this: 2162306a36Sopenharmony_ci * new_base_time = org_base_time + N * cycle_time 2262306a36Sopenharmony_ci * using the lowest N so (new_base_time >= threshold_time 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_civoid sparx5_new_base_time(struct sparx5 *sparx5, const u32 cycle_time, 2562306a36Sopenharmony_ci const ktime_t org_base_time, ktime_t *new_base_time) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci ktime_t current_time, threshold_time, new_time; 2862306a36Sopenharmony_ci struct timespec64 ts; 2962306a36Sopenharmony_ci u64 nr_of_cycles_p2; 3062306a36Sopenharmony_ci u64 nr_of_cycles; 3162306a36Sopenharmony_ci u64 diff_time; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci new_time = org_base_time; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci sparx5_ptp_gettime64(&sparx5->phc[SPARX5_PHC_PORT].info, &ts); 3662306a36Sopenharmony_ci current_time = timespec64_to_ktime(ts); 3762306a36Sopenharmony_ci threshold_time = current_time + (2 * cycle_time); 3862306a36Sopenharmony_ci diff_time = threshold_time - new_time; 3962306a36Sopenharmony_ci nr_of_cycles = div_u64(diff_time, cycle_time); 4062306a36Sopenharmony_ci nr_of_cycles_p2 = 1; /* Use 2^0 as start value */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (new_time >= threshold_time) { 4362306a36Sopenharmony_ci *new_base_time = new_time; 4462306a36Sopenharmony_ci return; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* Calculate the smallest power of 2 (nr_of_cycles_p2) 4862306a36Sopenharmony_ci * that is larger than nr_of_cycles. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci while (nr_of_cycles_p2 < nr_of_cycles) 5162306a36Sopenharmony_ci nr_of_cycles_p2 <<= 1; /* Next (higher) power of 2 */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* Add as big chunks (power of 2 * cycle_time) 5462306a36Sopenharmony_ci * as possible for each power of 2 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci while (nr_of_cycles_p2) { 5762306a36Sopenharmony_ci if (new_time < threshold_time) { 5862306a36Sopenharmony_ci new_time += cycle_time * nr_of_cycles_p2; 5962306a36Sopenharmony_ci while (new_time < threshold_time) 6062306a36Sopenharmony_ci new_time += cycle_time * nr_of_cycles_p2; 6162306a36Sopenharmony_ci new_time -= cycle_time * nr_of_cycles_p2; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci nr_of_cycles_p2 >>= 1; /* Next (lower) power of 2 */ 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci new_time += cycle_time; 6662306a36Sopenharmony_ci *new_base_time = new_time; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Max rates for leak groups */ 7062306a36Sopenharmony_cistatic const u32 spx5_hsch_max_group_rate[SPX5_HSCH_LEAK_GRP_CNT] = { 7162306a36Sopenharmony_ci 1048568, /* 1.049 Gbps */ 7262306a36Sopenharmony_ci 2621420, /* 2.621 Gbps */ 7362306a36Sopenharmony_ci 10485680, /* 10.486 Gbps */ 7462306a36Sopenharmony_ci 26214200 /* 26.214 Gbps */ 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct sparx5_layer layers[SPX5_HSCH_LAYER_CNT]; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic u32 sparx5_lg_get_leak_time(struct sparx5 *sparx5, u32 layer, u32 group) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci u32 value; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci value = spx5_rd(sparx5, HSCH_HSCH_TIMER_CFG(layer, group)); 8462306a36Sopenharmony_ci return HSCH_HSCH_TIMER_CFG_LEAK_TIME_GET(value); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void sparx5_lg_set_leak_time(struct sparx5 *sparx5, u32 layer, u32 group, 8862306a36Sopenharmony_ci u32 leak_time) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci spx5_wr(HSCH_HSCH_TIMER_CFG_LEAK_TIME_SET(leak_time), sparx5, 9162306a36Sopenharmony_ci HSCH_HSCH_TIMER_CFG(layer, group)); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic u32 sparx5_lg_get_first(struct sparx5 *sparx5, u32 layer, u32 group) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci u32 value; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci value = spx5_rd(sparx5, HSCH_HSCH_LEAK_CFG(layer, group)); 9962306a36Sopenharmony_ci return HSCH_HSCH_LEAK_CFG_LEAK_FIRST_GET(value); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic u32 sparx5_lg_get_next(struct sparx5 *sparx5, u32 layer, u32 group, 10362306a36Sopenharmony_ci u32 idx) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci u32 value; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci value = spx5_rd(sparx5, HSCH_SE_CONNECT(idx)); 10962306a36Sopenharmony_ci return HSCH_SE_CONNECT_SE_LEAK_LINK_GET(value); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic u32 sparx5_lg_get_last(struct sparx5 *sparx5, u32 layer, u32 group) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u32 itr, next; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci itr = sparx5_lg_get_first(sparx5, layer, group); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci for (;;) { 11962306a36Sopenharmony_ci next = sparx5_lg_get_next(sparx5, layer, group, itr); 12062306a36Sopenharmony_ci if (itr == next) 12162306a36Sopenharmony_ci return itr; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci itr = next; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic bool sparx5_lg_is_last(struct sparx5 *sparx5, u32 layer, u32 group, 12862306a36Sopenharmony_ci u32 idx) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci return idx == sparx5_lg_get_next(sparx5, layer, group, idx); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic bool sparx5_lg_is_first(struct sparx5 *sparx5, u32 layer, u32 group, 13462306a36Sopenharmony_ci u32 idx) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci return idx == sparx5_lg_get_first(sparx5, layer, group); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic bool sparx5_lg_is_empty(struct sparx5 *sparx5, u32 layer, u32 group) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci return sparx5_lg_get_leak_time(sparx5, layer, group) == 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic bool sparx5_lg_is_singular(struct sparx5 *sparx5, u32 layer, u32 group) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci if (sparx5_lg_is_empty(sparx5, layer, group)) 14762306a36Sopenharmony_ci return false; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return sparx5_lg_get_first(sparx5, layer, group) == 15062306a36Sopenharmony_ci sparx5_lg_get_last(sparx5, layer, group); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void sparx5_lg_enable(struct sparx5 *sparx5, u32 layer, u32 group, 15462306a36Sopenharmony_ci u32 leak_time) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci sparx5_lg_set_leak_time(sparx5, layer, group, leak_time); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void sparx5_lg_disable(struct sparx5 *sparx5, u32 layer, u32 group) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci sparx5_lg_set_leak_time(sparx5, layer, group, 0); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int sparx5_lg_get_group_by_index(struct sparx5 *sparx5, u32 layer, 16562306a36Sopenharmony_ci u32 idx, u32 *group) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci u32 itr, next; 16862306a36Sopenharmony_ci int i; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci for (i = 0; i < SPX5_HSCH_LEAK_GRP_CNT; i++) { 17162306a36Sopenharmony_ci if (sparx5_lg_is_empty(sparx5, layer, i)) 17262306a36Sopenharmony_ci continue; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci itr = sparx5_lg_get_first(sparx5, layer, i); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci for (;;) { 17762306a36Sopenharmony_ci next = sparx5_lg_get_next(sparx5, layer, i, itr); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (itr == idx) { 18062306a36Sopenharmony_ci *group = i; 18162306a36Sopenharmony_ci return 0; /* Found it */ 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci if (itr == next) 18462306a36Sopenharmony_ci break; /* Was not found */ 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci itr = next; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return -1; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int sparx5_lg_get_group_by_rate(u32 layer, u32 rate, u32 *group) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct sparx5_layer *l = &layers[layer]; 19662306a36Sopenharmony_ci struct sparx5_lg *lg; 19762306a36Sopenharmony_ci u32 i; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci for (i = 0; i < SPX5_HSCH_LEAK_GRP_CNT; i++) { 20062306a36Sopenharmony_ci lg = &l->leak_groups[i]; 20162306a36Sopenharmony_ci if (rate <= lg->max_rate) { 20262306a36Sopenharmony_ci *group = i; 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return -1; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int sparx5_lg_get_adjacent(struct sparx5 *sparx5, u32 layer, u32 group, 21162306a36Sopenharmony_ci u32 idx, u32 *prev, u32 *next, u32 *first) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci u32 itr; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci *first = sparx5_lg_get_first(sparx5, layer, group); 21662306a36Sopenharmony_ci *prev = *first; 21762306a36Sopenharmony_ci *next = *first; 21862306a36Sopenharmony_ci itr = *first; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci for (;;) { 22162306a36Sopenharmony_ci *next = sparx5_lg_get_next(sparx5, layer, group, itr); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (itr == idx) 22462306a36Sopenharmony_ci return 0; /* Found it */ 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (itr == *next) 22762306a36Sopenharmony_ci return -1; /* Was not found */ 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci *prev = itr; 23062306a36Sopenharmony_ci itr = *next; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return -1; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int sparx5_lg_conf_set(struct sparx5 *sparx5, u32 layer, u32 group, 23762306a36Sopenharmony_ci u32 se_first, u32 idx, u32 idx_next, bool empty) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci u32 leak_time = layers[layer].leak_groups[group].leak_time; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* Stop leaking */ 24262306a36Sopenharmony_ci sparx5_lg_disable(sparx5, layer, group); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (empty) 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Select layer */ 24862306a36Sopenharmony_ci spx5_rmw(HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(layer), 24962306a36Sopenharmony_ci HSCH_HSCH_CFG_CFG_HSCH_LAYER, sparx5, HSCH_HSCH_CFG_CFG); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Link elements */ 25262306a36Sopenharmony_ci spx5_wr(HSCH_SE_CONNECT_SE_LEAK_LINK_SET(idx_next), sparx5, 25362306a36Sopenharmony_ci HSCH_SE_CONNECT(idx)); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Set the first element. */ 25662306a36Sopenharmony_ci spx5_rmw(HSCH_HSCH_LEAK_CFG_LEAK_FIRST_SET(se_first), 25762306a36Sopenharmony_ci HSCH_HSCH_LEAK_CFG_LEAK_FIRST, sparx5, 25862306a36Sopenharmony_ci HSCH_HSCH_LEAK_CFG(layer, group)); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Start leaking */ 26162306a36Sopenharmony_ci sparx5_lg_enable(sparx5, layer, group, leak_time); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic int sparx5_lg_del(struct sparx5 *sparx5, u32 layer, u32 group, u32 idx) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci u32 first, next, prev; 26962306a36Sopenharmony_ci bool empty = false; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* idx *must* be present in the leak group */ 27262306a36Sopenharmony_ci WARN_ON(sparx5_lg_get_adjacent(sparx5, layer, group, idx, &prev, &next, 27362306a36Sopenharmony_ci &first) < 0); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (sparx5_lg_is_singular(sparx5, layer, group)) { 27662306a36Sopenharmony_ci empty = true; 27762306a36Sopenharmony_ci } else if (sparx5_lg_is_last(sparx5, layer, group, idx)) { 27862306a36Sopenharmony_ci /* idx is removed, prev is now last */ 27962306a36Sopenharmony_ci idx = prev; 28062306a36Sopenharmony_ci next = prev; 28162306a36Sopenharmony_ci } else if (sparx5_lg_is_first(sparx5, layer, group, idx)) { 28262306a36Sopenharmony_ci /* idx is removed and points to itself, first is next */ 28362306a36Sopenharmony_ci first = next; 28462306a36Sopenharmony_ci next = idx; 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci /* Next is not touched */ 28762306a36Sopenharmony_ci idx = prev; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return sparx5_lg_conf_set(sparx5, layer, group, first, idx, next, 29162306a36Sopenharmony_ci empty); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int sparx5_lg_add(struct sparx5 *sparx5, u32 layer, u32 new_group, 29562306a36Sopenharmony_ci u32 idx) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci u32 first, next, old_group; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci pr_debug("ADD: layer: %d, new_group: %d, idx: %d", layer, new_group, 30062306a36Sopenharmony_ci idx); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Is this SE already shaping ? */ 30362306a36Sopenharmony_ci if (sparx5_lg_get_group_by_index(sparx5, layer, idx, &old_group) >= 0) { 30462306a36Sopenharmony_ci if (old_group != new_group) { 30562306a36Sopenharmony_ci /* Delete from old group */ 30662306a36Sopenharmony_ci sparx5_lg_del(sparx5, layer, old_group, idx); 30762306a36Sopenharmony_ci } else { 30862306a36Sopenharmony_ci /* Nothing to do here */ 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* We always add to head of the list */ 31462306a36Sopenharmony_ci first = idx; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (sparx5_lg_is_empty(sparx5, layer, new_group)) 31762306a36Sopenharmony_ci next = idx; 31862306a36Sopenharmony_ci else 31962306a36Sopenharmony_ci next = sparx5_lg_get_first(sparx5, layer, new_group); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return sparx5_lg_conf_set(sparx5, layer, new_group, first, idx, next, 32262306a36Sopenharmony_ci false); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int sparx5_shaper_conf_set(struct sparx5_port *port, 32662306a36Sopenharmony_ci const struct sparx5_shaper *sh, u32 layer, 32762306a36Sopenharmony_ci u32 idx, u32 group) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int (*sparx5_lg_action)(struct sparx5 *, u32, u32, u32); 33062306a36Sopenharmony_ci struct sparx5 *sparx5 = port->sparx5; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (!sh->rate && !sh->burst) 33362306a36Sopenharmony_ci sparx5_lg_action = &sparx5_lg_del; 33462306a36Sopenharmony_ci else 33562306a36Sopenharmony_ci sparx5_lg_action = &sparx5_lg_add; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Select layer */ 33862306a36Sopenharmony_ci spx5_rmw(HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(layer), 33962306a36Sopenharmony_ci HSCH_HSCH_CFG_CFG_HSCH_LAYER, sparx5, HSCH_HSCH_CFG_CFG); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* Set frame mode */ 34262306a36Sopenharmony_ci spx5_rmw(HSCH_SE_CFG_SE_FRM_MODE_SET(sh->mode), HSCH_SE_CFG_SE_FRM_MODE, 34362306a36Sopenharmony_ci sparx5, HSCH_SE_CFG(idx)); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Set committed rate and burst */ 34662306a36Sopenharmony_ci spx5_wr(HSCH_CIR_CFG_CIR_RATE_SET(sh->rate) | 34762306a36Sopenharmony_ci HSCH_CIR_CFG_CIR_BURST_SET(sh->burst), 34862306a36Sopenharmony_ci sparx5, HSCH_CIR_CFG(idx)); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* This has to be done after the shaper configuration has been set */ 35162306a36Sopenharmony_ci sparx5_lg_action(sparx5, layer, group, idx); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic u32 sparx5_weight_to_hw_cost(u32 weight_min, u32 weight) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci return ((((SPX5_DWRR_COST_MAX << 4) * weight_min / weight) + 8) >> 4) - 35962306a36Sopenharmony_ci 1; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int sparx5_dwrr_conf_set(struct sparx5_port *port, 36362306a36Sopenharmony_ci struct sparx5_dwrr *dwrr) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci int i; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci spx5_rmw(HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(2) | 36862306a36Sopenharmony_ci HSCH_HSCH_CFG_CFG_CFG_SE_IDX_SET(port->portno), 36962306a36Sopenharmony_ci HSCH_HSCH_CFG_CFG_HSCH_LAYER | HSCH_HSCH_CFG_CFG_CFG_SE_IDX, 37062306a36Sopenharmony_ci port->sparx5, HSCH_HSCH_CFG_CFG); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* Number of *lower* indexes that are arbitrated dwrr */ 37362306a36Sopenharmony_ci spx5_rmw(HSCH_SE_CFG_SE_DWRR_CNT_SET(dwrr->count), 37462306a36Sopenharmony_ci HSCH_SE_CFG_SE_DWRR_CNT, port->sparx5, 37562306a36Sopenharmony_ci HSCH_SE_CFG(port->portno)); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci for (i = 0; i < dwrr->count; i++) { 37862306a36Sopenharmony_ci spx5_rmw(HSCH_DWRR_ENTRY_DWRR_COST_SET(dwrr->cost[i]), 37962306a36Sopenharmony_ci HSCH_DWRR_ENTRY_DWRR_COST, port->sparx5, 38062306a36Sopenharmony_ci HSCH_DWRR_ENTRY(i)); 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int sparx5_leak_groups_init(struct sparx5 *sparx5) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct sparx5_layer *layer; 38962306a36Sopenharmony_ci u32 sys_clk_per_100ps; 39062306a36Sopenharmony_ci struct sparx5_lg *lg; 39162306a36Sopenharmony_ci u32 leak_time_us; 39262306a36Sopenharmony_ci int i, ii; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci sys_clk_per_100ps = spx5_rd(sparx5, HSCH_SYS_CLK_PER); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci for (i = 0; i < SPX5_HSCH_LAYER_CNT; i++) { 39762306a36Sopenharmony_ci layer = &layers[i]; 39862306a36Sopenharmony_ci for (ii = 0; ii < SPX5_HSCH_LEAK_GRP_CNT; ii++) { 39962306a36Sopenharmony_ci lg = &layer->leak_groups[ii]; 40062306a36Sopenharmony_ci lg->max_rate = spx5_hsch_max_group_rate[ii]; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Calculate the leak time in us, to serve a maximum 40362306a36Sopenharmony_ci * rate of 'max_rate' for this group 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci leak_time_us = (SPX5_SE_RATE_MAX * 1000) / lg->max_rate; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Hardware wants leak time in ns */ 40862306a36Sopenharmony_ci lg->leak_time = 1000 * leak_time_us; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Calculate resolution */ 41162306a36Sopenharmony_ci lg->resolution = 1000 / leak_time_us; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Maximum number of shapers that can be served by 41462306a36Sopenharmony_ci * this leak group 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci lg->max_ses = (1000 * leak_time_us) / sys_clk_per_100ps; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* Example: 41962306a36Sopenharmony_ci * Wanted bandwidth is 100Mbit: 42062306a36Sopenharmony_ci * 42162306a36Sopenharmony_ci * 100 mbps can be served by leak group zero. 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * leak_time is 125000 ns. 42462306a36Sopenharmony_ci * resolution is: 8 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * cir = 100000 / 8 = 12500 42762306a36Sopenharmony_ci * leaks_pr_sec = 125000 / 10^9 = 8000 42862306a36Sopenharmony_ci * bw = 12500 * 8000 = 10^8 (100 Mbit) 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Disable by default - this also indicates an empty 43262306a36Sopenharmony_ci * leak group 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci sparx5_lg_disable(sparx5, i, ii); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ciint sparx5_qos_init(struct sparx5 *sparx5) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci int ret; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ret = sparx5_leak_groups_init(sparx5); 44662306a36Sopenharmony_ci if (ret < 0) 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci ret = sparx5_dcb_init(sparx5); 45062306a36Sopenharmony_ci if (ret < 0) 45162306a36Sopenharmony_ci return ret; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci sparx5_psfp_init(sparx5); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return 0; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ciint sparx5_tc_mqprio_add(struct net_device *ndev, u8 num_tc) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci int i; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (num_tc != SPX5_PRIOS) { 46362306a36Sopenharmony_ci netdev_err(ndev, "Only %d traffic classes supported\n", 46462306a36Sopenharmony_ci SPX5_PRIOS); 46562306a36Sopenharmony_ci return -EINVAL; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci netdev_set_num_tc(ndev, num_tc); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci for (i = 0; i < num_tc; i++) 47162306a36Sopenharmony_ci netdev_set_tc_queue(ndev, i, 1, i); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci netdev_dbg(ndev, "dev->num_tc %u dev->real_num_tx_queues %u\n", 47462306a36Sopenharmony_ci ndev->num_tc, ndev->real_num_tx_queues); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ciint sparx5_tc_mqprio_del(struct net_device *ndev) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci netdev_reset_tc(ndev); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci netdev_dbg(ndev, "dev->num_tc %u dev->real_num_tx_queues %u\n", 48462306a36Sopenharmony_ci ndev->num_tc, ndev->real_num_tx_queues); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ciint sparx5_tc_tbf_add(struct sparx5_port *port, 49062306a36Sopenharmony_ci struct tc_tbf_qopt_offload_replace_params *params, 49162306a36Sopenharmony_ci u32 layer, u32 idx) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct sparx5_shaper sh = { 49462306a36Sopenharmony_ci .mode = SPX5_SE_MODE_DATARATE, 49562306a36Sopenharmony_ci .rate = div_u64(params->rate.rate_bytes_ps, 1000) * 8, 49662306a36Sopenharmony_ci .burst = params->max_size, 49762306a36Sopenharmony_ci }; 49862306a36Sopenharmony_ci struct sparx5_lg *lg; 49962306a36Sopenharmony_ci u32 group; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* Find suitable group for this se */ 50262306a36Sopenharmony_ci if (sparx5_lg_get_group_by_rate(layer, sh.rate, &group) < 0) { 50362306a36Sopenharmony_ci pr_debug("Could not find leak group for se with rate: %d", 50462306a36Sopenharmony_ci sh.rate); 50562306a36Sopenharmony_ci return -EINVAL; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci lg = &layers[layer].leak_groups[group]; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci pr_debug("Found matching group (speed: %d)\n", lg->max_rate); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (sh.rate < SPX5_SE_RATE_MIN || sh.burst < SPX5_SE_BURST_MIN) 51362306a36Sopenharmony_ci return -EINVAL; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* Calculate committed rate and burst */ 51662306a36Sopenharmony_ci sh.rate = DIV_ROUND_UP(sh.rate, lg->resolution); 51762306a36Sopenharmony_ci sh.burst = DIV_ROUND_UP(sh.burst, SPX5_SE_BURST_UNIT); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (sh.rate > SPX5_SE_RATE_MAX || sh.burst > SPX5_SE_BURST_MAX) 52062306a36Sopenharmony_ci return -EINVAL; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return sparx5_shaper_conf_set(port, &sh, layer, idx, group); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ciint sparx5_tc_tbf_del(struct sparx5_port *port, u32 layer, u32 idx) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct sparx5_shaper sh = {0}; 52862306a36Sopenharmony_ci u32 group; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci sparx5_lg_get_group_by_index(port->sparx5, layer, idx, &group); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return sparx5_shaper_conf_set(port, &sh, layer, idx, group); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ciint sparx5_tc_ets_add(struct sparx5_port *port, 53662306a36Sopenharmony_ci struct tc_ets_qopt_offload_replace_params *params) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct sparx5_dwrr dwrr = {0}; 53962306a36Sopenharmony_ci /* Minimum weight for each iteration */ 54062306a36Sopenharmony_ci unsigned int w_min = 100; 54162306a36Sopenharmony_ci int i; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* Find minimum weight for all dwrr bands */ 54462306a36Sopenharmony_ci for (i = 0; i < SPX5_PRIOS; i++) { 54562306a36Sopenharmony_ci if (params->quanta[i] == 0) 54662306a36Sopenharmony_ci continue; 54762306a36Sopenharmony_ci w_min = min(w_min, params->weights[i]); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci for (i = 0; i < SPX5_PRIOS; i++) { 55162306a36Sopenharmony_ci /* Strict band; skip */ 55262306a36Sopenharmony_ci if (params->quanta[i] == 0) 55362306a36Sopenharmony_ci continue; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci dwrr.count++; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* On the sparx5, bands with higher indexes are preferred and 55862306a36Sopenharmony_ci * arbitrated strict. Strict bands are put in the lower indexes, 55962306a36Sopenharmony_ci * by tc, so we reverse the bands here. 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * Also convert the weight to something the hardware 56262306a36Sopenharmony_ci * understands. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci dwrr.cost[SPX5_PRIOS - i - 1] = 56562306a36Sopenharmony_ci sparx5_weight_to_hw_cost(w_min, params->weights[i]); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return sparx5_dwrr_conf_set(port, &dwrr); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ciint sparx5_tc_ets_del(struct sparx5_port *port) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct sparx5_dwrr dwrr = {0}; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return sparx5_dwrr_conf_set(port, &dwrr); 57662306a36Sopenharmony_ci} 577