linux/drivers/net/ethernet/mscc/ocelot_police.c
<<
>>
Prefs
   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
  23int 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
 157int 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}
 186EXPORT_SYMBOL(ocelot_port_policer_add);
 187
 188int 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}
 209EXPORT_SYMBOL(ocelot_port_policer_del);
 210