162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* Microchip Sparx5 Switch driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <net/pkt_cls.h> 862306a36Sopenharmony_ci#include <net/pkt_sched.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "sparx5_tc.h" 1162306a36Sopenharmony_ci#include "sparx5_main.h" 1262306a36Sopenharmony_ci#include "sparx5_qos.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* tc block handling */ 1562306a36Sopenharmony_cistatic LIST_HEAD(sparx5_block_cb_list); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic int sparx5_tc_block_cb(enum tc_setup_type type, 1862306a36Sopenharmony_ci void *type_data, 1962306a36Sopenharmony_ci void *cb_priv, bool ingress) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct net_device *ndev = cb_priv; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci switch (type) { 2462306a36Sopenharmony_ci case TC_SETUP_CLSMATCHALL: 2562306a36Sopenharmony_ci return sparx5_tc_matchall(ndev, type_data, ingress); 2662306a36Sopenharmony_ci case TC_SETUP_CLSFLOWER: 2762306a36Sopenharmony_ci return sparx5_tc_flower(ndev, type_data, ingress); 2862306a36Sopenharmony_ci default: 2962306a36Sopenharmony_ci return -EOPNOTSUPP; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int sparx5_tc_block_cb_ingress(enum tc_setup_type type, 3462306a36Sopenharmony_ci void *type_data, 3562306a36Sopenharmony_ci void *cb_priv) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci return sparx5_tc_block_cb(type, type_data, cb_priv, true); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int sparx5_tc_block_cb_egress(enum tc_setup_type type, 4162306a36Sopenharmony_ci void *type_data, 4262306a36Sopenharmony_ci void *cb_priv) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci return sparx5_tc_block_cb(type, type_data, cb_priv, false); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int sparx5_tc_setup_block(struct net_device *ndev, 4862306a36Sopenharmony_ci struct flow_block_offload *fbo) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci flow_setup_cb_t *cb; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 5362306a36Sopenharmony_ci cb = sparx5_tc_block_cb_ingress; 5462306a36Sopenharmony_ci else if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) 5562306a36Sopenharmony_ci cb = sparx5_tc_block_cb_egress; 5662306a36Sopenharmony_ci else 5762306a36Sopenharmony_ci return -EOPNOTSUPP; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list, 6062306a36Sopenharmony_ci cb, ndev, ndev, false); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer, 6462306a36Sopenharmony_ci u32 *idx) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci if (parent == TC_H_ROOT) { 6762306a36Sopenharmony_ci *layer = 2; 6862306a36Sopenharmony_ci *idx = portno; 6962306a36Sopenharmony_ci } else { 7062306a36Sopenharmony_ci u32 queue = TC_H_MIN(parent) - 1; 7162306a36Sopenharmony_ci *layer = 0; 7262306a36Sopenharmony_ci *idx = SPX5_HSCH_L0_GET_IDX(portno, queue); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev, 7762306a36Sopenharmony_ci struct tc_mqprio_qopt_offload *m) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (m->qopt.num_tc == 0) 8262306a36Sopenharmony_ci return sparx5_tc_mqprio_del(ndev); 8362306a36Sopenharmony_ci else 8462306a36Sopenharmony_ci return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev, 8862306a36Sopenharmony_ci struct tc_tbf_qopt_offload *qopt) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct sparx5_port *port = netdev_priv(ndev); 9162306a36Sopenharmony_ci u32 layer, se_idx; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer, 9462306a36Sopenharmony_ci &se_idx); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci switch (qopt->command) { 9762306a36Sopenharmony_ci case TC_TBF_REPLACE: 9862306a36Sopenharmony_ci return sparx5_tc_tbf_add(port, &qopt->replace_params, layer, 9962306a36Sopenharmony_ci se_idx); 10062306a36Sopenharmony_ci case TC_TBF_DESTROY: 10162306a36Sopenharmony_ci return sparx5_tc_tbf_del(port, layer, se_idx); 10262306a36Sopenharmony_ci case TC_TBF_STATS: 10362306a36Sopenharmony_ci return -EOPNOTSUPP; 10462306a36Sopenharmony_ci default: 10562306a36Sopenharmony_ci return -EOPNOTSUPP; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return -EOPNOTSUPP; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int sparx5_tc_setup_qdisc_ets(struct net_device *ndev, 11262306a36Sopenharmony_ci struct tc_ets_qopt_offload *qopt) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct tc_ets_qopt_offload_replace_params *params = 11562306a36Sopenharmony_ci &qopt->replace_params; 11662306a36Sopenharmony_ci struct sparx5_port *port = netdev_priv(ndev); 11762306a36Sopenharmony_ci int i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Only allow ets on ports */ 12062306a36Sopenharmony_ci if (qopt->parent != TC_H_ROOT) 12162306a36Sopenharmony_ci return -EOPNOTSUPP; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci switch (qopt->command) { 12462306a36Sopenharmony_ci case TC_ETS_REPLACE: 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* We support eight priorities */ 12762306a36Sopenharmony_ci if (params->bands != SPX5_PRIOS) 12862306a36Sopenharmony_ci return -EOPNOTSUPP; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Sanity checks */ 13162306a36Sopenharmony_ci for (i = 0; i < SPX5_PRIOS; ++i) { 13262306a36Sopenharmony_ci /* Priority map is *always* reverse e.g: 7 6 5 .. 0 */ 13362306a36Sopenharmony_ci if (params->priomap[i] != (7 - i)) 13462306a36Sopenharmony_ci return -EOPNOTSUPP; 13562306a36Sopenharmony_ci /* Throw an error if we receive zero weights by tc */ 13662306a36Sopenharmony_ci if (params->quanta[i] && params->weights[i] == 0) { 13762306a36Sopenharmony_ci pr_err("Invalid ets configuration; band %d has weight zero", 13862306a36Sopenharmony_ci i); 13962306a36Sopenharmony_ci return -EINVAL; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return sparx5_tc_ets_add(port, params); 14462306a36Sopenharmony_ci case TC_ETS_DESTROY: 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return sparx5_tc_ets_del(port); 14762306a36Sopenharmony_ci case TC_ETS_GRAFT: 14862306a36Sopenharmony_ci return -EOPNOTSUPP; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci default: 15162306a36Sopenharmony_ci return -EOPNOTSUPP; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return -EOPNOTSUPP; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ciint sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type, 15862306a36Sopenharmony_ci void *type_data) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci switch (type) { 16162306a36Sopenharmony_ci case TC_SETUP_BLOCK: 16262306a36Sopenharmony_ci return sparx5_tc_setup_block(ndev, type_data); 16362306a36Sopenharmony_ci case TC_SETUP_QDISC_MQPRIO: 16462306a36Sopenharmony_ci return sparx5_tc_setup_qdisc_mqprio(ndev, type_data); 16562306a36Sopenharmony_ci case TC_SETUP_QDISC_TBF: 16662306a36Sopenharmony_ci return sparx5_tc_setup_qdisc_tbf(ndev, type_data); 16762306a36Sopenharmony_ci case TC_SETUP_QDISC_ETS: 16862306a36Sopenharmony_ci return sparx5_tc_setup_qdisc_ets(ndev, type_data); 16962306a36Sopenharmony_ci default: 17062306a36Sopenharmony_ci return -EOPNOTSUPP; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 175