Commit 21122542 authored by Daniel Machon's avatar Daniel Machon Committed by David S. Miller
Browse files

net: microchip: sparx5: add support for offloading ets qdisc



Add support for offloading ets qdisc to sparx5 switch.

The ets qdisc makes it possible to configure a mix og strict and
bandwidth-sharing bands. The ets qdisc must be attached as a root qdisc.

Signed-off-by: default avatarDaniel Machon <daniel.machon@microchip.com>
Signed-off-by: default avatarSteen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e02a5ac6
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -3098,6 +3098,21 @@ enum sparx5_target {
#define HSCH_SE_DLB_SENSE_SE_DLB_DPORT_ENA_GET(x)\
	FIELD_GET(HSCH_SE_DLB_SENSE_SE_DLB_DPORT_ENA, x)

/*      HSCH:HSCH_DWRR:DWRR_ENTRY */
#define HSCH_DWRR_ENTRY(g)        __REG(TARGET_HSCH, 0, 1, 162816, g, 72, 4, 0, 0, 1, 4)

#define HSCH_DWRR_ENTRY_DWRR_COST                GENMASK(24, 20)
#define HSCH_DWRR_ENTRY_DWRR_COST_SET(x)\
	FIELD_PREP(HSCH_DWRR_ENTRY_DWRR_COST, x)
#define HSCH_DWRR_ENTRY_DWRR_COST_GET(x)\
	FIELD_GET(HSCH_DWRR_ENTRY_DWRR_COST, x)

#define HSCH_DWRR_ENTRY_DWRR_BALANCE             GENMASK(19, 0)
#define HSCH_DWRR_ENTRY_DWRR_BALANCE_SET(x)\
	FIELD_PREP(HSCH_DWRR_ENTRY_DWRR_BALANCE, x)
#define HSCH_DWRR_ENTRY_DWRR_BALANCE_GET(x)\
	FIELD_GET(HSCH_DWRR_ENTRY_DWRR_BALANCE, x)

/*      HSCH:HSCH_MISC:HSCH_CFG_CFG */
#define HSCH_HSCH_CFG_CFG         __REG(TARGET_HSCH, 0, 1, 163104, 0, 1, 648, 284, 0, 1, 4)

+73 −0
Original line number Diff line number Diff line
@@ -296,6 +296,36 @@ static int sparx5_shaper_conf_set(struct sparx5_port *port,
	return 0;
}

static u32 sparx5_weight_to_hw_cost(u32 weight_min, u32 weight)
{
	return ((((SPX5_DWRR_COST_MAX << 4) * weight_min / weight) + 8) >> 4) -
	       1;
}

static int sparx5_dwrr_conf_set(struct sparx5_port *port,
				struct sparx5_dwrr *dwrr)
{
	int i;

	spx5_rmw(HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(2) |
		 HSCH_HSCH_CFG_CFG_CFG_SE_IDX_SET(port->portno),
		 HSCH_HSCH_CFG_CFG_HSCH_LAYER | HSCH_HSCH_CFG_CFG_CFG_SE_IDX,
		 port->sparx5, HSCH_HSCH_CFG_CFG);

	/* Number of *lower* indexes that are arbitrated dwrr */
	spx5_rmw(HSCH_SE_CFG_SE_DWRR_CNT_SET(dwrr->count),
		 HSCH_SE_CFG_SE_DWRR_CNT, port->sparx5,
		 HSCH_SE_CFG(port->portno));

	for (i = 0; i < dwrr->count; i++) {
		spx5_rmw(HSCH_DWRR_ENTRY_DWRR_COST_SET(dwrr->cost[i]),
			 HSCH_DWRR_ENTRY_DWRR_COST, port->sparx5,
			 HSCH_DWRR_ENTRY(i));
	}

	return 0;
}

static int sparx5_leak_groups_init(struct sparx5 *sparx5)
{
	struct sparx5_layer *layer;
@@ -438,3 +468,46 @@ int sparx5_tc_tbf_del(struct sparx5_port *port, u32 layer, u32 idx)

	return sparx5_shaper_conf_set(port, &sh, layer, idx, group);
}

int sparx5_tc_ets_add(struct sparx5_port *port,
		      struct tc_ets_qopt_offload_replace_params *params)
{
	struct sparx5_dwrr dwrr = {0};
	/* Minimum weight for each iteration */
	unsigned int w_min = 100;
	int i;

	/* Find minimum weight for all dwrr bands */
	for (i = 0; i < SPX5_PRIOS; i++) {
		if (params->quanta[i] == 0)
			continue;
		w_min = min(w_min, params->weights[i]);
	}

	for (i = 0; i < SPX5_PRIOS; i++) {
		/* Strict band; skip */
		if (params->quanta[i] == 0)
			continue;

		dwrr.count++;

		/* On the sparx5, bands with higher indexes are preferred and
		 * arbitrated strict. Strict bands are put in the lower indexes,
		 * by tc, so we reverse the bands here.
		 *
		 * Also convert the weight to something the hardware
		 * understands.
		 */
		dwrr.cost[SPX5_PRIOS - i - 1] =
			sparx5_weight_to_hw_cost(w_min, params->weights[i]);
	}

	return sparx5_dwrr_conf_set(port, &dwrr);
}

int sparx5_tc_ets_del(struct sparx5_port *port)
{
	struct sparx5_dwrr dwrr = {0};

	return sparx5_dwrr_conf_set(port, &dwrr);
}
+15 −0
Original line number Diff line number Diff line
@@ -34,6 +34,9 @@
#define SPX5_SE_BURST_MIN 1
#define SPX5_SE_BURST_UNIT 4096

/* Dwrr */
#define SPX5_DWRR_COST_MAX 63

struct sparx5_shaper {
	u32 mode;
	u32 rate;
@@ -51,6 +54,11 @@ struct sparx5_layer {
	struct sparx5_lg leak_groups[SPX5_HSCH_LEAK_GRP_CNT];
};

struct sparx5_dwrr {
	u32 count; /* Number of inputs running dwrr */
	u8 cost[SPX5_PRIOS];
};

int sparx5_qos_init(struct sparx5 *sparx5);

/* Multi-Queue Priority */
@@ -64,4 +72,11 @@ int sparx5_tc_tbf_add(struct sparx5_port *port,
		      u32 layer, u32 idx);
int sparx5_tc_tbf_del(struct sparx5_port *port, u32 layer, u32 idx);

/* Enhanced Transmission Selection */
struct tc_ets_qopt_offload_replace_params;
int sparx5_tc_ets_add(struct sparx5_port *port,
		      struct tc_ets_qopt_offload_replace_params *params);

int sparx5_tc_ets_del(struct sparx5_port *port);

#endif	/* __SPARX5_QOS_H__ */
+51 −0
Original line number Diff line number Diff line
@@ -58,6 +58,55 @@ static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev,
	return -EOPNOTSUPP;
}

static int sparx5_tc_setup_qdisc_ets(struct net_device *ndev,
				     struct tc_ets_qopt_offload *qopt)
{
	struct tc_ets_qopt_offload_replace_params *params =
		&qopt->replace_params;
	struct sparx5_port *port = netdev_priv(ndev);
	int i;

	/* Only allow ets on ports  */
	if (qopt->parent != TC_H_ROOT)
		return -EOPNOTSUPP;

	switch (qopt->command) {
	case TC_ETS_REPLACE:

		/* We support eight priorities */
		if (params->bands != SPX5_PRIOS)
			return -EOPNOTSUPP;

		/* Sanity checks */
		for (i = 0; i < SPX5_PRIOS; ++i) {
			/* Priority map is *always* reverse e.g: 7 6 5 .. 0 */
			if (params->priomap[i] != (7 - i))
				return -EOPNOTSUPP;
			/* Throw an error if we receive zero weights by tc */
			if (params->quanta[i] && params->weights[i] == 0) {
				pr_err("Invalid ets configuration; band %d has weight zero",
				       i);
				return -EINVAL;
			}
		}

		sparx5_tc_ets_add(port, params);
		break;
	case TC_ETS_DESTROY:

		sparx5_tc_ets_del(port);

		break;
	case TC_ETS_GRAFT:
		return -EOPNOTSUPP;

	default:
		return -EOPNOTSUPP;
	}

	return -EOPNOTSUPP;
}

int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
			 void *type_data)
{
@@ -66,6 +115,8 @@ int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
		return sparx5_tc_setup_qdisc_mqprio(ndev, type_data);
	case TC_SETUP_QDISC_TBF:
		return sparx5_tc_setup_qdisc_tbf(ndev, type_data);
	case TC_SETUP_QDISC_ETS:
		return sparx5_tc_setup_qdisc_ets(ndev, type_data);
	default:
		return -EOPNOTSUPP;
	}