1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */ 3 4 #include <linux/kernel.h> 5 #include <linux/types.h> 6 #include <linux/dcbnl.h> 7 #include <linux/if_ether.h> 8 #include <linux/list.h> 9 #include <linux/netlink.h> 10 11 #include "spectrum.h" 12 #include "core.h" 13 #include "port.h" 14 #include "reg.h" 15 16 struct mlxsw_sp_sb_pr { 17 enum mlxsw_reg_sbpr_mode mode; 18 u32 size; 19 u8 freeze_mode:1, 20 freeze_size:1; 21 }; 22 23 struct mlxsw_cp_sb_occ { 24 u32 cur; 25 u32 max; 26 }; 27 28 struct mlxsw_sp_sb_cm { 29 u32 min_buff; 30 u32 max_buff; 31 u16 pool_index; 32 struct mlxsw_cp_sb_occ occ; 33 u8 freeze_pool:1, 34 freeze_thresh:1; 35 }; 36 37 #define MLXSW_SP_SB_INFI -1U 38 #define MLXSW_SP_SB_REST -2U 39 40 struct mlxsw_sp_sb_pm { 41 u32 min_buff; 42 u32 max_buff; 43 struct mlxsw_cp_sb_occ occ; 44 }; 45 46 struct mlxsw_sp_sb_mm { 47 u32 min_buff; 48 u32 max_buff; 49 u16 pool_index; 50 }; 51 52 struct mlxsw_sp_sb_pool_des { 53 enum mlxsw_reg_sbxx_dir dir; 54 u8 pool; 55 }; 56 57 #define MLXSW_SP_SB_POOL_ING 0 58 #define MLXSW_SP_SB_POOL_EGR 4 59 #define MLXSW_SP_SB_POOL_EGR_MC 8 60 #define MLXSW_SP_SB_POOL_ING_CPU 9 61 #define MLXSW_SP_SB_POOL_EGR_CPU 10 62 63 static const struct mlxsw_sp_sb_pool_des mlxsw_sp1_sb_pool_dess[] = { 64 {MLXSW_REG_SBXX_DIR_INGRESS, 0}, 65 {MLXSW_REG_SBXX_DIR_INGRESS, 1}, 66 {MLXSW_REG_SBXX_DIR_INGRESS, 2}, 67 {MLXSW_REG_SBXX_DIR_INGRESS, 3}, 68 {MLXSW_REG_SBXX_DIR_EGRESS, 0}, 69 {MLXSW_REG_SBXX_DIR_EGRESS, 1}, 70 {MLXSW_REG_SBXX_DIR_EGRESS, 2}, 71 {MLXSW_REG_SBXX_DIR_EGRESS, 3}, 72 {MLXSW_REG_SBXX_DIR_EGRESS, 15}, 73 {MLXSW_REG_SBXX_DIR_INGRESS, 4}, 74 {MLXSW_REG_SBXX_DIR_EGRESS, 4}, 75 }; 76 77 static const struct mlxsw_sp_sb_pool_des mlxsw_sp2_sb_pool_dess[] = { 78 {MLXSW_REG_SBXX_DIR_INGRESS, 0}, 79 {MLXSW_REG_SBXX_DIR_INGRESS, 1}, 80 {MLXSW_REG_SBXX_DIR_INGRESS, 2}, 81 {MLXSW_REG_SBXX_DIR_INGRESS, 3}, 82 {MLXSW_REG_SBXX_DIR_EGRESS, 0}, 83 {MLXSW_REG_SBXX_DIR_EGRESS, 1}, 84 {MLXSW_REG_SBXX_DIR_EGRESS, 2}, 85 {MLXSW_REG_SBXX_DIR_EGRESS, 3}, 86 {MLXSW_REG_SBXX_DIR_EGRESS, 15}, 87 {MLXSW_REG_SBXX_DIR_INGRESS, 4}, 88 {MLXSW_REG_SBXX_DIR_EGRESS, 4}, 89 }; 90 91 #define MLXSW_SP_SB_ING_TC_COUNT 8 92 #define MLXSW_SP_SB_EG_TC_COUNT 16 93 94 struct mlxsw_sp_sb_port { 95 struct mlxsw_sp_sb_cm ing_cms[MLXSW_SP_SB_ING_TC_COUNT]; 96 struct mlxsw_sp_sb_cm eg_cms[MLXSW_SP_SB_EG_TC_COUNT]; 97 struct mlxsw_sp_sb_pm *pms; 98 }; 99 100 struct mlxsw_sp_sb { 101 struct mlxsw_sp_sb_pr *prs; 102 struct mlxsw_sp_sb_port *ports; 103 u32 cell_size; 104 u32 max_headroom_cells; 105 u64 sb_size; 106 }; 107 108 struct mlxsw_sp_sb_vals { 109 unsigned int pool_count; 110 const struct mlxsw_sp_sb_pool_des *pool_dess; 111 const struct mlxsw_sp_sb_pm *pms; 112 const struct mlxsw_sp_sb_pm *pms_cpu; 113 const struct mlxsw_sp_sb_pr *prs; 114 const struct mlxsw_sp_sb_mm *mms; 115 const struct mlxsw_sp_sb_cm *cms_ingress; 116 const struct mlxsw_sp_sb_cm *cms_egress; 117 const struct mlxsw_sp_sb_cm *cms_cpu; 118 unsigned int mms_count; 119 unsigned int cms_ingress_count; 120 unsigned int cms_egress_count; 121 unsigned int cms_cpu_count; 122 }; 123 124 u32 mlxsw_sp_cells_bytes(const struct mlxsw_sp *mlxsw_sp, u32 cells) 125 { 126 return mlxsw_sp->sb->cell_size * cells; 127 } 128 129 u32 mlxsw_sp_bytes_cells(const struct mlxsw_sp *mlxsw_sp, u32 bytes) 130 { 131 return DIV_ROUND_UP(bytes, mlxsw_sp->sb->cell_size); 132 } 133 134 u32 mlxsw_sp_sb_max_headroom_cells(const struct mlxsw_sp *mlxsw_sp) 135 { 136 return mlxsw_sp->sb->max_headroom_cells; 137 } 138 139 static struct mlxsw_sp_sb_pr *mlxsw_sp_sb_pr_get(struct mlxsw_sp *mlxsw_sp, 140 u16 pool_index) 141 { 142 return &mlxsw_sp->sb->prs[pool_index]; 143 } 144 145 static bool mlxsw_sp_sb_cm_exists(u8 pg_buff, enum mlxsw_reg_sbxx_dir dir) 146 { 147 if (dir == MLXSW_REG_SBXX_DIR_INGRESS) 148 return pg_buff < MLXSW_SP_SB_ING_TC_COUNT; 149 else 150 return pg_buff < MLXSW_SP_SB_EG_TC_COUNT; 151 } 152 153 static struct mlxsw_sp_sb_cm *mlxsw_sp_sb_cm_get(struct mlxsw_sp *mlxsw_sp, 154 u8 local_port, u8 pg_buff, 155 enum mlxsw_reg_sbxx_dir dir) 156 { 157 struct mlxsw_sp_sb_port *sb_port = &mlxsw_sp->sb->ports[local_port]; 158 159 WARN_ON(!mlxsw_sp_sb_cm_exists(pg_buff, dir)); 160 if (dir == MLXSW_REG_SBXX_DIR_INGRESS) 161 return &sb_port->ing_cms[pg_buff]; 162 else 163 return &sb_port->eg_cms[pg_buff]; 164 } 165 166 static struct mlxsw_sp_sb_pm *mlxsw_sp_sb_pm_get(struct mlxsw_sp *mlxsw_sp, 167 u8 local_port, u16 pool_index) 168 { 169 return &mlxsw_sp->sb->ports[local_port].pms[pool_index]; 170 } 171 172 static int mlxsw_sp_sb_pr_write(struct mlxsw_sp *mlxsw_sp, u16 pool_index, 173 enum mlxsw_reg_sbpr_mode mode, 174 u32 size, bool infi_size) 175 { 176 const struct mlxsw_sp_sb_pool_des *des = 177 &mlxsw_sp->sb_vals->pool_dess[pool_index]; 178 char sbpr_pl[MLXSW_REG_SBPR_LEN]; 179 struct mlxsw_sp_sb_pr *pr; 180 int err; 181 182 mlxsw_reg_sbpr_pack(sbpr_pl, des->pool, des->dir, mode, 183 size, infi_size); 184 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbpr), sbpr_pl); 185 if (err) 186 return err; 187 188 if (infi_size) 189 size = mlxsw_sp_bytes_cells(mlxsw_sp, mlxsw_sp->sb->sb_size); 190 pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index); 191 pr->mode = mode; 192 pr->size = size; 193 return 0; 194 } 195 196 static int mlxsw_sp_sb_cm_write(struct mlxsw_sp *mlxsw_sp, u8 local_port, 197 u8 pg_buff, u32 min_buff, u32 max_buff, 198 bool infi_max, u16 pool_index) 199 { 200 const struct mlxsw_sp_sb_pool_des *des = 201 &mlxsw_sp->sb_vals->pool_dess[pool_index]; 202 char sbcm_pl[MLXSW_REG_SBCM_LEN]; 203 struct mlxsw_sp_sb_cm *cm; 204 int err; 205 206 mlxsw_reg_sbcm_pack(sbcm_pl, local_port, pg_buff, des->dir, 207 min_buff, max_buff, infi_max, des->pool); 208 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbcm), sbcm_pl); 209 if (err) 210 return err; 211 212 if (mlxsw_sp_sb_cm_exists(pg_buff, des->dir)) { 213 if (infi_max) 214 max_buff = mlxsw_sp_bytes_cells(mlxsw_sp, 215 mlxsw_sp->sb->sb_size); 216 217 cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, pg_buff, 218 des->dir); 219 cm->min_buff = min_buff; 220 cm->max_buff = max_buff; 221 cm->pool_index = pool_index; 222 } 223 return 0; 224 } 225 226 static int mlxsw_sp_sb_pm_write(struct mlxsw_sp *mlxsw_sp, u8 local_port, 227 u16 pool_index, u32 min_buff, u32 max_buff) 228 { 229 const struct mlxsw_sp_sb_pool_des *des = 230 &mlxsw_sp->sb_vals->pool_dess[pool_index]; 231 char sbpm_pl[MLXSW_REG_SBPM_LEN]; 232 struct mlxsw_sp_sb_pm *pm; 233 int err; 234 235 mlxsw_reg_sbpm_pack(sbpm_pl, local_port, des->pool, des->dir, false, 236 min_buff, max_buff); 237 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbpm), sbpm_pl); 238 if (err) 239 return err; 240 241 pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port, pool_index); 242 pm->min_buff = min_buff; 243 pm->max_buff = max_buff; 244 return 0; 245 } 246 247 static int mlxsw_sp_sb_pm_occ_clear(struct mlxsw_sp *mlxsw_sp, u8 local_port, 248 u16 pool_index, struct list_head *bulk_list) 249 { 250 const struct mlxsw_sp_sb_pool_des *des = 251 &mlxsw_sp->sb_vals->pool_dess[pool_index]; 252 char sbpm_pl[MLXSW_REG_SBPM_LEN]; 253 254 if (local_port == MLXSW_PORT_CPU_PORT && 255 des->dir == MLXSW_REG_SBXX_DIR_INGRESS) 256 return 0; 257 258 mlxsw_reg_sbpm_pack(sbpm_pl, local_port, des->pool, des->dir, 259 true, 0, 0); 260 return mlxsw_reg_trans_query(mlxsw_sp->core, MLXSW_REG(sbpm), sbpm_pl, 261 bulk_list, NULL, 0); 262 } 263 264 static void mlxsw_sp_sb_pm_occ_query_cb(struct mlxsw_core *mlxsw_core, 265 char *sbpm_pl, size_t sbpm_pl_len, 266 unsigned long cb_priv) 267 { 268 struct mlxsw_sp_sb_pm *pm = (struct mlxsw_sp_sb_pm *) cb_priv; 269 270 mlxsw_reg_sbpm_unpack(sbpm_pl, &pm->occ.cur, &pm->occ.max); 271 } 272 273 static int mlxsw_sp_sb_pm_occ_query(struct mlxsw_sp *mlxsw_sp, u8 local_port, 274 u16 pool_index, struct list_head *bulk_list) 275 { 276 const struct mlxsw_sp_sb_pool_des *des = 277 &mlxsw_sp->sb_vals->pool_dess[pool_index]; 278 char sbpm_pl[MLXSW_REG_SBPM_LEN]; 279 struct mlxsw_sp_sb_pm *pm; 280 281 if (local_port == MLXSW_PORT_CPU_PORT && 282 des->dir == MLXSW_REG_SBXX_DIR_INGRESS) 283 return 0; 284 285 pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port, pool_index); 286 mlxsw_reg_sbpm_pack(sbpm_pl, local_port, des->pool, des->dir, 287 false, 0, 0); 288 return mlxsw_reg_trans_query(mlxsw_sp->core, MLXSW_REG(sbpm), sbpm_pl, 289 bulk_list, 290 mlxsw_sp_sb_pm_occ_query_cb, 291 (unsigned long) pm); 292 } 293 294 /* 1/4 of a headroom necessary for 100Gbps port and 100m cable. */ 295 #define MLXSW_SP_PB_HEADROOM 25632 296 #define MLXSW_SP_PB_UNUSED 8 297 298 static int mlxsw_sp_port_pb_init(struct mlxsw_sp_port *mlxsw_sp_port) 299 { 300 const u32 pbs[] = { 301 [0] = MLXSW_SP_PB_HEADROOM * mlxsw_sp_port->mapping.width, 302 [9] = MLXSW_PORT_MAX_MTU, 303 }; 304 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 305 char pbmc_pl[MLXSW_REG_PBMC_LEN]; 306 int i; 307 308 mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port, 309 0xffff, 0xffff / 2); 310 for (i = 0; i < ARRAY_SIZE(pbs); i++) { 311 u16 size = mlxsw_sp_bytes_cells(mlxsw_sp, pbs[i]); 312 313 if (i == MLXSW_SP_PB_UNUSED) 314 continue; 315 size = mlxsw_sp_port_headroom_8x_adjust(mlxsw_sp_port, size); 316 mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, i, size); 317 } 318 mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, 319 MLXSW_REG_PBMC_PORT_SHARED_BUF_IDX, 0); 320 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl); 321 } 322 323 static int mlxsw_sp_port_pb_prio_init(struct mlxsw_sp_port *mlxsw_sp_port) 324 { 325 char pptb_pl[MLXSW_REG_PPTB_LEN]; 326 int i; 327 328 mlxsw_reg_pptb_pack(pptb_pl, mlxsw_sp_port->local_port); 329 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 330 mlxsw_reg_pptb_prio_to_buff_pack(pptb_pl, i, 0); 331 return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb), 332 pptb_pl); 333 } 334 335 static int mlxsw_sp_port_headroom_init(struct mlxsw_sp_port *mlxsw_sp_port) 336 { 337 int err; 338 339 err = mlxsw_sp_port_pb_init(mlxsw_sp_port); 340 if (err) 341 return err; 342 return mlxsw_sp_port_pb_prio_init(mlxsw_sp_port); 343 } 344 345 static int mlxsw_sp_sb_port_init(struct mlxsw_sp *mlxsw_sp, 346 struct mlxsw_sp_sb_port *sb_port) 347 { 348 struct mlxsw_sp_sb_pm *pms; 349 350 pms = kcalloc(mlxsw_sp->sb_vals->pool_count, sizeof(*pms), 351 GFP_KERNEL); 352 if (!pms) 353 return -ENOMEM; 354 sb_port->pms = pms; 355 return 0; 356 } 357 358 static void mlxsw_sp_sb_port_fini(struct mlxsw_sp_sb_port *sb_port) 359 { 360 kfree(sb_port->pms); 361 } 362 363 static int mlxsw_sp_sb_ports_init(struct mlxsw_sp *mlxsw_sp) 364 { 365 unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); 366 struct mlxsw_sp_sb_pr *prs; 367 int i; 368 int err; 369 370 mlxsw_sp->sb->ports = kcalloc(max_ports, 371 sizeof(struct mlxsw_sp_sb_port), 372 GFP_KERNEL); 373 if (!mlxsw_sp->sb->ports) 374 return -ENOMEM; 375 376 prs = kcalloc(mlxsw_sp->sb_vals->pool_count, sizeof(*prs), 377 GFP_KERNEL); 378 if (!prs) { 379 err = -ENOMEM; 380 goto err_alloc_prs; 381 } 382 mlxsw_sp->sb->prs = prs; 383 384 for (i = 0; i < max_ports; i++) { 385 err = mlxsw_sp_sb_port_init(mlxsw_sp, &mlxsw_sp->sb->ports[i]); 386 if (err) 387 goto err_sb_port_init; 388 } 389 390 return 0; 391 392 err_sb_port_init: 393 for (i--; i >= 0; i--) 394 mlxsw_sp_sb_port_fini(&mlxsw_sp->sb->ports[i]); 395 kfree(mlxsw_sp->sb->prs); 396 err_alloc_prs: 397 kfree(mlxsw_sp->sb->ports); 398 return err; 399 } 400 401 static void mlxsw_sp_sb_ports_fini(struct mlxsw_sp *mlxsw_sp) 402 { 403 int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); 404 int i; 405 406 for (i = max_ports - 1; i >= 0; i--) 407 mlxsw_sp_sb_port_fini(&mlxsw_sp->sb->ports[i]); 408 kfree(mlxsw_sp->sb->prs); 409 kfree(mlxsw_sp->sb->ports); 410 } 411 412 #define MLXSW_SP_SB_PR(_mode, _size) \ 413 { \ 414 .mode = _mode, \ 415 .size = _size, \ 416 } 417 418 #define MLXSW_SP_SB_PR_EXT(_mode, _size, _freeze_mode, _freeze_size) \ 419 { \ 420 .mode = _mode, \ 421 .size = _size, \ 422 .freeze_mode = _freeze_mode, \ 423 .freeze_size = _freeze_size, \ 424 } 425 426 #define MLXSW_SP1_SB_PR_CPU_SIZE (256 * 1000) 427 428 /* Order according to mlxsw_sp1_sb_pool_dess */ 429 static const struct mlxsw_sp_sb_pr mlxsw_sp1_sb_prs[] = { 430 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, MLXSW_SP_SB_REST), 431 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), 432 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), 433 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), 434 MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC, MLXSW_SP_SB_REST, 435 true, false), 436 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), 437 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), 438 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), 439 MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_STATIC, MLXSW_SP_SB_INFI, 440 true, true), 441 MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC, 442 MLXSW_SP1_SB_PR_CPU_SIZE, true, false), 443 MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC, 444 MLXSW_SP1_SB_PR_CPU_SIZE, true, false), 445 }; 446 447 #define MLXSW_SP2_SB_PR_CPU_SIZE (256 * 1000) 448 449 /* Order according to mlxsw_sp2_sb_pool_dess */ 450 static const struct mlxsw_sp_sb_pr mlxsw_sp2_sb_prs[] = { 451 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, MLXSW_SP_SB_REST), 452 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0), 453 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0), 454 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0), 455 MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC, MLXSW_SP_SB_REST, 456 true, false), 457 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0), 458 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0), 459 MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0), 460 MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_STATIC, MLXSW_SP_SB_INFI, 461 true, true), 462 MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC, 463 MLXSW_SP2_SB_PR_CPU_SIZE, true, false), 464 MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC, 465 MLXSW_SP2_SB_PR_CPU_SIZE, true, false), 466 }; 467 468 static int mlxsw_sp_sb_prs_init(struct mlxsw_sp *mlxsw_sp, 469 const struct mlxsw_sp_sb_pr *prs, 470 const struct mlxsw_sp_sb_pool_des *pool_dess, 471 size_t prs_len) 472 { 473 /* Round down, unlike mlxsw_sp_bytes_cells(). */ 474 u32 sb_cells = div_u64(mlxsw_sp->sb->sb_size, mlxsw_sp->sb->cell_size); 475 u32 rest_cells[2] = {sb_cells, sb_cells}; 476 int i; 477 int err; 478 479 /* Calculate how much space to give to the "REST" pools in either 480 * direction. 481 */ 482 for (i = 0; i < prs_len; i++) { 483 enum mlxsw_reg_sbxx_dir dir = pool_dess[i].dir; 484 u32 size = prs[i].size; 485 u32 size_cells; 486 487 if (size == MLXSW_SP_SB_INFI || size == MLXSW_SP_SB_REST) 488 continue; 489 490 size_cells = mlxsw_sp_bytes_cells(mlxsw_sp, size); 491 if (WARN_ON_ONCE(size_cells > rest_cells[dir])) 492 continue; 493 494 rest_cells[dir] -= size_cells; 495 } 496 497 for (i = 0; i < prs_len; i++) { 498 u32 size = prs[i].size; 499 u32 size_cells; 500 501 if (size == MLXSW_SP_SB_INFI) { 502 err = mlxsw_sp_sb_pr_write(mlxsw_sp, i, prs[i].mode, 503 0, true); 504 } else if (size == MLXSW_SP_SB_REST) { 505 size_cells = rest_cells[pool_dess[i].dir]; 506 err = mlxsw_sp_sb_pr_write(mlxsw_sp, i, prs[i].mode, 507 size_cells, false); 508 } else { 509 size_cells = mlxsw_sp_bytes_cells(mlxsw_sp, size); 510 err = mlxsw_sp_sb_pr_write(mlxsw_sp, i, prs[i].mode, 511 size_cells, false); 512 } 513 if (err) 514 return err; 515 } 516 return 0; 517 } 518 519 #define MLXSW_SP_SB_CM(_min_buff, _max_buff, _pool) \ 520 { \ 521 .min_buff = _min_buff, \ 522 .max_buff = _max_buff, \ 523 .pool_index = _pool, \ 524 } 525 526 #define MLXSW_SP_SB_CM_ING(_min_buff, _max_buff) \ 527 { \ 528 .min_buff = _min_buff, \ 529 .max_buff = _max_buff, \ 530 .pool_index = MLXSW_SP_SB_POOL_ING, \ 531 } 532 533 #define MLXSW_SP_SB_CM_EGR(_min_buff, _max_buff) \ 534 { \ 535 .min_buff = _min_buff, \ 536 .max_buff = _max_buff, \ 537 .pool_index = MLXSW_SP_SB_POOL_EGR, \ 538 } 539 540 #define MLXSW_SP_SB_CM_EGR_MC(_min_buff, _max_buff) \ 541 { \ 542 .min_buff = _min_buff, \ 543 .max_buff = _max_buff, \ 544 .pool_index = MLXSW_SP_SB_POOL_EGR_MC, \ 545 .freeze_pool = true, \ 546 .freeze_thresh = true, \ 547 } 548 549 static const struct mlxsw_sp_sb_cm mlxsw_sp1_sb_cms_ingress[] = { 550 MLXSW_SP_SB_CM_ING(10000, 8), 551 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 552 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 553 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 554 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 555 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 556 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 557 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 558 MLXSW_SP_SB_CM_ING(0, 0), /* dummy, this PG does not exist */ 559 MLXSW_SP_SB_CM(10000, 8, MLXSW_SP_SB_POOL_ING_CPU), 560 }; 561 562 static const struct mlxsw_sp_sb_cm mlxsw_sp2_sb_cms_ingress[] = { 563 MLXSW_SP_SB_CM_ING(0, 7), 564 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 565 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 566 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 567 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 568 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 569 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 570 MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 571 MLXSW_SP_SB_CM_ING(0, 0), /* dummy, this PG does not exist */ 572 MLXSW_SP_SB_CM(10000, 8, MLXSW_SP_SB_POOL_ING_CPU), 573 }; 574 575 static const struct mlxsw_sp_sb_cm mlxsw_sp1_sb_cms_egress[] = { 576 MLXSW_SP_SB_CM_EGR(1500, 9), 577 MLXSW_SP_SB_CM_EGR(1500, 9), 578 MLXSW_SP_SB_CM_EGR(1500, 9), 579 MLXSW_SP_SB_CM_EGR(1500, 9), 580 MLXSW_SP_SB_CM_EGR(1500, 9), 581 MLXSW_SP_SB_CM_EGR(1500, 9), 582 MLXSW_SP_SB_CM_EGR(1500, 9), 583 MLXSW_SP_SB_CM_EGR(1500, 9), 584 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 585 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 586 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 587 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 588 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 589 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 590 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 591 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 592 MLXSW_SP_SB_CM_EGR(1, 0xff), 593 }; 594 595 static const struct mlxsw_sp_sb_cm mlxsw_sp2_sb_cms_egress[] = { 596 MLXSW_SP_SB_CM_EGR(0, 7), 597 MLXSW_SP_SB_CM_EGR(0, 7), 598 MLXSW_SP_SB_CM_EGR(0, 7), 599 MLXSW_SP_SB_CM_EGR(0, 7), 600 MLXSW_SP_SB_CM_EGR(0, 7), 601 MLXSW_SP_SB_CM_EGR(0, 7), 602 MLXSW_SP_SB_CM_EGR(0, 7), 603 MLXSW_SP_SB_CM_EGR(0, 7), 604 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 605 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 606 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 607 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 608 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 609 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 610 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 611 MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI), 612 MLXSW_SP_SB_CM_EGR(1, 0xff), 613 }; 614 615 #define MLXSW_SP_CPU_PORT_SB_CM MLXSW_SP_SB_CM(0, 0, MLXSW_SP_SB_POOL_EGR_CPU) 616 617 static const struct mlxsw_sp_sb_cm mlxsw_sp_cpu_port_sb_cms[] = { 618 MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU), 619 MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU), 620 MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU), 621 MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU), 622 MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU), 623 MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU), 624 MLXSW_SP_CPU_PORT_SB_CM, 625 MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU), 626 MLXSW_SP_CPU_PORT_SB_CM, 627 MLXSW_SP_CPU_PORT_SB_CM, 628 MLXSW_SP_CPU_PORT_SB_CM, 629 MLXSW_SP_CPU_PORT_SB_CM, 630 MLXSW_SP_CPU_PORT_SB_CM, 631 MLXSW_SP_CPU_PORT_SB_CM, 632 MLXSW_SP_CPU_PORT_SB_CM, 633 MLXSW_SP_CPU_PORT_SB_CM, 634 MLXSW_SP_CPU_PORT_SB_CM, 635 MLXSW_SP_CPU_PORT_SB_CM, 636 MLXSW_SP_CPU_PORT_SB_CM, 637 MLXSW_SP_CPU_PORT_SB_CM, 638 MLXSW_SP_CPU_PORT_SB_CM, 639 MLXSW_SP_CPU_PORT_SB_CM, 640 MLXSW_SP_CPU_PORT_SB_CM, 641 MLXSW_SP_CPU_PORT_SB_CM, 642 MLXSW_SP_CPU_PORT_SB_CM, 643 MLXSW_SP_CPU_PORT_SB_CM, 644 MLXSW_SP_CPU_PORT_SB_CM, 645 MLXSW_SP_CPU_PORT_SB_CM, 646 MLXSW_SP_CPU_PORT_SB_CM, 647 MLXSW_SP_CPU_PORT_SB_CM, 648 MLXSW_SP_CPU_PORT_SB_CM, 649 MLXSW_SP_CPU_PORT_SB_CM, 650 }; 651 652 static bool 653 mlxsw_sp_sb_pool_is_static(struct mlxsw_sp *mlxsw_sp, u16 pool_index) 654 { 655 struct mlxsw_sp_sb_pr *pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index); 656 657 return pr->mode == MLXSW_REG_SBPR_MODE_STATIC; 658 } 659 660 static int __mlxsw_sp_sb_cms_init(struct mlxsw_sp *mlxsw_sp, u8 local_port, 661 enum mlxsw_reg_sbxx_dir dir, 662 const struct mlxsw_sp_sb_cm *cms, 663 size_t cms_len) 664 { 665 const struct mlxsw_sp_sb_vals *sb_vals = mlxsw_sp->sb_vals; 666 int i; 667 int err; 668 669 for (i = 0; i < cms_len; i++) { 670 const struct mlxsw_sp_sb_cm *cm; 671 u32 min_buff; 672 u32 max_buff; 673 674 if (i == 8 && dir == MLXSW_REG_SBXX_DIR_INGRESS) 675 continue; /* PG number 8 does not exist, skip it */ 676 cm = &cms[i]; 677 if (WARN_ON(sb_vals->pool_dess[cm->pool_index].dir != dir)) 678 continue; 679 680 min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, cm->min_buff); 681 max_buff = cm->max_buff; 682 if (max_buff == MLXSW_SP_SB_INFI) { 683 err = mlxsw_sp_sb_cm_write(mlxsw_sp, local_port, i, 684 min_buff, 0, 685 true, cm->pool_index); 686 } else { 687 if (mlxsw_sp_sb_pool_is_static(mlxsw_sp, 688 cm->pool_index)) 689 max_buff = mlxsw_sp_bytes_cells(mlxsw_sp, 690 max_buff); 691 err = mlxsw_sp_sb_cm_write(mlxsw_sp, local_port, i, 692 min_buff, max_buff, 693 false, cm->pool_index); 694 } 695 if (err) 696 return err; 697 } 698 return 0; 699 } 700 701 static int mlxsw_sp_port_sb_cms_init(struct mlxsw_sp_port *mlxsw_sp_port) 702 { 703 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 704 int err; 705 706 err = __mlxsw_sp_sb_cms_init(mlxsw_sp, 707 mlxsw_sp_port->local_port, 708 MLXSW_REG_SBXX_DIR_INGRESS, 709 mlxsw_sp->sb_vals->cms_ingress, 710 mlxsw_sp->sb_vals->cms_ingress_count); 711 if (err) 712 return err; 713 return __mlxsw_sp_sb_cms_init(mlxsw_sp_port->mlxsw_sp, 714 mlxsw_sp_port->local_port, 715 MLXSW_REG_SBXX_DIR_EGRESS, 716 mlxsw_sp->sb_vals->cms_egress, 717 mlxsw_sp->sb_vals->cms_egress_count); 718 } 719 720 static int mlxsw_sp_cpu_port_sb_cms_init(struct mlxsw_sp *mlxsw_sp) 721 { 722 return __mlxsw_sp_sb_cms_init(mlxsw_sp, 0, MLXSW_REG_SBXX_DIR_EGRESS, 723 mlxsw_sp->sb_vals->cms_cpu, 724 mlxsw_sp->sb_vals->cms_cpu_count); 725 } 726 727 #define MLXSW_SP_SB_PM(_min_buff, _max_buff) \ 728 { \ 729 .min_buff = _min_buff, \ 730 .max_buff = _max_buff, \ 731 } 732 733 /* Order according to mlxsw_sp1_sb_pool_dess */ 734 static const struct mlxsw_sp_sb_pm mlxsw_sp1_sb_pms[] = { 735 MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MAX), 736 MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 737 MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 738 MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 739 MLXSW_SP_SB_PM(0, 7), 740 MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 741 MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 742 MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 743 MLXSW_SP_SB_PM(10000, 90000), 744 MLXSW_SP_SB_PM(0, 8), /* 50% occupancy */ 745 MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 746 }; 747 748 /* Order according to mlxsw_sp2_sb_pool_dess */ 749 static const struct mlxsw_sp_sb_pm mlxsw_sp2_sb_pms[] = { 750 MLXSW_SP_SB_PM(0, 7), 751 MLXSW_SP_SB_PM(0, 0), 752 MLXSW_SP_SB_PM(0, 0), 753 MLXSW_SP_SB_PM(0, 0), 754 MLXSW_SP_SB_PM(0, 7), 755 MLXSW_SP_SB_PM(0, 0), 756 MLXSW_SP_SB_PM(0, 0), 757 MLXSW_SP_SB_PM(0, 0), 758 MLXSW_SP_SB_PM(10000, 90000), 759 MLXSW_SP_SB_PM(0, 8), /* 50% occupancy */ 760 MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN), 761 }; 762 763 /* Order according to mlxsw_sp*_sb_pool_dess */ 764 static const struct mlxsw_sp_sb_pm mlxsw_sp_cpu_port_sb_pms[] = { 765 MLXSW_SP_SB_PM(0, 0), 766 MLXSW_SP_SB_PM(0, 0), 767 MLXSW_SP_SB_PM(0, 0), 768 MLXSW_SP_SB_PM(0, 0), 769 MLXSW_SP_SB_PM(0, 0), 770 MLXSW_SP_SB_PM(0, 0), 771 MLXSW_SP_SB_PM(0, 0), 772 MLXSW_SP_SB_PM(0, 0), 773 MLXSW_SP_SB_PM(0, 90000), 774 MLXSW_SP_SB_PM(0, 0), 775 MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MAX), 776 }; 777 778 static int mlxsw_sp_sb_pms_init(struct mlxsw_sp *mlxsw_sp, u8 local_port, 779 const struct mlxsw_sp_sb_pm *pms, 780 bool skip_ingress) 781 { 782 int i, err; 783 784 for (i = 0; i < mlxsw_sp->sb_vals->pool_count; i++) { 785 const struct mlxsw_sp_sb_pm *pm = &pms[i]; 786 const struct mlxsw_sp_sb_pool_des *des; 787 u32 max_buff; 788 u32 min_buff; 789 790 des = &mlxsw_sp->sb_vals->pool_dess[i]; 791 if (skip_ingress && des->dir == MLXSW_REG_SBXX_DIR_INGRESS) 792 continue; 793 794 min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, pm->min_buff); 795 max_buff = pm->max_buff; 796 if (mlxsw_sp_sb_pool_is_static(mlxsw_sp, i)) 797 max_buff = mlxsw_sp_bytes_cells(mlxsw_sp, max_buff); 798 err = mlxsw_sp_sb_pm_write(mlxsw_sp, local_port, i, min_buff, 799 max_buff); 800 if (err) 801 return err; 802 } 803 return 0; 804 } 805 806 static int mlxsw_sp_port_sb_pms_init(struct mlxsw_sp_port *mlxsw_sp_port) 807 { 808 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 809 810 return mlxsw_sp_sb_pms_init(mlxsw_sp, mlxsw_sp_port->local_port, 811 mlxsw_sp->sb_vals->pms, false); 812 } 813 814 static int mlxsw_sp_cpu_port_sb_pms_init(struct mlxsw_sp *mlxsw_sp) 815 { 816 return mlxsw_sp_sb_pms_init(mlxsw_sp, 0, mlxsw_sp->sb_vals->pms_cpu, 817 true); 818 } 819 820 #define MLXSW_SP_SB_MM(_min_buff, _max_buff) \ 821 { \ 822 .min_buff = _min_buff, \ 823 .max_buff = _max_buff, \ 824 .pool_index = MLXSW_SP_SB_POOL_EGR, \ 825 } 826 827 static const struct mlxsw_sp_sb_mm mlxsw_sp_sb_mms[] = { 828 MLXSW_SP_SB_MM(0, 6), 829 MLXSW_SP_SB_MM(0, 6), 830 MLXSW_SP_SB_MM(0, 6), 831 MLXSW_SP_SB_MM(0, 6), 832 MLXSW_SP_SB_MM(0, 6), 833 MLXSW_SP_SB_MM(0, 6), 834 MLXSW_SP_SB_MM(0, 6), 835 MLXSW_SP_SB_MM(0, 6), 836 MLXSW_SP_SB_MM(0, 6), 837 MLXSW_SP_SB_MM(0, 6), 838 MLXSW_SP_SB_MM(0, 6), 839 MLXSW_SP_SB_MM(0, 6), 840 MLXSW_SP_SB_MM(0, 6), 841 MLXSW_SP_SB_MM(0, 6), 842 MLXSW_SP_SB_MM(0, 6), 843 }; 844 845 static int mlxsw_sp_sb_mms_init(struct mlxsw_sp *mlxsw_sp) 846 { 847 char sbmm_pl[MLXSW_REG_SBMM_LEN]; 848 int i; 849 int err; 850 851 for (i = 0; i < mlxsw_sp->sb_vals->mms_count; i++) { 852 const struct mlxsw_sp_sb_pool_des *des; 853 const struct mlxsw_sp_sb_mm *mc; 854 u32 min_buff; 855 856 mc = &mlxsw_sp->sb_vals->mms[i]; 857 des = &mlxsw_sp->sb_vals->pool_dess[mc->pool_index]; 858 /* All pools used by sb_mm's are initialized using dynamic 859 * thresholds, therefore 'max_buff' isn't specified in cells. 860 */ 861 min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, mc->min_buff); 862 mlxsw_reg_sbmm_pack(sbmm_pl, i, min_buff, mc->max_buff, 863 des->pool); 864 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbmm), sbmm_pl); 865 if (err) 866 return err; 867 } 868 return 0; 869 } 870 871 static void mlxsw_sp_pool_count(struct mlxsw_sp *mlxsw_sp, 872 u16 *p_ingress_len, u16 *p_egress_len) 873 { 874 int i; 875 876 for (i = 0; i < mlxsw_sp->sb_vals->pool_count; ++i) { 877 if (mlxsw_sp->sb_vals->pool_dess[i].dir == 878 MLXSW_REG_SBXX_DIR_INGRESS) 879 (*p_ingress_len)++; 880 else 881 (*p_egress_len)++; 882 } 883 884 WARN(*p_egress_len == 0, "No egress pools\n"); 885 } 886 887 const struct mlxsw_sp_sb_vals mlxsw_sp1_sb_vals = { 888 .pool_count = ARRAY_SIZE(mlxsw_sp1_sb_pool_dess), 889 .pool_dess = mlxsw_sp1_sb_pool_dess, 890 .pms = mlxsw_sp1_sb_pms, 891 .pms_cpu = mlxsw_sp_cpu_port_sb_pms, 892 .prs = mlxsw_sp1_sb_prs, 893 .mms = mlxsw_sp_sb_mms, 894 .cms_ingress = mlxsw_sp1_sb_cms_ingress, 895 .cms_egress = mlxsw_sp1_sb_cms_egress, 896 .cms_cpu = mlxsw_sp_cpu_port_sb_cms, 897 .mms_count = ARRAY_SIZE(mlxsw_sp_sb_mms), 898 .cms_ingress_count = ARRAY_SIZE(mlxsw_sp1_sb_cms_ingress), 899 .cms_egress_count = ARRAY_SIZE(mlxsw_sp1_sb_cms_egress), 900 .cms_cpu_count = ARRAY_SIZE(mlxsw_sp_cpu_port_sb_cms), 901 }; 902 903 const struct mlxsw_sp_sb_vals mlxsw_sp2_sb_vals = { 904 .pool_count = ARRAY_SIZE(mlxsw_sp2_sb_pool_dess), 905 .pool_dess = mlxsw_sp2_sb_pool_dess, 906 .pms = mlxsw_sp2_sb_pms, 907 .pms_cpu = mlxsw_sp_cpu_port_sb_pms, 908 .prs = mlxsw_sp2_sb_prs, 909 .mms = mlxsw_sp_sb_mms, 910 .cms_ingress = mlxsw_sp2_sb_cms_ingress, 911 .cms_egress = mlxsw_sp2_sb_cms_egress, 912 .cms_cpu = mlxsw_sp_cpu_port_sb_cms, 913 .mms_count = ARRAY_SIZE(mlxsw_sp_sb_mms), 914 .cms_ingress_count = ARRAY_SIZE(mlxsw_sp2_sb_cms_ingress), 915 .cms_egress_count = ARRAY_SIZE(mlxsw_sp2_sb_cms_egress), 916 .cms_cpu_count = ARRAY_SIZE(mlxsw_sp_cpu_port_sb_cms), 917 }; 918 919 int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp) 920 { 921 u32 max_headroom_size; 922 u16 ing_pool_count = 0; 923 u16 eg_pool_count = 0; 924 int err; 925 926 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, CELL_SIZE)) 927 return -EIO; 928 929 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, GUARANTEED_SHARED_BUFFER)) 930 return -EIO; 931 932 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_HEADROOM_SIZE)) 933 return -EIO; 934 935 mlxsw_sp->sb = kzalloc(sizeof(*mlxsw_sp->sb), GFP_KERNEL); 936 if (!mlxsw_sp->sb) 937 return -ENOMEM; 938 mlxsw_sp->sb->cell_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, CELL_SIZE); 939 mlxsw_sp->sb->sb_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, 940 GUARANTEED_SHARED_BUFFER); 941 max_headroom_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, 942 MAX_HEADROOM_SIZE); 943 /* Round down, because this limit must not be overstepped. */ 944 mlxsw_sp->sb->max_headroom_cells = max_headroom_size / 945 mlxsw_sp->sb->cell_size; 946 947 err = mlxsw_sp_sb_ports_init(mlxsw_sp); 948 if (err) 949 goto err_sb_ports_init; 950 err = mlxsw_sp_sb_prs_init(mlxsw_sp, mlxsw_sp->sb_vals->prs, 951 mlxsw_sp->sb_vals->pool_dess, 952 mlxsw_sp->sb_vals->pool_count); 953 if (err) 954 goto err_sb_prs_init; 955 err = mlxsw_sp_cpu_port_sb_cms_init(mlxsw_sp); 956 if (err) 957 goto err_sb_cpu_port_sb_cms_init; 958 err = mlxsw_sp_cpu_port_sb_pms_init(mlxsw_sp); 959 if (err) 960 goto err_sb_cpu_port_pms_init; 961 err = mlxsw_sp_sb_mms_init(mlxsw_sp); 962 if (err) 963 goto err_sb_mms_init; 964 mlxsw_sp_pool_count(mlxsw_sp, &ing_pool_count, &eg_pool_count); 965 err = devlink_sb_register(priv_to_devlink(mlxsw_sp->core), 0, 966 mlxsw_sp->sb->sb_size, 967 ing_pool_count, 968 eg_pool_count, 969 MLXSW_SP_SB_ING_TC_COUNT, 970 MLXSW_SP_SB_EG_TC_COUNT); 971 if (err) 972 goto err_devlink_sb_register; 973 974 return 0; 975 976 err_devlink_sb_register: 977 err_sb_mms_init: 978 err_sb_cpu_port_pms_init: 979 err_sb_cpu_port_sb_cms_init: 980 err_sb_prs_init: 981 mlxsw_sp_sb_ports_fini(mlxsw_sp); 982 err_sb_ports_init: 983 kfree(mlxsw_sp->sb); 984 return err; 985 } 986 987 void mlxsw_sp_buffers_fini(struct mlxsw_sp *mlxsw_sp) 988 { 989 devlink_sb_unregister(priv_to_devlink(mlxsw_sp->core), 0); 990 mlxsw_sp_sb_ports_fini(mlxsw_sp); 991 kfree(mlxsw_sp->sb); 992 } 993 994 int mlxsw_sp_port_buffers_init(struct mlxsw_sp_port *mlxsw_sp_port) 995 { 996 int err; 997 998 err = mlxsw_sp_port_headroom_init(mlxsw_sp_port); 999 if (err) 1000 return err; 1001 err = mlxsw_sp_port_sb_cms_init(mlxsw_sp_port); 1002 if (err) 1003 return err; 1004 err = mlxsw_sp_port_sb_pms_init(mlxsw_sp_port); 1005 1006 return err; 1007 } 1008 1009 int mlxsw_sp_sb_pool_get(struct mlxsw_core *mlxsw_core, 1010 unsigned int sb_index, u16 pool_index, 1011 struct devlink_sb_pool_info *pool_info) 1012 { 1013 struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); 1014 enum mlxsw_reg_sbxx_dir dir; 1015 struct mlxsw_sp_sb_pr *pr; 1016 1017 dir = mlxsw_sp->sb_vals->pool_dess[pool_index].dir; 1018 pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index); 1019 pool_info->pool_type = (enum devlink_sb_pool_type) dir; 1020 pool_info->size = mlxsw_sp_cells_bytes(mlxsw_sp, pr->size); 1021 pool_info->threshold_type = (enum devlink_sb_threshold_type) pr->mode; 1022 pool_info->cell_size = mlxsw_sp->sb->cell_size; 1023 return 0; 1024 } 1025 1026 int mlxsw_sp_sb_pool_set(struct mlxsw_core *mlxsw_core, 1027 unsigned int sb_index, u16 pool_index, u32 size, 1028 enum devlink_sb_threshold_type threshold_type, 1029 struct netlink_ext_ack *extack) 1030 { 1031 struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); 1032 u32 pool_size = mlxsw_sp_bytes_cells(mlxsw_sp, size); 1033 const struct mlxsw_sp_sb_pr *pr; 1034 enum mlxsw_reg_sbpr_mode mode; 1035 1036 mode = (enum mlxsw_reg_sbpr_mode) threshold_type; 1037 pr = &mlxsw_sp->sb_vals->prs[pool_index]; 1038 1039 if (size > MLXSW_CORE_RES_GET(mlxsw_sp->core, 1040 GUARANTEED_SHARED_BUFFER)) { 1041 NL_SET_ERR_MSG_MOD(extack, "Exceeded shared buffer size"); 1042 return -EINVAL; 1043 } 1044 1045 if (pr->freeze_mode && pr->mode != mode) { 1046 NL_SET_ERR_MSG_MOD(extack, "Changing this pool's threshold type is forbidden"); 1047 return -EINVAL; 1048 } 1049 1050 if (pr->freeze_size && pr->size != size) { 1051 NL_SET_ERR_MSG_MOD(extack, "Changing this pool's size is forbidden"); 1052 return -EINVAL; 1053 } 1054 1055 return mlxsw_sp_sb_pr_write(mlxsw_sp, pool_index, mode, 1056 pool_size, false); 1057 } 1058 1059 #define MLXSW_SP_SB_THRESHOLD_TO_ALPHA_OFFSET (-2) /* 3->1, 16->14 */ 1060 1061 static u32 mlxsw_sp_sb_threshold_out(struct mlxsw_sp *mlxsw_sp, u16 pool_index, 1062 u32 max_buff) 1063 { 1064 struct mlxsw_sp_sb_pr *pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index); 1065 1066 if (pr->mode == MLXSW_REG_SBPR_MODE_DYNAMIC) 1067 return max_buff - MLXSW_SP_SB_THRESHOLD_TO_ALPHA_OFFSET; 1068 return mlxsw_sp_cells_bytes(mlxsw_sp, max_buff); 1069 } 1070 1071 static int mlxsw_sp_sb_threshold_in(struct mlxsw_sp *mlxsw_sp, u16 pool_index, 1072 u32 threshold, u32 *p_max_buff, 1073 struct netlink_ext_ack *extack) 1074 { 1075 struct mlxsw_sp_sb_pr *pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index); 1076 1077 if (pr->mode == MLXSW_REG_SBPR_MODE_DYNAMIC) { 1078 int val; 1079 1080 val = threshold + MLXSW_SP_SB_THRESHOLD_TO_ALPHA_OFFSET; 1081 if (val < MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN || 1082 val > MLXSW_REG_SBXX_DYN_MAX_BUFF_MAX) { 1083 NL_SET_ERR_MSG_MOD(extack, "Invalid dynamic threshold value"); 1084 return -EINVAL; 1085 } 1086 *p_max_buff = val; 1087 } else { 1088 *p_max_buff = mlxsw_sp_bytes_cells(mlxsw_sp, threshold); 1089 } 1090 return 0; 1091 } 1092 1093 int mlxsw_sp_sb_port_pool_get(struct mlxsw_core_port *mlxsw_core_port, 1094 unsigned int sb_index, u16 pool_index, 1095 u32 *p_threshold) 1096 { 1097 struct mlxsw_sp_port *mlxsw_sp_port = 1098 mlxsw_core_port_driver_priv(mlxsw_core_port); 1099 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1100 u8 local_port = mlxsw_sp_port->local_port; 1101 struct mlxsw_sp_sb_pm *pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port, 1102 pool_index); 1103 1104 *p_threshold = mlxsw_sp_sb_threshold_out(mlxsw_sp, pool_index, 1105 pm->max_buff); 1106 return 0; 1107 } 1108 1109 int mlxsw_sp_sb_port_pool_set(struct mlxsw_core_port *mlxsw_core_port, 1110 unsigned int sb_index, u16 pool_index, 1111 u32 threshold, struct netlink_ext_ack *extack) 1112 { 1113 struct mlxsw_sp_port *mlxsw_sp_port = 1114 mlxsw_core_port_driver_priv(mlxsw_core_port); 1115 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1116 u8 local_port = mlxsw_sp_port->local_port; 1117 u32 max_buff; 1118 int err; 1119 1120 if (local_port == MLXSW_PORT_CPU_PORT) { 1121 NL_SET_ERR_MSG_MOD(extack, "Changing CPU port's threshold is forbidden"); 1122 return -EINVAL; 1123 } 1124 1125 err = mlxsw_sp_sb_threshold_in(mlxsw_sp, pool_index, 1126 threshold, &max_buff, extack); 1127 if (err) 1128 return err; 1129 1130 return mlxsw_sp_sb_pm_write(mlxsw_sp, local_port, pool_index, 1131 0, max_buff); 1132 } 1133 1134 int mlxsw_sp_sb_tc_pool_bind_get(struct mlxsw_core_port *mlxsw_core_port, 1135 unsigned int sb_index, u16 tc_index, 1136 enum devlink_sb_pool_type pool_type, 1137 u16 *p_pool_index, u32 *p_threshold) 1138 { 1139 struct mlxsw_sp_port *mlxsw_sp_port = 1140 mlxsw_core_port_driver_priv(mlxsw_core_port); 1141 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1142 u8 local_port = mlxsw_sp_port->local_port; 1143 u8 pg_buff = tc_index; 1144 enum mlxsw_reg_sbxx_dir dir = (enum mlxsw_reg_sbxx_dir) pool_type; 1145 struct mlxsw_sp_sb_cm *cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, 1146 pg_buff, dir); 1147 1148 *p_threshold = mlxsw_sp_sb_threshold_out(mlxsw_sp, cm->pool_index, 1149 cm->max_buff); 1150 *p_pool_index = cm->pool_index; 1151 return 0; 1152 } 1153 1154 int mlxsw_sp_sb_tc_pool_bind_set(struct mlxsw_core_port *mlxsw_core_port, 1155 unsigned int sb_index, u16 tc_index, 1156 enum devlink_sb_pool_type pool_type, 1157 u16 pool_index, u32 threshold, 1158 struct netlink_ext_ack *extack) 1159 { 1160 struct mlxsw_sp_port *mlxsw_sp_port = 1161 mlxsw_core_port_driver_priv(mlxsw_core_port); 1162 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1163 u8 local_port = mlxsw_sp_port->local_port; 1164 const struct mlxsw_sp_sb_cm *cm; 1165 u8 pg_buff = tc_index; 1166 enum mlxsw_reg_sbxx_dir dir = (enum mlxsw_reg_sbxx_dir) pool_type; 1167 u32 max_buff; 1168 int err; 1169 1170 if (local_port == MLXSW_PORT_CPU_PORT) { 1171 NL_SET_ERR_MSG_MOD(extack, "Changing CPU port's binding is forbidden"); 1172 return -EINVAL; 1173 } 1174 1175 if (dir != mlxsw_sp->sb_vals->pool_dess[pool_index].dir) { 1176 NL_SET_ERR_MSG_MOD(extack, "Binding egress TC to ingress pool and vice versa is forbidden"); 1177 return -EINVAL; 1178 } 1179 1180 if (dir == MLXSW_REG_SBXX_DIR_INGRESS) 1181 cm = &mlxsw_sp->sb_vals->cms_ingress[tc_index]; 1182 else 1183 cm = &mlxsw_sp->sb_vals->cms_egress[tc_index]; 1184 1185 if (cm->freeze_pool && cm->pool_index != pool_index) { 1186 NL_SET_ERR_MSG_MOD(extack, "Binding this TC to a different pool is forbidden"); 1187 return -EINVAL; 1188 } 1189 1190 if (cm->freeze_thresh && cm->max_buff != threshold) { 1191 NL_SET_ERR_MSG_MOD(extack, "Changing this TC's threshold is forbidden"); 1192 return -EINVAL; 1193 } 1194 1195 err = mlxsw_sp_sb_threshold_in(mlxsw_sp, pool_index, 1196 threshold, &max_buff, extack); 1197 if (err) 1198 return err; 1199 1200 return mlxsw_sp_sb_cm_write(mlxsw_sp, local_port, pg_buff, 1201 0, max_buff, false, pool_index); 1202 } 1203 1204 #define MASKED_COUNT_MAX \ 1205 (MLXSW_REG_SBSR_REC_MAX_COUNT / \ 1206 (MLXSW_SP_SB_ING_TC_COUNT + MLXSW_SP_SB_EG_TC_COUNT)) 1207 1208 struct mlxsw_sp_sb_sr_occ_query_cb_ctx { 1209 u8 masked_count; 1210 u8 local_port_1; 1211 }; 1212 1213 static void mlxsw_sp_sb_sr_occ_query_cb(struct mlxsw_core *mlxsw_core, 1214 char *sbsr_pl, size_t sbsr_pl_len, 1215 unsigned long cb_priv) 1216 { 1217 struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); 1218 struct mlxsw_sp_sb_sr_occ_query_cb_ctx cb_ctx; 1219 u8 masked_count; 1220 u8 local_port; 1221 int rec_index = 0; 1222 struct mlxsw_sp_sb_cm *cm; 1223 int i; 1224 1225 memcpy(&cb_ctx, &cb_priv, sizeof(cb_ctx)); 1226 1227 masked_count = 0; 1228 for (local_port = cb_ctx.local_port_1; 1229 local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) { 1230 if (!mlxsw_sp->ports[local_port]) 1231 continue; 1232 if (local_port == MLXSW_PORT_CPU_PORT) { 1233 /* Ingress quotas are not supported for the CPU port */ 1234 masked_count++; 1235 continue; 1236 } 1237 for (i = 0; i < MLXSW_SP_SB_ING_TC_COUNT; i++) { 1238 cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, i, 1239 MLXSW_REG_SBXX_DIR_INGRESS); 1240 mlxsw_reg_sbsr_rec_unpack(sbsr_pl, rec_index++, 1241 &cm->occ.cur, &cm->occ.max); 1242 } 1243 if (++masked_count == cb_ctx.masked_count) 1244 break; 1245 } 1246 masked_count = 0; 1247 for (local_port = cb_ctx.local_port_1; 1248 local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) { 1249 if (!mlxsw_sp->ports[local_port]) 1250 continue; 1251 for (i = 0; i < MLXSW_SP_SB_EG_TC_COUNT; i++) { 1252 cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, i, 1253 MLXSW_REG_SBXX_DIR_EGRESS); 1254 mlxsw_reg_sbsr_rec_unpack(sbsr_pl, rec_index++, 1255 &cm->occ.cur, &cm->occ.max); 1256 } 1257 if (++masked_count == cb_ctx.masked_count) 1258 break; 1259 } 1260 } 1261 1262 int mlxsw_sp_sb_occ_snapshot(struct mlxsw_core *mlxsw_core, 1263 unsigned int sb_index) 1264 { 1265 struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); 1266 struct mlxsw_sp_sb_sr_occ_query_cb_ctx cb_ctx; 1267 unsigned long cb_priv; 1268 LIST_HEAD(bulk_list); 1269 char *sbsr_pl; 1270 u8 masked_count; 1271 u8 local_port_1; 1272 u8 local_port; 1273 int i; 1274 int err; 1275 int err2; 1276 1277 sbsr_pl = kmalloc(MLXSW_REG_SBSR_LEN, GFP_KERNEL); 1278 if (!sbsr_pl) 1279 return -ENOMEM; 1280 1281 local_port = MLXSW_PORT_CPU_PORT; 1282 next_batch: 1283 local_port_1 = local_port; 1284 masked_count = 0; 1285 mlxsw_reg_sbsr_pack(sbsr_pl, false); 1286 for (i = 0; i < MLXSW_SP_SB_ING_TC_COUNT; i++) 1287 mlxsw_reg_sbsr_pg_buff_mask_set(sbsr_pl, i, 1); 1288 for (i = 0; i < MLXSW_SP_SB_EG_TC_COUNT; i++) 1289 mlxsw_reg_sbsr_tclass_mask_set(sbsr_pl, i, 1); 1290 for (; local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) { 1291 if (!mlxsw_sp->ports[local_port]) 1292 continue; 1293 if (local_port != MLXSW_PORT_CPU_PORT) { 1294 /* Ingress quotas are not supported for the CPU port */ 1295 mlxsw_reg_sbsr_ingress_port_mask_set(sbsr_pl, 1296 local_port, 1); 1297 } 1298 mlxsw_reg_sbsr_egress_port_mask_set(sbsr_pl, local_port, 1); 1299 for (i = 0; i < mlxsw_sp->sb_vals->pool_count; i++) { 1300 err = mlxsw_sp_sb_pm_occ_query(mlxsw_sp, local_port, i, 1301 &bulk_list); 1302 if (err) 1303 goto out; 1304 } 1305 if (++masked_count == MASKED_COUNT_MAX) 1306 goto do_query; 1307 } 1308 1309 do_query: 1310 cb_ctx.masked_count = masked_count; 1311 cb_ctx.local_port_1 = local_port_1; 1312 memcpy(&cb_priv, &cb_ctx, sizeof(cb_ctx)); 1313 err = mlxsw_reg_trans_query(mlxsw_core, MLXSW_REG(sbsr), sbsr_pl, 1314 &bulk_list, mlxsw_sp_sb_sr_occ_query_cb, 1315 cb_priv); 1316 if (err) 1317 goto out; 1318 if (local_port < mlxsw_core_max_ports(mlxsw_core)) { 1319 local_port++; 1320 goto next_batch; 1321 } 1322 1323 out: 1324 err2 = mlxsw_reg_trans_bulk_wait(&bulk_list); 1325 if (!err) 1326 err = err2; 1327 kfree(sbsr_pl); 1328 return err; 1329 } 1330 1331 int mlxsw_sp_sb_occ_max_clear(struct mlxsw_core *mlxsw_core, 1332 unsigned int sb_index) 1333 { 1334 struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); 1335 LIST_HEAD(bulk_list); 1336 char *sbsr_pl; 1337 unsigned int masked_count; 1338 u8 local_port; 1339 int i; 1340 int err; 1341 int err2; 1342 1343 sbsr_pl = kmalloc(MLXSW_REG_SBSR_LEN, GFP_KERNEL); 1344 if (!sbsr_pl) 1345 return -ENOMEM; 1346 1347 local_port = MLXSW_PORT_CPU_PORT; 1348 next_batch: 1349 masked_count = 0; 1350 mlxsw_reg_sbsr_pack(sbsr_pl, true); 1351 for (i = 0; i < MLXSW_SP_SB_ING_TC_COUNT; i++) 1352 mlxsw_reg_sbsr_pg_buff_mask_set(sbsr_pl, i, 1); 1353 for (i = 0; i < MLXSW_SP_SB_EG_TC_COUNT; i++) 1354 mlxsw_reg_sbsr_tclass_mask_set(sbsr_pl, i, 1); 1355 for (; local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) { 1356 if (!mlxsw_sp->ports[local_port]) 1357 continue; 1358 if (local_port != MLXSW_PORT_CPU_PORT) { 1359 /* Ingress quotas are not supported for the CPU port */ 1360 mlxsw_reg_sbsr_ingress_port_mask_set(sbsr_pl, 1361 local_port, 1); 1362 } 1363 mlxsw_reg_sbsr_egress_port_mask_set(sbsr_pl, local_port, 1); 1364 for (i = 0; i < mlxsw_sp->sb_vals->pool_count; i++) { 1365 err = mlxsw_sp_sb_pm_occ_clear(mlxsw_sp, local_port, i, 1366 &bulk_list); 1367 if (err) 1368 goto out; 1369 } 1370 if (++masked_count == MASKED_COUNT_MAX) 1371 goto do_query; 1372 } 1373 1374 do_query: 1375 err = mlxsw_reg_trans_query(mlxsw_core, MLXSW_REG(sbsr), sbsr_pl, 1376 &bulk_list, NULL, 0); 1377 if (err) 1378 goto out; 1379 if (local_port < mlxsw_core_max_ports(mlxsw_core)) { 1380 local_port++; 1381 goto next_batch; 1382 } 1383 1384 out: 1385 err2 = mlxsw_reg_trans_bulk_wait(&bulk_list); 1386 if (!err) 1387 err = err2; 1388 kfree(sbsr_pl); 1389 return err; 1390 } 1391 1392 int mlxsw_sp_sb_occ_port_pool_get(struct mlxsw_core_port *mlxsw_core_port, 1393 unsigned int sb_index, u16 pool_index, 1394 u32 *p_cur, u32 *p_max) 1395 { 1396 struct mlxsw_sp_port *mlxsw_sp_port = 1397 mlxsw_core_port_driver_priv(mlxsw_core_port); 1398 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1399 u8 local_port = mlxsw_sp_port->local_port; 1400 struct mlxsw_sp_sb_pm *pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port, 1401 pool_index); 1402 1403 *p_cur = mlxsw_sp_cells_bytes(mlxsw_sp, pm->occ.cur); 1404 *p_max = mlxsw_sp_cells_bytes(mlxsw_sp, pm->occ.max); 1405 return 0; 1406 } 1407 1408 int mlxsw_sp_sb_occ_tc_port_bind_get(struct mlxsw_core_port *mlxsw_core_port, 1409 unsigned int sb_index, u16 tc_index, 1410 enum devlink_sb_pool_type pool_type, 1411 u32 *p_cur, u32 *p_max) 1412 { 1413 struct mlxsw_sp_port *mlxsw_sp_port = 1414 mlxsw_core_port_driver_priv(mlxsw_core_port); 1415 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1416 u8 local_port = mlxsw_sp_port->local_port; 1417 u8 pg_buff = tc_index; 1418 enum mlxsw_reg_sbxx_dir dir = (enum mlxsw_reg_sbxx_dir) pool_type; 1419 struct mlxsw_sp_sb_cm *cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, 1420 pg_buff, dir); 1421 1422 *p_cur = mlxsw_sp_cells_bytes(mlxsw_sp, cm->occ.cur); 1423 *p_max = mlxsw_sp_cells_bytes(mlxsw_sp, cm->occ.max); 1424 return 0; 1425 } 1426