Merge tag 'drm-next-2020-04-03-1' of git://anongit.freedesktop.org/drm/drm
[linux-2.6-microblaze.git] / drivers / net / ethernet / mscc / ocelot_police.c
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /* Microsemi Ocelot Switch driver
3  *
4  * Copyright (c) 2019 Microsemi Corporation
5  */
6
7 #include <soc/mscc/ocelot.h>
8 #include "ocelot_police.h"
9
10 enum mscc_qos_rate_mode {
11         MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */
12         MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */
13         MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */
14         MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */
15         __MSCC_QOS_RATE_MODE_END,
16         NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END,
17         MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1,
18 };
19
20 /* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */
21 #define POL_MODE_LINERATE   0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */
22 #define POL_MODE_DATARATE   1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes  */
23 #define POL_MODE_FRMRATE_HI 2 /* Unit: 33 1/3 fps, 32.8 frames */
24 #define POL_MODE_FRMRATE_LO 3 /* Unit: 1/3 fps, 0.3 frames */
25
26 /* Policer indexes */
27 #define POL_IX_PORT    0    /* 0-11    : Port policers */
28 #define POL_IX_QUEUE   32   /* 32-127  : Queue policers  */
29
30 /* Default policer order */
31 #define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */
32
33 struct qos_policer_conf {
34         enum mscc_qos_rate_mode mode;
35         bool dlb; /* Enable DLB (dual leaky bucket mode */
36         bool cf;  /* Coupling flag (ignored in SLB mode) */
37         u32  cir; /* CIR in kbps/fps (ignored in SLB mode) */
38         u32  cbs; /* CBS in bytes/frames (ignored in SLB mode) */
39         u32  pir; /* PIR in kbps/fps */
40         u32  pbs; /* PBS in bytes/frames */
41         u8   ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */
42 };
43
44 static int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix,
45                                 struct qos_policer_conf *conf)
46 {
47         u32 cf = 0, cir_ena = 0, frm_mode = POL_MODE_LINERATE;
48         u32 cir = 0, cbs = 0, pir = 0, pbs = 0;
49         bool cir_discard = 0, pir_discard = 0;
50         u32 pbs_max = 0, cbs_max = 0;
51         u8 ipg = 20;
52         u32 value;
53
54         pir = conf->pir;
55         pbs = conf->pbs;
56
57         switch (conf->mode) {
58         case MSCC_QOS_RATE_MODE_LINE:
59         case MSCC_QOS_RATE_MODE_DATA:
60                 if (conf->mode == MSCC_QOS_RATE_MODE_LINE) {
61                         frm_mode = POL_MODE_LINERATE;
62                         ipg = min_t(u8, GENMASK(4, 0), conf->ipg);
63                 } else {
64                         frm_mode = POL_MODE_DATARATE;
65                 }
66                 if (conf->dlb) {
67                         cir_ena = 1;
68                         cir = conf->cir;
69                         cbs = conf->cbs;
70                         if (cir == 0 && cbs == 0) {
71                                 /* Discard cir frames */
72                                 cir_discard = 1;
73                         } else {
74                                 cir = DIV_ROUND_UP(cir, 100);
75                                 cir *= 3; /* 33 1/3 kbps */
76                                 cbs = DIV_ROUND_UP(cbs, 4096);
77                                 cbs = (cbs ? cbs : 1); /* No zero burst size */
78                                 cbs_max = 60; /* Limit burst size */
79                                 cf = conf->cf;
80                                 if (cf)
81                                         pir += conf->cir;
82                         }
83                 }
84                 if (pir == 0 && pbs == 0) {
85                         /* Discard PIR frames */
86                         pir_discard = 1;
87                 } else {
88                         pir = DIV_ROUND_UP(pir, 100);
89                         pir *= 3;  /* 33 1/3 kbps */
90                         pbs = DIV_ROUND_UP(pbs, 4096);
91                         pbs = (pbs ? pbs : 1); /* No zero burst size */
92                         pbs_max = 60; /* Limit burst size */
93                 }
94                 break;
95         case MSCC_QOS_RATE_MODE_FRAME:
96                 if (pir >= 100) {
97                         frm_mode = POL_MODE_FRMRATE_HI;
98                         pir = DIV_ROUND_UP(pir, 100);
99                         pir *= 3;  /* 33 1/3 fps */
100                         pbs = (pbs * 10) / 328; /* 32.8 frames */
101                         pbs = (pbs ? pbs : 1); /* No zero burst size */
102                         pbs_max = GENMASK(6, 0); /* Limit burst size */
103                 } else {
104                         frm_mode = POL_MODE_FRMRATE_LO;
105                         if (pir == 0 && pbs == 0) {
106                                 /* Discard all frames */
107                                 pir_discard = 1;
108                                 cir_discard = 1;
109                         } else {
110                                 pir *= 3; /* 1/3 fps */
111                                 pbs = (pbs * 10) / 3; /* 0.3 frames */
112                                 pbs = (pbs ? pbs : 1); /* No zero burst size */
113                                 pbs_max = 61; /* Limit burst size */
114                         }
115                 }
116                 break;
117         default: /* MSCC_QOS_RATE_MODE_DISABLED */
118                 /* Disable policer using maximum rate and zero burst */
119                 pir = GENMASK(15, 0);
120                 pbs = 0;
121                 break;
122         }
123
124         /* Check limits */
125         if (pir > GENMASK(15, 0)) {
126                 dev_err(ocelot->dev, "Invalid pir for port %d: %u (max %lu)\n",
127                         port, pir, GENMASK(15, 0));
128                 return -EINVAL;
129         }
130
131         if (cir > GENMASK(15, 0)) {
132                 dev_err(ocelot->dev, "Invalid cir for port %d: %u (max %lu)\n",
133                         port, cir, GENMASK(15, 0));
134                 return -EINVAL;
135         }
136
137         if (pbs > pbs_max) {
138                 dev_err(ocelot->dev, "Invalid pbs for port %d: %u (max %u)\n",
139                         port, pbs, pbs_max);
140                 return -EINVAL;
141         }
142
143         if (cbs > cbs_max) {
144                 dev_err(ocelot->dev, "Invalid cbs for port %d: %u (max %u)\n",
145                         port, cbs, cbs_max);
146                 return -EINVAL;
147         }
148
149         value = (ANA_POL_MODE_CFG_IPG_SIZE(ipg) |
150                  ANA_POL_MODE_CFG_FRM_MODE(frm_mode) |
151                  (cf ? ANA_POL_MODE_CFG_DLB_COUPLED : 0) |
152                  (cir_ena ? ANA_POL_MODE_CFG_CIR_ENA : 0) |
153                  ANA_POL_MODE_CFG_OVERSHOOT_ENA);
154
155         ocelot_write_gix(ocelot, value, ANA_POL_MODE_CFG, pol_ix);
156
157         ocelot_write_gix(ocelot,
158                          ANA_POL_PIR_CFG_PIR_RATE(pir) |
159                          ANA_POL_PIR_CFG_PIR_BURST(pbs),
160                          ANA_POL_PIR_CFG, pol_ix);
161
162         ocelot_write_gix(ocelot,
163                          (pir_discard ? GENMASK(22, 0) : 0),
164                          ANA_POL_PIR_STATE, pol_ix);
165
166         ocelot_write_gix(ocelot,
167                          ANA_POL_CIR_CFG_CIR_RATE(cir) |
168                          ANA_POL_CIR_CFG_CIR_BURST(cbs),
169                          ANA_POL_CIR_CFG, pol_ix);
170
171         ocelot_write_gix(ocelot,
172                          (cir_discard ? GENMASK(22, 0) : 0),
173                          ANA_POL_CIR_STATE, pol_ix);
174
175         return 0;
176 }
177
178 int ocelot_port_policer_add(struct ocelot *ocelot, int port,
179                             struct ocelot_policer *pol)
180 {
181         struct qos_policer_conf pp = { 0 };
182         int err;
183
184         if (!pol)
185                 return -EINVAL;
186
187         pp.mode = MSCC_QOS_RATE_MODE_DATA;
188         pp.pir = pol->rate;
189         pp.pbs = pol->burst;
190
191         dev_dbg(ocelot->dev, "%s: port %u pir %u kbps, pbs %u bytes\n",
192                 __func__, port, pp.pir, pp.pbs);
193
194         err = qos_policer_conf_set(ocelot, port, POL_IX_PORT + port, &pp);
195         if (err)
196                 return err;
197
198         ocelot_rmw_gix(ocelot,
199                        ANA_PORT_POL_CFG_PORT_POL_ENA |
200                        ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
201                        ANA_PORT_POL_CFG_PORT_POL_ENA |
202                        ANA_PORT_POL_CFG_POL_ORDER_M,
203                        ANA_PORT_POL_CFG, port);
204
205         return 0;
206 }
207 EXPORT_SYMBOL(ocelot_port_policer_add);
208
209 int ocelot_port_policer_del(struct ocelot *ocelot, int port)
210 {
211         struct qos_policer_conf pp = { 0 };
212         int err;
213
214         dev_dbg(ocelot->dev, "%s: port %u\n", __func__, port);
215
216         pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
217
218         err = qos_policer_conf_set(ocelot, port, POL_IX_PORT + port, &pp);
219         if (err)
220                 return err;
221
222         ocelot_rmw_gix(ocelot,
223                        ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
224                        ANA_PORT_POL_CFG_PORT_POL_ENA |
225                        ANA_PORT_POL_CFG_POL_ORDER_M,
226                        ANA_PORT_POL_CFG, port);
227
228         return 0;
229 }
230 EXPORT_SYMBOL(ocelot_port_policer_del);
231
232 int ocelot_ace_policer_add(struct ocelot *ocelot, u32 pol_ix,
233                            struct ocelot_policer *pol)
234 {
235         struct qos_policer_conf pp = { 0 };
236
237         if (!pol)
238                 return -EINVAL;
239
240         pp.mode = MSCC_QOS_RATE_MODE_DATA;
241         pp.pir = pol->rate;
242         pp.pbs = pol->burst;
243
244         return qos_policer_conf_set(ocelot, 0, pol_ix, &pp);
245 }
246
247 int ocelot_ace_policer_del(struct ocelot *ocelot, u32 pol_ix)
248 {
249         struct qos_policer_conf pp = { 0 };
250
251         pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
252
253         return qos_policer_conf_set(ocelot, 0, pol_ix, &pp);
254 }