1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Microchip Sparx5 Switch driver 3 * 4 * Copyright (c) 2023 Microchip Technology Inc. and its subsidiaries. 5 */ 6 7 #include "sparx5_main_regs.h" 8 #include "sparx5_main.h" 9 10 struct 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 23 int 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 37 static 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 47 int 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 59 static 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 66 static 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 73 static 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 82 static 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 92 static 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 98 static 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 104 static 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 113 static 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 128 static 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 137 static 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 162 static 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 179 int 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 XLBs 194 * 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 206 int 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 234 static 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 258 int 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 else 268 next = sparx5_sdlb_group_get_first(sparx5, group); 269 270 return sparx5_sdlb_group_link(sparx5, group, idx, first, next, false); 271 } 272 273 int 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 303 void 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