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