aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c
blob: e05429c751eefb150930d8f60a33f3573c392cad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// SPDX-License-Identifier: GPL-2.0+
/* Microchip Sparx5 Switch driver
 *
 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
 */

#include <net/pkt_cls.h>

#include "sparx5_tc.h"
#include "sparx5_main.h"
#include "sparx5_qos.h"

static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer,
					u32 *idx)
{
	if (parent == TC_H_ROOT) {
		*layer = 2;
		*idx = portno;
	} else {
		u32 queue = TC_H_MIN(parent) - 1;
		*layer = 0;
		*idx = SPX5_HSCH_L0_GET_IDX(portno, queue);
	}
}

static int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev,
					struct tc_mqprio_qopt_offload *m)
{
	m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;

	if (m->qopt.num_tc == 0)
		return sparx5_tc_mqprio_del(ndev);
	else
		return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc);
}

static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev,
				     struct tc_tbf_qopt_offload *qopt)
{
	struct sparx5_port *port = netdev_priv(ndev);
	u32 layer, se_idx;

	sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer,
				    &se_idx);

	switch (qopt->command) {
	case TC_TBF_REPLACE:
		return sparx5_tc_tbf_add(port, &qopt->replace_params, layer,
					 se_idx);
	case TC_TBF_DESTROY:
		return sparx5_tc_tbf_del(port, layer, se_idx);
	case TC_TBF_STATS:
		return -EOPNOTSUPP;
	default:
		return -EOPNOTSUPP;
	}

	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)
{
	switch (type) {
	case TC_SETUP_QDISC_MQPRIO:
		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;
	}

	return 0;
}