336 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Microchip Sparx5 Switch driver3 *4 * Copyright (c) 2023 Microchip Technology Inc. and its subsidiaries.5 */6 7#include "sparx5_main_regs.h"8#include "sparx5_main.h"9 10struct sparx5_sdlb_group sdlb_groups[SPX5_SDLB_GROUP_CNT] = {11 { SPX5_SDLB_GROUP_RATE_MAX, 8192 / 1, 64 }, /* 25 G */12 { 15000000000ULL, 8192 / 1, 64 }, /* 15 G */13 { 10000000000ULL, 8192 / 1, 64 }, /* 10 G */14 { 5000000000ULL, 8192 / 1, 64 }, /* 5 G */15 { 2500000000ULL, 8192 / 1, 64 }, /* 2.5 G */16 { 1000000000ULL, 8192 / 2, 64 }, /* 1 G */17 { 500000000ULL, 8192 / 2, 64 }, /* 500 M */18 { 100000000ULL, 8192 / 4, 64 }, /* 100 M */19 { 50000000ULL, 8192 / 4, 64 }, /* 50 M */20 { 5000000ULL, 8192 / 8, 64 } /* 5 M */21};22 23int sparx5_sdlb_clk_hz_get(struct sparx5 *sparx5)24{25 u32 clk_per_100ps;26 u64 clk_hz;27 28 clk_per_100ps = HSCH_SYS_CLK_PER_100PS_GET(spx5_rd(sparx5,29 HSCH_SYS_CLK_PER));30 if (!clk_per_100ps)31 clk_per_100ps = SPX5_CLK_PER_100PS_DEFAULT;32 33 clk_hz = (10 * 1000 * 1000) / clk_per_100ps;34 return clk_hz *= 1000;35}36 37static int sparx5_sdlb_pup_interval_get(struct sparx5 *sparx5, u32 max_token,38 u64 max_rate)39{40 u64 clk_hz;41 42 clk_hz = sparx5_sdlb_clk_hz_get(sparx5);43 44 return div64_u64((8 * clk_hz * max_token), max_rate);45}46 47int sparx5_sdlb_pup_token_get(struct sparx5 *sparx5, u32 pup_interval, u64 rate)48{49 u64 clk_hz;50 51 if (!rate)52 return SPX5_SDLB_PUP_TOKEN_DISABLE;53 54 clk_hz = sparx5_sdlb_clk_hz_get(sparx5);55 56 return DIV64_U64_ROUND_UP((rate * pup_interval), (clk_hz * 8));57}58 59static void sparx5_sdlb_group_disable(struct sparx5 *sparx5, u32 group)60{61 spx5_rmw(ANA_AC_SDLB_PUP_CTRL_PUP_ENA_SET(0),62 ANA_AC_SDLB_PUP_CTRL_PUP_ENA, sparx5,63 ANA_AC_SDLB_PUP_CTRL(group));64}65 66static void sparx5_sdlb_group_enable(struct sparx5 *sparx5, u32 group)67{68 spx5_rmw(ANA_AC_SDLB_PUP_CTRL_PUP_ENA_SET(1),69 ANA_AC_SDLB_PUP_CTRL_PUP_ENA, sparx5,70 ANA_AC_SDLB_PUP_CTRL(group));71}72 73static u32 sparx5_sdlb_group_get_first(struct sparx5 *sparx5, u32 group)74{75 u32 val;76 77 val = spx5_rd(sparx5, ANA_AC_SDLB_XLB_START(group));78 79 return ANA_AC_SDLB_XLB_START_LBSET_START_GET(val);80}81 82static u32 sparx5_sdlb_group_get_next(struct sparx5 *sparx5, u32 group,83 u32 lb)84{85 u32 val;86 87 val = spx5_rd(sparx5, ANA_AC_SDLB_XLB_NEXT(lb));88 89 return ANA_AC_SDLB_XLB_NEXT_LBSET_NEXT_GET(val);90}91 92static bool sparx5_sdlb_group_is_first(struct sparx5 *sparx5, u32 group,93 u32 lb)94{95 return lb == sparx5_sdlb_group_get_first(sparx5, group);96}97 98static bool sparx5_sdlb_group_is_last(struct sparx5 *sparx5, u32 group,99 u32 lb)100{101 return lb == sparx5_sdlb_group_get_next(sparx5, group, lb);102}103 104static bool sparx5_sdlb_group_is_empty(struct sparx5 *sparx5, u32 group)105{106 u32 val;107 108 val = spx5_rd(sparx5, ANA_AC_SDLB_PUP_CTRL(group));109 110 return ANA_AC_SDLB_PUP_CTRL_PUP_ENA_GET(val) == 0;111}112 113static u32 sparx5_sdlb_group_get_last(struct sparx5 *sparx5, u32 group)114{115 u32 itr, next;116 117 itr = sparx5_sdlb_group_get_first(sparx5, group);118 119 for (;;) {120 next = sparx5_sdlb_group_get_next(sparx5, group, itr);121 if (itr == next)122 return itr;123 124 itr = next;125 }126}127 128static bool sparx5_sdlb_group_is_singular(struct sparx5 *sparx5, u32 group)129{130 if (sparx5_sdlb_group_is_empty(sparx5, group))131 return false;132 133 return sparx5_sdlb_group_get_first(sparx5, group) ==134 sparx5_sdlb_group_get_last(sparx5, group);135}136 137static int sparx5_sdlb_group_get_adjacent(struct sparx5 *sparx5, u32 group,138 u32 idx, u32 *prev, u32 *next,139 u32 *first)140{141 u32 itr;142 143 *first = sparx5_sdlb_group_get_first(sparx5, group);144 *prev = *first;145 *next = *first;146 itr = *first;147 148 for (;;) {149 *next = sparx5_sdlb_group_get_next(sparx5, group, itr);150 151 if (itr == idx)152 return 0; /* Found it */153 154 if (itr == *next)155 return -EINVAL; /* Was not found */156 157 *prev = itr;158 itr = *next;159 }160}161 162static int sparx5_sdlb_group_get_count(struct sparx5 *sparx5, u32 group)163{164 u32 itr, next;165 int count = 0;166 167 itr = sparx5_sdlb_group_get_first(sparx5, group);168 169 for (;;) {170 next = sparx5_sdlb_group_get_next(sparx5, group, itr);171 if (itr == next)172 return count;173 174 itr = next;175 count++;176 }177}178 179int sparx5_sdlb_group_get_by_rate(struct sparx5 *sparx5, u32 rate, u32 burst)180{181 const struct sparx5_sdlb_group *group;182 u64 rate_bps;183 int i, count;184 185 rate_bps = rate * 1000;186 187 for (i = SPX5_SDLB_GROUP_CNT - 1; i >= 0; i--) {188 group = &sdlb_groups[i];189 190 count = sparx5_sdlb_group_get_count(sparx5, i);191 192 /* Check that this group is not full.193 * According to LB group configuration rules: the number of XLBs194 * in a group must not exceed PUP_INTERVAL/4 - 1.195 */196 if (count > ((group->pup_interval / 4) - 1))197 continue;198 199 if (rate_bps < group->max_rate)200 return i;201 }202 203 return -ENOSPC;204}205 206int sparx5_sdlb_group_get_by_index(struct sparx5 *sparx5, u32 idx, u32 *group)207{208 u32 itr, next;209 int i;210 211 for (i = 0; i < SPX5_SDLB_GROUP_CNT; i++) {212 if (sparx5_sdlb_group_is_empty(sparx5, i))213 continue;214 215 itr = sparx5_sdlb_group_get_first(sparx5, i);216 217 for (;;) {218 next = sparx5_sdlb_group_get_next(sparx5, i, itr);219 220 if (itr == idx) {221 *group = i;222 return 0; /* Found it */223 }224 if (itr == next)225 break; /* Was not found */226 227 itr = next;228 }229 }230 231 return -EINVAL;232}233 234static int sparx5_sdlb_group_link(struct sparx5 *sparx5, u32 group, u32 idx,235 u32 first, u32 next, bool empty)236{237 /* Stop leaking */238 sparx5_sdlb_group_disable(sparx5, group);239 240 if (empty)241 return 0;242 243 /* Link insertion lb to next lb */244 spx5_wr(ANA_AC_SDLB_XLB_NEXT_LBSET_NEXT_SET(next) |245 ANA_AC_SDLB_XLB_NEXT_LBGRP_SET(group),246 sparx5, ANA_AC_SDLB_XLB_NEXT(idx));247 248 /* Set the first lb */249 spx5_wr(ANA_AC_SDLB_XLB_START_LBSET_START_SET(first), sparx5,250 ANA_AC_SDLB_XLB_START(group));251 252 /* Start leaking */253 sparx5_sdlb_group_enable(sparx5, group);254 255 return 0;256};257 258int sparx5_sdlb_group_add(struct sparx5 *sparx5, u32 group, u32 idx)259{260 u32 first, next;261 262 /* We always add to head of the list */263 first = idx;264 265 if (sparx5_sdlb_group_is_empty(sparx5, group))266 next = idx;267 else268 next = sparx5_sdlb_group_get_first(sparx5, group);269 270 return sparx5_sdlb_group_link(sparx5, group, idx, first, next, false);271}272 273int sparx5_sdlb_group_del(struct sparx5 *sparx5, u32 group, u32 idx)274{275 u32 first, next, prev;276 bool empty = false;277 278 if (sparx5_sdlb_group_get_adjacent(sparx5, group, idx, &prev, &next,279 &first) < 0) {280 pr_err("%s:%d Could not find idx: %d in group: %d", __func__,281 __LINE__, idx, group);282 return -EINVAL;283 }284 285 if (sparx5_sdlb_group_is_singular(sparx5, group)) {286 empty = true;287 } else if (sparx5_sdlb_group_is_last(sparx5, group, idx)) {288 /* idx is removed, prev is now last */289 idx = prev;290 next = prev;291 } else if (sparx5_sdlb_group_is_first(sparx5, group, idx)) {292 /* idx is removed and points to itself, first is next */293 first = next;294 next = idx;295 } else {296 /* Next is not touched */297 idx = prev;298 }299 300 return sparx5_sdlb_group_link(sparx5, group, idx, first, next, empty);301}302 303void sparx5_sdlb_group_init(struct sparx5 *sparx5, u64 max_rate, u32 min_burst,304 u32 frame_size, u32 idx)305{306 u32 thres_shift, mask = 0x01, power = 0;307 struct sparx5_sdlb_group *group;308 u64 max_token;309 310 group = &sdlb_groups[idx];311 312 /* Number of positions to right-shift LB's threshold value. */313 while ((min_burst & mask) == 0) {314 power++;315 mask <<= 1;316 }317 thres_shift = SPX5_SDLB_2CYCLES_TYPE2_THRES_OFFSET - power;318 319 max_token = (min_burst > SPX5_SDLB_PUP_TOKEN_MAX) ?320 SPX5_SDLB_PUP_TOKEN_MAX :321 min_burst;322 group->pup_interval =323 sparx5_sdlb_pup_interval_get(sparx5, max_token, max_rate);324 325 group->frame_size = frame_size;326 327 spx5_wr(ANA_AC_SDLB_PUP_INTERVAL_PUP_INTERVAL_SET(group->pup_interval),328 sparx5, ANA_AC_SDLB_PUP_INTERVAL(idx));329 330 spx5_wr(ANA_AC_SDLB_FRM_RATE_TOKENS_FRM_RATE_TOKENS_SET(frame_size),331 sparx5, ANA_AC_SDLB_FRM_RATE_TOKENS(idx));332 333 spx5_wr(ANA_AC_SDLB_LBGRP_MISC_THRES_SHIFT_SET(thres_shift), sparx5,334 ANA_AC_SDLB_LBGRP_MISC(idx));335}336