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