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 static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer, 14 u32 *idx) 15 { 16 if (parent == TC_H_ROOT) { 17 *layer = 2; 18 *idx = portno; 19 } else { 20 u32 queue = TC_H_MIN(parent) - 1; 21 *layer = 0; 22 *idx = SPX5_HSCH_L0_GET_IDX(portno, queue); 23 } 24 } 25 26 static int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev, 27 struct tc_mqprio_qopt_offload *m) 28 { 29 m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS; 30 31 if (m->qopt.num_tc == 0) 32 return sparx5_tc_mqprio_del(ndev); 33 else 34 return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc); 35 } 36 37 static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev, 38 struct tc_tbf_qopt_offload *qopt) 39 { 40 struct sparx5_port *port = netdev_priv(ndev); 41 u32 layer, se_idx; 42 43 sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer, 44 &se_idx); 45 46 switch (qopt->command) { 47 case TC_TBF_REPLACE: 48 return sparx5_tc_tbf_add(port, &qopt->replace_params, layer, 49 se_idx); 50 case TC_TBF_DESTROY: 51 return sparx5_tc_tbf_del(port, layer, se_idx); 52 case TC_TBF_STATS: 53 return -EOPNOTSUPP; 54 default: 55 return -EOPNOTSUPP; 56 } 57 58 return -EOPNOTSUPP; 59 } 60 61 static int sparx5_tc_setup_qdisc_ets(struct net_device *ndev, 62 struct tc_ets_qopt_offload *qopt) 63 { 64 struct tc_ets_qopt_offload_replace_params *params = 65 &qopt->replace_params; 66 struct sparx5_port *port = netdev_priv(ndev); 67 int i; 68 69 /* Only allow ets on ports */ 70 if (qopt->parent != TC_H_ROOT) 71 return -EOPNOTSUPP; 72 73 switch (qopt->command) { 74 case TC_ETS_REPLACE: 75 76 /* We support eight priorities */ 77 if (params->bands != SPX5_PRIOS) 78 return -EOPNOTSUPP; 79 80 /* Sanity checks */ 81 for (i = 0; i < SPX5_PRIOS; ++i) { 82 /* Priority map is *always* reverse e.g: 7 6 5 .. 0 */ 83 if (params->priomap[i] != (7 - i)) 84 return -EOPNOTSUPP; 85 /* Throw an error if we receive zero weights by tc */ 86 if (params->quanta[i] && params->weights[i] == 0) { 87 pr_err("Invalid ets configuration; band %d has weight zero", 88 i); 89 return -EINVAL; 90 } 91 } 92 93 return sparx5_tc_ets_add(port, params); 94 case TC_ETS_DESTROY: 95 96 return sparx5_tc_ets_del(port); 97 case TC_ETS_GRAFT: 98 return -EOPNOTSUPP; 99 100 default: 101 return -EOPNOTSUPP; 102 } 103 104 return -EOPNOTSUPP; 105 } 106 107 int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type, 108 void *type_data) 109 { 110 switch (type) { 111 case TC_SETUP_QDISC_MQPRIO: 112 return sparx5_tc_setup_qdisc_mqprio(ndev, type_data); 113 case TC_SETUP_QDISC_TBF: 114 return sparx5_tc_setup_qdisc_tbf(ndev, type_data); 115 case TC_SETUP_QDISC_ETS: 116 return sparx5_tc_setup_qdisc_ets(ndev, type_data); 117 default: 118 return -EOPNOTSUPP; 119 } 120 121 return 0; 122 } 123