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