162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
262306a36Sopenharmony_ci#include "dpaa2-eth.h"
362306a36Sopenharmony_ci/* Copyright 2020 NXP
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define DPAA2_ETH_TRAP_DROP(_id, _group_id)					\
762306a36Sopenharmony_ci	DEVLINK_TRAP_GENERIC(DROP, DROP, _id,					\
862306a36Sopenharmony_ci			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, 0)
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic const struct devlink_trap_group dpaa2_eth_trap_groups_arr[] = {
1162306a36Sopenharmony_ci	DEVLINK_TRAP_GROUP_GENERIC(PARSER_ERROR_DROPS, 0),
1262306a36Sopenharmony_ci};
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic const struct devlink_trap dpaa2_eth_traps_arr[] = {
1562306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(VXLAN_PARSING, PARSER_ERROR_DROPS),
1662306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(LLC_SNAP_PARSING, PARSER_ERROR_DROPS),
1762306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(VLAN_PARSING, PARSER_ERROR_DROPS),
1862306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(PPPOE_PPP_PARSING, PARSER_ERROR_DROPS),
1962306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(MPLS_PARSING, PARSER_ERROR_DROPS),
2062306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(ARP_PARSING, PARSER_ERROR_DROPS),
2162306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(IP_1_PARSING, PARSER_ERROR_DROPS),
2262306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(IP_N_PARSING, PARSER_ERROR_DROPS),
2362306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(GRE_PARSING, PARSER_ERROR_DROPS),
2462306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(UDP_PARSING, PARSER_ERROR_DROPS),
2562306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(TCP_PARSING, PARSER_ERROR_DROPS),
2662306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(IPSEC_PARSING, PARSER_ERROR_DROPS),
2762306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(SCTP_PARSING, PARSER_ERROR_DROPS),
2862306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(DCCP_PARSING, PARSER_ERROR_DROPS),
2962306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(GTP_PARSING, PARSER_ERROR_DROPS),
3062306a36Sopenharmony_ci	DPAA2_ETH_TRAP_DROP(ESP_PARSING, PARSER_ERROR_DROPS),
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int dpaa2_eth_dl_info_get(struct devlink *devlink,
3462306a36Sopenharmony_ci				 struct devlink_info_req *req,
3562306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct dpaa2_eth_devlink_priv *dl_priv = devlink_priv(devlink);
3862306a36Sopenharmony_ci	struct dpaa2_eth_priv *priv = dl_priv->dpaa2_priv;
3962306a36Sopenharmony_ci	char buf[10];
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	scnprintf(buf, 10, "%d.%d", priv->dpni_ver_major, priv->dpni_ver_minor);
4262306a36Sopenharmony_ci	return devlink_info_version_running_put(req, "dpni", buf);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic struct dpaa2_eth_trap_item *
4662306a36Sopenharmony_cidpaa2_eth_dl_trap_item_lookup(struct dpaa2_eth_priv *priv, u16 trap_id)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct dpaa2_eth_trap_data *dpaa2_eth_trap_data = priv->trap_data;
4962306a36Sopenharmony_ci	int i;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(dpaa2_eth_traps_arr); i++) {
5262306a36Sopenharmony_ci		if (dpaa2_eth_traps_arr[i].id == trap_id)
5362306a36Sopenharmony_ci			return &dpaa2_eth_trap_data->trap_items_arr[i];
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return NULL;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct dpaa2_eth_trap_item *dpaa2_eth_dl_get_trap(struct dpaa2_eth_priv *priv,
6062306a36Sopenharmony_ci						  struct dpaa2_fapr *fapr)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	static const struct dpaa2_faf_error_bit {
6362306a36Sopenharmony_ci		int position;
6462306a36Sopenharmony_ci		enum devlink_trap_generic_id trap_id;
6562306a36Sopenharmony_ci	} faf_bits[] = {
6662306a36Sopenharmony_ci		{ .position = 5,  .trap_id = DEVLINK_TRAP_GENERIC_ID_VXLAN_PARSING },
6762306a36Sopenharmony_ci		{ .position = 20, .trap_id = DEVLINK_TRAP_GENERIC_ID_LLC_SNAP_PARSING },
6862306a36Sopenharmony_ci		{ .position = 24, .trap_id = DEVLINK_TRAP_GENERIC_ID_VLAN_PARSING },
6962306a36Sopenharmony_ci		{ .position = 26, .trap_id = DEVLINK_TRAP_GENERIC_ID_PPPOE_PPP_PARSING },
7062306a36Sopenharmony_ci		{ .position = 29, .trap_id = DEVLINK_TRAP_GENERIC_ID_MPLS_PARSING },
7162306a36Sopenharmony_ci		{ .position = 31, .trap_id = DEVLINK_TRAP_GENERIC_ID_ARP_PARSING },
7262306a36Sopenharmony_ci		{ .position = 52, .trap_id = DEVLINK_TRAP_GENERIC_ID_IP_1_PARSING },
7362306a36Sopenharmony_ci		{ .position = 61, .trap_id = DEVLINK_TRAP_GENERIC_ID_IP_N_PARSING },
7462306a36Sopenharmony_ci		{ .position = 67, .trap_id = DEVLINK_TRAP_GENERIC_ID_GRE_PARSING },
7562306a36Sopenharmony_ci		{ .position = 71, .trap_id = DEVLINK_TRAP_GENERIC_ID_UDP_PARSING },
7662306a36Sopenharmony_ci		{ .position = 76, .trap_id = DEVLINK_TRAP_GENERIC_ID_TCP_PARSING },
7762306a36Sopenharmony_ci		{ .position = 80, .trap_id = DEVLINK_TRAP_GENERIC_ID_IPSEC_PARSING },
7862306a36Sopenharmony_ci		{ .position = 82, .trap_id = DEVLINK_TRAP_GENERIC_ID_SCTP_PARSING },
7962306a36Sopenharmony_ci		{ .position = 84, .trap_id = DEVLINK_TRAP_GENERIC_ID_DCCP_PARSING },
8062306a36Sopenharmony_ci		{ .position = 88, .trap_id = DEVLINK_TRAP_GENERIC_ID_GTP_PARSING },
8162306a36Sopenharmony_ci		{ .position = 90, .trap_id = DEVLINK_TRAP_GENERIC_ID_ESP_PARSING },
8262306a36Sopenharmony_ci	};
8362306a36Sopenharmony_ci	u64 faf_word;
8462306a36Sopenharmony_ci	u64 mask;
8562306a36Sopenharmony_ci	int i;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(faf_bits); i++) {
8862306a36Sopenharmony_ci		if (faf_bits[i].position < 32) {
8962306a36Sopenharmony_ci			/* Low part of FAF.
9062306a36Sopenharmony_ci			 * position ranges from 31 to 0, mask from 0 to 31.
9162306a36Sopenharmony_ci			 */
9262306a36Sopenharmony_ci			mask = 1ull << (31 - faf_bits[i].position);
9362306a36Sopenharmony_ci			faf_word = __le32_to_cpu(fapr->faf_lo);
9462306a36Sopenharmony_ci		} else {
9562306a36Sopenharmony_ci			/* High part of FAF.
9662306a36Sopenharmony_ci			 * position ranges from 95 to 32, mask from 0 to 63.
9762306a36Sopenharmony_ci			 */
9862306a36Sopenharmony_ci			mask = 1ull << (63 - (faf_bits[i].position - 32));
9962306a36Sopenharmony_ci			faf_word = __le64_to_cpu(fapr->faf_hi);
10062306a36Sopenharmony_ci		}
10162306a36Sopenharmony_ci		if (faf_word & mask)
10262306a36Sopenharmony_ci			return dpaa2_eth_dl_trap_item_lookup(priv, faf_bits[i].trap_id);
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	return NULL;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic int dpaa2_eth_dl_trap_init(struct devlink *devlink,
10862306a36Sopenharmony_ci				  const struct devlink_trap *trap,
10962306a36Sopenharmony_ci				  void *trap_ctx)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct dpaa2_eth_devlink_priv *dl_priv = devlink_priv(devlink);
11262306a36Sopenharmony_ci	struct dpaa2_eth_priv *priv = dl_priv->dpaa2_priv;
11362306a36Sopenharmony_ci	struct dpaa2_eth_trap_item *dpaa2_eth_trap_item;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	dpaa2_eth_trap_item = dpaa2_eth_dl_trap_item_lookup(priv, trap->id);
11662306a36Sopenharmony_ci	if (WARN_ON(!dpaa2_eth_trap_item))
11762306a36Sopenharmony_ci		return -ENOENT;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	dpaa2_eth_trap_item->trap_ctx = trap_ctx;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int dpaa2_eth_dl_trap_action_set(struct devlink *devlink,
12562306a36Sopenharmony_ci					const struct devlink_trap *trap,
12662306a36Sopenharmony_ci					enum devlink_trap_action action,
12762306a36Sopenharmony_ci					struct netlink_ext_ack *extack)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	/* No support for changing the action of an independent packet trap,
13062306a36Sopenharmony_ci	 * only per trap group - parser error drops
13162306a36Sopenharmony_ci	 */
13262306a36Sopenharmony_ci	NL_SET_ERR_MSG_MOD(extack,
13362306a36Sopenharmony_ci			   "Cannot change trap action independently of group");
13462306a36Sopenharmony_ci	return -EOPNOTSUPP;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int dpaa2_eth_dl_trap_group_action_set(struct devlink *devlink,
13862306a36Sopenharmony_ci					      const struct devlink_trap_group *group,
13962306a36Sopenharmony_ci					      enum devlink_trap_action action,
14062306a36Sopenharmony_ci					      struct netlink_ext_ack *extack)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct dpaa2_eth_devlink_priv *dl_priv = devlink_priv(devlink);
14362306a36Sopenharmony_ci	struct dpaa2_eth_priv *priv = dl_priv->dpaa2_priv;
14462306a36Sopenharmony_ci	struct net_device *net_dev = priv->net_dev;
14562306a36Sopenharmony_ci	struct device *dev = net_dev->dev.parent;
14662306a36Sopenharmony_ci	struct dpni_error_cfg err_cfg = {0};
14762306a36Sopenharmony_ci	int err;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (group->id != DEVLINK_TRAP_GROUP_GENERIC_ID_PARSER_ERROR_DROPS)
15062306a36Sopenharmony_ci		return -EOPNOTSUPP;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* Configure handling of frames marked as errors from the parser */
15362306a36Sopenharmony_ci	err_cfg.errors = DPAA2_FAS_RX_ERR_MASK;
15462306a36Sopenharmony_ci	err_cfg.set_frame_annotation = 1;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	switch (action) {
15762306a36Sopenharmony_ci	case DEVLINK_TRAP_ACTION_DROP:
15862306a36Sopenharmony_ci		err_cfg.error_action = DPNI_ERROR_ACTION_DISCARD;
15962306a36Sopenharmony_ci		break;
16062306a36Sopenharmony_ci	case DEVLINK_TRAP_ACTION_TRAP:
16162306a36Sopenharmony_ci		err_cfg.error_action = DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE;
16262306a36Sopenharmony_ci		break;
16362306a36Sopenharmony_ci	default:
16462306a36Sopenharmony_ci		return -EOPNOTSUPP;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	err = dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token, &err_cfg);
16862306a36Sopenharmony_ci	if (err) {
16962306a36Sopenharmony_ci		dev_err(dev, "dpni_set_errors_behavior failed\n");
17062306a36Sopenharmony_ci		return err;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic const struct devlink_ops dpaa2_eth_devlink_ops = {
17762306a36Sopenharmony_ci	.info_get = dpaa2_eth_dl_info_get,
17862306a36Sopenharmony_ci	.trap_init = dpaa2_eth_dl_trap_init,
17962306a36Sopenharmony_ci	.trap_action_set = dpaa2_eth_dl_trap_action_set,
18062306a36Sopenharmony_ci	.trap_group_action_set = dpaa2_eth_dl_trap_group_action_set,
18162306a36Sopenharmony_ci};
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ciint dpaa2_eth_dl_alloc(struct dpaa2_eth_priv *priv)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct net_device *net_dev = priv->net_dev;
18662306a36Sopenharmony_ci	struct device *dev = net_dev->dev.parent;
18762306a36Sopenharmony_ci	struct dpaa2_eth_devlink_priv *dl_priv;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	priv->devlink =
19062306a36Sopenharmony_ci		devlink_alloc(&dpaa2_eth_devlink_ops, sizeof(*dl_priv), dev);
19162306a36Sopenharmony_ci	if (!priv->devlink) {
19262306a36Sopenharmony_ci		dev_err(dev, "devlink_alloc failed\n");
19362306a36Sopenharmony_ci		return -ENOMEM;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci	dl_priv = devlink_priv(priv->devlink);
19662306a36Sopenharmony_ci	dl_priv->dpaa2_priv = priv;
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_civoid dpaa2_eth_dl_free(struct dpaa2_eth_priv *priv)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	devlink_free(priv->devlink);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_civoid dpaa2_eth_dl_register(struct dpaa2_eth_priv *priv)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	devlink_register(priv->devlink);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_civoid dpaa2_eth_dl_unregister(struct dpaa2_eth_priv *priv)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	devlink_unregister(priv->devlink);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ciint dpaa2_eth_dl_port_add(struct dpaa2_eth_priv *priv)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct devlink_port *devlink_port = &priv->devlink_port;
21962306a36Sopenharmony_ci	struct devlink_port_attrs attrs = {};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
22262306a36Sopenharmony_ci	devlink_port_attrs_set(devlink_port, &attrs);
22362306a36Sopenharmony_ci	return devlink_port_register(priv->devlink, devlink_port, 0);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_civoid dpaa2_eth_dl_port_del(struct dpaa2_eth_priv *priv)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct devlink_port *devlink_port = &priv->devlink_port;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	devlink_port_unregister(devlink_port);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciint dpaa2_eth_dl_traps_register(struct dpaa2_eth_priv *priv)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct dpaa2_eth_trap_data *dpaa2_eth_trap_data;
23662306a36Sopenharmony_ci	struct net_device *net_dev = priv->net_dev;
23762306a36Sopenharmony_ci	struct device *dev = net_dev->dev.parent;
23862306a36Sopenharmony_ci	int err;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	dpaa2_eth_trap_data = kzalloc(sizeof(*dpaa2_eth_trap_data), GFP_KERNEL);
24162306a36Sopenharmony_ci	if (!dpaa2_eth_trap_data)
24262306a36Sopenharmony_ci		return -ENOMEM;
24362306a36Sopenharmony_ci	priv->trap_data = dpaa2_eth_trap_data;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	dpaa2_eth_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(dpaa2_eth_traps_arr),
24662306a36Sopenharmony_ci						      sizeof(struct dpaa2_eth_trap_item),
24762306a36Sopenharmony_ci						      GFP_KERNEL);
24862306a36Sopenharmony_ci	if (!dpaa2_eth_trap_data->trap_items_arr) {
24962306a36Sopenharmony_ci		err = -ENOMEM;
25062306a36Sopenharmony_ci		goto trap_data_free;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	err = devlink_trap_groups_register(priv->devlink, dpaa2_eth_trap_groups_arr,
25462306a36Sopenharmony_ci					   ARRAY_SIZE(dpaa2_eth_trap_groups_arr));
25562306a36Sopenharmony_ci	if (err) {
25662306a36Sopenharmony_ci		dev_err(dev, "devlink_trap_groups_register() = %d\n", err);
25762306a36Sopenharmony_ci		goto trap_items_arr_free;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	err = devlink_traps_register(priv->devlink, dpaa2_eth_traps_arr,
26162306a36Sopenharmony_ci				     ARRAY_SIZE(dpaa2_eth_traps_arr), priv);
26262306a36Sopenharmony_ci	if (err) {
26362306a36Sopenharmony_ci		dev_err(dev, "devlink_traps_register() = %d\n", err);
26462306a36Sopenharmony_ci		goto trap_groups_unregiser;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return 0;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_citrap_groups_unregiser:
27062306a36Sopenharmony_ci	devlink_trap_groups_unregister(priv->devlink, dpaa2_eth_trap_groups_arr,
27162306a36Sopenharmony_ci				       ARRAY_SIZE(dpaa2_eth_trap_groups_arr));
27262306a36Sopenharmony_citrap_items_arr_free:
27362306a36Sopenharmony_ci	kfree(dpaa2_eth_trap_data->trap_items_arr);
27462306a36Sopenharmony_citrap_data_free:
27562306a36Sopenharmony_ci	kfree(dpaa2_eth_trap_data);
27662306a36Sopenharmony_ci	priv->trap_data = NULL;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return err;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_civoid dpaa2_eth_dl_traps_unregister(struct dpaa2_eth_priv *priv)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	devlink_traps_unregister(priv->devlink, dpaa2_eth_traps_arr,
28462306a36Sopenharmony_ci				 ARRAY_SIZE(dpaa2_eth_traps_arr));
28562306a36Sopenharmony_ci	devlink_trap_groups_unregister(priv->devlink, dpaa2_eth_trap_groups_arr,
28662306a36Sopenharmony_ci				       ARRAY_SIZE(dpaa2_eth_trap_groups_arr));
28762306a36Sopenharmony_ci	kfree(priv->trap_data->trap_items_arr);
28862306a36Sopenharmony_ci	kfree(priv->trap_data);
28962306a36Sopenharmony_ci}
290