spectrum_buffers.c 37.0 KB
Newer Older
1 2
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */
3 4 5

#include <linux/kernel.h>
#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 12 13 14 15

#include "spectrum.h"
#include "core.h"
#include "port.h"
#include "reg.h"

16 17 18
struct mlxsw_sp_sb_pr {
	enum mlxsw_reg_sbpr_mode mode;
	u32 size;
19 20
	u8 freeze_mode:1,
	   freeze_size:1;
21 22 23 24 25 26 27 28 29 30
};

struct mlxsw_cp_sb_occ {
	u32 cur;
	u32 max;
};

struct mlxsw_sp_sb_cm {
	u32 min_buff;
	u32 max_buff;
31
	u16 pool_index;
32
	struct mlxsw_cp_sb_occ occ;
33 34
	u8 freeze_pool:1,
	   freeze_thresh:1;
35 36
};

37 38
#define MLXSW_SP_SB_INFI -1U

39 40 41 42 43 44
struct mlxsw_sp_sb_pm {
	u32 min_buff;
	u32 max_buff;
	struct mlxsw_cp_sb_occ occ;
};

45 46 47 48 49 50
struct mlxsw_sp_sb_mm {
	u32 min_buff;
	u32 max_buff;
	u16 pool_index;
};

51 52 53 54 55
struct mlxsw_sp_sb_pool_des {
	enum mlxsw_reg_sbxx_dir dir;
	u8 pool;
};

56 57 58 59 60
#define MLXSW_SP_SB_POOL_ING		0
#define MLXSW_SP_SB_POOL_ING_MNG	3
#define MLXSW_SP_SB_POOL_EGR		4
#define MLXSW_SP_SB_POOL_EGR_MC		8

61
/* Order ingress pools before egress pools. */
62
static const struct mlxsw_sp_sb_pool_des mlxsw_sp1_sb_pool_dess[] = {
63 64 65 66 67 68 69 70 71 72 73
	{MLXSW_REG_SBXX_DIR_INGRESS, 0},
	{MLXSW_REG_SBXX_DIR_INGRESS, 1},
	{MLXSW_REG_SBXX_DIR_INGRESS, 2},
	{MLXSW_REG_SBXX_DIR_INGRESS, 3},
	{MLXSW_REG_SBXX_DIR_EGRESS, 0},
	{MLXSW_REG_SBXX_DIR_EGRESS, 1},
	{MLXSW_REG_SBXX_DIR_EGRESS, 2},
	{MLXSW_REG_SBXX_DIR_EGRESS, 3},
	{MLXSW_REG_SBXX_DIR_EGRESS, 15},
};

74 75 76 77 78 79 80 81 82
static const struct mlxsw_sp_sb_pool_des mlxsw_sp2_sb_pool_dess[] = {
	{MLXSW_REG_SBXX_DIR_INGRESS, 0},
	{MLXSW_REG_SBXX_DIR_INGRESS, 1},
	{MLXSW_REG_SBXX_DIR_INGRESS, 2},
	{MLXSW_REG_SBXX_DIR_INGRESS, 3},
	{MLXSW_REG_SBXX_DIR_EGRESS, 0},
	{MLXSW_REG_SBXX_DIR_EGRESS, 1},
	{MLXSW_REG_SBXX_DIR_EGRESS, 2},
	{MLXSW_REG_SBXX_DIR_EGRESS, 3},
83
	{MLXSW_REG_SBXX_DIR_EGRESS, 15},
84 85
};

86 87
#define MLXSW_SP_SB_ING_TC_COUNT 8
#define MLXSW_SP_SB_EG_TC_COUNT 16
88 89

struct mlxsw_sp_sb_port {
90 91
	struct mlxsw_sp_sb_cm ing_cms[MLXSW_SP_SB_ING_TC_COUNT];
	struct mlxsw_sp_sb_cm eg_cms[MLXSW_SP_SB_EG_TC_COUNT];
92
	struct mlxsw_sp_sb_pm *pms;
93 94 95
};

struct mlxsw_sp_sb {
96
	struct mlxsw_sp_sb_pr *prs;
97 98
	struct mlxsw_sp_sb_port *ports;
	u32 cell_size;
99
	u32 max_headroom_cells;
100
	u64 sb_size;
101 102
};

103
struct mlxsw_sp_sb_vals {
104 105
	unsigned int pool_count;
	const struct mlxsw_sp_sb_pool_des *pool_dess;
106
	const struct mlxsw_sp_sb_pm *pms;
107
	const struct mlxsw_sp_sb_pr *prs;
108
	const struct mlxsw_sp_sb_mm *mms;
109 110 111
	const struct mlxsw_sp_sb_cm *cms_ingress;
	const struct mlxsw_sp_sb_cm *cms_egress;
	const struct mlxsw_sp_sb_cm *cms_cpu;
112
	unsigned int mms_count;
113 114 115
	unsigned int cms_ingress_count;
	unsigned int cms_egress_count;
	unsigned int cms_cpu_count;
116 117
};

118 119 120 121 122 123 124 125 126 127
u32 mlxsw_sp_cells_bytes(const struct mlxsw_sp *mlxsw_sp, u32 cells)
{
	return mlxsw_sp->sb->cell_size * cells;
}

u32 mlxsw_sp_bytes_cells(const struct mlxsw_sp *mlxsw_sp, u32 bytes)
{
	return DIV_ROUND_UP(bytes, mlxsw_sp->sb->cell_size);
}

128 129 130 131 132
u32 mlxsw_sp_sb_max_headroom_cells(const struct mlxsw_sp *mlxsw_sp)
{
	return mlxsw_sp->sb->max_headroom_cells;
}

133
static struct mlxsw_sp_sb_pr *mlxsw_sp_sb_pr_get(struct mlxsw_sp *mlxsw_sp,
134
						 u16 pool_index)
135
{
136
	return &mlxsw_sp->sb->prs[pool_index];
137 138
}

139 140 141 142 143 144 145 146
static bool mlxsw_sp_sb_cm_exists(u8 pg_buff, enum mlxsw_reg_sbxx_dir dir)
{
	if (dir == MLXSW_REG_SBXX_DIR_INGRESS)
		return pg_buff < MLXSW_SP_SB_ING_TC_COUNT;
	else
		return pg_buff < MLXSW_SP_SB_EG_TC_COUNT;
}

147 148 149 150
static struct mlxsw_sp_sb_cm *mlxsw_sp_sb_cm_get(struct mlxsw_sp *mlxsw_sp,
						 u8 local_port, u8 pg_buff,
						 enum mlxsw_reg_sbxx_dir dir)
{
151 152 153 154 155 156 157
	struct mlxsw_sp_sb_port *sb_port = &mlxsw_sp->sb->ports[local_port];

	WARN_ON(!mlxsw_sp_sb_cm_exists(pg_buff, dir));
	if (dir == MLXSW_REG_SBXX_DIR_INGRESS)
		return &sb_port->ing_cms[pg_buff];
	else
		return &sb_port->eg_cms[pg_buff];
158 159 160
}

static struct mlxsw_sp_sb_pm *mlxsw_sp_sb_pm_get(struct mlxsw_sp *mlxsw_sp,
161
						 u8 local_port, u16 pool_index)
162
{
163
	return &mlxsw_sp->sb->ports[local_port].pms[pool_index];
164 165
}

166
static int mlxsw_sp_sb_pr_write(struct mlxsw_sp *mlxsw_sp, u16 pool_index,
167 168
				enum mlxsw_reg_sbpr_mode mode,
				u32 size, bool infi_size)
169
{
170
	const struct mlxsw_sp_sb_pool_des *des =
171
		&mlxsw_sp->sb_vals->pool_dess[pool_index];
172
	char sbpr_pl[MLXSW_REG_SBPR_LEN];
173 174
	struct mlxsw_sp_sb_pr *pr;
	int err;
175

176 177
	mlxsw_reg_sbpr_pack(sbpr_pl, des->pool, des->dir, mode,
			    size, infi_size);
178 179 180 181
	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbpr), sbpr_pl);
	if (err)
		return err;

182 183
	if (infi_size)
		size = mlxsw_sp_bytes_cells(mlxsw_sp, mlxsw_sp->sb->sb_size);
184
	pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index);
185 186 187
	pr->mode = mode;
	pr->size = size;
	return 0;
188 189 190
}

static int mlxsw_sp_sb_cm_write(struct mlxsw_sp *mlxsw_sp, u8 local_port,
191
				u8 pg_buff, u32 min_buff, u32 max_buff,
192
				bool infi_max, u16 pool_index)
193
{
194
	const struct mlxsw_sp_sb_pool_des *des =
195
		&mlxsw_sp->sb_vals->pool_dess[pool_index];
196
	char sbcm_pl[MLXSW_REG_SBCM_LEN];
197
	struct mlxsw_sp_sb_cm *cm;
198
	int err;
199

200
	mlxsw_reg_sbcm_pack(sbcm_pl, local_port, pg_buff, des->dir,
201
			    min_buff, max_buff, infi_max, des->pool);
202 203 204
	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbcm), sbcm_pl);
	if (err)
		return err;
205

206
	if (mlxsw_sp_sb_cm_exists(pg_buff, des->dir)) {
207 208 209
		if (infi_max)
			max_buff = mlxsw_sp_bytes_cells(mlxsw_sp,
							mlxsw_sp->sb->sb_size);
210

211 212
		cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, pg_buff,
					des->dir);
213 214
		cm->min_buff = min_buff;
		cm->max_buff = max_buff;
215
		cm->pool_index = pool_index;
216 217
	}
	return 0;
218 219 220
}

static int mlxsw_sp_sb_pm_write(struct mlxsw_sp *mlxsw_sp, u8 local_port,
221
				u16 pool_index, u32 min_buff, u32 max_buff)
222
{
223
	const struct mlxsw_sp_sb_pool_des *des =
224
		&mlxsw_sp->sb_vals->pool_dess[pool_index];
225
	char sbpm_pl[MLXSW_REG_SBPM_LEN];
226 227
	struct mlxsw_sp_sb_pm *pm;
	int err;
228

229
	mlxsw_reg_sbpm_pack(sbpm_pl, local_port, des->pool, des->dir, false,
230
			    min_buff, max_buff);
231 232 233 234
	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbpm), sbpm_pl);
	if (err)
		return err;

235
	pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port, pool_index);
236 237 238
	pm->min_buff = min_buff;
	pm->max_buff = max_buff;
	return 0;
239 240
}

241
static int mlxsw_sp_sb_pm_occ_clear(struct mlxsw_sp *mlxsw_sp, u8 local_port,
242
				    u16 pool_index, struct list_head *bulk_list)
243
{
244
	const struct mlxsw_sp_sb_pool_des *des =
245
		&mlxsw_sp->sb_vals->pool_dess[pool_index];
246 247
	char sbpm_pl[MLXSW_REG_SBPM_LEN];

248 249
	mlxsw_reg_sbpm_pack(sbpm_pl, local_port, des->pool, des->dir,
			    true, 0, 0);
250 251 252 253 254 255 256 257 258 259 260 261 262 263
	return mlxsw_reg_trans_query(mlxsw_sp->core, MLXSW_REG(sbpm), sbpm_pl,
				     bulk_list, NULL, 0);
}

static void mlxsw_sp_sb_pm_occ_query_cb(struct mlxsw_core *mlxsw_core,
					char *sbpm_pl, size_t sbpm_pl_len,
					unsigned long cb_priv)
{
	struct mlxsw_sp_sb_pm *pm = (struct mlxsw_sp_sb_pm *) cb_priv;

	mlxsw_reg_sbpm_unpack(sbpm_pl, &pm->occ.cur, &pm->occ.max);
}

static int mlxsw_sp_sb_pm_occ_query(struct mlxsw_sp *mlxsw_sp, u8 local_port,
264
				    u16 pool_index, struct list_head *bulk_list)
265
{
266
	const struct mlxsw_sp_sb_pool_des *des =
267
		&mlxsw_sp->sb_vals->pool_dess[pool_index];
268 269 270
	char sbpm_pl[MLXSW_REG_SBPM_LEN];
	struct mlxsw_sp_sb_pm *pm;

271 272 273
	pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port, pool_index);
	mlxsw_reg_sbpm_pack(sbpm_pl, local_port, des->pool, des->dir,
			    false, 0, 0);
274 275 276 277 278 279
	return mlxsw_reg_trans_query(mlxsw_sp->core, MLXSW_REG(sbpm), sbpm_pl,
				     bulk_list,
				     mlxsw_sp_sb_pm_occ_query_cb,
				     (unsigned long) pm);
}

280 281
/* 1/4 of a headroom necessary for 100Gbps port and 100m cable. */
#define MLXSW_SP_PB_HEADROOM 25632
282
#define MLXSW_SP_PB_UNUSED 8
283 284 285

static int mlxsw_sp_port_pb_init(struct mlxsw_sp_port *mlxsw_sp_port)
{
286 287 288 289
	const u32 pbs[] = {
		[0] = MLXSW_SP_PB_HEADROOM * mlxsw_sp_port->mapping.width,
		[9] = 2 * MLXSW_PORT_MAX_MTU,
	};
290
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
291 292 293 294 295
	char pbmc_pl[MLXSW_REG_PBMC_LEN];
	int i;

	mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port,
			    0xffff, 0xffff / 2);
296 297
	for (i = 0; i < ARRAY_SIZE(pbs); i++) {
		u16 size = mlxsw_sp_bytes_cells(mlxsw_sp, pbs[i]);
298

299
		if (i == MLXSW_SP_PB_UNUSED)
300
			continue;
301
		mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, i, size);
302
	}
303 304
	mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl,
					 MLXSW_REG_PBMC_PORT_SHARED_BUF_IDX, 0);
305
	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
306 307
}

308 309 310 311 312 313 314
static int mlxsw_sp_port_pb_prio_init(struct mlxsw_sp_port *mlxsw_sp_port)
{
	char pptb_pl[MLXSW_REG_PPTB_LEN];
	int i;

	mlxsw_reg_pptb_pack(pptb_pl, mlxsw_sp_port->local_port);
	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
315
		mlxsw_reg_pptb_prio_to_buff_pack(pptb_pl, i, 0);
316 317 318 319 320 321 322 323 324 325 326 327 328 329
	return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb),
			       pptb_pl);
}

static int mlxsw_sp_port_headroom_init(struct mlxsw_sp_port *mlxsw_sp_port)
{
	int err;

	err = mlxsw_sp_port_pb_init(mlxsw_sp_port);
	if (err)
		return err;
	return mlxsw_sp_port_pb_prio_init(mlxsw_sp_port);
}

330 331 332 333 334
static int mlxsw_sp_sb_port_init(struct mlxsw_sp *mlxsw_sp,
				 struct mlxsw_sp_sb_port *sb_port)
{
	struct mlxsw_sp_sb_pm *pms;

335 336
	pms = kcalloc(mlxsw_sp->sb_vals->pool_count, sizeof(*pms),
		      GFP_KERNEL);
337 338 339 340 341 342 343 344 345 346 347
	if (!pms)
		return -ENOMEM;
	sb_port->pms = pms;
	return 0;
}

static void mlxsw_sp_sb_port_fini(struct mlxsw_sp_sb_port *sb_port)
{
	kfree(sb_port->pms);
}

348 349 350
static int mlxsw_sp_sb_ports_init(struct mlxsw_sp *mlxsw_sp)
{
	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
351 352 353
	struct mlxsw_sp_sb_pr *prs;
	int i;
	int err;
354

355 356 357 358
	mlxsw_sp->sb->ports = kcalloc(max_ports,
				      sizeof(struct mlxsw_sp_sb_port),
				      GFP_KERNEL);
	if (!mlxsw_sp->sb->ports)
359
		return -ENOMEM;
360

361 362
	prs = kcalloc(mlxsw_sp->sb_vals->pool_count, sizeof(*prs),
		      GFP_KERNEL);
363 364 365 366 367 368 369 370 371 372 373 374
	if (!prs) {
		err = -ENOMEM;
		goto err_alloc_prs;
	}
	mlxsw_sp->sb->prs = prs;

	for (i = 0; i < max_ports; i++) {
		err = mlxsw_sp_sb_port_init(mlxsw_sp, &mlxsw_sp->sb->ports[i]);
		if (err)
			goto err_sb_port_init;
	}

375
	return 0;
376 377 378 379 380 381 382 383

err_sb_port_init:
	for (i--; i >= 0; i--)
		mlxsw_sp_sb_port_fini(&mlxsw_sp->sb->ports[i]);
	kfree(mlxsw_sp->sb->prs);
err_alloc_prs:
	kfree(mlxsw_sp->sb->ports);
	return err;
384 385 386 387
}

static void mlxsw_sp_sb_ports_fini(struct mlxsw_sp *mlxsw_sp)
{
388 389 390 391 392 393
	int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
	int i;

	for (i = max_ports - 1; i >= 0; i--)
		mlxsw_sp_sb_port_fini(&mlxsw_sp->sb->ports[i]);
	kfree(mlxsw_sp->sb->prs);
394
	kfree(mlxsw_sp->sb->ports);
395 396
}

397
#define MLXSW_SP_SB_PR(_mode, _size)	\
398 399 400
	{				\
		.mode = _mode,		\
		.size = _size,		\
401 402
	}

403 404 405 406 407 408 409 410
#define MLXSW_SP_SB_PR_EXT(_mode, _size, _freeze_mode, _freeze_size)	\
	{								\
		.mode = _mode,						\
		.size = _size,						\
		.freeze_mode = _freeze_mode,				\
		.freeze_size = _freeze_size,				\
	}

411 412 413 414 415
#define MLXSW_SP1_SB_PR_INGRESS_SIZE	12440000
#define MLXSW_SP1_SB_PR_INGRESS_MNG_SIZE (200 * 1000)
#define MLXSW_SP1_SB_PR_EGRESS_SIZE	13232000

static const struct mlxsw_sp_sb_pr mlxsw_sp1_sb_prs[] = {
416
	/* Ingress pools. */
417
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC,
418
		       MLXSW_SP1_SB_PR_INGRESS_SIZE),
419 420
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
421
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC,
422
		       MLXSW_SP1_SB_PR_INGRESS_MNG_SIZE),
423
	/* Egress pools. */
424 425
	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC,
			   MLXSW_SP1_SB_PR_EGRESS_SIZE, true, false),
426 427
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
428
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
429 430
	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_STATIC, MLXSW_SP_SB_INFI,
			   true, true),
431 432
};

433 434 435 436 437 438 439 440 441 442 443 444 445
#define MLXSW_SP2_SB_PR_INGRESS_SIZE	40960000
#define MLXSW_SP2_SB_PR_INGRESS_MNG_SIZE (200 * 1000)
#define MLXSW_SP2_SB_PR_EGRESS_SIZE	40960000

static const struct mlxsw_sp_sb_pr mlxsw_sp2_sb_prs[] = {
	/* Ingress pools. */
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC,
		       MLXSW_SP2_SB_PR_INGRESS_SIZE),
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC,
		       MLXSW_SP2_SB_PR_INGRESS_MNG_SIZE),
	/* Egress pools. */
446 447
	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC,
			   MLXSW_SP2_SB_PR_EGRESS_SIZE, true, false),
448 449 450
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
451 452
	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_STATIC, MLXSW_SP_SB_INFI,
			   true, true),
453 454
};

455 456 457
static int mlxsw_sp_sb_prs_init(struct mlxsw_sp *mlxsw_sp,
				const struct mlxsw_sp_sb_pr *prs,
				size_t prs_len)
458 459 460 461
{
	int i;
	int err;

462
	for (i = 0; i < prs_len; i++) {
463 464 465 466 467 468 469 470 471 472 473
		u32 size = prs[i].size;
		u32 size_cells;

		if (size == MLXSW_SP_SB_INFI) {
			err = mlxsw_sp_sb_pr_write(mlxsw_sp, i, prs[i].mode,
						   0, true);
		} else {
			size_cells = mlxsw_sp_bytes_cells(mlxsw_sp, size);
			err = mlxsw_sp_sb_pr_write(mlxsw_sp, i, prs[i].mode,
						   size_cells, false);
		}
474 475 476 477 478 479
		if (err)
			return err;
	}
	return 0;
}

480 481 482 483
#define MLXSW_SP_SB_CM(_min_buff, _max_buff, _pool)	\
	{						\
		.min_buff = _min_buff,			\
		.max_buff = _max_buff,			\
484
		.pool_index = _pool,			\
485 486
	}

487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
#define MLXSW_SP_SB_CM_ING(_min_buff, _max_buff)	\
	{						\
		.min_buff = _min_buff,			\
		.max_buff = _max_buff,			\
		.pool_index = MLXSW_SP_SB_POOL_ING,	\
	}

#define MLXSW_SP_SB_CM_EGR(_min_buff, _max_buff)	\
	{						\
		.min_buff = _min_buff,			\
		.max_buff = _max_buff,			\
		.pool_index = MLXSW_SP_SB_POOL_EGR,	\
	}

#define MLXSW_SP_SB_CM_EGR_MC(_min_buff, _max_buff)	\
	{						\
		.min_buff = _min_buff,			\
		.max_buff = _max_buff,			\
		.pool_index = MLXSW_SP_SB_POOL_EGR_MC,	\
506 507
		.freeze_pool = true,			\
		.freeze_thresh = true,			\
508 509
	}

510
static const struct mlxsw_sp_sb_cm mlxsw_sp1_sb_cms_ingress[] = {
511 512 513 514 515 516 517 518 519 520
	MLXSW_SP_SB_CM_ING(10000, 8),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, 0), /* dummy, this PG does not exist */
	MLXSW_SP_SB_CM(20000, 1, MLXSW_SP_SB_POOL_ING_MNG),
521 522
};

523
static const struct mlxsw_sp_sb_cm mlxsw_sp2_sb_cms_ingress[] = {
524 525 526 527 528 529 530 531 532 533
	MLXSW_SP_SB_CM_ING(0, 7),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_CM_ING(0, 0), /* dummy, this PG does not exist */
	MLXSW_SP_SB_CM(20000, 1, MLXSW_SP_SB_POOL_ING_MNG),
534 535 536
};

static const struct mlxsw_sp_sb_cm mlxsw_sp1_sb_cms_egress[] = {
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
	MLXSW_SP_SB_CM_EGR(1500, 9),
	MLXSW_SP_SB_CM_EGR(1500, 9),
	MLXSW_SP_SB_CM_EGR(1500, 9),
	MLXSW_SP_SB_CM_EGR(1500, 9),
	MLXSW_SP_SB_CM_EGR(1500, 9),
	MLXSW_SP_SB_CM_EGR(1500, 9),
	MLXSW_SP_SB_CM_EGR(1500, 9),
	MLXSW_SP_SB_CM_EGR(1500, 9),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR(1, 0xff),
554 555
};

556
static const struct mlxsw_sp_sb_cm mlxsw_sp2_sb_cms_egress[] = {
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
	MLXSW_SP_SB_CM_EGR(0, 7),
	MLXSW_SP_SB_CM_EGR(0, 7),
	MLXSW_SP_SB_CM_EGR(0, 7),
	MLXSW_SP_SB_CM_EGR(0, 7),
	MLXSW_SP_SB_CM_EGR(0, 7),
	MLXSW_SP_SB_CM_EGR(0, 7),
	MLXSW_SP_SB_CM_EGR(0, 7),
	MLXSW_SP_SB_CM_EGR(0, 7),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
	MLXSW_SP_SB_CM_EGR(1, 0xff),
574 575
};

576
#define MLXSW_SP_CPU_PORT_SB_CM MLXSW_SP_SB_CM(0, 0, MLXSW_SP_SB_POOL_EGR)
577 578

static const struct mlxsw_sp_sb_cm mlxsw_sp_cpu_port_sb_cms[] = {
579
	MLXSW_SP_CPU_PORT_SB_CM,
580 581 582 583 584
	MLXSW_SP_SB_CM(MLXSW_PORT_MAX_MTU, 0, MLXSW_SP_SB_POOL_EGR),
	MLXSW_SP_SB_CM(MLXSW_PORT_MAX_MTU, 0, MLXSW_SP_SB_POOL_EGR),
	MLXSW_SP_SB_CM(MLXSW_PORT_MAX_MTU, 0, MLXSW_SP_SB_POOL_EGR),
	MLXSW_SP_SB_CM(MLXSW_PORT_MAX_MTU, 0, MLXSW_SP_SB_POOL_EGR),
	MLXSW_SP_SB_CM(MLXSW_PORT_MAX_MTU, 0, MLXSW_SP_SB_POOL_EGR),
585
	MLXSW_SP_CPU_PORT_SB_CM,
586
	MLXSW_SP_SB_CM(MLXSW_PORT_MAX_MTU, 0, MLXSW_SP_SB_POOL_EGR),
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
	MLXSW_SP_CPU_PORT_SB_CM,
611 612
};

613 614 615 616 617 618 619 620
static bool
mlxsw_sp_sb_pool_is_static(struct mlxsw_sp *mlxsw_sp, u16 pool_index)
{
	struct mlxsw_sp_sb_pr *pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index);

	return pr->mode == MLXSW_REG_SBPR_MODE_STATIC;
}

621 622 623 624
static int __mlxsw_sp_sb_cms_init(struct mlxsw_sp *mlxsw_sp, u8 local_port,
				  enum mlxsw_reg_sbxx_dir dir,
				  const struct mlxsw_sp_sb_cm *cms,
				  size_t cms_len)
625
{
626
	const struct mlxsw_sp_sb_vals *sb_vals = mlxsw_sp->sb_vals;
627 628 629 630 631
	int i;
	int err;

	for (i = 0; i < cms_len; i++) {
		const struct mlxsw_sp_sb_cm *cm;
632
		u32 min_buff;
633
		u32 max_buff;
634

635 636
		if (i == 8 && dir == MLXSW_REG_SBXX_DIR_INGRESS)
			continue; /* PG number 8 does not exist, skip it */
637
		cm = &cms[i];
638
		if (WARN_ON(sb_vals->pool_dess[cm->pool_index].dir != dir))
639 640
			continue;

641
		min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, cm->min_buff);
642 643
		max_buff = cm->max_buff;
		if (max_buff == MLXSW_SP_SB_INFI) {
644 645 646
			err = mlxsw_sp_sb_cm_write(mlxsw_sp, local_port, i,
						   min_buff, 0,
						   true, cm->pool_index);
647 648 649 650 651
		} else {
			if (mlxsw_sp_sb_pool_is_static(mlxsw_sp,
						       cm->pool_index))
				max_buff = mlxsw_sp_bytes_cells(mlxsw_sp,
								max_buff);
652
			err = mlxsw_sp_sb_cm_write(mlxsw_sp, local_port, i,
653
						   min_buff, max_buff,
654
						   false, cm->pool_index);
655
		}
656 657 658 659 660 661 662 663
		if (err)
			return err;
	}
	return 0;
}

static int mlxsw_sp_port_sb_cms_init(struct mlxsw_sp_port *mlxsw_sp_port)
{
664
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
665 666
	int err;

667
	err = __mlxsw_sp_sb_cms_init(mlxsw_sp,
668 669
				     mlxsw_sp_port->local_port,
				     MLXSW_REG_SBXX_DIR_INGRESS,
670 671
				     mlxsw_sp->sb_vals->cms_ingress,
				     mlxsw_sp->sb_vals->cms_ingress_count);
672 673 674 675 676
	if (err)
		return err;
	return __mlxsw_sp_sb_cms_init(mlxsw_sp_port->mlxsw_sp,
				      mlxsw_sp_port->local_port,
				      MLXSW_REG_SBXX_DIR_EGRESS,
677 678
				      mlxsw_sp->sb_vals->cms_egress,
				      mlxsw_sp->sb_vals->cms_egress_count);
679 680 681 682
}

static int mlxsw_sp_cpu_port_sb_cms_init(struct mlxsw_sp *mlxsw_sp)
{
683
	return __mlxsw_sp_sb_cms_init(mlxsw_sp, 0, MLXSW_REG_SBXX_DIR_EGRESS,
684 685
				      mlxsw_sp->sb_vals->cms_cpu,
				      mlxsw_sp->sb_vals->cms_cpu_count);
686 687
}

688 689 690 691
#define MLXSW_SP_SB_PM(_min_buff, _max_buff)	\
	{					\
		.min_buff = _min_buff,		\
		.max_buff = _max_buff,		\
692 693
	}

694
static const struct mlxsw_sp_sb_pm mlxsw_sp1_sb_pms[] = {
695
	/* Ingress pools. */
696 697 698 699
	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MAX),
	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MAX),
700
	/* Egress pools. */
701
	MLXSW_SP_SB_PM(0, 7),
702 703 704
	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
705
	MLXSW_SP_SB_PM(10000, 90000),
706 707
};

708 709 710 711 712 713 714 715 716 717 718
static const struct mlxsw_sp_sb_pm mlxsw_sp2_sb_pms[] = {
	/* Ingress pools. */
	MLXSW_SP_SB_PM(0, 7),
	MLXSW_SP_SB_PM(0, 0),
	MLXSW_SP_SB_PM(0, 0),
	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MAX),
	/* Egress pools. */
	MLXSW_SP_SB_PM(0, 7),
	MLXSW_SP_SB_PM(0, 0),
	MLXSW_SP_SB_PM(0, 0),
	MLXSW_SP_SB_PM(0, 0),
719
	MLXSW_SP_SB_PM(10000, 90000),
720 721
};

722
static int mlxsw_sp_port_sb_pms_init(struct mlxsw_sp_port *mlxsw_sp_port)
723
{
724
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
725 726 727
	int i;
	int err;

728 729
	for (i = 0; i < mlxsw_sp->sb_vals->pool_count; i++) {
		const struct mlxsw_sp_sb_pm *pm = &mlxsw_sp->sb_vals->pms[i];
730
		u32 max_buff;
731
		u32 min_buff;
732

733
		min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, pm->min_buff);
734 735 736
		max_buff = pm->max_buff;
		if (mlxsw_sp_sb_pool_is_static(mlxsw_sp, i))
			max_buff = mlxsw_sp_bytes_cells(mlxsw_sp, max_buff);
737
		err = mlxsw_sp_sb_pm_write(mlxsw_sp, mlxsw_sp_port->local_port,
738
					   i, min_buff, max_buff);
739 740 741 742 743 744
		if (err)
			return err;
	}
	return 0;
}

745
#define MLXSW_SP_SB_MM(_min_buff, _max_buff)		\
746 747 748
	{						\
		.min_buff = _min_buff,			\
		.max_buff = _max_buff,			\
749
		.pool_index = MLXSW_SP_SB_POOL_EGR,	\
750 751 752
	}

static const struct mlxsw_sp_sb_mm mlxsw_sp_sb_mms[] = {
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
	MLXSW_SP_SB_MM(0, 6),
768 769 770 771 772 773 774 775
};

static int mlxsw_sp_sb_mms_init(struct mlxsw_sp *mlxsw_sp)
{
	char sbmm_pl[MLXSW_REG_SBMM_LEN];
	int i;
	int err;

776
	for (i = 0; i < mlxsw_sp->sb_vals->mms_count; i++) {
777
		const struct mlxsw_sp_sb_pool_des *des;
778
		const struct mlxsw_sp_sb_mm *mc;
779
		u32 min_buff;
780

781
		mc = &mlxsw_sp->sb_vals->mms[i];
782
		des = &mlxsw_sp->sb_vals->pool_dess[mc->pool_index];
783 784
		/* All pools used by sb_mm's are initialized using dynamic
		 * thresholds, therefore 'max_buff' isn't specified in cells.
785 786 787
		 */
		min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, mc->min_buff);
		mlxsw_reg_sbmm_pack(sbmm_pl, i, min_buff, mc->max_buff,
788
				    des->pool);
789 790 791 792 793 794 795
		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbmm), sbmm_pl);
		if (err)
			return err;
	}
	return 0;
}

796 797
static void mlxsw_sp_pool_count(struct mlxsw_sp *mlxsw_sp,
				u16 *p_ingress_len, u16 *p_egress_len)
798 799 800
{
	int i;

801 802 803
	for (i = 0; i < mlxsw_sp->sb_vals->pool_count; ++i)
		if (mlxsw_sp->sb_vals->pool_dess[i].dir ==
		    MLXSW_REG_SBXX_DIR_EGRESS)
804 805 806 807 808
			goto out;
	WARN(1, "No egress pools\n");

out:
	*p_ingress_len = i;
809
	*p_egress_len = mlxsw_sp->sb_vals->pool_count - i;
810 811
}

812
const struct mlxsw_sp_sb_vals mlxsw_sp1_sb_vals = {
813 814 815 816
	.pool_count = ARRAY_SIZE(mlxsw_sp1_sb_pool_dess),
	.pool_dess = mlxsw_sp1_sb_pool_dess,
	.pms = mlxsw_sp1_sb_pms,
	.prs = mlxsw_sp1_sb_prs,
817
	.mms = mlxsw_sp_sb_mms,
818 819
	.cms_ingress = mlxsw_sp1_sb_cms_ingress,
	.cms_egress = mlxsw_sp1_sb_cms_egress,
820
	.cms_cpu = mlxsw_sp_cpu_port_sb_cms,
821
	.mms_count = ARRAY_SIZE(mlxsw_sp_sb_mms),
822 823
	.cms_ingress_count = ARRAY_SIZE(mlxsw_sp1_sb_cms_ingress),
	.cms_egress_count = ARRAY_SIZE(mlxsw_sp1_sb_cms_egress),
824
	.cms_cpu_count = ARRAY_SIZE(mlxsw_sp_cpu_port_sb_cms),
825 826 827
};

const struct mlxsw_sp_sb_vals mlxsw_sp2_sb_vals = {
828 829 830 831
	.pool_count = ARRAY_SIZE(mlxsw_sp2_sb_pool_dess),
	.pool_dess = mlxsw_sp2_sb_pool_dess,
	.pms = mlxsw_sp2_sb_pms,
	.prs = mlxsw_sp2_sb_prs,
832
	.mms = mlxsw_sp_sb_mms,
833 834
	.cms_ingress = mlxsw_sp2_sb_cms_ingress,
	.cms_egress = mlxsw_sp2_sb_cms_egress,
835
	.cms_cpu = mlxsw_sp_cpu_port_sb_cms,
836
	.mms_count = ARRAY_SIZE(mlxsw_sp_sb_mms),
837 838
	.cms_ingress_count = ARRAY_SIZE(mlxsw_sp2_sb_cms_ingress),
	.cms_egress_count = ARRAY_SIZE(mlxsw_sp2_sb_cms_egress),
839
	.cms_cpu_count = ARRAY_SIZE(mlxsw_sp_cpu_port_sb_cms),
840 841
};

842 843
int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp)
{
844
	u32 max_headroom_size;
845 846
	u16 ing_pool_count;
	u16 eg_pool_count;
847 848
	int err;

849 850 851
	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, CELL_SIZE))
		return -EIO;

852 853 854
	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_BUFFER_SIZE))
		return -EIO;

855 856 857
	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_HEADROOM_SIZE))
		return -EIO;

858 859 860 861
	mlxsw_sp->sb = kzalloc(sizeof(*mlxsw_sp->sb), GFP_KERNEL);
	if (!mlxsw_sp->sb)
		return -ENOMEM;
	mlxsw_sp->sb->cell_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, CELL_SIZE);
862 863
	mlxsw_sp->sb->sb_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
						   MAX_BUFFER_SIZE);
864 865 866 867 868 869
	max_headroom_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
					       MAX_HEADROOM_SIZE);
	/* Round down, because this limit must not be overstepped. */
	mlxsw_sp->sb->max_headroom_cells = max_headroom_size /
						mlxsw_sp->sb->cell_size;

870
	err = mlxsw_sp_sb_ports_init(mlxsw_sp);
871
	if (err)
872
		goto err_sb_ports_init;
873 874
	err = mlxsw_sp_sb_prs_init(mlxsw_sp, mlxsw_sp->sb_vals->prs,
				   mlxsw_sp->sb_vals->pool_count);
875 876
	if (err)
		goto err_sb_prs_init;
877 878
	err = mlxsw_sp_cpu_port_sb_cms_init(mlxsw_sp);
	if (err)
879
		goto err_sb_cpu_port_sb_cms_init;
880
	err = mlxsw_sp_sb_mms_init(mlxsw_sp);
881
	if (err)
882
		goto err_sb_mms_init;
883
	mlxsw_sp_pool_count(mlxsw_sp, &ing_pool_count, &eg_pool_count);
884 885
	err = devlink_sb_register(priv_to_devlink(mlxsw_sp->core), 0,
				  mlxsw_sp->sb->sb_size,
886 887
				  ing_pool_count,
				  eg_pool_count,
888 889
				  MLXSW_SP_SB_ING_TC_COUNT,
				  MLXSW_SP_SB_EG_TC_COUNT);
890 891 892 893 894 895 896 897 898 899
	if (err)
		goto err_devlink_sb_register;

	return 0;

err_devlink_sb_register:
err_sb_mms_init:
err_sb_cpu_port_sb_cms_init:
err_sb_prs_init:
	mlxsw_sp_sb_ports_fini(mlxsw_sp);
900 901
err_sb_ports_init:
	kfree(mlxsw_sp->sb);
902
	return err;
903
}
904

905 906 907
void mlxsw_sp_buffers_fini(struct mlxsw_sp *mlxsw_sp)
{
	devlink_sb_unregister(priv_to_devlink(mlxsw_sp->core), 0);
908
	mlxsw_sp_sb_ports_fini(mlxsw_sp);
909
	kfree(mlxsw_sp->sb);
910 911 912 913 914 915
}

int mlxsw_sp_port_buffers_init(struct mlxsw_sp_port *mlxsw_sp_port)
{
	int err;

916
	err = mlxsw_sp_port_headroom_init(mlxsw_sp_port);
917 918 919 920 921 922 923 924 925
	if (err)
		return err;
	err = mlxsw_sp_port_sb_cms_init(mlxsw_sp_port);
	if (err)
		return err;
	err = mlxsw_sp_port_sb_pms_init(mlxsw_sp_port);

	return err;
}
926 927 928 929 930 931

int mlxsw_sp_sb_pool_get(struct mlxsw_core *mlxsw_core,
			 unsigned int sb_index, u16 pool_index,
			 struct devlink_sb_pool_info *pool_info)
{
	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
932
	enum mlxsw_reg_sbxx_dir dir;
933
	struct mlxsw_sp_sb_pr *pr;
934

935
	dir = mlxsw_sp->sb_vals->pool_dess[pool_index].dir;
936
	pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index);
937
	pool_info->pool_type = (enum devlink_sb_pool_type) dir;
938
	pool_info->size = mlxsw_sp_cells_bytes(mlxsw_sp, pr->size);
939
	pool_info->threshold_type = (enum devlink_sb_threshold_type) pr->mode;
940
	pool_info->cell_size = mlxsw_sp->sb->cell_size;
941 942 943 944 945
	return 0;
}

int mlxsw_sp_sb_pool_set(struct mlxsw_core *mlxsw_core,
			 unsigned int sb_index, u16 pool_index, u32 size,
946 947
			 enum devlink_sb_threshold_type threshold_type,
			 struct netlink_ext_ack *extack)
948 949
{
	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
950
	u32 pool_size = mlxsw_sp_bytes_cells(mlxsw_sp, size);
951
	const struct mlxsw_sp_sb_pr *pr;
952
	enum mlxsw_reg_sbpr_mode mode;
953

954 955 956
	mode = (enum mlxsw_reg_sbpr_mode) threshold_type;
	pr = &mlxsw_sp->sb_vals->prs[pool_index];

957 958
	if (size > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) {
		NL_SET_ERR_MSG_MOD(extack, "Exceeded shared buffer size");
959
		return -EINVAL;
960
	}
961

962 963 964 965 966 967 968 969 970 971
	if (pr->freeze_mode && pr->mode != mode) {
		NL_SET_ERR_MSG_MOD(extack, "Changing this pool's threshold type is forbidden");
		return -EINVAL;
	};

	if (pr->freeze_size && pr->size != size) {
		NL_SET_ERR_MSG_MOD(extack, "Changing this pool's size is forbidden");
		return -EINVAL;
	};

972 973
	return mlxsw_sp_sb_pr_write(mlxsw_sp, pool_index, mode,
				    pool_size, false);
974 975 976 977
}

#define MLXSW_SP_SB_THRESHOLD_TO_ALPHA_OFFSET (-2) /* 3->1, 16->14 */

978 979
static u32 mlxsw_sp_sb_threshold_out(struct mlxsw_sp *mlxsw_sp, u16 pool_index,
				     u32 max_buff)
980
{
981
	struct mlxsw_sp_sb_pr *pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index);
982 983 984

	if (pr->mode == MLXSW_REG_SBPR_MODE_DYNAMIC)
		return max_buff - MLXSW_SP_SB_THRESHOLD_TO_ALPHA_OFFSET;
985
	return mlxsw_sp_cells_bytes(mlxsw_sp, max_buff);
986 987
}

988
static int mlxsw_sp_sb_threshold_in(struct mlxsw_sp *mlxsw_sp, u16 pool_index,
989 990
				    u32 threshold, u32 *p_max_buff,
				    struct netlink_ext_ack *extack)
991
{
992
	struct mlxsw_sp_sb_pr *pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index);
993 994 995 996 997 998

	if (pr->mode == MLXSW_REG_SBPR_MODE_DYNAMIC) {
		int val;

		val = threshold + MLXSW_SP_SB_THRESHOLD_TO_ALPHA_OFFSET;
		if (val < MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN ||
999 1000
		    val > MLXSW_REG_SBXX_DYN_MAX_BUFF_MAX) {
			NL_SET_ERR_MSG_MOD(extack, "Invalid dynamic threshold value");
1001
			return -EINVAL;
1002
		}
1003 1004
		*p_max_buff = val;
	} else {
1005
		*p_max_buff = mlxsw_sp_bytes_cells(mlxsw_sp, threshold);
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
	}
	return 0;
}

int mlxsw_sp_sb_port_pool_get(struct mlxsw_core_port *mlxsw_core_port,
			      unsigned int sb_index, u16 pool_index,
			      u32 *p_threshold)
{
	struct mlxsw_sp_port *mlxsw_sp_port =
			mlxsw_core_port_driver_priv(mlxsw_core_port);
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
	u8 local_port = mlxsw_sp_port->local_port;
	struct mlxsw_sp_sb_pm *pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port,
1019
						       pool_index);
1020

1021
	*p_threshold = mlxsw_sp_sb_threshold_out(mlxsw_sp, pool_index,
1022 1023 1024 1025 1026 1027
						 pm->max_buff);
	return 0;
}

int mlxsw_sp_sb_port_pool_set(struct mlxsw_core_port *mlxsw_core_port,
			      unsigned int sb_index, u16 pool_index,
1028
			      u32 threshold, struct netlink_ext_ack *extack)
1029 1030 1031 1032 1033 1034 1035 1036
{
	struct mlxsw_sp_port *mlxsw_sp_port =
			mlxsw_core_port_driver_priv(mlxsw_core_port);
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
	u8 local_port = mlxsw_sp_port->local_port;
	u32 max_buff;
	int err;

1037
	err = mlxsw_sp_sb_threshold_in(mlxsw_sp, pool_index,
1038
				       threshold, &max_buff, extack);
1039 1040 1041
	if (err)
		return err;

1042
	return mlxsw_sp_sb_pm_write(mlxsw_sp, local_port, pool_index,
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
				    0, max_buff);
}

int mlxsw_sp_sb_tc_pool_bind_get(struct mlxsw_core_port *mlxsw_core_port,
				 unsigned int sb_index, u16 tc_index,
				 enum devlink_sb_pool_type pool_type,
				 u16 *p_pool_index, u32 *p_threshold)
{
	struct mlxsw_sp_port *mlxsw_sp_port =
			mlxsw_core_port_driver_priv(mlxsw_core_port);
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
	u8 local_port = mlxsw_sp_port->local_port;
	u8 pg_buff = tc_index;
1056
	enum mlxsw_reg_sbxx_dir dir = (enum mlxsw_reg_sbxx_dir) pool_type;
1057 1058 1059
	struct mlxsw_sp_sb_cm *cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port,
						       pg_buff, dir);

1060
	*p_threshold = mlxsw_sp_sb_threshold_out(mlxsw_sp, cm->pool_index,
1061
						 cm->max_buff);
1062
	*p_pool_index = cm->pool_index;
1063 1064 1065 1066 1067 1068
	return 0;
}

int mlxsw_sp_sb_tc_pool_bind_set(struct mlxsw_core_port *mlxsw_core_port,
				 unsigned int sb_index, u16 tc_index,
				 enum devlink_sb_pool_type pool_type,
1069 1070
				 u16 pool_index, u32 threshold,
				 struct netlink_ext_ack *extack)
1071 1072 1073 1074 1075
{
	struct mlxsw_sp_port *mlxsw_sp_port =
			mlxsw_core_port_driver_priv(mlxsw_core_port);
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
	u8 local_port = mlxsw_sp_port->local_port;
1076
	const struct mlxsw_sp_sb_cm *cm;
1077
	u8 pg_buff = tc_index;
1078
	enum mlxsw_reg_sbxx_dir dir = (enum mlxsw_reg_sbxx_dir) pool_type;
1079 1080 1081
	u32 max_buff;
	int err;

1082 1083
	if (dir != mlxsw_sp->sb_vals->pool_dess[pool_index].dir) {
		NL_SET_ERR_MSG_MOD(extack, "Binding egress TC to ingress pool and vice versa is forbidden");
1084
		return -EINVAL;
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
	}

	if (dir == MLXSW_REG_SBXX_DIR_INGRESS)
		cm = &mlxsw_sp->sb_vals->cms_ingress[tc_index];
	else
		cm = &mlxsw_sp->sb_vals->cms_egress[tc_index];

	if (cm->freeze_pool && cm->pool_index != pool_index) {
		NL_SET_ERR_MSG_MOD(extack, "Binding this TC to a different pool is forbidden");
		return -EINVAL;
	}

	if (cm->freeze_thresh && cm->max_buff != threshold) {
		NL_SET_ERR_MSG_MOD(extack, "Changing this TC's threshold is forbidden");
		return -EINVAL;
1100
	}
1101

1102
	err = mlxsw_sp_sb_threshold_in(mlxsw_sp, pool_index,
1103
				       threshold, &max_buff, extack);
1104 1105 1106
	if (err)
		return err;

1107
	return mlxsw_sp_sb_cm_write(mlxsw_sp, local_port, pg_buff,
1108
				    0, max_buff, false, pool_index);
1109
}
1110 1111

#define MASKED_COUNT_MAX \
1112 1113
	(MLXSW_REG_SBSR_REC_MAX_COUNT / \
	 (MLXSW_SP_SB_ING_TC_COUNT + MLXSW_SP_SB_EG_TC_COUNT))
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135

struct mlxsw_sp_sb_sr_occ_query_cb_ctx {
	u8 masked_count;
	u8 local_port_1;
};

static void mlxsw_sp_sb_sr_occ_query_cb(struct mlxsw_core *mlxsw_core,
					char *sbsr_pl, size_t sbsr_pl_len,
					unsigned long cb_priv)
{
	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
	struct mlxsw_sp_sb_sr_occ_query_cb_ctx cb_ctx;
	u8 masked_count;
	u8 local_port;
	int rec_index = 0;
	struct mlxsw_sp_sb_cm *cm;
	int i;

	memcpy(&cb_ctx, &cb_priv, sizeof(cb_ctx));

	masked_count = 0;
	for (local_port = cb_ctx.local_port_1;
1136
	     local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) {
1137 1138
		if (!mlxsw_sp->ports[local_port])
			continue;
1139
		for (i = 0; i < MLXSW_SP_SB_ING_TC_COUNT; i++) {
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
			cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, i,
						MLXSW_REG_SBXX_DIR_INGRESS);
			mlxsw_reg_sbsr_rec_unpack(sbsr_pl, rec_index++,
						  &cm->occ.cur, &cm->occ.max);
		}
		if (++masked_count == cb_ctx.masked_count)
			break;
	}
	masked_count = 0;
	for (local_port = cb_ctx.local_port_1;
1150
	     local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) {
1151 1152
		if (!mlxsw_sp->ports[local_port])
			continue;
1153
		for (i = 0; i < MLXSW_SP_SB_EG_TC_COUNT; i++) {
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
			cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, i,
						MLXSW_REG_SBXX_DIR_EGRESS);
			mlxsw_reg_sbsr_rec_unpack(sbsr_pl, rec_index++,
						  &cm->occ.cur, &cm->occ.max);
		}
		if (++masked_count == cb_ctx.masked_count)
			break;
	}
}

int mlxsw_sp_sb_occ_snapshot(struct mlxsw_core *mlxsw_core,
			     unsigned int sb_index)
{
	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
	struct mlxsw_sp_sb_sr_occ_query_cb_ctx cb_ctx;
	unsigned long cb_priv;
	LIST_HEAD(bulk_list);
	char *sbsr_pl;
	u8 masked_count;
	u8 local_port_1;
	u8 local_port = 0;
	int i;
	int err;
	int err2;

	sbsr_pl = kmalloc(MLXSW_REG_SBSR_LEN, GFP_KERNEL);
	if (!sbsr_pl)
		return -ENOMEM;

next_batch:
	local_port++;
	local_port_1 = local_port;
	masked_count = 0;
	mlxsw_reg_sbsr_pack(sbsr_pl, false);
1188
	for (i = 0; i < MLXSW_SP_SB_ING_TC_COUNT; i++)
1189
		mlxsw_reg_sbsr_pg_buff_mask_set(sbsr_pl, i, 1);
1190
	for (i = 0; i < MLXSW_SP_SB_EG_TC_COUNT; i++)
1191
		mlxsw_reg_sbsr_tclass_mask_set(sbsr_pl, i, 1);
1192
	for (; local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) {
1193 1194 1195 1196
		if (!mlxsw_sp->ports[local_port])
			continue;
		mlxsw_reg_sbsr_ingress_port_mask_set(sbsr_pl, local_port, 1);
		mlxsw_reg_sbsr_egress_port_mask_set(sbsr_pl, local_port, 1);
1197
		for (i = 0; i < mlxsw_sp->sb_vals->pool_count; i++) {
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
			err = mlxsw_sp_sb_pm_occ_query(mlxsw_sp, local_port, i,
						       &bulk_list);
			if (err)
				goto out;
		}
		if (++masked_count == MASKED_COUNT_MAX)
			goto do_query;
	}

do_query:
	cb_ctx.masked_count = masked_count;
	cb_ctx.local_port_1 = local_port_1;
	memcpy(&cb_priv, &cb_ctx, sizeof(cb_ctx));
	err = mlxsw_reg_trans_query(mlxsw_core, MLXSW_REG(sbsr), sbsr_pl,
				    &bulk_list, mlxsw_sp_sb_sr_occ_query_cb,
				    cb_priv);
	if (err)
		goto out;
1216
	if (local_port < mlxsw_core_max_ports(mlxsw_core))
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
		goto next_batch;

out:
	err2 = mlxsw_reg_trans_bulk_wait(&bulk_list);
	if (!err)
		err = err2;
	kfree(sbsr_pl);
	return err;
}

int mlxsw_sp_sb_occ_max_clear(struct mlxsw_core *mlxsw_core,
			      unsigned int sb_index)
{
	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
	LIST_HEAD(bulk_list);
	char *sbsr_pl;
	unsigned int masked_count;
	u8 local_port = 0;
	int i;
	int err;
	int err2;

	sbsr_pl = kmalloc(MLXSW_REG_SBSR_LEN, GFP_KERNEL);
	if (!sbsr_pl)
		return -ENOMEM;

next_batch:
	local_port++;
	masked_count = 0;
	mlxsw_reg_sbsr_pack(sbsr_pl, true);
1247
	for (i = 0; i < MLXSW_SP_SB_ING_TC_COUNT; i++)
1248
		mlxsw_reg_sbsr_pg_buff_mask_set(sbsr_pl, i, 1);
1249
	for (i = 0; i < MLXSW_SP_SB_EG_TC_COUNT; i++)
1250
		mlxsw_reg_sbsr_tclass_mask_set(sbsr_pl, i, 1);
1251
	for (; local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) {
1252 1253 1254 1255
		if (!mlxsw_sp->ports[local_port])
			continue;
		mlxsw_reg_sbsr_ingress_port_mask_set(sbsr_pl, local_port, 1);
		mlxsw_reg_sbsr_egress_port_mask_set(sbsr_pl, local_port, 1);
1256
		for (i = 0; i < mlxsw_sp->sb_vals->pool_count; i++) {
1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
			err = mlxsw_sp_sb_pm_occ_clear(mlxsw_sp, local_port, i,
						       &bulk_list);
			if (err)
				goto out;
		}
		if (++masked_count == MASKED_COUNT_MAX)
			goto do_query;
	}

do_query:
	err = mlxsw_reg_trans_query(mlxsw_core, MLXSW_REG(sbsr), sbsr_pl,
				    &bulk_list, NULL, 0);
	if (err)
		goto out;
1271
	if (local_port < mlxsw_core_max_ports(mlxsw_core))
1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290
		goto next_batch;

out:
	err2 = mlxsw_reg_trans_bulk_wait(&bulk_list);
	if (!err)
		err = err2;
	kfree(sbsr_pl);
	return err;
}

int mlxsw_sp_sb_occ_port_pool_get(struct mlxsw_core_port *mlxsw_core_port,
				  unsigned int sb_index, u16 pool_index,
				  u32 *p_cur, u32 *p_max)
{
	struct mlxsw_sp_port *mlxsw_sp_port =
			mlxsw_core_port_driver_priv(mlxsw_core_port);
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
	u8 local_port = mlxsw_sp_port->local_port;
	struct mlxsw_sp_sb_pm *pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port,
1291
						       pool_index);
1292

1293 1294
	*p_cur = mlxsw_sp_cells_bytes(mlxsw_sp, pm->occ.cur);
	*p_max = mlxsw_sp_cells_bytes(mlxsw_sp, pm->occ.max);
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
	return 0;
}

int mlxsw_sp_sb_occ_tc_port_bind_get(struct mlxsw_core_port *mlxsw_core_port,
				     unsigned int sb_index, u16 tc_index,
				     enum devlink_sb_pool_type pool_type,
				     u32 *p_cur, u32 *p_max)
{
	struct mlxsw_sp_port *mlxsw_sp_port =
			mlxsw_core_port_driver_priv(mlxsw_core_port);
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
	u8 local_port = mlxsw_sp_port->local_port;
	u8 pg_buff = tc_index;
1308
	enum mlxsw_reg_sbxx_dir dir = (enum mlxsw_reg_sbxx_dir) pool_type;
1309 1310 1311
	struct mlxsw_sp_sb_cm *cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port,
						       pg_buff, dir);

1312 1313
	*p_cur = mlxsw_sp_cells_bytes(mlxsw_sp, cm->occ.cur);
	*p_max = mlxsw_sp_cells_bytes(mlxsw_sp, cm->occ.max);
1314 1315
	return 0;
}