ocelot_police.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // SPDX-License-Identifier: (GPL-2.0 OR MIT)
  2. /* Microsemi Ocelot Switch driver
  3. *
  4. * Copyright (c) 2019 Microsemi Corporation
  5. */
  6. #include <soc/mscc/ocelot.h>
  7. #include "ocelot_police.h"
  8. /* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */
  9. #define POL_MODE_LINERATE 0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */
  10. #define POL_MODE_DATARATE 1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes */
  11. #define POL_MODE_FRMRATE_HI 2 /* Unit: 33 1/3 fps, 32.8 frames */
  12. #define POL_MODE_FRMRATE_LO 3 /* Unit: 1/3 fps, 0.3 frames */
  13. /* Policer indexes */
  14. #define POL_IX_PORT 0 /* 0-11 : Port policers */
  15. #define POL_IX_QUEUE 32 /* 32-127 : Queue policers */
  16. /* Default policer order */
  17. #define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */
  18. int qos_policer_conf_set(struct ocelot *ocelot, u32 pol_ix,
  19. struct qos_policer_conf *conf)
  20. {
  21. u32 cf = 0, cir_ena = 0, frm_mode = POL_MODE_LINERATE;
  22. u32 cir = 0, cbs = 0, pir = 0, pbs = 0;
  23. bool cir_discard = 0, pir_discard = 0;
  24. u32 pbs_max = 0, cbs_max = 0;
  25. u8 ipg = 20;
  26. u32 value;
  27. pir = conf->pir;
  28. pbs = conf->pbs;
  29. switch (conf->mode) {
  30. case MSCC_QOS_RATE_MODE_LINE:
  31. case MSCC_QOS_RATE_MODE_DATA:
  32. if (conf->mode == MSCC_QOS_RATE_MODE_LINE) {
  33. frm_mode = POL_MODE_LINERATE;
  34. ipg = min_t(u8, GENMASK(4, 0), conf->ipg);
  35. } else {
  36. frm_mode = POL_MODE_DATARATE;
  37. }
  38. if (conf->dlb) {
  39. cir_ena = 1;
  40. cir = conf->cir;
  41. cbs = conf->cbs;
  42. if (cir == 0 && cbs == 0) {
  43. /* Discard cir frames */
  44. cir_discard = 1;
  45. } else {
  46. cir = DIV_ROUND_UP(cir, 100);
  47. cir *= 3; /* 33 1/3 kbps */
  48. cbs = DIV_ROUND_UP(cbs, 4096);
  49. cbs = (cbs ? cbs : 1); /* No zero burst size */
  50. cbs_max = 60; /* Limit burst size */
  51. cf = conf->cf;
  52. if (cf)
  53. pir += conf->cir;
  54. }
  55. }
  56. if (pir == 0 && pbs == 0) {
  57. /* Discard PIR frames */
  58. pir_discard = 1;
  59. } else {
  60. pir = DIV_ROUND_UP(pir, 100);
  61. pir *= 3; /* 33 1/3 kbps */
  62. pbs = DIV_ROUND_UP(pbs, 4096);
  63. pbs = (pbs ? pbs : 1); /* No zero burst size */
  64. pbs_max = 60; /* Limit burst size */
  65. }
  66. break;
  67. case MSCC_QOS_RATE_MODE_FRAME:
  68. if (pir >= 100) {
  69. frm_mode = POL_MODE_FRMRATE_HI;
  70. pir = DIV_ROUND_UP(pir, 100);
  71. pir *= 3; /* 33 1/3 fps */
  72. pbs = (pbs * 10) / 328; /* 32.8 frames */
  73. pbs = (pbs ? pbs : 1); /* No zero burst size */
  74. pbs_max = GENMASK(6, 0); /* Limit burst size */
  75. } else {
  76. frm_mode = POL_MODE_FRMRATE_LO;
  77. if (pir == 0 && pbs == 0) {
  78. /* Discard all frames */
  79. pir_discard = 1;
  80. cir_discard = 1;
  81. } else {
  82. pir *= 3; /* 1/3 fps */
  83. pbs = (pbs * 10) / 3; /* 0.3 frames */
  84. pbs = (pbs ? pbs : 1); /* No zero burst size */
  85. pbs_max = 61; /* Limit burst size */
  86. }
  87. }
  88. break;
  89. default: /* MSCC_QOS_RATE_MODE_DISABLED */
  90. /* Disable policer using maximum rate and zero burst */
  91. pir = GENMASK(15, 0);
  92. pbs = 0;
  93. break;
  94. }
  95. /* Check limits */
  96. if (pir > GENMASK(15, 0)) {
  97. dev_err(ocelot->dev,
  98. "Invalid pir for policer %u: %u (max %lu)\n",
  99. pol_ix, pir, GENMASK(15, 0));
  100. return -EINVAL;
  101. }
  102. if (cir > GENMASK(15, 0)) {
  103. dev_err(ocelot->dev,
  104. "Invalid cir for policer %u: %u (max %lu)\n",
  105. pol_ix, cir, GENMASK(15, 0));
  106. return -EINVAL;
  107. }
  108. if (pbs > pbs_max) {
  109. dev_err(ocelot->dev,
  110. "Invalid pbs for policer %u: %u (max %u)\n",
  111. pol_ix, pbs, pbs_max);
  112. return -EINVAL;
  113. }
  114. if (cbs > cbs_max) {
  115. dev_err(ocelot->dev,
  116. "Invalid cbs for policer %u: %u (max %u)\n",
  117. pol_ix, cbs, cbs_max);
  118. return -EINVAL;
  119. }
  120. value = (ANA_POL_MODE_CFG_IPG_SIZE(ipg) |
  121. ANA_POL_MODE_CFG_FRM_MODE(frm_mode) |
  122. (cf ? ANA_POL_MODE_CFG_DLB_COUPLED : 0) |
  123. (cir_ena ? ANA_POL_MODE_CFG_CIR_ENA : 0) |
  124. ANA_POL_MODE_CFG_OVERSHOOT_ENA);
  125. ocelot_write_gix(ocelot, value, ANA_POL_MODE_CFG, pol_ix);
  126. ocelot_write_gix(ocelot,
  127. ANA_POL_PIR_CFG_PIR_RATE(pir) |
  128. ANA_POL_PIR_CFG_PIR_BURST(pbs),
  129. ANA_POL_PIR_CFG, pol_ix);
  130. ocelot_write_gix(ocelot,
  131. (pir_discard ? GENMASK(22, 0) : 0),
  132. ANA_POL_PIR_STATE, pol_ix);
  133. ocelot_write_gix(ocelot,
  134. ANA_POL_CIR_CFG_CIR_RATE(cir) |
  135. ANA_POL_CIR_CFG_CIR_BURST(cbs),
  136. ANA_POL_CIR_CFG, pol_ix);
  137. ocelot_write_gix(ocelot,
  138. (cir_discard ? GENMASK(22, 0) : 0),
  139. ANA_POL_CIR_STATE, pol_ix);
  140. return 0;
  141. }
  142. int ocelot_policer_validate(const struct flow_action *action,
  143. const struct flow_action_entry *a,
  144. struct netlink_ext_ack *extack)
  145. {
  146. if (a->police.exceed.act_id != FLOW_ACTION_DROP) {
  147. NL_SET_ERR_MSG_MOD(extack,
  148. "Offload not supported when exceed action is not drop");
  149. return -EOPNOTSUPP;
  150. }
  151. if (a->police.notexceed.act_id != FLOW_ACTION_PIPE &&
  152. a->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
  153. NL_SET_ERR_MSG_MOD(extack,
  154. "Offload not supported when conform action is not pipe or ok");
  155. return -EOPNOTSUPP;
  156. }
  157. if (a->police.notexceed.act_id == FLOW_ACTION_ACCEPT &&
  158. !flow_action_is_last_entry(action, a)) {
  159. NL_SET_ERR_MSG_MOD(extack,
  160. "Offload not supported when conform action is ok, but police action is not last");
  161. return -EOPNOTSUPP;
  162. }
  163. if (a->police.peakrate_bytes_ps ||
  164. a->police.avrate || a->police.overhead) {
  165. NL_SET_ERR_MSG_MOD(extack,
  166. "Offload not supported when peakrate/avrate/overhead is configured");
  167. return -EOPNOTSUPP;
  168. }
  169. if (a->police.rate_pkt_ps) {
  170. NL_SET_ERR_MSG_MOD(extack,
  171. "Offload does not support packets per second");
  172. return -EOPNOTSUPP;
  173. }
  174. return 0;
  175. }
  176. EXPORT_SYMBOL(ocelot_policer_validate);
  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. if (!pol)
  183. return -EINVAL;
  184. pp.mode = MSCC_QOS_RATE_MODE_DATA;
  185. pp.pir = pol->rate;
  186. pp.pbs = pol->burst;
  187. dev_dbg(ocelot->dev, "%s: port %u pir %u kbps, pbs %u bytes\n",
  188. __func__, port, pp.pir, pp.pbs);
  189. err = qos_policer_conf_set(ocelot, POL_IX_PORT + port, &pp);
  190. if (err)
  191. return err;
  192. ocelot_rmw_gix(ocelot,
  193. ANA_PORT_POL_CFG_PORT_POL_ENA |
  194. ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
  195. ANA_PORT_POL_CFG_PORT_POL_ENA |
  196. ANA_PORT_POL_CFG_POL_ORDER_M,
  197. ANA_PORT_POL_CFG, port);
  198. return 0;
  199. }
  200. EXPORT_SYMBOL(ocelot_port_policer_add);
  201. int ocelot_port_policer_del(struct ocelot *ocelot, int port)
  202. {
  203. struct qos_policer_conf pp = { 0 };
  204. int err;
  205. dev_dbg(ocelot->dev, "%s: port %u\n", __func__, port);
  206. pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
  207. err = qos_policer_conf_set(ocelot, POL_IX_PORT + port, &pp);
  208. if (err)
  209. return err;
  210. ocelot_rmw_gix(ocelot,
  211. ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
  212. ANA_PORT_POL_CFG_PORT_POL_ENA |
  213. ANA_PORT_POL_CFG_POL_ORDER_M,
  214. ANA_PORT_POL_CFG, port);
  215. return 0;
  216. }
  217. EXPORT_SYMBOL(ocelot_port_policer_del);