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