162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <net/switchdev.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include "br_private_mrp.h"
662306a36Sopenharmony_ci
762306a36Sopenharmony_cistatic enum br_mrp_hw_support
862306a36Sopenharmony_cibr_mrp_switchdev_port_obj(struct net_bridge *br,
962306a36Sopenharmony_ci			  const struct switchdev_obj *obj, bool add)
1062306a36Sopenharmony_ci{
1162306a36Sopenharmony_ci	int err;
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci	if (add)
1462306a36Sopenharmony_ci		err = switchdev_port_obj_add(br->dev, obj, NULL);
1562306a36Sopenharmony_ci	else
1662306a36Sopenharmony_ci		err = switchdev_port_obj_del(br->dev, obj);
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	/* In case of success just return and notify the SW that doesn't need
1962306a36Sopenharmony_ci	 * to do anything
2062306a36Sopenharmony_ci	 */
2162306a36Sopenharmony_ci	if (!err)
2262306a36Sopenharmony_ci		return BR_MRP_HW;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	if (err != -EOPNOTSUPP)
2562306a36Sopenharmony_ci		return BR_MRP_NONE;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	/* Continue with SW backup */
2862306a36Sopenharmony_ci	return BR_MRP_SW;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciint br_mrp_switchdev_add(struct net_bridge *br, struct br_mrp *mrp)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct switchdev_obj_mrp mrp_obj = {
3462306a36Sopenharmony_ci		.obj.orig_dev = br->dev,
3562306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_MRP,
3662306a36Sopenharmony_ci		.p_port = rtnl_dereference(mrp->p_port)->dev,
3762306a36Sopenharmony_ci		.s_port = rtnl_dereference(mrp->s_port)->dev,
3862306a36Sopenharmony_ci		.ring_id = mrp->ring_id,
3962306a36Sopenharmony_ci		.prio = mrp->prio,
4062306a36Sopenharmony_ci	};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
4362306a36Sopenharmony_ci		return 0;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return switchdev_port_obj_add(br->dev, &mrp_obj.obj, NULL);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciint br_mrp_switchdev_del(struct net_bridge *br, struct br_mrp *mrp)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct switchdev_obj_mrp mrp_obj = {
5162306a36Sopenharmony_ci		.obj.orig_dev = br->dev,
5262306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_MRP,
5362306a36Sopenharmony_ci		.p_port = NULL,
5462306a36Sopenharmony_ci		.s_port = NULL,
5562306a36Sopenharmony_ci		.ring_id = mrp->ring_id,
5662306a36Sopenharmony_ci	};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
5962306a36Sopenharmony_ci		return 0;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return switchdev_port_obj_del(br->dev, &mrp_obj.obj);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cienum br_mrp_hw_support
6562306a36Sopenharmony_cibr_mrp_switchdev_set_ring_role(struct net_bridge *br, struct br_mrp *mrp,
6662306a36Sopenharmony_ci			       enum br_mrp_ring_role_type role)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct switchdev_obj_ring_role_mrp mrp_role = {
6962306a36Sopenharmony_ci		.obj.orig_dev = br->dev,
7062306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_RING_ROLE_MRP,
7162306a36Sopenharmony_ci		.ring_role = role,
7262306a36Sopenharmony_ci		.ring_id = mrp->ring_id,
7362306a36Sopenharmony_ci		.sw_backup = false,
7462306a36Sopenharmony_ci	};
7562306a36Sopenharmony_ci	enum br_mrp_hw_support support;
7662306a36Sopenharmony_ci	int err;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
7962306a36Sopenharmony_ci		return BR_MRP_SW;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	support = br_mrp_switchdev_port_obj(br, &mrp_role.obj,
8262306a36Sopenharmony_ci					    role != BR_MRP_RING_ROLE_DISABLED);
8362306a36Sopenharmony_ci	if (support != BR_MRP_SW)
8462306a36Sopenharmony_ci		return support;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* If the driver can't configure to run completely the protocol in HW,
8762306a36Sopenharmony_ci	 * then try again to configure the HW so the SW can run the protocol.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	mrp_role.sw_backup = true;
9062306a36Sopenharmony_ci	if (role != BR_MRP_RING_ROLE_DISABLED)
9162306a36Sopenharmony_ci		err = switchdev_port_obj_add(br->dev, &mrp_role.obj, NULL);
9262306a36Sopenharmony_ci	else
9362306a36Sopenharmony_ci		err = switchdev_port_obj_del(br->dev, &mrp_role.obj);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (!err)
9662306a36Sopenharmony_ci		return BR_MRP_SW;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return BR_MRP_NONE;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cienum br_mrp_hw_support
10262306a36Sopenharmony_cibr_mrp_switchdev_send_ring_test(struct net_bridge *br, struct br_mrp *mrp,
10362306a36Sopenharmony_ci				u32 interval, u8 max_miss, u32 period,
10462306a36Sopenharmony_ci				bool monitor)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct switchdev_obj_ring_test_mrp test = {
10762306a36Sopenharmony_ci		.obj.orig_dev = br->dev,
10862306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_RING_TEST_MRP,
10962306a36Sopenharmony_ci		.interval = interval,
11062306a36Sopenharmony_ci		.max_miss = max_miss,
11162306a36Sopenharmony_ci		.ring_id = mrp->ring_id,
11262306a36Sopenharmony_ci		.period = period,
11362306a36Sopenharmony_ci		.monitor = monitor,
11462306a36Sopenharmony_ci	};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
11762306a36Sopenharmony_ci		return BR_MRP_SW;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return br_mrp_switchdev_port_obj(br, &test.obj, interval != 0);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ciint br_mrp_switchdev_set_ring_state(struct net_bridge *br,
12362306a36Sopenharmony_ci				    struct br_mrp *mrp,
12462306a36Sopenharmony_ci				    enum br_mrp_ring_state_type state)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct switchdev_obj_ring_state_mrp mrp_state = {
12762306a36Sopenharmony_ci		.obj.orig_dev = br->dev,
12862306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_RING_STATE_MRP,
12962306a36Sopenharmony_ci		.ring_state = state,
13062306a36Sopenharmony_ci		.ring_id = mrp->ring_id,
13162306a36Sopenharmony_ci	};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
13462306a36Sopenharmony_ci		return 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return switchdev_port_obj_add(br->dev, &mrp_state.obj, NULL);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cienum br_mrp_hw_support
14062306a36Sopenharmony_cibr_mrp_switchdev_set_in_role(struct net_bridge *br, struct br_mrp *mrp,
14162306a36Sopenharmony_ci			     u16 in_id, u32 ring_id,
14262306a36Sopenharmony_ci			     enum br_mrp_in_role_type role)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct switchdev_obj_in_role_mrp mrp_role = {
14562306a36Sopenharmony_ci		.obj.orig_dev = br->dev,
14662306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_IN_ROLE_MRP,
14762306a36Sopenharmony_ci		.in_role = role,
14862306a36Sopenharmony_ci		.in_id = mrp->in_id,
14962306a36Sopenharmony_ci		.ring_id = mrp->ring_id,
15062306a36Sopenharmony_ci		.i_port = rtnl_dereference(mrp->i_port)->dev,
15162306a36Sopenharmony_ci		.sw_backup = false,
15262306a36Sopenharmony_ci	};
15362306a36Sopenharmony_ci	enum br_mrp_hw_support support;
15462306a36Sopenharmony_ci	int err;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
15762306a36Sopenharmony_ci		return BR_MRP_SW;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	support = br_mrp_switchdev_port_obj(br, &mrp_role.obj,
16062306a36Sopenharmony_ci					    role != BR_MRP_IN_ROLE_DISABLED);
16162306a36Sopenharmony_ci	if (support != BR_MRP_NONE)
16262306a36Sopenharmony_ci		return support;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* If the driver can't configure to run completely the protocol in HW,
16562306a36Sopenharmony_ci	 * then try again to configure the HW so the SW can run the protocol.
16662306a36Sopenharmony_ci	 */
16762306a36Sopenharmony_ci	mrp_role.sw_backup = true;
16862306a36Sopenharmony_ci	if (role != BR_MRP_IN_ROLE_DISABLED)
16962306a36Sopenharmony_ci		err = switchdev_port_obj_add(br->dev, &mrp_role.obj, NULL);
17062306a36Sopenharmony_ci	else
17162306a36Sopenharmony_ci		err = switchdev_port_obj_del(br->dev, &mrp_role.obj);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (!err)
17462306a36Sopenharmony_ci		return BR_MRP_SW;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return BR_MRP_NONE;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciint br_mrp_switchdev_set_in_state(struct net_bridge *br, struct br_mrp *mrp,
18062306a36Sopenharmony_ci				  enum br_mrp_in_state_type state)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct switchdev_obj_in_state_mrp mrp_state = {
18362306a36Sopenharmony_ci		.obj.orig_dev = br->dev,
18462306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_IN_STATE_MRP,
18562306a36Sopenharmony_ci		.in_state = state,
18662306a36Sopenharmony_ci		.in_id = mrp->in_id,
18762306a36Sopenharmony_ci	};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
19062306a36Sopenharmony_ci		return 0;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return switchdev_port_obj_add(br->dev, &mrp_state.obj, NULL);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cienum br_mrp_hw_support
19662306a36Sopenharmony_cibr_mrp_switchdev_send_in_test(struct net_bridge *br, struct br_mrp *mrp,
19762306a36Sopenharmony_ci			      u32 interval, u8 max_miss, u32 period)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct switchdev_obj_in_test_mrp test = {
20062306a36Sopenharmony_ci		.obj.orig_dev = br->dev,
20162306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_IN_TEST_MRP,
20262306a36Sopenharmony_ci		.interval = interval,
20362306a36Sopenharmony_ci		.max_miss = max_miss,
20462306a36Sopenharmony_ci		.in_id = mrp->in_id,
20562306a36Sopenharmony_ci		.period = period,
20662306a36Sopenharmony_ci	};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
20962306a36Sopenharmony_ci		return BR_MRP_SW;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return br_mrp_switchdev_port_obj(br, &test.obj, interval != 0);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ciint br_mrp_port_switchdev_set_state(struct net_bridge_port *p, u32 state)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct switchdev_attr attr = {
21762306a36Sopenharmony_ci		.orig_dev = p->dev,
21862306a36Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_PORT_STP_STATE,
21962306a36Sopenharmony_ci		.u.stp_state = state,
22062306a36Sopenharmony_ci	};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
22362306a36Sopenharmony_ci		return 0;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return switchdev_port_attr_set(p->dev, &attr, NULL);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ciint br_mrp_port_switchdev_set_role(struct net_bridge_port *p,
22962306a36Sopenharmony_ci				   enum br_mrp_port_role_type role)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct switchdev_attr attr = {
23262306a36Sopenharmony_ci		.orig_dev = p->dev,
23362306a36Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_MRP_PORT_ROLE,
23462306a36Sopenharmony_ci		.u.mrp_port_role = role,
23562306a36Sopenharmony_ci	};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
23862306a36Sopenharmony_ci		return 0;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	return switchdev_port_attr_set(p->dev, &attr, NULL);
24162306a36Sopenharmony_ci}
242