xref: /openbmc/linux/drivers/net/ethernet/microchip/lan966x/lan966x_police.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
15390334bSHoratiu Vultur // SPDX-License-Identifier: GPL-2.0+
25390334bSHoratiu Vultur 
35390334bSHoratiu Vultur #include "lan966x_main.h"
45390334bSHoratiu Vultur 
55390334bSHoratiu Vultur /* 0-8 : 9 port policers */
65390334bSHoratiu Vultur #define POL_IDX_PORT	0
75390334bSHoratiu Vultur 
85390334bSHoratiu Vultur /* Policer order: Serial (QoS -> Port -> VCAP) */
95390334bSHoratiu Vultur #define POL_ORDER	0x1d3
105390334bSHoratiu Vultur 
115390334bSHoratiu Vultur struct lan966x_tc_policer {
125390334bSHoratiu Vultur 	/* kilobit per second */
135390334bSHoratiu Vultur 	u32 rate;
145390334bSHoratiu Vultur 	/* bytes */
155390334bSHoratiu Vultur 	u32 burst;
165390334bSHoratiu Vultur };
175390334bSHoratiu Vultur 
lan966x_police_add(struct lan966x_port * port,struct lan966x_tc_policer * pol,u16 pol_idx)185390334bSHoratiu Vultur static int lan966x_police_add(struct lan966x_port *port,
195390334bSHoratiu Vultur 			      struct lan966x_tc_policer *pol,
205390334bSHoratiu Vultur 			      u16 pol_idx)
215390334bSHoratiu Vultur {
225390334bSHoratiu Vultur 	struct lan966x *lan966x = port->lan966x;
235390334bSHoratiu Vultur 
245390334bSHoratiu Vultur 	/* Rate unit is 33 1/3 kpps */
255390334bSHoratiu Vultur 	pol->rate = DIV_ROUND_UP(pol->rate * 3, 100);
265390334bSHoratiu Vultur 	/* Avoid zero burst size */
275390334bSHoratiu Vultur 	pol->burst = pol->burst ?: 1;
285390334bSHoratiu Vultur 	/* Unit is 4kB */
295390334bSHoratiu Vultur 	pol->burst = DIV_ROUND_UP(pol->burst, 4096);
305390334bSHoratiu Vultur 
315390334bSHoratiu Vultur 	if (pol->rate > GENMASK(15, 0) ||
325390334bSHoratiu Vultur 	    pol->burst > GENMASK(6, 0))
335390334bSHoratiu Vultur 		return -EINVAL;
345390334bSHoratiu Vultur 
355390334bSHoratiu Vultur 	lan_wr(ANA_POL_MODE_DROP_ON_YELLOW_ENA_SET(0) |
365390334bSHoratiu Vultur 	       ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA_SET(0) |
375390334bSHoratiu Vultur 	       ANA_POL_MODE_IPG_SIZE_SET(20) |
385390334bSHoratiu Vultur 	       ANA_POL_MODE_FRM_MODE_SET(1) |
395390334bSHoratiu Vultur 	       ANA_POL_MODE_OVERSHOOT_ENA_SET(1),
405390334bSHoratiu Vultur 	       lan966x, ANA_POL_MODE(pol_idx));
415390334bSHoratiu Vultur 
425390334bSHoratiu Vultur 	lan_wr(ANA_POL_PIR_STATE_PIR_LVL_SET(0),
435390334bSHoratiu Vultur 	       lan966x, ANA_POL_PIR_STATE(pol_idx));
445390334bSHoratiu Vultur 
455390334bSHoratiu Vultur 	lan_wr(ANA_POL_PIR_CFG_PIR_RATE_SET(pol->rate) |
465390334bSHoratiu Vultur 	       ANA_POL_PIR_CFG_PIR_BURST_SET(pol->burst),
475390334bSHoratiu Vultur 	       lan966x, ANA_POL_PIR_CFG(pol_idx));
485390334bSHoratiu Vultur 
495390334bSHoratiu Vultur 	return 0;
505390334bSHoratiu Vultur }
515390334bSHoratiu Vultur 
lan966x_police_del(struct lan966x_port * port,u16 pol_idx)52*68a84a12SHoratiu Vultur static void lan966x_police_del(struct lan966x_port *port, u16 pol_idx)
535390334bSHoratiu Vultur {
545390334bSHoratiu Vultur 	struct lan966x *lan966x = port->lan966x;
555390334bSHoratiu Vultur 
565390334bSHoratiu Vultur 	lan_wr(ANA_POL_MODE_DROP_ON_YELLOW_ENA_SET(0) |
575390334bSHoratiu Vultur 	       ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA_SET(0) |
585390334bSHoratiu Vultur 	       ANA_POL_MODE_IPG_SIZE_SET(20) |
595390334bSHoratiu Vultur 	       ANA_POL_MODE_FRM_MODE_SET(2) |
605390334bSHoratiu Vultur 	       ANA_POL_MODE_OVERSHOOT_ENA_SET(1),
615390334bSHoratiu Vultur 	       lan966x, ANA_POL_MODE(pol_idx));
625390334bSHoratiu Vultur 
635390334bSHoratiu Vultur 	lan_wr(ANA_POL_PIR_STATE_PIR_LVL_SET(0),
645390334bSHoratiu Vultur 	       lan966x, ANA_POL_PIR_STATE(pol_idx));
655390334bSHoratiu Vultur 
665390334bSHoratiu Vultur 	lan_wr(ANA_POL_PIR_CFG_PIR_RATE_SET(GENMASK(14, 0)) |
675390334bSHoratiu Vultur 	       ANA_POL_PIR_CFG_PIR_BURST_SET(0),
685390334bSHoratiu Vultur 	       lan966x, ANA_POL_PIR_CFG(pol_idx));
695390334bSHoratiu Vultur }
705390334bSHoratiu Vultur 
lan966x_police_validate(struct lan966x_port * port,const struct flow_action * action,const struct flow_action_entry * act,unsigned long police_id,bool ingress,struct netlink_ext_ack * extack)715390334bSHoratiu Vultur static int lan966x_police_validate(struct lan966x_port *port,
725390334bSHoratiu Vultur 				   const struct flow_action *action,
735390334bSHoratiu Vultur 				   const struct flow_action_entry *act,
745390334bSHoratiu Vultur 				   unsigned long police_id,
755390334bSHoratiu Vultur 				   bool ingress,
765390334bSHoratiu Vultur 				   struct netlink_ext_ack *extack)
775390334bSHoratiu Vultur {
785390334bSHoratiu Vultur 	if (act->police.exceed.act_id != FLOW_ACTION_DROP) {
795390334bSHoratiu Vultur 		NL_SET_ERR_MSG_MOD(extack,
805390334bSHoratiu Vultur 				   "Offload not supported when exceed action is not drop");
815390334bSHoratiu Vultur 		return -EOPNOTSUPP;
825390334bSHoratiu Vultur 	}
835390334bSHoratiu Vultur 
845390334bSHoratiu Vultur 	if (act->police.notexceed.act_id != FLOW_ACTION_PIPE &&
855390334bSHoratiu Vultur 	    act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
865390334bSHoratiu Vultur 		NL_SET_ERR_MSG_MOD(extack,
875390334bSHoratiu Vultur 				   "Offload not supported when conform action is not pipe or ok");
885390334bSHoratiu Vultur 		return -EOPNOTSUPP;
895390334bSHoratiu Vultur 	}
905390334bSHoratiu Vultur 
915390334bSHoratiu Vultur 	if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT &&
925390334bSHoratiu Vultur 	    !flow_action_is_last_entry(action, act)) {
935390334bSHoratiu Vultur 		NL_SET_ERR_MSG_MOD(extack,
945390334bSHoratiu Vultur 				   "Offload not supported when conform action is ok, but action is not last");
955390334bSHoratiu Vultur 		return -EOPNOTSUPP;
965390334bSHoratiu Vultur 	}
975390334bSHoratiu Vultur 
985390334bSHoratiu Vultur 	if (act->police.peakrate_bytes_ps ||
995390334bSHoratiu Vultur 	    act->police.avrate || act->police.overhead) {
1005390334bSHoratiu Vultur 		NL_SET_ERR_MSG_MOD(extack,
1015390334bSHoratiu Vultur 				   "Offload not supported when peakrate/avrate/overhead is configured");
1025390334bSHoratiu Vultur 		return -EOPNOTSUPP;
1035390334bSHoratiu Vultur 	}
1045390334bSHoratiu Vultur 
1055390334bSHoratiu Vultur 	if (act->police.rate_pkt_ps) {
1065390334bSHoratiu Vultur 		NL_SET_ERR_MSG_MOD(extack,
1075390334bSHoratiu Vultur 				   "QoS offload not support packets per second");
1085390334bSHoratiu Vultur 		return -EOPNOTSUPP;
1095390334bSHoratiu Vultur 	}
1105390334bSHoratiu Vultur 
1115390334bSHoratiu Vultur 	if (!ingress) {
1125390334bSHoratiu Vultur 		NL_SET_ERR_MSG_MOD(extack,
1135390334bSHoratiu Vultur 				   "Policer is not supported on egress");
1145390334bSHoratiu Vultur 		return -EOPNOTSUPP;
1155390334bSHoratiu Vultur 	}
1165390334bSHoratiu Vultur 
1175390334bSHoratiu Vultur 	if (port->tc.ingress_shared_block) {
1185390334bSHoratiu Vultur 		NL_SET_ERR_MSG_MOD(extack,
1195390334bSHoratiu Vultur 				   "Policer is not supported on shared ingress blocks");
1205390334bSHoratiu Vultur 		return -EOPNOTSUPP;
1215390334bSHoratiu Vultur 	}
1225390334bSHoratiu Vultur 
1235390334bSHoratiu Vultur 	if (port->tc.police_id && port->tc.police_id != police_id) {
1245390334bSHoratiu Vultur 		NL_SET_ERR_MSG_MOD(extack,
1255390334bSHoratiu Vultur 				   "Only one policer per port is supported");
1265390334bSHoratiu Vultur 		return -EEXIST;
1275390334bSHoratiu Vultur 	}
1285390334bSHoratiu Vultur 
1295390334bSHoratiu Vultur 	return 0;
1305390334bSHoratiu Vultur }
1315390334bSHoratiu Vultur 
lan966x_police_port_add(struct lan966x_port * port,struct flow_action * action,struct flow_action_entry * act,unsigned long police_id,bool ingress,struct netlink_ext_ack * extack)1325390334bSHoratiu Vultur int lan966x_police_port_add(struct lan966x_port *port,
1335390334bSHoratiu Vultur 			    struct flow_action *action,
1345390334bSHoratiu Vultur 			    struct flow_action_entry *act,
1355390334bSHoratiu Vultur 			    unsigned long police_id,
1365390334bSHoratiu Vultur 			    bool ingress,
1375390334bSHoratiu Vultur 			    struct netlink_ext_ack *extack)
1385390334bSHoratiu Vultur {
1395390334bSHoratiu Vultur 	struct lan966x *lan966x = port->lan966x;
1405390334bSHoratiu Vultur 	struct rtnl_link_stats64 new_stats;
1415390334bSHoratiu Vultur 	struct lan966x_tc_policer pol;
1425390334bSHoratiu Vultur 	struct flow_stats *old_stats;
1435390334bSHoratiu Vultur 	int err;
1445390334bSHoratiu Vultur 
1455390334bSHoratiu Vultur 	err = lan966x_police_validate(port, action, act, police_id, ingress,
1465390334bSHoratiu Vultur 				      extack);
1475390334bSHoratiu Vultur 	if (err)
1485390334bSHoratiu Vultur 		return err;
1495390334bSHoratiu Vultur 
1505390334bSHoratiu Vultur 	memset(&pol, 0, sizeof(pol));
1515390334bSHoratiu Vultur 
1525390334bSHoratiu Vultur 	pol.rate = div_u64(act->police.rate_bytes_ps, 1000) * 8;
1535390334bSHoratiu Vultur 	pol.burst = act->police.burst;
1545390334bSHoratiu Vultur 
1555390334bSHoratiu Vultur 	err = lan966x_police_add(port, &pol, POL_IDX_PORT + port->chip_port);
1565390334bSHoratiu Vultur 	if (err) {
1575390334bSHoratiu Vultur 		NL_SET_ERR_MSG_MOD(extack,
1585390334bSHoratiu Vultur 				   "Failed to add policer to port");
1595390334bSHoratiu Vultur 		return err;
1605390334bSHoratiu Vultur 	}
1615390334bSHoratiu Vultur 
1625390334bSHoratiu Vultur 	lan_rmw(ANA_POL_CFG_PORT_POL_ENA_SET(1) |
1635390334bSHoratiu Vultur 		ANA_POL_CFG_POL_ORDER_SET(POL_ORDER),
1645390334bSHoratiu Vultur 		ANA_POL_CFG_PORT_POL_ENA |
1655390334bSHoratiu Vultur 		ANA_POL_CFG_POL_ORDER,
1665390334bSHoratiu Vultur 		lan966x, ANA_POL_CFG(port->chip_port));
1675390334bSHoratiu Vultur 
1685390334bSHoratiu Vultur 	port->tc.police_id = police_id;
1695390334bSHoratiu Vultur 
1705390334bSHoratiu Vultur 	/* Setup initial stats */
1715390334bSHoratiu Vultur 	old_stats = &port->tc.police_stat;
1725390334bSHoratiu Vultur 	lan966x_stats_get(port->dev, &new_stats);
1735390334bSHoratiu Vultur 	old_stats->bytes = new_stats.rx_bytes;
1745390334bSHoratiu Vultur 	old_stats->pkts = new_stats.rx_packets;
1755390334bSHoratiu Vultur 	old_stats->drops = new_stats.rx_dropped;
1765390334bSHoratiu Vultur 	old_stats->lastused = jiffies;
1775390334bSHoratiu Vultur 
1785390334bSHoratiu Vultur 	return 0;
1795390334bSHoratiu Vultur }
1805390334bSHoratiu Vultur 
lan966x_police_port_del(struct lan966x_port * port,unsigned long police_id,struct netlink_ext_ack * extack)1815390334bSHoratiu Vultur int lan966x_police_port_del(struct lan966x_port *port,
1825390334bSHoratiu Vultur 			    unsigned long police_id,
1835390334bSHoratiu Vultur 			    struct netlink_ext_ack *extack)
1845390334bSHoratiu Vultur {
1855390334bSHoratiu Vultur 	struct lan966x *lan966x = port->lan966x;
1865390334bSHoratiu Vultur 
1875390334bSHoratiu Vultur 	if (port->tc.police_id != police_id) {
1885390334bSHoratiu Vultur 		NL_SET_ERR_MSG_MOD(extack,
1895390334bSHoratiu Vultur 				   "Invalid policer id");
1905390334bSHoratiu Vultur 		return -EINVAL;
1915390334bSHoratiu Vultur 	}
1925390334bSHoratiu Vultur 
193*68a84a12SHoratiu Vultur 	lan966x_police_del(port, POL_IDX_PORT + port->chip_port);
1945390334bSHoratiu Vultur 
1955390334bSHoratiu Vultur 	lan_rmw(ANA_POL_CFG_PORT_POL_ENA_SET(0) |
1965390334bSHoratiu Vultur 		ANA_POL_CFG_POL_ORDER_SET(POL_ORDER),
1975390334bSHoratiu Vultur 		ANA_POL_CFG_PORT_POL_ENA |
1985390334bSHoratiu Vultur 		ANA_POL_CFG_POL_ORDER,
1995390334bSHoratiu Vultur 		lan966x, ANA_POL_CFG(port->chip_port));
2005390334bSHoratiu Vultur 
2015390334bSHoratiu Vultur 	port->tc.police_id = 0;
2025390334bSHoratiu Vultur 
2035390334bSHoratiu Vultur 	return 0;
2045390334bSHoratiu Vultur }
2055390334bSHoratiu Vultur 
lan966x_police_port_stats(struct lan966x_port * port,struct flow_stats * stats)2065390334bSHoratiu Vultur void lan966x_police_port_stats(struct lan966x_port *port,
2075390334bSHoratiu Vultur 			       struct flow_stats *stats)
2085390334bSHoratiu Vultur {
2095390334bSHoratiu Vultur 	struct rtnl_link_stats64 new_stats;
2105390334bSHoratiu Vultur 	struct flow_stats *old_stats;
2115390334bSHoratiu Vultur 
2125390334bSHoratiu Vultur 	old_stats = &port->tc.police_stat;
2135390334bSHoratiu Vultur 	lan966x_stats_get(port->dev, &new_stats);
2145390334bSHoratiu Vultur 
2155390334bSHoratiu Vultur 	flow_stats_update(stats,
2165390334bSHoratiu Vultur 			  new_stats.rx_bytes - old_stats->bytes,
2175390334bSHoratiu Vultur 			  new_stats.rx_packets - old_stats->pkts,
2185390334bSHoratiu Vultur 			  new_stats.rx_dropped - old_stats->drops,
2195390334bSHoratiu Vultur 			  old_stats->lastused,
2205390334bSHoratiu Vultur 			  FLOW_ACTION_HW_STATS_IMMEDIATE);
2215390334bSHoratiu Vultur 
2225390334bSHoratiu Vultur 	old_stats->bytes = new_stats.rx_bytes;
2235390334bSHoratiu Vultur 	old_stats->pkts = new_stats.rx_packets;
2245390334bSHoratiu Vultur 	old_stats->drops = new_stats.rx_dropped;
2255390334bSHoratiu Vultur 	old_stats->lastused = jiffies;
2265390334bSHoratiu Vultur }
227