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
16struct 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
23struct mlxsw_cp_sb_occ {
24	u32 cur;
25	u32 max;
26};
27
28struct 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
40struct mlxsw_sp_sb_pm {
41	u32 min_buff;
42	u32 max_buff;
43	struct mlxsw_cp_sb_occ occ;
44};
45
46struct mlxsw_sp_sb_mm {
47	u32 min_buff;
48	u32 max_buff;
49	u16 pool_index;
50};
51
52struct 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
63static 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
77static 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
94struct 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
100struct 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
108struct 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
124struct mlxsw_sp_sb_ops {
125	u32 (*int_buf_size_get)(int mtu, u32 speed);
126};
127
128u32 mlxsw_sp_cells_bytes(const struct mlxsw_sp *mlxsw_sp, u32 cells)
129{
130	return mlxsw_sp->sb->cell_size * cells;
131}
132
133u32 mlxsw_sp_bytes_cells(const struct mlxsw_sp *mlxsw_sp, u32 bytes)
134{
135	return DIV_ROUND_UP(bytes, mlxsw_sp->sb->cell_size);
136}
137
138static u32 mlxsw_sp_port_headroom_8x_adjust(const struct mlxsw_sp_port *mlxsw_sp_port,
139					    u32 size_cells)
140{
141	/* Ports with eight lanes use two headroom buffers between which the
142	 * configured headroom size is split. Therefore, multiply the calculated
143	 * headroom size by two.
144	 */
145	return mlxsw_sp_port->mapping.width == 8 ? 2 * size_cells : size_cells;
146}
147
148static struct mlxsw_sp_sb_pr *mlxsw_sp_sb_pr_get(struct mlxsw_sp *mlxsw_sp,
149						 u16 pool_index)
150{
151	return &mlxsw_sp->sb->prs[pool_index];
152}
153
154static bool mlxsw_sp_sb_cm_exists(u8 pg_buff, enum mlxsw_reg_sbxx_dir dir)
155{
156	if (dir == MLXSW_REG_SBXX_DIR_INGRESS)
157		return pg_buff < MLXSW_SP_SB_ING_TC_COUNT;
158	else
159		return pg_buff < MLXSW_SP_SB_EG_TC_COUNT;
160}
161
162static struct mlxsw_sp_sb_cm *mlxsw_sp_sb_cm_get(struct mlxsw_sp *mlxsw_sp,
163						 u16 local_port, u8 pg_buff,
164						 enum mlxsw_reg_sbxx_dir dir)
165{
166	struct mlxsw_sp_sb_port *sb_port = &mlxsw_sp->sb->ports[local_port];
167
168	WARN_ON(!mlxsw_sp_sb_cm_exists(pg_buff, dir));
169	if (dir == MLXSW_REG_SBXX_DIR_INGRESS)
170		return &sb_port->ing_cms[pg_buff];
171	else
172		return &sb_port->eg_cms[pg_buff];
173}
174
175static struct mlxsw_sp_sb_pm *mlxsw_sp_sb_pm_get(struct mlxsw_sp *mlxsw_sp,
176						 u16 local_port, u16 pool_index)
177{
178	return &mlxsw_sp->sb->ports[local_port].pms[pool_index];
179}
180
181static int mlxsw_sp_sb_pr_write(struct mlxsw_sp *mlxsw_sp, u16 pool_index,
182				enum mlxsw_reg_sbpr_mode mode,
183				u32 size, bool infi_size)
184{
185	const struct mlxsw_sp_sb_pool_des *des =
186		&mlxsw_sp->sb_vals->pool_dess[pool_index];
187	char sbpr_pl[MLXSW_REG_SBPR_LEN];
188	struct mlxsw_sp_sb_pr *pr;
189	int err;
190
191	mlxsw_reg_sbpr_pack(sbpr_pl, des->pool, des->dir, mode,
192			    size, infi_size);
193	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbpr), sbpr_pl);
194	if (err)
195		return err;
196
197	if (infi_size)
198		size = mlxsw_sp_bytes_cells(mlxsw_sp, mlxsw_sp->sb->sb_size);
199	pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index);
200	pr->mode = mode;
201	pr->size = size;
202	return 0;
203}
204
205static int mlxsw_sp_sb_pr_desc_write(struct mlxsw_sp *mlxsw_sp,
206				     enum mlxsw_reg_sbxx_dir dir,
207				     enum mlxsw_reg_sbpr_mode mode,
208				     u32 size, bool infi_size)
209{
210	char sbpr_pl[MLXSW_REG_SBPR_LEN];
211
212	/* The FW default descriptor buffer configuration uses only pool 14 for
213	 * descriptors.
214	 */
215	mlxsw_reg_sbpr_pack(sbpr_pl, 14, dir, mode, size, infi_size);
216	mlxsw_reg_sbpr_desc_set(sbpr_pl, true);
217	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbpr), sbpr_pl);
218}
219
220static int mlxsw_sp_sb_cm_write(struct mlxsw_sp *mlxsw_sp, u16 local_port,
221				u8 pg_buff, u32 min_buff, u32 max_buff,
222				bool infi_max, u16 pool_index)
223{
224	const struct mlxsw_sp_sb_pool_des *des =
225		&mlxsw_sp->sb_vals->pool_dess[pool_index];
226	char sbcm_pl[MLXSW_REG_SBCM_LEN];
227	struct mlxsw_sp_sb_cm *cm;
228	int err;
229
230	mlxsw_reg_sbcm_pack(sbcm_pl, local_port, pg_buff, des->dir,
231			    min_buff, max_buff, infi_max, des->pool);
232	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbcm), sbcm_pl);
233	if (err)
234		return err;
235
236	if (mlxsw_sp_sb_cm_exists(pg_buff, des->dir)) {
237		if (infi_max)
238			max_buff = mlxsw_sp_bytes_cells(mlxsw_sp,
239							mlxsw_sp->sb->sb_size);
240
241		cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, pg_buff,
242					des->dir);
243		cm->min_buff = min_buff;
244		cm->max_buff = max_buff;
245		cm->pool_index = pool_index;
246	}
247	return 0;
248}
249
250static int mlxsw_sp_sb_pm_write(struct mlxsw_sp *mlxsw_sp, u16 local_port,
251				u16 pool_index, u32 min_buff, u32 max_buff)
252{
253	const struct mlxsw_sp_sb_pool_des *des =
254		&mlxsw_sp->sb_vals->pool_dess[pool_index];
255	char sbpm_pl[MLXSW_REG_SBPM_LEN];
256	struct mlxsw_sp_sb_pm *pm;
257	int err;
258
259	mlxsw_reg_sbpm_pack(sbpm_pl, local_port, des->pool, des->dir, false,
260			    min_buff, max_buff);
261	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbpm), sbpm_pl);
262	if (err)
263		return err;
264
265	pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port, pool_index);
266	pm->min_buff = min_buff;
267	pm->max_buff = max_buff;
268	return 0;
269}
270
271static int mlxsw_sp_sb_pm_occ_clear(struct mlxsw_sp *mlxsw_sp, u16 local_port,
272				    u16 pool_index, struct list_head *bulk_list)
273{
274	const struct mlxsw_sp_sb_pool_des *des =
275		&mlxsw_sp->sb_vals->pool_dess[pool_index];
276	char sbpm_pl[MLXSW_REG_SBPM_LEN];
277
278	if (local_port == MLXSW_PORT_CPU_PORT &&
279	    des->dir == MLXSW_REG_SBXX_DIR_INGRESS)
280		return 0;
281
282	mlxsw_reg_sbpm_pack(sbpm_pl, local_port, des->pool, des->dir,
283			    true, 0, 0);
284	return mlxsw_reg_trans_query(mlxsw_sp->core, MLXSW_REG(sbpm), sbpm_pl,
285				     bulk_list, NULL, 0);
286}
287
288static void mlxsw_sp_sb_pm_occ_query_cb(struct mlxsw_core *mlxsw_core,
289					char *sbpm_pl, size_t sbpm_pl_len,
290					unsigned long cb_priv)
291{
292	struct mlxsw_sp_sb_pm *pm = (struct mlxsw_sp_sb_pm *) cb_priv;
293
294	mlxsw_reg_sbpm_unpack(sbpm_pl, &pm->occ.cur, &pm->occ.max);
295}
296
297static int mlxsw_sp_sb_pm_occ_query(struct mlxsw_sp *mlxsw_sp, u16 local_port,
298				    u16 pool_index, struct list_head *bulk_list)
299{
300	const struct mlxsw_sp_sb_pool_des *des =
301		&mlxsw_sp->sb_vals->pool_dess[pool_index];
302	char sbpm_pl[MLXSW_REG_SBPM_LEN];
303	struct mlxsw_sp_sb_pm *pm;
304
305	if (local_port == MLXSW_PORT_CPU_PORT &&
306	    des->dir == MLXSW_REG_SBXX_DIR_INGRESS)
307		return 0;
308
309	pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port, pool_index);
310	mlxsw_reg_sbpm_pack(sbpm_pl, local_port, des->pool, des->dir,
311			    false, 0, 0);
312	return mlxsw_reg_trans_query(mlxsw_sp->core, MLXSW_REG(sbpm), sbpm_pl,
313				     bulk_list,
314				     mlxsw_sp_sb_pm_occ_query_cb,
315				     (unsigned long) pm);
316}
317
318void mlxsw_sp_hdroom_prios_reset_buf_idx(struct mlxsw_sp_hdroom *hdroom)
319{
320	int prio;
321
322	for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) {
323		switch (hdroom->mode) {
324		case MLXSW_SP_HDROOM_MODE_DCB:
325			hdroom->prios.prio[prio].buf_idx = hdroom->prios.prio[prio].ets_buf_idx;
326			break;
327		case MLXSW_SP_HDROOM_MODE_TC:
328			hdroom->prios.prio[prio].buf_idx = hdroom->prios.prio[prio].set_buf_idx;
329			break;
330		}
331	}
332}
333
334void mlxsw_sp_hdroom_bufs_reset_lossiness(struct mlxsw_sp_hdroom *hdroom)
335{
336	int prio;
337	int i;
338
339	for (i = 0; i < DCBX_MAX_BUFFERS; i++)
340		hdroom->bufs.buf[i].lossy = true;
341
342	for (prio = 0; prio < IEEE_8021Q_MAX_PRIORITIES; prio++) {
343		if (!hdroom->prios.prio[prio].lossy)
344			hdroom->bufs.buf[hdroom->prios.prio[prio].buf_idx].lossy = false;
345	}
346}
347
348static u16 mlxsw_sp_hdroom_buf_threshold_get(const struct mlxsw_sp *mlxsw_sp, int mtu)
349{
350	return 2 * mlxsw_sp_bytes_cells(mlxsw_sp, mtu);
351}
352
353static void mlxsw_sp_hdroom_buf_pack(char *pbmc_pl, int index, u16 size, u16 thres, bool lossy)
354{
355	if (lossy)
356		mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, index, size);
357	else
358		mlxsw_reg_pbmc_lossless_buffer_pack(pbmc_pl, index, size,
359						    thres);
360}
361
362static u16 mlxsw_sp_hdroom_buf_delay_get(const struct mlxsw_sp *mlxsw_sp,
363					 const struct mlxsw_sp_hdroom *hdroom)
364{
365	u16 delay_cells;
366
367	delay_cells = mlxsw_sp_bytes_cells(mlxsw_sp, hdroom->delay_bytes);
368
369	/* In the worst case scenario the delay will be made up of packets that
370	 * are all of size CELL_SIZE + 1, which means each packet will require
371	 * almost twice its true size when buffered in the switch. We therefore
372	 * multiply this value by the "cell factor", which is close to 2.
373	 *
374	 * Another MTU is added in case the transmitting host already started
375	 * transmitting a maximum length frame when the PFC packet was received.
376	 */
377	return 2 * delay_cells + mlxsw_sp_bytes_cells(mlxsw_sp, hdroom->mtu);
378}
379
380static u32 mlxsw_sp_hdroom_int_buf_size_get(struct mlxsw_sp *mlxsw_sp, int mtu, u32 speed)
381{
382	u32 buffsize = mlxsw_sp->sb_ops->int_buf_size_get(mtu, speed);
383
384	return mlxsw_sp_bytes_cells(mlxsw_sp, buffsize) + 1;
385}
386
387static bool mlxsw_sp_hdroom_buf_is_used(const struct mlxsw_sp_hdroom *hdroom, int buf)
388{
389	int prio;
390
391	for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) {
392		if (hdroom->prios.prio[prio].buf_idx == buf)
393			return true;
394	}
395	return false;
396}
397
398void mlxsw_sp_hdroom_bufs_reset_sizes(struct mlxsw_sp_port *mlxsw_sp_port,
399				      struct mlxsw_sp_hdroom *hdroom)
400{
401	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
402	u16 reserve_cells;
403	int i;
404
405	/* Internal buffer. */
406	reserve_cells = mlxsw_sp_hdroom_int_buf_size_get(mlxsw_sp, mlxsw_sp_port->max_mtu,
407							 mlxsw_sp_port->max_speed);
408	reserve_cells = mlxsw_sp_port_headroom_8x_adjust(mlxsw_sp_port, reserve_cells);
409	hdroom->int_buf.reserve_cells = reserve_cells;
410
411	if (hdroom->int_buf.enable)
412		hdroom->int_buf.size_cells = reserve_cells;
413	else
414		hdroom->int_buf.size_cells = 0;
415
416	/* PG buffers. */
417	for (i = 0; i < DCBX_MAX_BUFFERS; i++) {
418		struct mlxsw_sp_hdroom_buf *buf = &hdroom->bufs.buf[i];
419		u16 thres_cells;
420		u16 delay_cells;
421
422		if (!mlxsw_sp_hdroom_buf_is_used(hdroom, i)) {
423			thres_cells = 0;
424			delay_cells = 0;
425		} else if (buf->lossy) {
426			thres_cells = mlxsw_sp_hdroom_buf_threshold_get(mlxsw_sp, hdroom->mtu);
427			delay_cells = 0;
428		} else {
429			thres_cells = mlxsw_sp_hdroom_buf_threshold_get(mlxsw_sp, hdroom->mtu);
430			delay_cells = mlxsw_sp_hdroom_buf_delay_get(mlxsw_sp, hdroom);
431		}
432
433		thres_cells = mlxsw_sp_port_headroom_8x_adjust(mlxsw_sp_port, thres_cells);
434		delay_cells = mlxsw_sp_port_headroom_8x_adjust(mlxsw_sp_port, delay_cells);
435
436		buf->thres_cells = thres_cells;
437		if (hdroom->mode == MLXSW_SP_HDROOM_MODE_DCB) {
438			buf->size_cells = thres_cells + delay_cells;
439		} else {
440			/* Do not allow going below the minimum size, even if
441			 * the user requested it.
442			 */
443			buf->size_cells = max(buf->set_size_cells, buf->thres_cells);
444		}
445	}
446}
447
448#define MLXSW_SP_PB_UNUSED 8
449
450static int mlxsw_sp_hdroom_configure_buffers(struct mlxsw_sp_port *mlxsw_sp_port,
451					     const struct mlxsw_sp_hdroom *hdroom, bool force)
452{
453	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
454	char pbmc_pl[MLXSW_REG_PBMC_LEN];
455	bool dirty;
456	int err;
457	int i;
458
459	dirty = memcmp(&mlxsw_sp_port->hdroom->bufs, &hdroom->bufs, sizeof(hdroom->bufs));
460	if (!dirty && !force)
461		return 0;
462
463	mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port, 0xffff, 0xffff / 2);
464	for (i = 0; i < MLXSW_SP_PB_COUNT; i++) {
465		const struct mlxsw_sp_hdroom_buf *buf = &hdroom->bufs.buf[i];
466
467		if (i == MLXSW_SP_PB_UNUSED)
468			continue;
469
470		mlxsw_sp_hdroom_buf_pack(pbmc_pl, i, buf->size_cells, buf->thres_cells, buf->lossy);
471	}
472
473	mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, MLXSW_REG_PBMC_PORT_SHARED_BUF_IDX, 0);
474	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
475	if (err)
476		return err;
477
478	mlxsw_sp_port->hdroom->bufs = hdroom->bufs;
479	return 0;
480}
481
482static int mlxsw_sp_hdroom_configure_priomap(struct mlxsw_sp_port *mlxsw_sp_port,
483					     const struct mlxsw_sp_hdroom *hdroom, bool force)
484{
485	char pptb_pl[MLXSW_REG_PPTB_LEN];
486	bool dirty;
487	int prio;
488	int err;
489
490	dirty = memcmp(&mlxsw_sp_port->hdroom->prios, &hdroom->prios, sizeof(hdroom->prios));
491	if (!dirty && !force)
492		return 0;
493
494	mlxsw_reg_pptb_pack(pptb_pl, mlxsw_sp_port->local_port);
495	for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++)
496		mlxsw_reg_pptb_prio_to_buff_pack(pptb_pl, prio, hdroom->prios.prio[prio].buf_idx);
497
498	err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb), pptb_pl);
499	if (err)
500		return err;
501
502	mlxsw_sp_port->hdroom->prios = hdroom->prios;
503	return 0;
504}
505
506static int mlxsw_sp_hdroom_configure_int_buf(struct mlxsw_sp_port *mlxsw_sp_port,
507					     const struct mlxsw_sp_hdroom *hdroom, bool force)
508{
509	char sbib_pl[MLXSW_REG_SBIB_LEN];
510	bool dirty;
511	int err;
512
513	dirty = memcmp(&mlxsw_sp_port->hdroom->int_buf, &hdroom->int_buf, sizeof(hdroom->int_buf));
514	if (!dirty && !force)
515		return 0;
516
517	mlxsw_reg_sbib_pack(sbib_pl, mlxsw_sp_port->local_port, hdroom->int_buf.size_cells);
518	err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
519	if (err)
520		return err;
521
522	mlxsw_sp_port->hdroom->int_buf = hdroom->int_buf;
523	return 0;
524}
525
526static bool mlxsw_sp_hdroom_bufs_fit(struct mlxsw_sp *mlxsw_sp,
527				     const struct mlxsw_sp_hdroom *hdroom)
528{
529	u32 taken_headroom_cells = 0;
530	int i;
531
532	for (i = 0; i < MLXSW_SP_PB_COUNT; i++)
533		taken_headroom_cells += hdroom->bufs.buf[i].size_cells;
534
535	taken_headroom_cells += hdroom->int_buf.reserve_cells;
536	return taken_headroom_cells <= mlxsw_sp->sb->max_headroom_cells;
537}
538
539static int __mlxsw_sp_hdroom_configure(struct mlxsw_sp_port *mlxsw_sp_port,
540				       const struct mlxsw_sp_hdroom *hdroom, bool force)
541{
542	struct mlxsw_sp_hdroom orig_hdroom;
543	struct mlxsw_sp_hdroom tmp_hdroom;
544	int err;
545	int i;
546
547	/* Port buffers need to be configured in three steps. First, all buffers
548	 * with non-zero size are configured. Then, prio-to-buffer map is
549	 * updated, allowing traffic to flow to the now non-zero buffers.
550	 * Finally, zero-sized buffers are configured, because now no traffic
551	 * should be directed to them anymore. This way, in a non-congested
552	 * system, no packet drops are introduced by the reconfiguration.
553	 */
554
555	orig_hdroom = *mlxsw_sp_port->hdroom;
556	tmp_hdroom = orig_hdroom;
557	for (i = 0; i < MLXSW_SP_PB_COUNT; i++) {
558		if (hdroom->bufs.buf[i].size_cells)
559			tmp_hdroom.bufs.buf[i] = hdroom->bufs.buf[i];
560	}
561
562	if (!mlxsw_sp_hdroom_bufs_fit(mlxsw_sp_port->mlxsw_sp, &tmp_hdroom) ||
563	    !mlxsw_sp_hdroom_bufs_fit(mlxsw_sp_port->mlxsw_sp, hdroom))
564		return -ENOBUFS;
565
566	err = mlxsw_sp_hdroom_configure_buffers(mlxsw_sp_port, &tmp_hdroom, force);
567	if (err)
568		return err;
569
570	err = mlxsw_sp_hdroom_configure_priomap(mlxsw_sp_port, hdroom, force);
571	if (err)
572		goto err_configure_priomap;
573
574	err = mlxsw_sp_hdroom_configure_buffers(mlxsw_sp_port, hdroom, false);
575	if (err)
576		goto err_configure_buffers;
577
578	err = mlxsw_sp_hdroom_configure_int_buf(mlxsw_sp_port, hdroom, false);
579	if (err)
580		goto err_configure_int_buf;
581
582	*mlxsw_sp_port->hdroom = *hdroom;
583	return 0;
584
585err_configure_int_buf:
586	mlxsw_sp_hdroom_configure_buffers(mlxsw_sp_port, &tmp_hdroom, false);
587err_configure_buffers:
588	mlxsw_sp_hdroom_configure_priomap(mlxsw_sp_port, &tmp_hdroom, false);
589err_configure_priomap:
590	mlxsw_sp_hdroom_configure_buffers(mlxsw_sp_port, &orig_hdroom, false);
591	return err;
592}
593
594int mlxsw_sp_hdroom_configure(struct mlxsw_sp_port *mlxsw_sp_port,
595			      const struct mlxsw_sp_hdroom *hdroom)
596{
597	return __mlxsw_sp_hdroom_configure(mlxsw_sp_port, hdroom, false);
598}
599
600static int mlxsw_sp_port_headroom_init(struct mlxsw_sp_port *mlxsw_sp_port)
601{
602	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
603	struct mlxsw_sp_hdroom hdroom = {};
604	u32 size9;
605	int prio;
606
607	hdroom.mtu = mlxsw_sp_port->dev->mtu;
608	hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB;
609	for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++)
610		hdroom.prios.prio[prio].lossy = true;
611
612	mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
613	mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
614
615	/* Buffer 9 is used for control traffic. */
616	size9 = mlxsw_sp_port_headroom_8x_adjust(mlxsw_sp_port, mlxsw_sp_port->max_mtu);
617	hdroom.bufs.buf[9].size_cells = mlxsw_sp_bytes_cells(mlxsw_sp, size9);
618
619	return __mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom, true);
620}
621
622static int mlxsw_sp_sb_port_init(struct mlxsw_sp *mlxsw_sp,
623				 struct mlxsw_sp_sb_port *sb_port)
624{
625	struct mlxsw_sp_sb_pm *pms;
626
627	pms = kcalloc(mlxsw_sp->sb_vals->pool_count, sizeof(*pms),
628		      GFP_KERNEL);
629	if (!pms)
630		return -ENOMEM;
631	sb_port->pms = pms;
632	return 0;
633}
634
635static void mlxsw_sp_sb_port_fini(struct mlxsw_sp_sb_port *sb_port)
636{
637	kfree(sb_port->pms);
638}
639
640static int mlxsw_sp_sb_ports_init(struct mlxsw_sp *mlxsw_sp)
641{
642	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
643	struct mlxsw_sp_sb_pr *prs;
644	int i;
645	int err;
646
647	mlxsw_sp->sb->ports = kcalloc(max_ports,
648				      sizeof(struct mlxsw_sp_sb_port),
649				      GFP_KERNEL);
650	if (!mlxsw_sp->sb->ports)
651		return -ENOMEM;
652
653	prs = kcalloc(mlxsw_sp->sb_vals->pool_count, sizeof(*prs),
654		      GFP_KERNEL);
655	if (!prs) {
656		err = -ENOMEM;
657		goto err_alloc_prs;
658	}
659	mlxsw_sp->sb->prs = prs;
660
661	for (i = 0; i < max_ports; i++) {
662		err = mlxsw_sp_sb_port_init(mlxsw_sp, &mlxsw_sp->sb->ports[i]);
663		if (err)
664			goto err_sb_port_init;
665	}
666
667	return 0;
668
669err_sb_port_init:
670	for (i--; i >= 0; i--)
671		mlxsw_sp_sb_port_fini(&mlxsw_sp->sb->ports[i]);
672	kfree(mlxsw_sp->sb->prs);
673err_alloc_prs:
674	kfree(mlxsw_sp->sb->ports);
675	return err;
676}
677
678static void mlxsw_sp_sb_ports_fini(struct mlxsw_sp *mlxsw_sp)
679{
680	int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
681	int i;
682
683	for (i = max_ports - 1; i >= 0; i--)
684		mlxsw_sp_sb_port_fini(&mlxsw_sp->sb->ports[i]);
685	kfree(mlxsw_sp->sb->prs);
686	kfree(mlxsw_sp->sb->ports);
687}
688
689#define MLXSW_SP_SB_PR(_mode, _size)	\
690	{				\
691		.mode = _mode,		\
692		.size = _size,		\
693	}
694
695#define MLXSW_SP_SB_PR_EXT(_mode, _size, _freeze_mode, _freeze_size)	\
696	{								\
697		.mode = _mode,						\
698		.size = _size,						\
699		.freeze_mode = _freeze_mode,				\
700		.freeze_size = _freeze_size,				\
701	}
702
703#define MLXSW_SP1_SB_PR_CPU_SIZE	(256 * 1000)
704
705/* Order according to mlxsw_sp1_sb_pool_dess */
706static const struct mlxsw_sp_sb_pr mlxsw_sp1_sb_prs[] = {
707	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, MLXSW_SP_SB_REST),
708	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
709	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
710	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
711	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC, MLXSW_SP_SB_REST,
712			   true, false),
713	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
714	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
715	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0),
716	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_STATIC, MLXSW_SP_SB_INFI,
717			   true, true),
718	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC,
719			   MLXSW_SP1_SB_PR_CPU_SIZE, true, false),
720	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC,
721			   MLXSW_SP1_SB_PR_CPU_SIZE, true, false),
722};
723
724#define MLXSW_SP2_SB_PR_CPU_SIZE	(256 * 1000)
725
726/* Order according to mlxsw_sp2_sb_pool_dess */
727static const struct mlxsw_sp_sb_pr mlxsw_sp2_sb_prs[] = {
728	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, MLXSW_SP_SB_REST),
729	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
730	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
731	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
732	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC, MLXSW_SP_SB_REST,
733			   true, false),
734	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
735	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
736	MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_STATIC, 0),
737	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_STATIC, MLXSW_SP_SB_INFI,
738			   true, true),
739	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC,
740			   MLXSW_SP2_SB_PR_CPU_SIZE, true, false),
741	MLXSW_SP_SB_PR_EXT(MLXSW_REG_SBPR_MODE_DYNAMIC,
742			   MLXSW_SP2_SB_PR_CPU_SIZE, true, false),
743};
744
745static int mlxsw_sp_sb_prs_init(struct mlxsw_sp *mlxsw_sp,
746				const struct mlxsw_sp_sb_pr *prs,
747				const struct mlxsw_sp_sb_pool_des *pool_dess,
748				size_t prs_len)
749{
750	/* Round down, unlike mlxsw_sp_bytes_cells(). */
751	u32 sb_cells = div_u64(mlxsw_sp->sb->sb_size, mlxsw_sp->sb->cell_size);
752	u32 rest_cells[2] = {sb_cells, sb_cells};
753	int i;
754	int err;
755
756	/* Calculate how much space to give to the "REST" pools in either
757	 * direction.
758	 */
759	for (i = 0; i < prs_len; i++) {
760		enum mlxsw_reg_sbxx_dir dir = pool_dess[i].dir;
761		u32 size = prs[i].size;
762		u32 size_cells;
763
764		if (size == MLXSW_SP_SB_INFI || size == MLXSW_SP_SB_REST)
765			continue;
766
767		size_cells = mlxsw_sp_bytes_cells(mlxsw_sp, size);
768		if (WARN_ON_ONCE(size_cells > rest_cells[dir]))
769			continue;
770
771		rest_cells[dir] -= size_cells;
772	}
773
774	for (i = 0; i < prs_len; i++) {
775		u32 size = prs[i].size;
776		u32 size_cells;
777
778		if (size == MLXSW_SP_SB_INFI) {
779			err = mlxsw_sp_sb_pr_write(mlxsw_sp, i, prs[i].mode,
780						   0, true);
781		} else if (size == MLXSW_SP_SB_REST) {
782			size_cells = rest_cells[pool_dess[i].dir];
783			err = mlxsw_sp_sb_pr_write(mlxsw_sp, i, prs[i].mode,
784						   size_cells, false);
785		} else {
786			size_cells = mlxsw_sp_bytes_cells(mlxsw_sp, size);
787			err = mlxsw_sp_sb_pr_write(mlxsw_sp, i, prs[i].mode,
788						   size_cells, false);
789		}
790		if (err)
791			return err;
792	}
793
794	err = mlxsw_sp_sb_pr_desc_write(mlxsw_sp, MLXSW_REG_SBXX_DIR_INGRESS,
795					MLXSW_REG_SBPR_MODE_DYNAMIC, 0, true);
796	if (err)
797		return err;
798
799	err = mlxsw_sp_sb_pr_desc_write(mlxsw_sp, MLXSW_REG_SBXX_DIR_EGRESS,
800					MLXSW_REG_SBPR_MODE_DYNAMIC, 0, true);
801	if (err)
802		return err;
803
804	return 0;
805}
806
807#define MLXSW_SP_SB_CM(_min_buff, _max_buff, _pool)	\
808	{						\
809		.min_buff = _min_buff,			\
810		.max_buff = _max_buff,			\
811		.pool_index = _pool,			\
812	}
813
814#define MLXSW_SP_SB_CM_ING(_min_buff, _max_buff)	\
815	{						\
816		.min_buff = _min_buff,			\
817		.max_buff = _max_buff,			\
818		.pool_index = MLXSW_SP_SB_POOL_ING,	\
819	}
820
821#define MLXSW_SP_SB_CM_EGR(_min_buff, _max_buff)	\
822	{						\
823		.min_buff = _min_buff,			\
824		.max_buff = _max_buff,			\
825		.pool_index = MLXSW_SP_SB_POOL_EGR,	\
826	}
827
828#define MLXSW_SP_SB_CM_EGR_MC(_min_buff, _max_buff)	\
829	{						\
830		.min_buff = _min_buff,			\
831		.max_buff = _max_buff,			\
832		.pool_index = MLXSW_SP_SB_POOL_EGR_MC,	\
833		.freeze_pool = true,			\
834		.freeze_thresh = true,			\
835	}
836
837static const struct mlxsw_sp_sb_cm mlxsw_sp1_sb_cms_ingress[] = {
838	MLXSW_SP_SB_CM_ING(10000, 8),
839	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
840	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
841	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
842	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
843	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
844	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
845	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
846	MLXSW_SP_SB_CM_ING(0, 0), /* dummy, this PG does not exist */
847	MLXSW_SP_SB_CM(10000, 8, MLXSW_SP_SB_POOL_ING_CPU),
848};
849
850static const struct mlxsw_sp_sb_cm mlxsw_sp2_sb_cms_ingress[] = {
851	MLXSW_SP_SB_CM_ING(0, 7),
852	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
853	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
854	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
855	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
856	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
857	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
858	MLXSW_SP_SB_CM_ING(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
859	MLXSW_SP_SB_CM_ING(0, 0), /* dummy, this PG does not exist */
860	MLXSW_SP_SB_CM(10000, 8, MLXSW_SP_SB_POOL_ING_CPU),
861};
862
863static const struct mlxsw_sp_sb_cm mlxsw_sp1_sb_cms_egress[] = {
864	MLXSW_SP_SB_CM_EGR(1500, 9),
865	MLXSW_SP_SB_CM_EGR(1500, 9),
866	MLXSW_SP_SB_CM_EGR(1500, 9),
867	MLXSW_SP_SB_CM_EGR(1500, 9),
868	MLXSW_SP_SB_CM_EGR(1500, 9),
869	MLXSW_SP_SB_CM_EGR(1500, 9),
870	MLXSW_SP_SB_CM_EGR(1500, 9),
871	MLXSW_SP_SB_CM_EGR(1500, 9),
872	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
873	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
874	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
875	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
876	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
877	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
878	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
879	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
880	MLXSW_SP_SB_CM_EGR(1, 0xff),
881};
882
883static const struct mlxsw_sp_sb_cm mlxsw_sp2_sb_cms_egress[] = {
884	MLXSW_SP_SB_CM_EGR(0, 7),
885	MLXSW_SP_SB_CM_EGR(0, 7),
886	MLXSW_SP_SB_CM_EGR(0, 7),
887	MLXSW_SP_SB_CM_EGR(0, 7),
888	MLXSW_SP_SB_CM_EGR(0, 7),
889	MLXSW_SP_SB_CM_EGR(0, 7),
890	MLXSW_SP_SB_CM_EGR(0, 7),
891	MLXSW_SP_SB_CM_EGR(0, 7),
892	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
893	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
894	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
895	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
896	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
897	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
898	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
899	MLXSW_SP_SB_CM_EGR_MC(0, MLXSW_SP_SB_INFI),
900	MLXSW_SP_SB_CM_EGR(1, 0xff),
901};
902
903#define MLXSW_SP_CPU_PORT_SB_CM MLXSW_SP_SB_CM(0, 0, MLXSW_SP_SB_POOL_EGR_CPU)
904
905static const struct mlxsw_sp_sb_cm mlxsw_sp_cpu_port_sb_cms[] = {
906	MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU),
907	MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU),
908	MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU),
909	MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU),
910	MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU),
911	MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU),
912	MLXSW_SP_CPU_PORT_SB_CM,
913	MLXSW_SP_SB_CM(1000, 8, MLXSW_SP_SB_POOL_EGR_CPU),
914	MLXSW_SP_CPU_PORT_SB_CM,
915	MLXSW_SP_CPU_PORT_SB_CM,
916	MLXSW_SP_CPU_PORT_SB_CM,
917	MLXSW_SP_CPU_PORT_SB_CM,
918	MLXSW_SP_CPU_PORT_SB_CM,
919	MLXSW_SP_CPU_PORT_SB_CM,
920	MLXSW_SP_CPU_PORT_SB_CM,
921	MLXSW_SP_CPU_PORT_SB_CM,
922	MLXSW_SP_CPU_PORT_SB_CM,
923	MLXSW_SP_CPU_PORT_SB_CM,
924	MLXSW_SP_CPU_PORT_SB_CM,
925	MLXSW_SP_CPU_PORT_SB_CM,
926	MLXSW_SP_CPU_PORT_SB_CM,
927	MLXSW_SP_CPU_PORT_SB_CM,
928	MLXSW_SP_CPU_PORT_SB_CM,
929	MLXSW_SP_CPU_PORT_SB_CM,
930	MLXSW_SP_CPU_PORT_SB_CM,
931	MLXSW_SP_CPU_PORT_SB_CM,
932	MLXSW_SP_CPU_PORT_SB_CM,
933	MLXSW_SP_CPU_PORT_SB_CM,
934	MLXSW_SP_CPU_PORT_SB_CM,
935	MLXSW_SP_CPU_PORT_SB_CM,
936	MLXSW_SP_CPU_PORT_SB_CM,
937	MLXSW_SP_CPU_PORT_SB_CM,
938};
939
940static bool
941mlxsw_sp_sb_pool_is_static(struct mlxsw_sp *mlxsw_sp, u16 pool_index)
942{
943	struct mlxsw_sp_sb_pr *pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index);
944
945	return pr->mode == MLXSW_REG_SBPR_MODE_STATIC;
946}
947
948static int __mlxsw_sp_sb_cms_init(struct mlxsw_sp *mlxsw_sp, u16 local_port,
949				  enum mlxsw_reg_sbxx_dir dir,
950				  const struct mlxsw_sp_sb_cm *cms,
951				  size_t cms_len)
952{
953	const struct mlxsw_sp_sb_vals *sb_vals = mlxsw_sp->sb_vals;
954	int i;
955	int err;
956
957	for (i = 0; i < cms_len; i++) {
958		const struct mlxsw_sp_sb_cm *cm;
959		u32 min_buff;
960		u32 max_buff;
961
962		if (i == 8 && dir == MLXSW_REG_SBXX_DIR_INGRESS)
963			continue; /* PG number 8 does not exist, skip it */
964		cm = &cms[i];
965		if (WARN_ON(sb_vals->pool_dess[cm->pool_index].dir != dir))
966			continue;
967
968		min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, cm->min_buff);
969		max_buff = cm->max_buff;
970		if (max_buff == MLXSW_SP_SB_INFI) {
971			err = mlxsw_sp_sb_cm_write(mlxsw_sp, local_port, i,
972						   min_buff, 0,
973						   true, cm->pool_index);
974		} else {
975			if (mlxsw_sp_sb_pool_is_static(mlxsw_sp,
976						       cm->pool_index))
977				max_buff = mlxsw_sp_bytes_cells(mlxsw_sp,
978								max_buff);
979			err = mlxsw_sp_sb_cm_write(mlxsw_sp, local_port, i,
980						   min_buff, max_buff,
981						   false, cm->pool_index);
982		}
983		if (err)
984			return err;
985	}
986	return 0;
987}
988
989static int mlxsw_sp_port_sb_cms_init(struct mlxsw_sp_port *mlxsw_sp_port)
990{
991	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
992	int err;
993
994	err = __mlxsw_sp_sb_cms_init(mlxsw_sp,
995				     mlxsw_sp_port->local_port,
996				     MLXSW_REG_SBXX_DIR_INGRESS,
997				     mlxsw_sp->sb_vals->cms_ingress,
998				     mlxsw_sp->sb_vals->cms_ingress_count);
999	if (err)
1000		return err;
1001	return __mlxsw_sp_sb_cms_init(mlxsw_sp_port->mlxsw_sp,
1002				      mlxsw_sp_port->local_port,
1003				      MLXSW_REG_SBXX_DIR_EGRESS,
1004				      mlxsw_sp->sb_vals->cms_egress,
1005				      mlxsw_sp->sb_vals->cms_egress_count);
1006}
1007
1008static int mlxsw_sp_cpu_port_sb_cms_init(struct mlxsw_sp *mlxsw_sp)
1009{
1010	return __mlxsw_sp_sb_cms_init(mlxsw_sp, 0, MLXSW_REG_SBXX_DIR_EGRESS,
1011				      mlxsw_sp->sb_vals->cms_cpu,
1012				      mlxsw_sp->sb_vals->cms_cpu_count);
1013}
1014
1015#define MLXSW_SP_SB_PM(_min_buff, _max_buff)	\
1016	{					\
1017		.min_buff = _min_buff,		\
1018		.max_buff = _max_buff,		\
1019	}
1020
1021/* Order according to mlxsw_sp1_sb_pool_dess */
1022static const struct mlxsw_sp_sb_pm mlxsw_sp1_sb_pms[] = {
1023	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MAX),
1024	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
1025	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
1026	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
1027	MLXSW_SP_SB_PM(0, 7),
1028	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
1029	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
1030	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
1031	MLXSW_SP_SB_PM(10000, 90000),
1032	MLXSW_SP_SB_PM(0, 8),	/* 50% occupancy */
1033	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
1034};
1035
1036/* Order according to mlxsw_sp2_sb_pool_dess */
1037static const struct mlxsw_sp_sb_pm mlxsw_sp2_sb_pms[] = {
1038	MLXSW_SP_SB_PM(0, 7),
1039	MLXSW_SP_SB_PM(0, 0),
1040	MLXSW_SP_SB_PM(0, 0),
1041	MLXSW_SP_SB_PM(0, 0),
1042	MLXSW_SP_SB_PM(0, 7),
1043	MLXSW_SP_SB_PM(0, 0),
1044	MLXSW_SP_SB_PM(0, 0),
1045	MLXSW_SP_SB_PM(0, 0),
1046	MLXSW_SP_SB_PM(10000, 90000),
1047	MLXSW_SP_SB_PM(0, 8),	/* 50% occupancy */
1048	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN),
1049};
1050
1051/* Order according to mlxsw_sp*_sb_pool_dess */
1052static const struct mlxsw_sp_sb_pm mlxsw_sp_cpu_port_sb_pms[] = {
1053	MLXSW_SP_SB_PM(0, 0),
1054	MLXSW_SP_SB_PM(0, 0),
1055	MLXSW_SP_SB_PM(0, 0),
1056	MLXSW_SP_SB_PM(0, 0),
1057	MLXSW_SP_SB_PM(0, 0),
1058	MLXSW_SP_SB_PM(0, 0),
1059	MLXSW_SP_SB_PM(0, 0),
1060	MLXSW_SP_SB_PM(0, 0),
1061	MLXSW_SP_SB_PM(0, 90000),
1062	MLXSW_SP_SB_PM(0, 0),
1063	MLXSW_SP_SB_PM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MAX),
1064};
1065
1066static int mlxsw_sp_sb_pms_init(struct mlxsw_sp *mlxsw_sp, u16 local_port,
1067				const struct mlxsw_sp_sb_pm *pms,
1068				bool skip_ingress)
1069{
1070	int i, err;
1071
1072	for (i = 0; i < mlxsw_sp->sb_vals->pool_count; i++) {
1073		const struct mlxsw_sp_sb_pm *pm = &pms[i];
1074		const struct mlxsw_sp_sb_pool_des *des;
1075		u32 max_buff;
1076		u32 min_buff;
1077
1078		des = &mlxsw_sp->sb_vals->pool_dess[i];
1079		if (skip_ingress && des->dir == MLXSW_REG_SBXX_DIR_INGRESS)
1080			continue;
1081
1082		min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, pm->min_buff);
1083		max_buff = pm->max_buff;
1084		if (mlxsw_sp_sb_pool_is_static(mlxsw_sp, i))
1085			max_buff = mlxsw_sp_bytes_cells(mlxsw_sp, max_buff);
1086		err = mlxsw_sp_sb_pm_write(mlxsw_sp, local_port, i, min_buff,
1087					   max_buff);
1088		if (err)
1089			return err;
1090	}
1091	return 0;
1092}
1093
1094static int mlxsw_sp_port_sb_pms_init(struct mlxsw_sp_port *mlxsw_sp_port)
1095{
1096	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1097
1098	return mlxsw_sp_sb_pms_init(mlxsw_sp, mlxsw_sp_port->local_port,
1099				    mlxsw_sp->sb_vals->pms, false);
1100}
1101
1102static int mlxsw_sp_cpu_port_sb_pms_init(struct mlxsw_sp *mlxsw_sp)
1103{
1104	return mlxsw_sp_sb_pms_init(mlxsw_sp, 0, mlxsw_sp->sb_vals->pms_cpu,
1105				    true);
1106}
1107
1108#define MLXSW_SP_SB_MM(_min_buff, _max_buff)		\
1109	{						\
1110		.min_buff = _min_buff,			\
1111		.max_buff = _max_buff,			\
1112		.pool_index = MLXSW_SP_SB_POOL_EGR,	\
1113	}
1114
1115static const struct mlxsw_sp_sb_mm mlxsw_sp_sb_mms[] = {
1116	MLXSW_SP_SB_MM(0, 6),
1117	MLXSW_SP_SB_MM(0, 6),
1118	MLXSW_SP_SB_MM(0, 6),
1119	MLXSW_SP_SB_MM(0, 6),
1120	MLXSW_SP_SB_MM(0, 6),
1121	MLXSW_SP_SB_MM(0, 6),
1122	MLXSW_SP_SB_MM(0, 6),
1123	MLXSW_SP_SB_MM(0, 6),
1124	MLXSW_SP_SB_MM(0, 6),
1125	MLXSW_SP_SB_MM(0, 6),
1126	MLXSW_SP_SB_MM(0, 6),
1127	MLXSW_SP_SB_MM(0, 6),
1128	MLXSW_SP_SB_MM(0, 6),
1129	MLXSW_SP_SB_MM(0, 6),
1130	MLXSW_SP_SB_MM(0, 6),
1131};
1132
1133static int mlxsw_sp_sb_mms_init(struct mlxsw_sp *mlxsw_sp)
1134{
1135	char sbmm_pl[MLXSW_REG_SBMM_LEN];
1136	int i;
1137	int err;
1138
1139	for (i = 0; i < mlxsw_sp->sb_vals->mms_count; i++) {
1140		const struct mlxsw_sp_sb_pool_des *des;
1141		const struct mlxsw_sp_sb_mm *mc;
1142		u32 min_buff;
1143
1144		mc = &mlxsw_sp->sb_vals->mms[i];
1145		des = &mlxsw_sp->sb_vals->pool_dess[mc->pool_index];
1146		/* All pools used by sb_mm's are initialized using dynamic
1147		 * thresholds, therefore 'max_buff' isn't specified in cells.
1148		 */
1149		min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, mc->min_buff);
1150		mlxsw_reg_sbmm_pack(sbmm_pl, i, min_buff, mc->max_buff,
1151				    des->pool);
1152		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbmm), sbmm_pl);
1153		if (err)
1154			return err;
1155	}
1156	return 0;
1157}
1158
1159static void mlxsw_sp_pool_count(struct mlxsw_sp *mlxsw_sp,
1160				u16 *p_ingress_len, u16 *p_egress_len)
1161{
1162	int i;
1163
1164	for (i = 0; i < mlxsw_sp->sb_vals->pool_count; ++i) {
1165		if (mlxsw_sp->sb_vals->pool_dess[i].dir ==
1166		    MLXSW_REG_SBXX_DIR_INGRESS)
1167			(*p_ingress_len)++;
1168		else
1169			(*p_egress_len)++;
1170	}
1171
1172	WARN(*p_egress_len == 0, "No egress pools\n");
1173}
1174
1175const struct mlxsw_sp_sb_vals mlxsw_sp1_sb_vals = {
1176	.pool_count = ARRAY_SIZE(mlxsw_sp1_sb_pool_dess),
1177	.pool_dess = mlxsw_sp1_sb_pool_dess,
1178	.pms = mlxsw_sp1_sb_pms,
1179	.pms_cpu = mlxsw_sp_cpu_port_sb_pms,
1180	.prs = mlxsw_sp1_sb_prs,
1181	.mms = mlxsw_sp_sb_mms,
1182	.cms_ingress = mlxsw_sp1_sb_cms_ingress,
1183	.cms_egress = mlxsw_sp1_sb_cms_egress,
1184	.cms_cpu = mlxsw_sp_cpu_port_sb_cms,
1185	.mms_count = ARRAY_SIZE(mlxsw_sp_sb_mms),
1186	.cms_ingress_count = ARRAY_SIZE(mlxsw_sp1_sb_cms_ingress),
1187	.cms_egress_count = ARRAY_SIZE(mlxsw_sp1_sb_cms_egress),
1188	.cms_cpu_count = ARRAY_SIZE(mlxsw_sp_cpu_port_sb_cms),
1189};
1190
1191const struct mlxsw_sp_sb_vals mlxsw_sp2_sb_vals = {
1192	.pool_count = ARRAY_SIZE(mlxsw_sp2_sb_pool_dess),
1193	.pool_dess = mlxsw_sp2_sb_pool_dess,
1194	.pms = mlxsw_sp2_sb_pms,
1195	.pms_cpu = mlxsw_sp_cpu_port_sb_pms,
1196	.prs = mlxsw_sp2_sb_prs,
1197	.mms = mlxsw_sp_sb_mms,
1198	.cms_ingress = mlxsw_sp2_sb_cms_ingress,
1199	.cms_egress = mlxsw_sp2_sb_cms_egress,
1200	.cms_cpu = mlxsw_sp_cpu_port_sb_cms,
1201	.mms_count = ARRAY_SIZE(mlxsw_sp_sb_mms),
1202	.cms_ingress_count = ARRAY_SIZE(mlxsw_sp2_sb_cms_ingress),
1203	.cms_egress_count = ARRAY_SIZE(mlxsw_sp2_sb_cms_egress),
1204	.cms_cpu_count = ARRAY_SIZE(mlxsw_sp_cpu_port_sb_cms),
1205};
1206
1207static u32 mlxsw_sp1_pb_int_buf_size_get(int mtu, u32 speed)
1208{
1209	return mtu * 5 / 2;
1210}
1211
1212static u32 __mlxsw_sp_pb_int_buf_size_get(int mtu, u32 speed, u32 buffer_factor)
1213{
1214	return 3 * mtu + buffer_factor * speed / 1000;
1215}
1216
1217#define MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR 38
1218
1219static u32 mlxsw_sp2_pb_int_buf_size_get(int mtu, u32 speed)
1220{
1221	int factor = MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR;
1222
1223	return __mlxsw_sp_pb_int_buf_size_get(mtu, speed, factor);
1224}
1225
1226#define MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR 50
1227
1228static u32 mlxsw_sp3_pb_int_buf_size_get(int mtu, u32 speed)
1229{
1230	int factor = MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR;
1231
1232	return __mlxsw_sp_pb_int_buf_size_get(mtu, speed, factor);
1233}
1234
1235const struct mlxsw_sp_sb_ops mlxsw_sp1_sb_ops = {
1236	.int_buf_size_get = mlxsw_sp1_pb_int_buf_size_get,
1237};
1238
1239const struct mlxsw_sp_sb_ops mlxsw_sp2_sb_ops = {
1240	.int_buf_size_get = mlxsw_sp2_pb_int_buf_size_get,
1241};
1242
1243const struct mlxsw_sp_sb_ops mlxsw_sp3_sb_ops = {
1244	.int_buf_size_get = mlxsw_sp3_pb_int_buf_size_get,
1245};
1246
1247int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp)
1248{
1249	u32 max_headroom_size;
1250	u16 ing_pool_count = 0;
1251	u16 eg_pool_count = 0;
1252	int err;
1253
1254	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, CELL_SIZE))
1255		return -EIO;
1256
1257	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, GUARANTEED_SHARED_BUFFER))
1258		return -EIO;
1259
1260	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_HEADROOM_SIZE))
1261		return -EIO;
1262
1263	mlxsw_sp->sb = kzalloc(sizeof(*mlxsw_sp->sb), GFP_KERNEL);
1264	if (!mlxsw_sp->sb)
1265		return -ENOMEM;
1266	mlxsw_sp->sb->cell_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, CELL_SIZE);
1267	mlxsw_sp->sb->sb_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
1268						   GUARANTEED_SHARED_BUFFER);
1269	max_headroom_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
1270					       MAX_HEADROOM_SIZE);
1271	/* Round down, because this limit must not be overstepped. */
1272	mlxsw_sp->sb->max_headroom_cells = max_headroom_size /
1273						mlxsw_sp->sb->cell_size;
1274
1275	err = mlxsw_sp_sb_ports_init(mlxsw_sp);
1276	if (err)
1277		goto err_sb_ports_init;
1278	err = mlxsw_sp_sb_prs_init(mlxsw_sp, mlxsw_sp->sb_vals->prs,
1279				   mlxsw_sp->sb_vals->pool_dess,
1280				   mlxsw_sp->sb_vals->pool_count);
1281	if (err)
1282		goto err_sb_prs_init;
1283	err = mlxsw_sp_cpu_port_sb_cms_init(mlxsw_sp);
1284	if (err)
1285		goto err_sb_cpu_port_sb_cms_init;
1286	err = mlxsw_sp_cpu_port_sb_pms_init(mlxsw_sp);
1287	if (err)
1288		goto err_sb_cpu_port_pms_init;
1289	err = mlxsw_sp_sb_mms_init(mlxsw_sp);
1290	if (err)
1291		goto err_sb_mms_init;
1292	mlxsw_sp_pool_count(mlxsw_sp, &ing_pool_count, &eg_pool_count);
1293	err = devl_sb_register(priv_to_devlink(mlxsw_sp->core), 0,
1294			       mlxsw_sp->sb->sb_size,
1295			       ing_pool_count,
1296			       eg_pool_count,
1297			       MLXSW_SP_SB_ING_TC_COUNT,
1298			       MLXSW_SP_SB_EG_TC_COUNT);
1299	if (err)
1300		goto err_devlink_sb_register;
1301
1302	return 0;
1303
1304err_devlink_sb_register:
1305err_sb_mms_init:
1306err_sb_cpu_port_pms_init:
1307err_sb_cpu_port_sb_cms_init:
1308err_sb_prs_init:
1309	mlxsw_sp_sb_ports_fini(mlxsw_sp);
1310err_sb_ports_init:
1311	kfree(mlxsw_sp->sb);
1312	return err;
1313}
1314
1315void mlxsw_sp_buffers_fini(struct mlxsw_sp *mlxsw_sp)
1316{
1317	devl_sb_unregister(priv_to_devlink(mlxsw_sp->core), 0);
1318	mlxsw_sp_sb_ports_fini(mlxsw_sp);
1319	kfree(mlxsw_sp->sb);
1320}
1321
1322int mlxsw_sp_port_buffers_init(struct mlxsw_sp_port *mlxsw_sp_port)
1323{
1324	int err;
1325
1326	mlxsw_sp_port->hdroom = kzalloc(sizeof(*mlxsw_sp_port->hdroom), GFP_KERNEL);
1327	if (!mlxsw_sp_port->hdroom)
1328		return -ENOMEM;
1329	mlxsw_sp_port->hdroom->mtu = mlxsw_sp_port->dev->mtu;
1330
1331	err = mlxsw_sp_port_headroom_init(mlxsw_sp_port);
1332	if (err)
1333		goto err_headroom_init;
1334	err = mlxsw_sp_port_sb_cms_init(mlxsw_sp_port);
1335	if (err)
1336		goto err_port_sb_cms_init;
1337	err = mlxsw_sp_port_sb_pms_init(mlxsw_sp_port);
1338	if (err)
1339		goto err_port_sb_pms_init;
1340	return 0;
1341
1342err_port_sb_pms_init:
1343err_port_sb_cms_init:
1344err_headroom_init:
1345	kfree(mlxsw_sp_port->hdroom);
1346	return err;
1347}
1348
1349void mlxsw_sp_port_buffers_fini(struct mlxsw_sp_port *mlxsw_sp_port)
1350{
1351	kfree(mlxsw_sp_port->hdroom);
1352}
1353
1354int mlxsw_sp_sb_pool_get(struct mlxsw_core *mlxsw_core,
1355			 unsigned int sb_index, u16 pool_index,
1356			 struct devlink_sb_pool_info *pool_info)
1357{
1358	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1359	enum mlxsw_reg_sbxx_dir dir;
1360	struct mlxsw_sp_sb_pr *pr;
1361
1362	dir = mlxsw_sp->sb_vals->pool_dess[pool_index].dir;
1363	pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index);
1364	pool_info->pool_type = (enum devlink_sb_pool_type) dir;
1365	pool_info->size = mlxsw_sp_cells_bytes(mlxsw_sp, pr->size);
1366	pool_info->threshold_type = (enum devlink_sb_threshold_type) pr->mode;
1367	pool_info->cell_size = mlxsw_sp->sb->cell_size;
1368	return 0;
1369}
1370
1371int mlxsw_sp_sb_pool_set(struct mlxsw_core *mlxsw_core,
1372			 unsigned int sb_index, u16 pool_index, u32 size,
1373			 enum devlink_sb_threshold_type threshold_type,
1374			 struct netlink_ext_ack *extack)
1375{
1376	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1377	u32 pool_size = mlxsw_sp_bytes_cells(mlxsw_sp, size);
1378	const struct mlxsw_sp_sb_pr *pr;
1379	enum mlxsw_reg_sbpr_mode mode;
1380
1381	mode = (enum mlxsw_reg_sbpr_mode) threshold_type;
1382	pr = &mlxsw_sp->sb_vals->prs[pool_index];
1383
1384	if (size > MLXSW_CORE_RES_GET(mlxsw_sp->core,
1385				      GUARANTEED_SHARED_BUFFER)) {
1386		NL_SET_ERR_MSG_MOD(extack, "Exceeded shared buffer size");
1387		return -EINVAL;
1388	}
1389
1390	if (pr->freeze_mode && pr->mode != mode) {
1391		NL_SET_ERR_MSG_MOD(extack, "Changing this pool's threshold type is forbidden");
1392		return -EINVAL;
1393	}
1394
1395	if (pr->freeze_size && pr->size != size) {
1396		NL_SET_ERR_MSG_MOD(extack, "Changing this pool's size is forbidden");
1397		return -EINVAL;
1398	}
1399
1400	return mlxsw_sp_sb_pr_write(mlxsw_sp, pool_index, mode,
1401				    pool_size, false);
1402}
1403
1404#define MLXSW_SP_SB_THRESHOLD_TO_ALPHA_OFFSET (-2) /* 3->1, 16->14 */
1405
1406static u32 mlxsw_sp_sb_threshold_out(struct mlxsw_sp *mlxsw_sp, u16 pool_index,
1407				     u32 max_buff)
1408{
1409	struct mlxsw_sp_sb_pr *pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index);
1410
1411	if (pr->mode == MLXSW_REG_SBPR_MODE_DYNAMIC)
1412		return max_buff - MLXSW_SP_SB_THRESHOLD_TO_ALPHA_OFFSET;
1413	return mlxsw_sp_cells_bytes(mlxsw_sp, max_buff);
1414}
1415
1416static int mlxsw_sp_sb_threshold_in(struct mlxsw_sp *mlxsw_sp, u16 pool_index,
1417				    u32 threshold, u32 *p_max_buff,
1418				    struct netlink_ext_ack *extack)
1419{
1420	struct mlxsw_sp_sb_pr *pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool_index);
1421
1422	if (pr->mode == MLXSW_REG_SBPR_MODE_DYNAMIC) {
1423		int val;
1424
1425		val = threshold + MLXSW_SP_SB_THRESHOLD_TO_ALPHA_OFFSET;
1426		if (val < MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN ||
1427		    val > MLXSW_REG_SBXX_DYN_MAX_BUFF_MAX) {
1428			NL_SET_ERR_MSG_MOD(extack, "Invalid dynamic threshold value");
1429			return -EINVAL;
1430		}
1431		*p_max_buff = val;
1432	} else {
1433		*p_max_buff = mlxsw_sp_bytes_cells(mlxsw_sp, threshold);
1434	}
1435	return 0;
1436}
1437
1438int mlxsw_sp_sb_port_pool_get(struct mlxsw_core_port *mlxsw_core_port,
1439			      unsigned int sb_index, u16 pool_index,
1440			      u32 *p_threshold)
1441{
1442	struct mlxsw_sp_port *mlxsw_sp_port =
1443			mlxsw_core_port_driver_priv(mlxsw_core_port);
1444	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1445	u16 local_port = mlxsw_sp_port->local_port;
1446	struct mlxsw_sp_sb_pm *pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port,
1447						       pool_index);
1448
1449	*p_threshold = mlxsw_sp_sb_threshold_out(mlxsw_sp, pool_index,
1450						 pm->max_buff);
1451	return 0;
1452}
1453
1454int mlxsw_sp_sb_port_pool_set(struct mlxsw_core_port *mlxsw_core_port,
1455			      unsigned int sb_index, u16 pool_index,
1456			      u32 threshold, struct netlink_ext_ack *extack)
1457{
1458	struct mlxsw_sp_port *mlxsw_sp_port =
1459			mlxsw_core_port_driver_priv(mlxsw_core_port);
1460	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1461	u16 local_port = mlxsw_sp_port->local_port;
1462	u32 max_buff;
1463	int err;
1464
1465	if (local_port == MLXSW_PORT_CPU_PORT) {
1466		NL_SET_ERR_MSG_MOD(extack, "Changing CPU port's threshold is forbidden");
1467		return -EINVAL;
1468	}
1469
1470	err = mlxsw_sp_sb_threshold_in(mlxsw_sp, pool_index,
1471				       threshold, &max_buff, extack);
1472	if (err)
1473		return err;
1474
1475	return mlxsw_sp_sb_pm_write(mlxsw_sp, local_port, pool_index,
1476				    0, max_buff);
1477}
1478
1479int mlxsw_sp_sb_tc_pool_bind_get(struct mlxsw_core_port *mlxsw_core_port,
1480				 unsigned int sb_index, u16 tc_index,
1481				 enum devlink_sb_pool_type pool_type,
1482				 u16 *p_pool_index, u32 *p_threshold)
1483{
1484	struct mlxsw_sp_port *mlxsw_sp_port =
1485			mlxsw_core_port_driver_priv(mlxsw_core_port);
1486	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1487	u16 local_port = mlxsw_sp_port->local_port;
1488	u8 pg_buff = tc_index;
1489	enum mlxsw_reg_sbxx_dir dir = (enum mlxsw_reg_sbxx_dir) pool_type;
1490	struct mlxsw_sp_sb_cm *cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port,
1491						       pg_buff, dir);
1492
1493	*p_threshold = mlxsw_sp_sb_threshold_out(mlxsw_sp, cm->pool_index,
1494						 cm->max_buff);
1495	*p_pool_index = cm->pool_index;
1496	return 0;
1497}
1498
1499int mlxsw_sp_sb_tc_pool_bind_set(struct mlxsw_core_port *mlxsw_core_port,
1500				 unsigned int sb_index, u16 tc_index,
1501				 enum devlink_sb_pool_type pool_type,
1502				 u16 pool_index, u32 threshold,
1503				 struct netlink_ext_ack *extack)
1504{
1505	struct mlxsw_sp_port *mlxsw_sp_port =
1506			mlxsw_core_port_driver_priv(mlxsw_core_port);
1507	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1508	u16 local_port = mlxsw_sp_port->local_port;
1509	const struct mlxsw_sp_sb_cm *cm;
1510	u8 pg_buff = tc_index;
1511	enum mlxsw_reg_sbxx_dir dir = (enum mlxsw_reg_sbxx_dir) pool_type;
1512	u32 max_buff;
1513	int err;
1514
1515	if (local_port == MLXSW_PORT_CPU_PORT) {
1516		NL_SET_ERR_MSG_MOD(extack, "Changing CPU port's binding is forbidden");
1517		return -EINVAL;
1518	}
1519
1520	if (dir != mlxsw_sp->sb_vals->pool_dess[pool_index].dir) {
1521		NL_SET_ERR_MSG_MOD(extack, "Binding egress TC to ingress pool and vice versa is forbidden");
1522		return -EINVAL;
1523	}
1524
1525	if (dir == MLXSW_REG_SBXX_DIR_INGRESS)
1526		cm = &mlxsw_sp->sb_vals->cms_ingress[tc_index];
1527	else
1528		cm = &mlxsw_sp->sb_vals->cms_egress[tc_index];
1529
1530	if (cm->freeze_pool && cm->pool_index != pool_index) {
1531		NL_SET_ERR_MSG_MOD(extack, "Binding this TC to a different pool is forbidden");
1532		return -EINVAL;
1533	}
1534
1535	if (cm->freeze_thresh && cm->max_buff != threshold) {
1536		NL_SET_ERR_MSG_MOD(extack, "Changing this TC's threshold is forbidden");
1537		return -EINVAL;
1538	}
1539
1540	err = mlxsw_sp_sb_threshold_in(mlxsw_sp, pool_index,
1541				       threshold, &max_buff, extack);
1542	if (err)
1543		return err;
1544
1545	return mlxsw_sp_sb_cm_write(mlxsw_sp, local_port, pg_buff,
1546				    0, max_buff, false, pool_index);
1547}
1548
1549#define MASKED_COUNT_MAX \
1550	(MLXSW_REG_SBSR_REC_MAX_COUNT / \
1551	 (MLXSW_SP_SB_ING_TC_COUNT + MLXSW_SP_SB_EG_TC_COUNT))
1552
1553struct mlxsw_sp_sb_sr_occ_query_cb_ctx {
1554	u8 masked_count;
1555	u16 local_port_1;
1556};
1557
1558static void mlxsw_sp_sb_sr_occ_query_cb(struct mlxsw_core *mlxsw_core,
1559					char *sbsr_pl, size_t sbsr_pl_len,
1560					unsigned long cb_priv)
1561{
1562	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1563	struct mlxsw_sp_sb_sr_occ_query_cb_ctx cb_ctx;
1564	u8 masked_count;
1565	u16 local_port;
1566	int rec_index = 0;
1567	struct mlxsw_sp_sb_cm *cm;
1568	int i;
1569
1570	memcpy(&cb_ctx, &cb_priv, sizeof(cb_ctx));
1571
1572	masked_count = 0;
1573	for (local_port = cb_ctx.local_port_1;
1574	     local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) {
1575		if (!mlxsw_sp->ports[local_port])
1576			continue;
1577		if (local_port == MLXSW_PORT_CPU_PORT) {
1578			/* Ingress quotas are not supported for the CPU port */
1579			masked_count++;
1580			continue;
1581		}
1582		for (i = 0; i < MLXSW_SP_SB_ING_TC_COUNT; i++) {
1583			cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, i,
1584						MLXSW_REG_SBXX_DIR_INGRESS);
1585			mlxsw_reg_sbsr_rec_unpack(sbsr_pl, rec_index++,
1586						  &cm->occ.cur, &cm->occ.max);
1587		}
1588		if (++masked_count == cb_ctx.masked_count)
1589			break;
1590	}
1591	masked_count = 0;
1592	for (local_port = cb_ctx.local_port_1;
1593	     local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) {
1594		if (!mlxsw_sp->ports[local_port])
1595			continue;
1596		for (i = 0; i < MLXSW_SP_SB_EG_TC_COUNT; i++) {
1597			cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, i,
1598						MLXSW_REG_SBXX_DIR_EGRESS);
1599			mlxsw_reg_sbsr_rec_unpack(sbsr_pl, rec_index++,
1600						  &cm->occ.cur, &cm->occ.max);
1601		}
1602		if (++masked_count == cb_ctx.masked_count)
1603			break;
1604	}
1605}
1606
1607int mlxsw_sp_sb_occ_snapshot(struct mlxsw_core *mlxsw_core,
1608			     unsigned int sb_index)
1609{
1610	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1611	u16 local_port, local_port_1, last_local_port;
1612	struct mlxsw_sp_sb_sr_occ_query_cb_ctx cb_ctx;
1613	u8 masked_count, current_page = 0;
1614	unsigned long cb_priv = 0;
1615	LIST_HEAD(bulk_list);
1616	char *sbsr_pl;
1617	int i;
1618	int err;
1619	int err2;
1620
1621	sbsr_pl = kmalloc(MLXSW_REG_SBSR_LEN, GFP_KERNEL);
1622	if (!sbsr_pl)
1623		return -ENOMEM;
1624
1625	local_port = MLXSW_PORT_CPU_PORT;
1626next_batch:
1627	local_port_1 = local_port;
1628	masked_count = 0;
1629	mlxsw_reg_sbsr_pack(sbsr_pl, false);
1630	mlxsw_reg_sbsr_port_page_set(sbsr_pl, current_page);
1631	last_local_port = current_page * MLXSW_REG_SBSR_NUM_PORTS_IN_PAGE +
1632			  MLXSW_REG_SBSR_NUM_PORTS_IN_PAGE - 1;
1633
1634	for (i = 0; i < MLXSW_SP_SB_ING_TC_COUNT; i++)
1635		mlxsw_reg_sbsr_pg_buff_mask_set(sbsr_pl, i, 1);
1636	for (i = 0; i < MLXSW_SP_SB_EG_TC_COUNT; i++)
1637		mlxsw_reg_sbsr_tclass_mask_set(sbsr_pl, i, 1);
1638	for (; local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) {
1639		if (!mlxsw_sp->ports[local_port])
1640			continue;
1641		if (local_port > last_local_port) {
1642			current_page++;
1643			goto do_query;
1644		}
1645		if (local_port != MLXSW_PORT_CPU_PORT) {
1646			/* Ingress quotas are not supported for the CPU port */
1647			mlxsw_reg_sbsr_ingress_port_mask_set(sbsr_pl,
1648							     local_port, 1);
1649		}
1650		mlxsw_reg_sbsr_egress_port_mask_set(sbsr_pl, local_port, 1);
1651		for (i = 0; i < mlxsw_sp->sb_vals->pool_count; i++) {
1652			err = mlxsw_sp_sb_pm_occ_query(mlxsw_sp, local_port, i,
1653						       &bulk_list);
1654			if (err)
1655				goto out;
1656		}
1657		if (++masked_count == MASKED_COUNT_MAX)
1658			goto do_query;
1659	}
1660
1661do_query:
1662	cb_ctx.masked_count = masked_count;
1663	cb_ctx.local_port_1 = local_port_1;
1664	memcpy(&cb_priv, &cb_ctx, sizeof(cb_ctx));
1665	err = mlxsw_reg_trans_query(mlxsw_core, MLXSW_REG(sbsr), sbsr_pl,
1666				    &bulk_list, mlxsw_sp_sb_sr_occ_query_cb,
1667				    cb_priv);
1668	if (err)
1669		goto out;
1670	if (local_port < mlxsw_core_max_ports(mlxsw_core)) {
1671		local_port++;
1672		goto next_batch;
1673	}
1674
1675out:
1676	err2 = mlxsw_reg_trans_bulk_wait(&bulk_list);
1677	if (!err)
1678		err = err2;
1679	kfree(sbsr_pl);
1680	return err;
1681}
1682
1683int mlxsw_sp_sb_occ_max_clear(struct mlxsw_core *mlxsw_core,
1684			      unsigned int sb_index)
1685{
1686	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1687	u16 local_port, last_local_port;
1688	LIST_HEAD(bulk_list);
1689	unsigned int masked_count;
1690	u8 current_page = 0;
1691	char *sbsr_pl;
1692	int i;
1693	int err;
1694	int err2;
1695
1696	sbsr_pl = kmalloc(MLXSW_REG_SBSR_LEN, GFP_KERNEL);
1697	if (!sbsr_pl)
1698		return -ENOMEM;
1699
1700	local_port = MLXSW_PORT_CPU_PORT;
1701next_batch:
1702	masked_count = 0;
1703	mlxsw_reg_sbsr_pack(sbsr_pl, true);
1704	mlxsw_reg_sbsr_port_page_set(sbsr_pl, current_page);
1705	last_local_port = current_page * MLXSW_REG_SBSR_NUM_PORTS_IN_PAGE +
1706			  MLXSW_REG_SBSR_NUM_PORTS_IN_PAGE - 1;
1707
1708	for (i = 0; i < MLXSW_SP_SB_ING_TC_COUNT; i++)
1709		mlxsw_reg_sbsr_pg_buff_mask_set(sbsr_pl, i, 1);
1710	for (i = 0; i < MLXSW_SP_SB_EG_TC_COUNT; i++)
1711		mlxsw_reg_sbsr_tclass_mask_set(sbsr_pl, i, 1);
1712	for (; local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) {
1713		if (!mlxsw_sp->ports[local_port])
1714			continue;
1715		if (local_port > last_local_port) {
1716			current_page++;
1717			goto do_query;
1718		}
1719		if (local_port != MLXSW_PORT_CPU_PORT) {
1720			/* Ingress quotas are not supported for the CPU port */
1721			mlxsw_reg_sbsr_ingress_port_mask_set(sbsr_pl,
1722							     local_port, 1);
1723		}
1724		mlxsw_reg_sbsr_egress_port_mask_set(sbsr_pl, local_port, 1);
1725		for (i = 0; i < mlxsw_sp->sb_vals->pool_count; i++) {
1726			err = mlxsw_sp_sb_pm_occ_clear(mlxsw_sp, local_port, i,
1727						       &bulk_list);
1728			if (err)
1729				goto out;
1730		}
1731		if (++masked_count == MASKED_COUNT_MAX)
1732			goto do_query;
1733	}
1734
1735do_query:
1736	err = mlxsw_reg_trans_query(mlxsw_core, MLXSW_REG(sbsr), sbsr_pl,
1737				    &bulk_list, NULL, 0);
1738	if (err)
1739		goto out;
1740	if (local_port < mlxsw_core_max_ports(mlxsw_core)) {
1741		local_port++;
1742		goto next_batch;
1743	}
1744
1745out:
1746	err2 = mlxsw_reg_trans_bulk_wait(&bulk_list);
1747	if (!err)
1748		err = err2;
1749	kfree(sbsr_pl);
1750	return err;
1751}
1752
1753int mlxsw_sp_sb_occ_port_pool_get(struct mlxsw_core_port *mlxsw_core_port,
1754				  unsigned int sb_index, u16 pool_index,
1755				  u32 *p_cur, u32 *p_max)
1756{
1757	struct mlxsw_sp_port *mlxsw_sp_port =
1758			mlxsw_core_port_driver_priv(mlxsw_core_port);
1759	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1760	u16 local_port = mlxsw_sp_port->local_port;
1761	struct mlxsw_sp_sb_pm *pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port,
1762						       pool_index);
1763
1764	*p_cur = mlxsw_sp_cells_bytes(mlxsw_sp, pm->occ.cur);
1765	*p_max = mlxsw_sp_cells_bytes(mlxsw_sp, pm->occ.max);
1766	return 0;
1767}
1768
1769int mlxsw_sp_sb_occ_tc_port_bind_get(struct mlxsw_core_port *mlxsw_core_port,
1770				     unsigned int sb_index, u16 tc_index,
1771				     enum devlink_sb_pool_type pool_type,
1772				     u32 *p_cur, u32 *p_max)
1773{
1774	struct mlxsw_sp_port *mlxsw_sp_port =
1775			mlxsw_core_port_driver_priv(mlxsw_core_port);
1776	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1777	u16 local_port = mlxsw_sp_port->local_port;
1778	u8 pg_buff = tc_index;
1779	enum mlxsw_reg_sbxx_dir dir = (enum mlxsw_reg_sbxx_dir) pool_type;
1780	struct mlxsw_sp_sb_cm *cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port,
1781						       pg_buff, dir);
1782
1783	*p_cur = mlxsw_sp_cells_bytes(mlxsw_sp, cm->occ.cur);
1784	*p_max = mlxsw_sp_cells_bytes(mlxsw_sp, cm->occ.max);
1785	return 0;
1786}
1787