162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include "lan966x_main.h" 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci/* 0-8 : 9 port policers */ 662306a36Sopenharmony_ci#define POL_IDX_PORT 0 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* Policer order: Serial (QoS -> Port -> VCAP) */ 962306a36Sopenharmony_ci#define POL_ORDER 0x1d3 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistruct lan966x_tc_policer { 1262306a36Sopenharmony_ci /* kilobit per second */ 1362306a36Sopenharmony_ci u32 rate; 1462306a36Sopenharmony_ci /* bytes */ 1562306a36Sopenharmony_ci u32 burst; 1662306a36Sopenharmony_ci}; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic int lan966x_police_add(struct lan966x_port *port, 1962306a36Sopenharmony_ci struct lan966x_tc_policer *pol, 2062306a36Sopenharmony_ci u16 pol_idx) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci /* Rate unit is 33 1/3 kpps */ 2562306a36Sopenharmony_ci pol->rate = DIV_ROUND_UP(pol->rate * 3, 100); 2662306a36Sopenharmony_ci /* Avoid zero burst size */ 2762306a36Sopenharmony_ci pol->burst = pol->burst ?: 1; 2862306a36Sopenharmony_ci /* Unit is 4kB */ 2962306a36Sopenharmony_ci pol->burst = DIV_ROUND_UP(pol->burst, 4096); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (pol->rate > GENMASK(15, 0) || 3262306a36Sopenharmony_ci pol->burst > GENMASK(6, 0)) 3362306a36Sopenharmony_ci return -EINVAL; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci lan_wr(ANA_POL_MODE_DROP_ON_YELLOW_ENA_SET(0) | 3662306a36Sopenharmony_ci ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA_SET(0) | 3762306a36Sopenharmony_ci ANA_POL_MODE_IPG_SIZE_SET(20) | 3862306a36Sopenharmony_ci ANA_POL_MODE_FRM_MODE_SET(1) | 3962306a36Sopenharmony_ci ANA_POL_MODE_OVERSHOOT_ENA_SET(1), 4062306a36Sopenharmony_ci lan966x, ANA_POL_MODE(pol_idx)); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci lan_wr(ANA_POL_PIR_STATE_PIR_LVL_SET(0), 4362306a36Sopenharmony_ci lan966x, ANA_POL_PIR_STATE(pol_idx)); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci lan_wr(ANA_POL_PIR_CFG_PIR_RATE_SET(pol->rate) | 4662306a36Sopenharmony_ci ANA_POL_PIR_CFG_PIR_BURST_SET(pol->burst), 4762306a36Sopenharmony_ci lan966x, ANA_POL_PIR_CFG(pol_idx)); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void lan966x_police_del(struct lan966x_port *port, u16 pol_idx) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci lan_wr(ANA_POL_MODE_DROP_ON_YELLOW_ENA_SET(0) | 5762306a36Sopenharmony_ci ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA_SET(0) | 5862306a36Sopenharmony_ci ANA_POL_MODE_IPG_SIZE_SET(20) | 5962306a36Sopenharmony_ci ANA_POL_MODE_FRM_MODE_SET(2) | 6062306a36Sopenharmony_ci ANA_POL_MODE_OVERSHOOT_ENA_SET(1), 6162306a36Sopenharmony_ci lan966x, ANA_POL_MODE(pol_idx)); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci lan_wr(ANA_POL_PIR_STATE_PIR_LVL_SET(0), 6462306a36Sopenharmony_ci lan966x, ANA_POL_PIR_STATE(pol_idx)); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci lan_wr(ANA_POL_PIR_CFG_PIR_RATE_SET(GENMASK(14, 0)) | 6762306a36Sopenharmony_ci ANA_POL_PIR_CFG_PIR_BURST_SET(0), 6862306a36Sopenharmony_ci lan966x, ANA_POL_PIR_CFG(pol_idx)); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int lan966x_police_validate(struct lan966x_port *port, 7262306a36Sopenharmony_ci const struct flow_action *action, 7362306a36Sopenharmony_ci const struct flow_action_entry *act, 7462306a36Sopenharmony_ci unsigned long police_id, 7562306a36Sopenharmony_ci bool ingress, 7662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci if (act->police.exceed.act_id != FLOW_ACTION_DROP) { 7962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 8062306a36Sopenharmony_ci "Offload not supported when exceed action is not drop"); 8162306a36Sopenharmony_ci return -EOPNOTSUPP; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && 8562306a36Sopenharmony_ci act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { 8662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 8762306a36Sopenharmony_ci "Offload not supported when conform action is not pipe or ok"); 8862306a36Sopenharmony_ci return -EOPNOTSUPP; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && 9262306a36Sopenharmony_ci !flow_action_is_last_entry(action, act)) { 9362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 9462306a36Sopenharmony_ci "Offload not supported when conform action is ok, but action is not last"); 9562306a36Sopenharmony_ci return -EOPNOTSUPP; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (act->police.peakrate_bytes_ps || 9962306a36Sopenharmony_ci act->police.avrate || act->police.overhead) { 10062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 10162306a36Sopenharmony_ci "Offload not supported when peakrate/avrate/overhead is configured"); 10262306a36Sopenharmony_ci return -EOPNOTSUPP; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (act->police.rate_pkt_ps) { 10662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 10762306a36Sopenharmony_ci "QoS offload not support packets per second"); 10862306a36Sopenharmony_ci return -EOPNOTSUPP; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (!ingress) { 11262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 11362306a36Sopenharmony_ci "Policer is not supported on egress"); 11462306a36Sopenharmony_ci return -EOPNOTSUPP; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (port->tc.ingress_shared_block) { 11862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 11962306a36Sopenharmony_ci "Policer is not supported on shared ingress blocks"); 12062306a36Sopenharmony_ci return -EOPNOTSUPP; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (port->tc.police_id && port->tc.police_id != police_id) { 12462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 12562306a36Sopenharmony_ci "Only one policer per port is supported"); 12662306a36Sopenharmony_ci return -EEXIST; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ciint lan966x_police_port_add(struct lan966x_port *port, 13362306a36Sopenharmony_ci struct flow_action *action, 13462306a36Sopenharmony_ci struct flow_action_entry *act, 13562306a36Sopenharmony_ci unsigned long police_id, 13662306a36Sopenharmony_ci bool ingress, 13762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 14062306a36Sopenharmony_ci struct rtnl_link_stats64 new_stats; 14162306a36Sopenharmony_ci struct lan966x_tc_policer pol; 14262306a36Sopenharmony_ci struct flow_stats *old_stats; 14362306a36Sopenharmony_ci int err; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci err = lan966x_police_validate(port, action, act, police_id, ingress, 14662306a36Sopenharmony_ci extack); 14762306a36Sopenharmony_ci if (err) 14862306a36Sopenharmony_ci return err; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci memset(&pol, 0, sizeof(pol)); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci pol.rate = div_u64(act->police.rate_bytes_ps, 1000) * 8; 15362306a36Sopenharmony_ci pol.burst = act->police.burst; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci err = lan966x_police_add(port, &pol, POL_IDX_PORT + port->chip_port); 15662306a36Sopenharmony_ci if (err) { 15762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 15862306a36Sopenharmony_ci "Failed to add policer to port"); 15962306a36Sopenharmony_ci return err; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci lan_rmw(ANA_POL_CFG_PORT_POL_ENA_SET(1) | 16362306a36Sopenharmony_ci ANA_POL_CFG_POL_ORDER_SET(POL_ORDER), 16462306a36Sopenharmony_ci ANA_POL_CFG_PORT_POL_ENA | 16562306a36Sopenharmony_ci ANA_POL_CFG_POL_ORDER, 16662306a36Sopenharmony_ci lan966x, ANA_POL_CFG(port->chip_port)); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci port->tc.police_id = police_id; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Setup initial stats */ 17162306a36Sopenharmony_ci old_stats = &port->tc.police_stat; 17262306a36Sopenharmony_ci lan966x_stats_get(port->dev, &new_stats); 17362306a36Sopenharmony_ci old_stats->bytes = new_stats.rx_bytes; 17462306a36Sopenharmony_ci old_stats->pkts = new_stats.rx_packets; 17562306a36Sopenharmony_ci old_stats->drops = new_stats.rx_dropped; 17662306a36Sopenharmony_ci old_stats->lastused = jiffies; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciint lan966x_police_port_del(struct lan966x_port *port, 18262306a36Sopenharmony_ci unsigned long police_id, 18362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (port->tc.police_id != police_id) { 18862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 18962306a36Sopenharmony_ci "Invalid policer id"); 19062306a36Sopenharmony_ci return -EINVAL; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci lan966x_police_del(port, POL_IDX_PORT + port->chip_port); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci lan_rmw(ANA_POL_CFG_PORT_POL_ENA_SET(0) | 19662306a36Sopenharmony_ci ANA_POL_CFG_POL_ORDER_SET(POL_ORDER), 19762306a36Sopenharmony_ci ANA_POL_CFG_PORT_POL_ENA | 19862306a36Sopenharmony_ci ANA_POL_CFG_POL_ORDER, 19962306a36Sopenharmony_ci lan966x, ANA_POL_CFG(port->chip_port)); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci port->tc.police_id = 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_civoid lan966x_police_port_stats(struct lan966x_port *port, 20762306a36Sopenharmony_ci struct flow_stats *stats) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct rtnl_link_stats64 new_stats; 21062306a36Sopenharmony_ci struct flow_stats *old_stats; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci old_stats = &port->tc.police_stat; 21362306a36Sopenharmony_ci lan966x_stats_get(port->dev, &new_stats); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci flow_stats_update(stats, 21662306a36Sopenharmony_ci new_stats.rx_bytes - old_stats->bytes, 21762306a36Sopenharmony_ci new_stats.rx_packets - old_stats->pkts, 21862306a36Sopenharmony_ci new_stats.rx_dropped - old_stats->drops, 21962306a36Sopenharmony_ci old_stats->lastused, 22062306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_IMMEDIATE); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci old_stats->bytes = new_stats.rx_bytes; 22362306a36Sopenharmony_ci old_stats->pkts = new_stats.rx_packets; 22462306a36Sopenharmony_ci old_stats->drops = new_stats.rx_dropped; 22562306a36Sopenharmony_ci old_stats->lastused = jiffies; 22662306a36Sopenharmony_ci} 227