Linux Audio

Check our new training course

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