Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  1// SPDX-License-Identifier: GPL-2.0+
  2/* Microchip Sparx5 Switch driver
  3 *
  4 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
  5 */
  6
  7#include <net/pkt_cls.h>
  8
  9#include "sparx5_tc.h"
 10#include "sparx5_main.h"
 11#include "sparx5_qos.h"
 12
 13/* tc block handling */
 14static LIST_HEAD(sparx5_block_cb_list);
 15
 16static int sparx5_tc_block_cb(enum tc_setup_type type,
 17			      void *type_data,
 18			      void *cb_priv, bool ingress)
 19{
 20	struct net_device *ndev = cb_priv;
 21
 22	switch (type) {
 23	case TC_SETUP_CLSMATCHALL:
 24		return sparx5_tc_matchall(ndev, type_data, ingress);
 25	case TC_SETUP_CLSFLOWER:
 26		return sparx5_tc_flower(ndev, type_data, ingress);
 27	default:
 28		return -EOPNOTSUPP;
 29	}
 30}
 31
 32static int sparx5_tc_block_cb_ingress(enum tc_setup_type type,
 33				      void *type_data,
 34				      void *cb_priv)
 35{
 36	return sparx5_tc_block_cb(type, type_data, cb_priv, true);
 37}
 38
 39static int sparx5_tc_block_cb_egress(enum tc_setup_type type,
 40				     void *type_data,
 41				     void *cb_priv)
 42{
 43	return sparx5_tc_block_cb(type, type_data, cb_priv, false);
 44}
 45
 46static int sparx5_tc_setup_block(struct net_device *ndev,
 47				 struct flow_block_offload *fbo)
 48{
 49	flow_setup_cb_t *cb;
 50
 51	if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
 52		cb = sparx5_tc_block_cb_ingress;
 53	else if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
 54		cb = sparx5_tc_block_cb_egress;
 55	else
 56		return -EOPNOTSUPP;
 57
 58	return flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list,
 59					  cb, ndev, ndev, false);
 60}
 61
 62static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer,
 63					u32 *idx)
 64{
 65	if (parent == TC_H_ROOT) {
 66		*layer = 2;
 67		*idx = portno;
 68	} else {
 69		u32 queue = TC_H_MIN(parent) - 1;
 70		*layer = 0;
 71		*idx = SPX5_HSCH_L0_GET_IDX(portno, queue);
 72	}
 73}
 74
 75static int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev,
 76					struct tc_mqprio_qopt_offload *m)
 77{
 78	m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
 79
 80	if (m->qopt.num_tc == 0)
 81		return sparx5_tc_mqprio_del(ndev);
 82	else
 83		return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc);
 84}
 85
 86static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev,
 87				     struct tc_tbf_qopt_offload *qopt)
 88{
 89	struct sparx5_port *port = netdev_priv(ndev);
 90	u32 layer, se_idx;
 91
 92	sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer,
 93				    &se_idx);
 94
 95	switch (qopt->command) {
 96	case TC_TBF_REPLACE:
 97		return sparx5_tc_tbf_add(port, &qopt->replace_params, layer,
 98					 se_idx);
 99	case TC_TBF_DESTROY:
100		return sparx5_tc_tbf_del(port, layer, se_idx);
101	case TC_TBF_STATS:
102		return -EOPNOTSUPP;
103	default:
104		return -EOPNOTSUPP;
105	}
106
107	return -EOPNOTSUPP;
108}
109
110static int sparx5_tc_setup_qdisc_ets(struct net_device *ndev,
111				     struct tc_ets_qopt_offload *qopt)
112{
113	struct tc_ets_qopt_offload_replace_params *params =
114		&qopt->replace_params;
115	struct sparx5_port *port = netdev_priv(ndev);
116	int i;
117
118	/* Only allow ets on ports  */
119	if (qopt->parent != TC_H_ROOT)
120		return -EOPNOTSUPP;
121
122	switch (qopt->command) {
123	case TC_ETS_REPLACE:
124
125		/* We support eight priorities */
126		if (params->bands != SPX5_PRIOS)
127			return -EOPNOTSUPP;
128
129		/* Sanity checks */
130		for (i = 0; i < SPX5_PRIOS; ++i) {
131			/* Priority map is *always* reverse e.g: 7 6 5 .. 0 */
132			if (params->priomap[i] != (7 - i))
133				return -EOPNOTSUPP;
134			/* Throw an error if we receive zero weights by tc */
135			if (params->quanta[i] && params->weights[i] == 0) {
136				pr_err("Invalid ets configuration; band %d has weight zero",
137				       i);
138				return -EINVAL;
139			}
140		}
141
142		return sparx5_tc_ets_add(port, params);
143	case TC_ETS_DESTROY:
144
145		return sparx5_tc_ets_del(port);
146	case TC_ETS_GRAFT:
147		return -EOPNOTSUPP;
148
149	default:
150		return -EOPNOTSUPP;
151	}
152
153	return -EOPNOTSUPP;
154}
155
156int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
157			 void *type_data)
158{
159	switch (type) {
160	case TC_SETUP_BLOCK:
161		return sparx5_tc_setup_block(ndev, type_data);
162	case TC_SETUP_QDISC_MQPRIO:
163		return sparx5_tc_setup_qdisc_mqprio(ndev, type_data);
164	case TC_SETUP_QDISC_TBF:
165		return sparx5_tc_setup_qdisc_tbf(ndev, type_data);
166	case TC_SETUP_QDISC_ETS:
167		return sparx5_tc_setup_qdisc_ets(ndev, type_data);
168	default:
169		return -EOPNOTSUPP;
170	}
171
172	return 0;
173}