brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · 7e1f67b Raw
255 lines · c
1// SPDX-License-Identifier: (GPL-2.0 OR MIT)2/* Microsemi Ocelot Switch driver3 *4 * Copyright (c) 2019 Microsemi Corporation5 */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, 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,106			"Invalid pir for policer %u: %u (max %lu)\n",107			pol_ix, pir, GENMASK(15, 0));108		return -EINVAL;109	}110 111	if (cir > GENMASK(15, 0)) {112		dev_err(ocelot->dev,113			"Invalid cir for policer %u: %u (max %lu)\n",114			pol_ix, cir, GENMASK(15, 0));115		return -EINVAL;116	}117 118	if (pbs > pbs_max) {119		dev_err(ocelot->dev,120			"Invalid pbs for policer %u: %u (max %u)\n",121			pol_ix, pbs, pbs_max);122		return -EINVAL;123	}124 125	if (cbs > cbs_max) {126		dev_err(ocelot->dev,127			"Invalid cbs for policer %u: %u (max %u)\n",128			pol_ix, cbs, cbs_max);129		return -EINVAL;130	}131 132	value = (ANA_POL_MODE_CFG_IPG_SIZE(ipg) |133		 ANA_POL_MODE_CFG_FRM_MODE(frm_mode) |134		 (cf ? ANA_POL_MODE_CFG_DLB_COUPLED : 0) |135		 (cir_ena ? ANA_POL_MODE_CFG_CIR_ENA : 0) |136		 ANA_POL_MODE_CFG_OVERSHOOT_ENA);137 138	ocelot_write_gix(ocelot, value, ANA_POL_MODE_CFG, pol_ix);139 140	ocelot_write_gix(ocelot,141			 ANA_POL_PIR_CFG_PIR_RATE(pir) |142			 ANA_POL_PIR_CFG_PIR_BURST(pbs),143			 ANA_POL_PIR_CFG, pol_ix);144 145	ocelot_write_gix(ocelot,146			 (pir_discard ? GENMASK(22, 0) : 0),147			 ANA_POL_PIR_STATE, pol_ix);148 149	ocelot_write_gix(ocelot,150			 ANA_POL_CIR_CFG_CIR_RATE(cir) |151			 ANA_POL_CIR_CFG_CIR_BURST(cbs),152			 ANA_POL_CIR_CFG, pol_ix);153 154	ocelot_write_gix(ocelot,155			 (cir_discard ? GENMASK(22, 0) : 0),156			 ANA_POL_CIR_STATE, pol_ix);157 158	return 0;159}160 161int ocelot_policer_validate(const struct flow_action *action,162			    const struct flow_action_entry *a,163			    struct netlink_ext_ack *extack)164{165	if (a->police.exceed.act_id != FLOW_ACTION_DROP) {166		NL_SET_ERR_MSG_MOD(extack,167				   "Offload not supported when exceed action is not drop");168		return -EOPNOTSUPP;169	}170 171	if (a->police.notexceed.act_id != FLOW_ACTION_PIPE &&172	    a->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {173		NL_SET_ERR_MSG_MOD(extack,174				   "Offload not supported when conform action is not pipe or ok");175		return -EOPNOTSUPP;176	}177 178	if (a->police.notexceed.act_id == FLOW_ACTION_ACCEPT &&179	    !flow_action_is_last_entry(action, a)) {180		NL_SET_ERR_MSG_MOD(extack,181				   "Offload not supported when conform action is ok, but police action is not last");182		return -EOPNOTSUPP;183	}184 185	if (a->police.peakrate_bytes_ps ||186	    a->police.avrate || a->police.overhead) {187		NL_SET_ERR_MSG_MOD(extack,188				   "Offload not supported when peakrate/avrate/overhead is configured");189		return -EOPNOTSUPP;190	}191 192	if (a->police.rate_pkt_ps) {193		NL_SET_ERR_MSG_MOD(extack,194				   "Offload does not support packets per second");195		return -EOPNOTSUPP;196	}197 198	return 0;199}200EXPORT_SYMBOL(ocelot_policer_validate);201 202int ocelot_port_policer_add(struct ocelot *ocelot, int port,203			    struct ocelot_policer *pol)204{205	struct qos_policer_conf pp = { 0 };206	int err;207 208	if (!pol)209		return -EINVAL;210 211	pp.mode = MSCC_QOS_RATE_MODE_DATA;212	pp.pir = pol->rate;213	pp.pbs = pol->burst;214 215	dev_dbg(ocelot->dev, "%s: port %u pir %u kbps, pbs %u bytes\n",216		__func__, port, pp.pir, pp.pbs);217 218	err = qos_policer_conf_set(ocelot, POL_IX_PORT + port, &pp);219	if (err)220		return err;221 222	ocelot_rmw_gix(ocelot,223		       ANA_PORT_POL_CFG_PORT_POL_ENA |224		       ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),225		       ANA_PORT_POL_CFG_PORT_POL_ENA |226		       ANA_PORT_POL_CFG_POL_ORDER_M,227		       ANA_PORT_POL_CFG, port);228 229	return 0;230}231EXPORT_SYMBOL(ocelot_port_policer_add);232 233int ocelot_port_policer_del(struct ocelot *ocelot, int port)234{235	struct qos_policer_conf pp = { 0 };236	int err;237 238	dev_dbg(ocelot->dev, "%s: port %u\n", __func__, port);239 240	pp.mode = MSCC_QOS_RATE_MODE_DISABLED;241 242	err = qos_policer_conf_set(ocelot, POL_IX_PORT + port, &pp);243	if (err)244		return err;245 246	ocelot_rmw_gix(ocelot,247		       ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),248		       ANA_PORT_POL_CFG_PORT_POL_ENA |249		       ANA_PORT_POL_CFG_POL_ORDER_M,250		       ANA_PORT_POL_CFG, port);251 252	return 0;253}254EXPORT_SYMBOL(ocelot_port_policer_del);255