]> Git Repo - J-linux.git/blob - drivers/net/ethernet/mscc/ocelot_police.c
Merge remote-tracking branch 'spi/for-5.14' into spi-linus
[J-linux.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 /* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */
11 #define POL_MODE_LINERATE   0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */
12 #define POL_MODE_DATARATE   1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes  */
13 #define POL_MODE_FRMRATE_HI 2 /* Unit: 33 1/3 fps, 32.8 frames */
14 #define POL_MODE_FRMRATE_LO 3 /* Unit: 1/3 fps, 0.3 frames */
15
16 /* Policer indexes */
17 #define POL_IX_PORT    0    /* 0-11    : Port policers */
18 #define POL_IX_QUEUE   32   /* 32-127  : Queue policers  */
19
20 /* Default policer order */
21 #define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */
22
23 int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix,
24                          struct qos_policer_conf *conf)
25 {
26         u32 cf = 0, cir_ena = 0, frm_mode = POL_MODE_LINERATE;
27         u32 cir = 0, cbs = 0, pir = 0, pbs = 0;
28         bool cir_discard = 0, pir_discard = 0;
29         u32 pbs_max = 0, cbs_max = 0;
30         u8 ipg = 20;
31         u32 value;
32
33         pir = conf->pir;
34         pbs = conf->pbs;
35
36         switch (conf->mode) {
37         case MSCC_QOS_RATE_MODE_LINE:
38         case MSCC_QOS_RATE_MODE_DATA:
39                 if (conf->mode == MSCC_QOS_RATE_MODE_LINE) {
40                         frm_mode = POL_MODE_LINERATE;
41                         ipg = min_t(u8, GENMASK(4, 0), conf->ipg);
42                 } else {
43                         frm_mode = POL_MODE_DATARATE;
44                 }
45                 if (conf->dlb) {
46                         cir_ena = 1;
47                         cir = conf->cir;
48                         cbs = conf->cbs;
49                         if (cir == 0 && cbs == 0) {
50                                 /* Discard cir frames */
51                                 cir_discard = 1;
52                         } else {
53                                 cir = DIV_ROUND_UP(cir, 100);
54                                 cir *= 3; /* 33 1/3 kbps */
55                                 cbs = DIV_ROUND_UP(cbs, 4096);
56                                 cbs = (cbs ? cbs : 1); /* No zero burst size */
57                                 cbs_max = 60; /* Limit burst size */
58                                 cf = conf->cf;
59                                 if (cf)
60                                         pir += conf->cir;
61                         }
62                 }
63                 if (pir == 0 && pbs == 0) {
64                         /* Discard PIR frames */
65                         pir_discard = 1;
66                 } else {
67                         pir = DIV_ROUND_UP(pir, 100);
68                         pir *= 3;  /* 33 1/3 kbps */
69                         pbs = DIV_ROUND_UP(pbs, 4096);
70                         pbs = (pbs ? pbs : 1); /* No zero burst size */
71                         pbs_max = 60; /* Limit burst size */
72                 }
73                 break;
74         case MSCC_QOS_RATE_MODE_FRAME:
75                 if (pir >= 100) {
76                         frm_mode = POL_MODE_FRMRATE_HI;
77                         pir = DIV_ROUND_UP(pir, 100);
78                         pir *= 3;  /* 33 1/3 fps */
79                         pbs = (pbs * 10) / 328; /* 32.8 frames */
80                         pbs = (pbs ? pbs : 1); /* No zero burst size */
81                         pbs_max = GENMASK(6, 0); /* Limit burst size */
82                 } else {
83                         frm_mode = POL_MODE_FRMRATE_LO;
84                         if (pir == 0 && pbs == 0) {
85                                 /* Discard all frames */
86                                 pir_discard = 1;
87                                 cir_discard = 1;
88                         } else {
89                                 pir *= 3; /* 1/3 fps */
90                                 pbs = (pbs * 10) / 3; /* 0.3 frames */
91                                 pbs = (pbs ? pbs : 1); /* No zero burst size */
92                                 pbs_max = 61; /* Limit burst size */
93                         }
94                 }
95                 break;
96         default: /* MSCC_QOS_RATE_MODE_DISABLED */
97                 /* Disable policer using maximum rate and zero burst */
98                 pir = GENMASK(15, 0);
99                 pbs = 0;
100                 break;
101         }
102
103         /* Check limits */
104         if (pir > GENMASK(15, 0)) {
105                 dev_err(ocelot->dev, "Invalid pir for port %d: %u (max %lu)\n",
106                         port, pir, GENMASK(15, 0));
107                 return -EINVAL;
108         }
109
110         if (cir > GENMASK(15, 0)) {
111                 dev_err(ocelot->dev, "Invalid cir for port %d: %u (max %lu)\n",
112                         port, cir, GENMASK(15, 0));
113                 return -EINVAL;
114         }
115
116         if (pbs > pbs_max) {
117                 dev_err(ocelot->dev, "Invalid pbs for port %d: %u (max %u)\n",
118                         port, pbs, pbs_max);
119                 return -EINVAL;
120         }
121
122         if (cbs > cbs_max) {
123                 dev_err(ocelot->dev, "Invalid cbs for port %d: %u (max %u)\n",
124                         port, cbs, cbs_max);
125                 return -EINVAL;
126         }
127
128         value = (ANA_POL_MODE_CFG_IPG_SIZE(ipg) |
129                  ANA_POL_MODE_CFG_FRM_MODE(frm_mode) |
130                  (cf ? ANA_POL_MODE_CFG_DLB_COUPLED : 0) |
131                  (cir_ena ? ANA_POL_MODE_CFG_CIR_ENA : 0) |
132                  ANA_POL_MODE_CFG_OVERSHOOT_ENA);
133
134         ocelot_write_gix(ocelot, value, ANA_POL_MODE_CFG, pol_ix);
135
136         ocelot_write_gix(ocelot,
137                          ANA_POL_PIR_CFG_PIR_RATE(pir) |
138                          ANA_POL_PIR_CFG_PIR_BURST(pbs),
139                          ANA_POL_PIR_CFG, pol_ix);
140
141         ocelot_write_gix(ocelot,
142                          (pir_discard ? GENMASK(22, 0) : 0),
143                          ANA_POL_PIR_STATE, pol_ix);
144
145         ocelot_write_gix(ocelot,
146                          ANA_POL_CIR_CFG_CIR_RATE(cir) |
147                          ANA_POL_CIR_CFG_CIR_BURST(cbs),
148                          ANA_POL_CIR_CFG, pol_ix);
149
150         ocelot_write_gix(ocelot,
151                          (cir_discard ? GENMASK(22, 0) : 0),
152                          ANA_POL_CIR_STATE, pol_ix);
153
154         return 0;
155 }
156
157 int ocelot_port_policer_add(struct ocelot *ocelot, int port,
158                             struct ocelot_policer *pol)
159 {
160         struct qos_policer_conf pp = { 0 };
161         int err;
162
163         if (!pol)
164                 return -EINVAL;
165
166         pp.mode = MSCC_QOS_RATE_MODE_DATA;
167         pp.pir = pol->rate;
168         pp.pbs = pol->burst;
169
170         dev_dbg(ocelot->dev, "%s: port %u pir %u kbps, pbs %u bytes\n",
171                 __func__, port, pp.pir, pp.pbs);
172
173         err = qos_policer_conf_set(ocelot, port, POL_IX_PORT + port, &pp);
174         if (err)
175                 return err;
176
177         ocelot_rmw_gix(ocelot,
178                        ANA_PORT_POL_CFG_PORT_POL_ENA |
179                        ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
180                        ANA_PORT_POL_CFG_PORT_POL_ENA |
181                        ANA_PORT_POL_CFG_POL_ORDER_M,
182                        ANA_PORT_POL_CFG, port);
183
184         return 0;
185 }
186 EXPORT_SYMBOL(ocelot_port_policer_add);
187
188 int ocelot_port_policer_del(struct ocelot *ocelot, int port)
189 {
190         struct qos_policer_conf pp = { 0 };
191         int err;
192
193         dev_dbg(ocelot->dev, "%s: port %u\n", __func__, port);
194
195         pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
196
197         err = qos_policer_conf_set(ocelot, port, POL_IX_PORT + port, &pp);
198         if (err)
199                 return err;
200
201         ocelot_rmw_gix(ocelot,
202                        ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
203                        ANA_PORT_POL_CFG_PORT_POL_ENA |
204                        ANA_PORT_POL_CFG_POL_ORDER_M,
205                        ANA_PORT_POL_CFG, port);
206
207         return 0;
208 }
209 EXPORT_SYMBOL(ocelot_port_policer_del);
This page took 0.042988 seconds and 4 git commands to generate.