Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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);