diff mbox series

[net-next,8/9] net: lan966x: Add port keyset config and callback interface

Message ID 20221125095010.124458-9-horatiu.vultur@microchip.com (mailing list archive)
State New, archived
Headers show
Series Add support for lan966x IS2 VCAP | expand

Commit Message

Horatiu Vultur Nov. 25, 2022, 9:50 a.m. UTC
Implement vcap_operations and enable default port keyset configuration
for each port. Now it is possible actually write/read/move entries in
the VCAP.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 .../microchip/lan966x/lan966x_vcap_impl.c     | 366 ++++++++++++++++++
 1 file changed, 366 insertions(+)
diff mbox series

Patch

diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
index 08253dd4a306f..44f40d9149470 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
@@ -13,6 +13,13 @@ 
 
 #define LAN966X_IS2_LOOKUPS 2
 
+enum vcap_is2_port_sel_ipv6 {
+	VCAP_IS2_PS_IPV6_TCPUDP_OTHER,
+	VCAP_IS2_PS_IPV6_STD,
+	VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER,
+	VCAP_IS2_PS_IPV6_MAC_ETYPE,
+};
+
 static struct lan966x_vcap_inst {
 	enum vcap_type vtype; /* type of vcap */
 	int tgt_inst; /* hardware instance number */
@@ -73,6 +80,347 @@  static void __lan966x_vcap_range_init(struct lan966x *lan966x,
 	lan966x_vcap_wait_update(lan966x, admin->tgt_inst);
 }
 
+static int lan966x_vcap_cid_to_lookup(int cid)
+{
+	if (cid >= LAN966X_VCAP_CID_IS2_L1 &&
+	    cid < LAN966X_VCAP_CID_IS2_MAX)
+		return 1;
+
+	return 0;
+}
+
+static int
+lan966x_vcap_is2_get_port_keysets(struct net_device *dev, int lookup,
+				  struct vcap_keyset_list *keysetlist,
+				  u16 l3_proto)
+{
+	struct lan966x_port *port = netdev_priv(dev);
+	struct lan966x *lan966x = port->lan966x;
+	bool found = false;
+	u32 val;
+
+	/* Check if the port keyset selection is enabled */
+	val = lan_rd(lan966x, ANA_VCAP_S2_CFG(port->chip_port));
+	if (!ANA_VCAP_S2_CFG_ENA_GET(val))
+		return -ENOENT;
+
+	/* Collect all keysets for the port in a list */
+	if (l3_proto == ETH_P_ALL)
+		vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+
+	if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_SNAP) {
+		if (ANA_VCAP_S2_CFG_SNAP_DIS_GET(val) & (BIT(0) << lookup))
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_LLC);
+		else
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_SNAP);
+
+		found = true;
+	}
+
+	if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_CFM) {
+		if (ANA_VCAP_S2_CFG_OAM_DIS_GET(val) & (BIT(0) << lookup))
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+		else
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_OAM);
+
+		found = true;
+	}
+
+	if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_ARP) {
+		if (ANA_VCAP_S2_CFG_ARP_DIS_GET(val) & (BIT(0) << lookup))
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+		else
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_ARP);
+
+		found = true;
+	}
+
+	if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) {
+		if (ANA_VCAP_S2_CFG_IP_OTHER_DIS_GET(val) & (BIT(0) << lookup))
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+		else
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
+
+		if (ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_GET(val) & (BIT(0) << lookup))
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+		else
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
+
+		found = true;
+	}
+
+	if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) {
+		switch (ANA_VCAP_S2_CFG_IP6_CFG_GET(val) & (0x3 << lookup)) {
+		case VCAP_IS2_PS_IPV6_TCPUDP_OTHER:
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_OTHER);
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_TCP_UDP);
+			break;
+		case VCAP_IS2_PS_IPV6_STD:
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD);
+			break;
+		case VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER:
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
+			break;
+		case VCAP_IS2_PS_IPV6_MAC_ETYPE:
+			vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+			break;
+		}
+
+		found = true;
+	}
+
+	if (!found)
+		vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+
+	return 0;
+}
+
+static enum vcap_keyfield_set
+lan966x_vcap_validate_keyset(struct net_device *dev,
+			     struct vcap_admin *admin,
+			     struct vcap_rule *rule,
+			     struct vcap_keyset_list *kslist,
+			     u16 l3_proto)
+{
+	struct vcap_keyset_list keysetlist = {};
+	enum vcap_keyfield_set keysets[10] = {};
+	int lookup;
+	int err;
+
+	if (!kslist || kslist->cnt == 0)
+		return VCAP_KFS_NO_VALUE;
+
+	lookup = lan966x_vcap_cid_to_lookup(rule->vcap_chain_id);
+	keysetlist.max = ARRAY_SIZE(keysets);
+	keysetlist.keysets = keysets;
+	err = lan966x_vcap_is2_get_port_keysets(dev, lookup, &keysetlist,
+						l3_proto);
+	if (err)
+		return VCAP_KFS_NO_VALUE;
+
+	/* Check if there is a match and return the match */
+	for (int i = 0; i < kslist->cnt; ++i)
+		for (int j = 0; j < keysetlist.cnt; ++j)
+			if (kslist->keysets[i] == keysets[j])
+				return kslist->keysets[i];
+
+	return VCAP_KFS_NO_VALUE;
+}
+
+static bool lan966x_vcap_is_first_chain(struct vcap_rule *rule)
+{
+	return (rule->vcap_chain_id >= LAN966X_VCAP_CID_IS2_L0 &&
+		rule->vcap_chain_id < LAN966X_VCAP_CID_IS2_L1);
+}
+
+static void lan966x_vcap_add_default_fields(struct net_device *dev,
+					    struct vcap_admin *admin,
+					    struct vcap_rule *rule)
+{
+	struct lan966x_port *port = netdev_priv(dev);
+
+	vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0,
+			      ~BIT(port->chip_port));
+
+	if (lan966x_vcap_is_first_chain(rule))
+		vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS,
+				      VCAP_BIT_1);
+	else
+		vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS,
+				      VCAP_BIT_0);
+}
+
+static void lan966x_vcap_cache_erase(struct vcap_admin *admin)
+{
+	memset(admin->cache.keystream, 0, STREAMSIZE);
+	memset(admin->cache.maskstream, 0, STREAMSIZE);
+	memset(admin->cache.actionstream, 0, STREAMSIZE);
+	memset(&admin->cache.counter, 0, sizeof(admin->cache.counter));
+}
+
+static void lan966x_vcap_cache_write(struct net_device *dev,
+				     struct vcap_admin *admin,
+				     enum vcap_selection sel,
+				     u32 start,
+				     u32 count)
+{
+	struct lan966x_port *port = netdev_priv(dev);
+	struct lan966x *lan966x = port->lan966x;
+	u32 *keystr, *mskstr, *actstr;
+
+	keystr = &admin->cache.keystream[start];
+	mskstr = &admin->cache.maskstream[start];
+	actstr = &admin->cache.actionstream[start];
+
+	switch (sel) {
+	case VCAP_SEL_ENTRY:
+		for (int i = 0; i < count; ++i) {
+			lan_wr(keystr[i] & mskstr[i], lan966x,
+			       VCAP_ENTRY_DAT(admin->tgt_inst, i));
+			lan_wr(~mskstr[i], lan966x,
+			       VCAP_MASK_DAT(admin->tgt_inst, i));
+		}
+		break;
+	case VCAP_SEL_ACTION:
+		for (int i = 0; i < count; ++i)
+			lan_wr(actstr[i], lan966x,
+			       VCAP_ACTION_DAT(admin->tgt_inst, i));
+		break;
+	case VCAP_SEL_COUNTER:
+		admin->cache.sticky = admin->cache.counter > 0;
+		lan_wr(admin->cache.counter, lan966x,
+		       VCAP_CNT_DAT(admin->tgt_inst, 0));
+		break;
+	default:
+		break;
+	}
+}
+
+static void lan966x_vcap_cache_read(struct net_device *dev,
+				    struct vcap_admin *admin,
+				    enum vcap_selection sel,
+				    u32 start,
+				    u32 count)
+{
+	struct lan966x_port *port = netdev_priv(dev);
+	struct lan966x *lan966x = port->lan966x;
+	int instance = admin->tgt_inst;
+	u32 *keystr, *mskstr, *actstr;
+
+	keystr = &admin->cache.keystream[start];
+	mskstr = &admin->cache.maskstream[start];
+	actstr = &admin->cache.actionstream[start];
+
+	if (sel & VCAP_SEL_ENTRY) {
+		for (int i = 0; i < count; ++i) {
+			keystr[i] =
+				lan_rd(lan966x, VCAP_ENTRY_DAT(instance, i));
+			mskstr[i] =
+				~lan_rd(lan966x, VCAP_MASK_DAT(instance, i));
+		}
+	}
+
+	if (sel & VCAP_SEL_ACTION)
+		for (int i = 0; i < count; ++i)
+			actstr[i] =
+				lan_rd(lan966x, VCAP_ACTION_DAT(instance, i));
+
+	if (sel & VCAP_SEL_COUNTER) {
+		admin->cache.counter =
+			lan_rd(lan966x, VCAP_CNT_DAT(instance, 0));
+		admin->cache.sticky = admin->cache.counter > 0;
+	}
+}
+
+static void lan966x_vcap_range_init(struct net_device *dev,
+				    struct vcap_admin *admin,
+				    u32 addr,
+				    u32 count)
+{
+	struct lan966x_port *port = netdev_priv(dev);
+	struct lan966x *lan966x = port->lan966x;
+
+	__lan966x_vcap_range_init(lan966x, admin, addr, count);
+}
+
+static void lan966x_vcap_update(struct net_device *dev,
+				struct vcap_admin *admin,
+				enum vcap_command cmd,
+				enum vcap_selection sel,
+				u32 addr)
+{
+	struct lan966x_port *port = netdev_priv(dev);
+	struct lan966x *lan966x = port->lan966x;
+	bool clear;
+
+	clear = (cmd == VCAP_CMD_INITIALIZE);
+
+	lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(0) |
+	       VCAP_MV_CFG_MV_SIZE_SET(0),
+	       lan966x, VCAP_MV_CFG(admin->tgt_inst));
+
+	lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) |
+	       VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET((VCAP_SEL_ENTRY & sel) == 0) |
+	       VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET((VCAP_SEL_ACTION & sel) == 0) |
+	       VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET((VCAP_SEL_COUNTER & sel) == 0) |
+	       VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) |
+	       VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(clear) |
+	       VCAP_UPDATE_CTRL_UPDATE_SHOT,
+	       lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst));
+
+	lan966x_vcap_wait_update(lan966x, admin->tgt_inst);
+}
+
+static void lan966x_vcap_move(struct net_device *dev,
+			      struct vcap_admin *admin,
+			      u32 addr, int offset, int count)
+{
+	struct lan966x_port *port = netdev_priv(dev);
+	struct lan966x *lan966x = port->lan966x;
+	enum vcap_command cmd;
+	u16 mv_num_pos;
+	u16 mv_size;
+
+	mv_size = count - 1;
+	if (offset > 0) {
+		mv_num_pos = offset - 1;
+		cmd = VCAP_CMD_MOVE_DOWN;
+	} else {
+		mv_num_pos = -offset - 1;
+		cmd = VCAP_CMD_MOVE_UP;
+	}
+
+	lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(mv_num_pos) |
+	       VCAP_MV_CFG_MV_SIZE_SET(mv_size),
+	       lan966x, VCAP_MV_CFG(admin->tgt_inst));
+
+	lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) |
+	       VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(0) |
+	       VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(0) |
+	       VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(0) |
+	       VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) |
+	       VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(false) |
+	       VCAP_UPDATE_CTRL_UPDATE_SHOT,
+	       lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst));
+
+	lan966x_vcap_wait_update(lan966x, admin->tgt_inst);
+}
+
+static int lan966x_vcap_port_info(struct net_device *dev,
+				  struct vcap_admin *admin,
+				  struct vcap_output_print *out)
+{
+	return 0;
+}
+
+static int lan966x_vcap_enable(struct net_device *dev,
+			       struct vcap_admin *admin,
+			       bool enable)
+{
+	struct lan966x_port *port = netdev_priv(dev);
+	struct lan966x *lan966x = port->lan966x;
+
+	lan_rmw(ANA_VCAP_S2_CFG_ENA_SET(enable),
+		ANA_VCAP_S2_CFG_ENA,
+		lan966x, ANA_VCAP_S2_CFG(port->chip_port));
+
+	return 0;
+}
+
+static struct vcap_operations lan966x_vcap_ops = {
+	.validate_keyset = lan966x_vcap_validate_keyset,
+	.add_default_fields = lan966x_vcap_add_default_fields,
+	.cache_erase = lan966x_vcap_cache_erase,
+	.cache_write = lan966x_vcap_cache_write,
+	.cache_read = lan966x_vcap_cache_read,
+	.init = lan966x_vcap_range_init,
+	.update = lan966x_vcap_update,
+	.move = lan966x_vcap_move,
+	.port_info = lan966x_vcap_port_info,
+	.enable = lan966x_vcap_enable,
+};
+
 static void lan966x_vcap_admin_free(struct vcap_admin *admin)
 {
 	if (!admin)
@@ -98,12 +446,19 @@  lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl,
 	mutex_init(&admin->lock);
 	INIT_LIST_HEAD(&admin->list);
 	INIT_LIST_HEAD(&admin->rules);
+	INIT_LIST_HEAD(&admin->enabled);
+
 	admin->vtype = cfg->vtype;
 	admin->vinst = 0;
+	admin->w32be = true;
+	admin->tgt_inst = cfg->tgt_inst;
+
 	admin->lookups = cfg->lookups;
 	admin->lookups_per_instance = cfg->lookups;
+
 	admin->first_cid = cfg->first_cid;
 	admin->last_cid = cfg->last_cid;
+
 	admin->cache.keystream = kzalloc(STREAMSIZE, GFP_KERNEL);
 	admin->cache.maskstream = kzalloc(STREAMSIZE, GFP_KERNEL);
 	admin->cache.actionstream = kzalloc(STREAMSIZE, GFP_KERNEL);
@@ -135,6 +490,13 @@  static void lan966x_vcap_block_init(struct lan966x *lan966x,
 					admin->first_valid_addr);
 }
 
+static void lan966x_vcap_port_key_deselection(struct lan966x *lan966x,
+					      struct vcap_admin *admin)
+{
+	for (int p = 0; p < lan966x->num_phys_ports; ++p)
+		lan_wr(0, lan966x, ANA_VCAP_S2_CFG(p));
+}
+
 int lan966x_vcap_init(struct lan966x *lan966x)
 {
 	struct lan966x_vcap_inst *cfg;
@@ -147,6 +509,7 @@  int lan966x_vcap_init(struct lan966x *lan966x)
 
 	ctrl->vcaps = lan966x_vcaps;
 	ctrl->stats = &lan966x_vcap_stats;
+	ctrl->ops = &lan966x_vcap_ops;
 
 	INIT_LIST_HEAD(&ctrl->list);
 	for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) {
@@ -157,6 +520,8 @@  int lan966x_vcap_init(struct lan966x *lan966x)
 			return PTR_ERR(admin);
 
 		lan966x_vcap_block_init(lan966x, admin, cfg);
+		lan966x_vcap_port_key_deselection(lan966x, admin);
+
 		list_add_tail(&admin->list, &ctrl->list);
 	}
 
@@ -175,6 +540,7 @@  void lan966x_vcap_deinit(struct lan966x *lan966x)
 		return;
 
 	list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) {
+		lan966x_vcap_port_key_deselection(lan966x, admin);
 		vcap_del_rules(ctrl, admin);
 		list_del(&admin->list);
 		lan966x_vcap_admin_free(admin);