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