xref: /openbmc/linux/drivers/net/ethernet/ti/am65-cpsw-switchdev.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
186e8b070SVignesh Raghavendra /* SPDX-License-Identifier: GPL-2.0 */
286e8b070SVignesh Raghavendra /* Texas Instruments K3 AM65 Ethernet Switchdev Driver
386e8b070SVignesh Raghavendra  *
486e8b070SVignesh Raghavendra  * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/
586e8b070SVignesh Raghavendra  *
686e8b070SVignesh Raghavendra  */
786e8b070SVignesh Raghavendra 
886e8b070SVignesh Raghavendra #include <linux/etherdevice.h>
986e8b070SVignesh Raghavendra #include <linux/if_bridge.h>
1086e8b070SVignesh Raghavendra #include <linux/netdevice.h>
1186e8b070SVignesh Raghavendra #include <linux/workqueue.h>
1286e8b070SVignesh Raghavendra #include <net/switchdev.h>
1386e8b070SVignesh Raghavendra 
1486e8b070SVignesh Raghavendra #include "am65-cpsw-nuss.h"
1586e8b070SVignesh Raghavendra #include "am65-cpsw-switchdev.h"
1686e8b070SVignesh Raghavendra #include "cpsw_ale.h"
1786e8b070SVignesh Raghavendra 
1886e8b070SVignesh Raghavendra struct am65_cpsw_switchdev_event_work {
1986e8b070SVignesh Raghavendra 	struct work_struct work;
2086e8b070SVignesh Raghavendra 	struct switchdev_notifier_fdb_info fdb_info;
2186e8b070SVignesh Raghavendra 	struct am65_cpsw_port *port;
2286e8b070SVignesh Raghavendra 	unsigned long event;
2386e8b070SVignesh Raghavendra };
2486e8b070SVignesh Raghavendra 
am65_cpsw_port_stp_state_set(struct am65_cpsw_port * port,u8 state)2586e8b070SVignesh Raghavendra static int am65_cpsw_port_stp_state_set(struct am65_cpsw_port *port, u8 state)
2686e8b070SVignesh Raghavendra {
2786e8b070SVignesh Raghavendra 	struct am65_cpsw_common *cpsw = port->common;
2886e8b070SVignesh Raghavendra 	u8 cpsw_state;
2986e8b070SVignesh Raghavendra 	int ret = 0;
3086e8b070SVignesh Raghavendra 
3186e8b070SVignesh Raghavendra 	switch (state) {
3286e8b070SVignesh Raghavendra 	case BR_STATE_FORWARDING:
3386e8b070SVignesh Raghavendra 		cpsw_state = ALE_PORT_STATE_FORWARD;
3486e8b070SVignesh Raghavendra 		break;
3586e8b070SVignesh Raghavendra 	case BR_STATE_LEARNING:
3686e8b070SVignesh Raghavendra 		cpsw_state = ALE_PORT_STATE_LEARN;
3786e8b070SVignesh Raghavendra 		break;
3886e8b070SVignesh Raghavendra 	case BR_STATE_DISABLED:
3986e8b070SVignesh Raghavendra 		cpsw_state = ALE_PORT_STATE_DISABLE;
4086e8b070SVignesh Raghavendra 		break;
4186e8b070SVignesh Raghavendra 	case BR_STATE_LISTENING:
4286e8b070SVignesh Raghavendra 	case BR_STATE_BLOCKING:
4386e8b070SVignesh Raghavendra 		cpsw_state = ALE_PORT_STATE_BLOCK;
4486e8b070SVignesh Raghavendra 		break;
4586e8b070SVignesh Raghavendra 	default:
4686e8b070SVignesh Raghavendra 		return -EOPNOTSUPP;
4786e8b070SVignesh Raghavendra 	}
4886e8b070SVignesh Raghavendra 
4986e8b070SVignesh Raghavendra 	ret = cpsw_ale_control_set(cpsw->ale, port->port_id,
5086e8b070SVignesh Raghavendra 				   ALE_PORT_STATE, cpsw_state);
5186e8b070SVignesh Raghavendra 	netdev_dbg(port->ndev, "ale state: %u\n", cpsw_state);
5286e8b070SVignesh Raghavendra 
5386e8b070SVignesh Raghavendra 	return ret;
5486e8b070SVignesh Raghavendra }
5586e8b070SVignesh Raghavendra 
am65_cpsw_port_attr_br_flags_set(struct am65_cpsw_port * port,struct net_device * orig_dev,struct switchdev_brport_flags flags)5686e8b070SVignesh Raghavendra static int am65_cpsw_port_attr_br_flags_set(struct am65_cpsw_port *port,
5786e8b070SVignesh Raghavendra 					    struct net_device *orig_dev,
58e18f4c18SVladimir Oltean 					    struct switchdev_brport_flags flags)
5986e8b070SVignesh Raghavendra {
6086e8b070SVignesh Raghavendra 	struct am65_cpsw_common *cpsw = port->common;
61e18f4c18SVladimir Oltean 
62e18f4c18SVladimir Oltean 	if (flags.mask & BR_MCAST_FLOOD) {
6386e8b070SVignesh Raghavendra 		bool unreg_mcast_add = false;
6486e8b070SVignesh Raghavendra 
65e18f4c18SVladimir Oltean 		if (flags.val & BR_MCAST_FLOOD)
6686e8b070SVignesh Raghavendra 			unreg_mcast_add = true;
67e18f4c18SVladimir Oltean 
6886e8b070SVignesh Raghavendra 		netdev_dbg(port->ndev, "BR_MCAST_FLOOD: %d port %u\n",
6986e8b070SVignesh Raghavendra 			   unreg_mcast_add, port->port_id);
7086e8b070SVignesh Raghavendra 
7186e8b070SVignesh Raghavendra 		cpsw_ale_set_unreg_mcast(cpsw->ale, BIT(port->port_id),
7286e8b070SVignesh Raghavendra 					 unreg_mcast_add);
73e18f4c18SVladimir Oltean 	}
7486e8b070SVignesh Raghavendra 
7586e8b070SVignesh Raghavendra 	return 0;
7686e8b070SVignesh Raghavendra }
7786e8b070SVignesh Raghavendra 
am65_cpsw_port_attr_br_flags_pre_set(struct net_device * netdev,struct switchdev_brport_flags flags)7886e8b070SVignesh Raghavendra static int am65_cpsw_port_attr_br_flags_pre_set(struct net_device *netdev,
79e18f4c18SVladimir Oltean 						struct switchdev_brport_flags flags)
8086e8b070SVignesh Raghavendra {
81e18f4c18SVladimir Oltean 	if (flags.mask & ~(BR_LEARNING | BR_MCAST_FLOOD))
8286e8b070SVignesh Raghavendra 		return -EINVAL;
8386e8b070SVignesh Raghavendra 
8486e8b070SVignesh Raghavendra 	return 0;
8586e8b070SVignesh Raghavendra }
8686e8b070SVignesh Raghavendra 
am65_cpsw_port_attr_set(struct net_device * ndev,const void * ctx,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)8769bfac96SVladimir Oltean static int am65_cpsw_port_attr_set(struct net_device *ndev, const void *ctx,
884c08c586SVladimir Oltean 				   const struct switchdev_attr *attr,
894c08c586SVladimir Oltean 				   struct netlink_ext_ack *extack)
9086e8b070SVignesh Raghavendra {
9186e8b070SVignesh Raghavendra 	struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
9286e8b070SVignesh Raghavendra 	int ret;
9386e8b070SVignesh Raghavendra 
9486e8b070SVignesh Raghavendra 	netdev_dbg(ndev, "attr: id %u port: %u\n", attr->id, port->port_id);
9586e8b070SVignesh Raghavendra 
9686e8b070SVignesh Raghavendra 	switch (attr->id) {
9786e8b070SVignesh Raghavendra 	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
9886e8b070SVignesh Raghavendra 		ret = am65_cpsw_port_attr_br_flags_pre_set(ndev,
9986e8b070SVignesh Raghavendra 							   attr->u.brport_flags);
10086e8b070SVignesh Raghavendra 		break;
10186e8b070SVignesh Raghavendra 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
10286e8b070SVignesh Raghavendra 		ret = am65_cpsw_port_stp_state_set(port, attr->u.stp_state);
10386e8b070SVignesh Raghavendra 		netdev_dbg(ndev, "stp state: %u\n", attr->u.stp_state);
10486e8b070SVignesh Raghavendra 		break;
10586e8b070SVignesh Raghavendra 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
10686e8b070SVignesh Raghavendra 		ret = am65_cpsw_port_attr_br_flags_set(port, attr->orig_dev,
10786e8b070SVignesh Raghavendra 						       attr->u.brport_flags);
10886e8b070SVignesh Raghavendra 		break;
10986e8b070SVignesh Raghavendra 	default:
11086e8b070SVignesh Raghavendra 		ret = -EOPNOTSUPP;
11186e8b070SVignesh Raghavendra 		break;
11286e8b070SVignesh Raghavendra 	}
11386e8b070SVignesh Raghavendra 
11486e8b070SVignesh Raghavendra 	return ret;
11586e8b070SVignesh Raghavendra }
11686e8b070SVignesh Raghavendra 
am65_cpsw_get_pvid(struct am65_cpsw_port * port)11786e8b070SVignesh Raghavendra static u16 am65_cpsw_get_pvid(struct am65_cpsw_port *port)
11886e8b070SVignesh Raghavendra {
11986e8b070SVignesh Raghavendra 	struct am65_cpsw_common *cpsw = port->common;
12086e8b070SVignesh Raghavendra 	struct am65_cpsw_host *host_p = am65_common_get_host(cpsw);
12186e8b070SVignesh Raghavendra 	u32 pvid;
12286e8b070SVignesh Raghavendra 
12386e8b070SVignesh Raghavendra 	if (port->port_id)
12486e8b070SVignesh Raghavendra 		pvid = readl(port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET);
12586e8b070SVignesh Raghavendra 	else
12686e8b070SVignesh Raghavendra 		pvid = readl(host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET);
12786e8b070SVignesh Raghavendra 
12886e8b070SVignesh Raghavendra 	pvid = pvid & 0xfff;
12986e8b070SVignesh Raghavendra 
13086e8b070SVignesh Raghavendra 	return pvid;
13186e8b070SVignesh Raghavendra }
13286e8b070SVignesh Raghavendra 
am65_cpsw_set_pvid(struct am65_cpsw_port * port,u16 vid,bool cfi,u32 cos)13386e8b070SVignesh Raghavendra static void am65_cpsw_set_pvid(struct am65_cpsw_port *port, u16 vid, bool cfi, u32 cos)
13486e8b070SVignesh Raghavendra {
13586e8b070SVignesh Raghavendra 	struct am65_cpsw_common *cpsw = port->common;
13686e8b070SVignesh Raghavendra 	struct am65_cpsw_host *host_p = am65_common_get_host(cpsw);
13786e8b070SVignesh Raghavendra 	u32 pvid;
13886e8b070SVignesh Raghavendra 
13986e8b070SVignesh Raghavendra 	pvid = vid;
14086e8b070SVignesh Raghavendra 	pvid |= cfi ? BIT(12) : 0;
14186e8b070SVignesh Raghavendra 	pvid |= (cos & 0x7) << 13;
14286e8b070SVignesh Raghavendra 
14386e8b070SVignesh Raghavendra 	if (port->port_id)
14486e8b070SVignesh Raghavendra 		writel(pvid, port->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET);
14586e8b070SVignesh Raghavendra 	else
14686e8b070SVignesh Raghavendra 		writel(pvid, host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET);
14786e8b070SVignesh Raghavendra }
14886e8b070SVignesh Raghavendra 
am65_cpsw_port_vlan_add(struct am65_cpsw_port * port,bool untag,bool pvid,u16 vid,struct net_device * orig_dev)14986e8b070SVignesh Raghavendra static int am65_cpsw_port_vlan_add(struct am65_cpsw_port *port, bool untag, bool pvid,
15086e8b070SVignesh Raghavendra 				   u16 vid, struct net_device *orig_dev)
15186e8b070SVignesh Raghavendra {
15286e8b070SVignesh Raghavendra 	bool cpu_port = netif_is_bridge_master(orig_dev);
15386e8b070SVignesh Raghavendra 	struct am65_cpsw_common *cpsw = port->common;
15486e8b070SVignesh Raghavendra 	int unreg_mcast_mask = 0;
15586e8b070SVignesh Raghavendra 	int reg_mcast_mask = 0;
15686e8b070SVignesh Raghavendra 	int untag_mask = 0;
15786e8b070SVignesh Raghavendra 	int port_mask;
15886e8b070SVignesh Raghavendra 	int ret = 0;
15986e8b070SVignesh Raghavendra 	u32 flags;
16086e8b070SVignesh Raghavendra 
16186e8b070SVignesh Raghavendra 	if (cpu_port) {
16286e8b070SVignesh Raghavendra 		port_mask = BIT(HOST_PORT_NUM);
16386e8b070SVignesh Raghavendra 		flags = orig_dev->flags;
16486e8b070SVignesh Raghavendra 		unreg_mcast_mask = port_mask;
16586e8b070SVignesh Raghavendra 	} else {
16686e8b070SVignesh Raghavendra 		port_mask = BIT(port->port_id);
16786e8b070SVignesh Raghavendra 		flags = port->ndev->flags;
16886e8b070SVignesh Raghavendra 	}
16986e8b070SVignesh Raghavendra 
17086e8b070SVignesh Raghavendra 	if (flags & IFF_MULTICAST)
17186e8b070SVignesh Raghavendra 		reg_mcast_mask = port_mask;
17286e8b070SVignesh Raghavendra 
17386e8b070SVignesh Raghavendra 	if (untag)
17486e8b070SVignesh Raghavendra 		untag_mask = port_mask;
17586e8b070SVignesh Raghavendra 
17686e8b070SVignesh Raghavendra 	ret = cpsw_ale_vlan_add_modify(cpsw->ale, vid, port_mask, untag_mask,
17786e8b070SVignesh Raghavendra 				       reg_mcast_mask, unreg_mcast_mask);
17886e8b070SVignesh Raghavendra 	if (ret) {
17986e8b070SVignesh Raghavendra 		netdev_err(port->ndev, "Unable to add vlan\n");
18086e8b070SVignesh Raghavendra 		return ret;
18186e8b070SVignesh Raghavendra 	}
18286e8b070SVignesh Raghavendra 
18386e8b070SVignesh Raghavendra 	if (cpu_port)
18486e8b070SVignesh Raghavendra 		cpsw_ale_add_ucast(cpsw->ale, port->slave.mac_addr,
18586e8b070SVignesh Raghavendra 				   HOST_PORT_NUM, ALE_VLAN | ALE_SECURE, vid);
18686e8b070SVignesh Raghavendra 	if (!pvid)
18786e8b070SVignesh Raghavendra 		return ret;
18886e8b070SVignesh Raghavendra 
18986e8b070SVignesh Raghavendra 	am65_cpsw_set_pvid(port, vid, 0, 0);
19086e8b070SVignesh Raghavendra 
19186e8b070SVignesh Raghavendra 	netdev_dbg(port->ndev, "VID add: %s: vid:%u ports:%X\n",
19286e8b070SVignesh Raghavendra 		   port->ndev->name, vid, port_mask);
19386e8b070SVignesh Raghavendra 
19486e8b070SVignesh Raghavendra 	return ret;
19586e8b070SVignesh Raghavendra }
19686e8b070SVignesh Raghavendra 
am65_cpsw_port_vlan_del(struct am65_cpsw_port * port,u16 vid,struct net_device * orig_dev)19786e8b070SVignesh Raghavendra static int am65_cpsw_port_vlan_del(struct am65_cpsw_port *port, u16 vid,
19886e8b070SVignesh Raghavendra 				   struct net_device *orig_dev)
19986e8b070SVignesh Raghavendra {
20086e8b070SVignesh Raghavendra 	bool cpu_port = netif_is_bridge_master(orig_dev);
20186e8b070SVignesh Raghavendra 	struct am65_cpsw_common *cpsw = port->common;
20286e8b070SVignesh Raghavendra 	int port_mask;
20386e8b070SVignesh Raghavendra 	int ret = 0;
20486e8b070SVignesh Raghavendra 
20586e8b070SVignesh Raghavendra 	if (cpu_port)
20686e8b070SVignesh Raghavendra 		port_mask = BIT(HOST_PORT_NUM);
20786e8b070SVignesh Raghavendra 	else
20886e8b070SVignesh Raghavendra 		port_mask = BIT(port->port_id);
20986e8b070SVignesh Raghavendra 
21086e8b070SVignesh Raghavendra 	ret = cpsw_ale_del_vlan(cpsw->ale, vid, port_mask);
21186e8b070SVignesh Raghavendra 	if (ret != 0)
21286e8b070SVignesh Raghavendra 		return ret;
21386e8b070SVignesh Raghavendra 
21486e8b070SVignesh Raghavendra 	/* We don't care for the return value here, error is returned only if
21586e8b070SVignesh Raghavendra 	 * the unicast entry is not present
21686e8b070SVignesh Raghavendra 	 */
21786e8b070SVignesh Raghavendra 	if (cpu_port)
21886e8b070SVignesh Raghavendra 		cpsw_ale_del_ucast(cpsw->ale, port->slave.mac_addr,
21986e8b070SVignesh Raghavendra 				   HOST_PORT_NUM, ALE_VLAN, vid);
22086e8b070SVignesh Raghavendra 
22186e8b070SVignesh Raghavendra 	if (vid == am65_cpsw_get_pvid(port))
22286e8b070SVignesh Raghavendra 		am65_cpsw_set_pvid(port, 0, 0, 0);
22386e8b070SVignesh Raghavendra 
22486e8b070SVignesh Raghavendra 	/* We don't care for the return value here, error is returned only if
22586e8b070SVignesh Raghavendra 	 * the multicast entry is not present
22686e8b070SVignesh Raghavendra 	 */
22786e8b070SVignesh Raghavendra 	cpsw_ale_del_mcast(cpsw->ale, port->ndev->broadcast, port_mask,
22886e8b070SVignesh Raghavendra 			   ALE_VLAN, vid);
22986e8b070SVignesh Raghavendra 	netdev_dbg(port->ndev, "VID del: %s: vid:%u ports:%X\n",
23086e8b070SVignesh Raghavendra 		   port->ndev->name, vid, port_mask);
23186e8b070SVignesh Raghavendra 
23286e8b070SVignesh Raghavendra 	return ret;
23386e8b070SVignesh Raghavendra }
23486e8b070SVignesh Raghavendra 
am65_cpsw_port_vlans_add(struct am65_cpsw_port * port,const struct switchdev_obj_port_vlan * vlan)23586e8b070SVignesh Raghavendra static int am65_cpsw_port_vlans_add(struct am65_cpsw_port *port,
23686e8b070SVignesh Raghavendra 				    const struct switchdev_obj_port_vlan *vlan)
23786e8b070SVignesh Raghavendra {
23886e8b070SVignesh Raghavendra 	bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
23986e8b070SVignesh Raghavendra 	struct net_device *orig_dev = vlan->obj.orig_dev;
24086e8b070SVignesh Raghavendra 	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
24186e8b070SVignesh Raghavendra 
24286e8b070SVignesh Raghavendra 	netdev_dbg(port->ndev, "VID add: %s: vid:%u flags:%X\n",
24386e8b070SVignesh Raghavendra 		   port->ndev->name, vlan->vid, vlan->flags);
24486e8b070SVignesh Raghavendra 
24586e8b070SVignesh Raghavendra 	return am65_cpsw_port_vlan_add(port, untag, pvid, vlan->vid, orig_dev);
24686e8b070SVignesh Raghavendra }
24786e8b070SVignesh Raghavendra 
am65_cpsw_port_vlans_del(struct am65_cpsw_port * port,const struct switchdev_obj_port_vlan * vlan)24886e8b070SVignesh Raghavendra static int am65_cpsw_port_vlans_del(struct am65_cpsw_port *port,
24986e8b070SVignesh Raghavendra 				    const struct switchdev_obj_port_vlan *vlan)
25086e8b070SVignesh Raghavendra 
25186e8b070SVignesh Raghavendra {
25286e8b070SVignesh Raghavendra 	return am65_cpsw_port_vlan_del(port, vlan->vid, vlan->obj.orig_dev);
25386e8b070SVignesh Raghavendra }
25486e8b070SVignesh Raghavendra 
am65_cpsw_port_mdb_add(struct am65_cpsw_port * port,struct switchdev_obj_port_mdb * mdb)25586e8b070SVignesh Raghavendra static int am65_cpsw_port_mdb_add(struct am65_cpsw_port *port,
25686e8b070SVignesh Raghavendra 				  struct switchdev_obj_port_mdb *mdb)
25786e8b070SVignesh Raghavendra 
25886e8b070SVignesh Raghavendra {
25986e8b070SVignesh Raghavendra 	struct net_device *orig_dev = mdb->obj.orig_dev;
26086e8b070SVignesh Raghavendra 	bool cpu_port = netif_is_bridge_master(orig_dev);
26186e8b070SVignesh Raghavendra 	struct am65_cpsw_common *cpsw = port->common;
26286e8b070SVignesh Raghavendra 	int port_mask;
26386e8b070SVignesh Raghavendra 	int err;
26486e8b070SVignesh Raghavendra 
26586e8b070SVignesh Raghavendra 	if (cpu_port)
26686e8b070SVignesh Raghavendra 		port_mask = BIT(HOST_PORT_NUM);
26786e8b070SVignesh Raghavendra 	else
26886e8b070SVignesh Raghavendra 		port_mask = BIT(port->port_id);
26986e8b070SVignesh Raghavendra 
27086e8b070SVignesh Raghavendra 	err = cpsw_ale_add_mcast(cpsw->ale, mdb->addr, port_mask,
27186e8b070SVignesh Raghavendra 				 ALE_VLAN, mdb->vid, 0);
27286e8b070SVignesh Raghavendra 	netdev_dbg(port->ndev, "MDB add: %s: vid %u:%pM  ports: %X\n",
27386e8b070SVignesh Raghavendra 		   port->ndev->name, mdb->vid, mdb->addr, port_mask);
27486e8b070SVignesh Raghavendra 
27586e8b070SVignesh Raghavendra 	return err;
27686e8b070SVignesh Raghavendra }
27786e8b070SVignesh Raghavendra 
am65_cpsw_port_mdb_del(struct am65_cpsw_port * port,struct switchdev_obj_port_mdb * mdb)27886e8b070SVignesh Raghavendra static int am65_cpsw_port_mdb_del(struct am65_cpsw_port *port,
27986e8b070SVignesh Raghavendra 				  struct switchdev_obj_port_mdb *mdb)
28086e8b070SVignesh Raghavendra 
28186e8b070SVignesh Raghavendra {
28286e8b070SVignesh Raghavendra 	struct net_device *orig_dev = mdb->obj.orig_dev;
28386e8b070SVignesh Raghavendra 	bool cpu_port = netif_is_bridge_master(orig_dev);
28486e8b070SVignesh Raghavendra 	struct am65_cpsw_common *cpsw = port->common;
28586e8b070SVignesh Raghavendra 	int del_mask;
28686e8b070SVignesh Raghavendra 
28786e8b070SVignesh Raghavendra 	if (cpu_port)
28886e8b070SVignesh Raghavendra 		del_mask = BIT(HOST_PORT_NUM);
28986e8b070SVignesh Raghavendra 	else
29086e8b070SVignesh Raghavendra 		del_mask = BIT(port->port_id);
29186e8b070SVignesh Raghavendra 
29286e8b070SVignesh Raghavendra 	/* Ignore error as error code is returned only when entry is already removed */
29386e8b070SVignesh Raghavendra 	cpsw_ale_del_mcast(cpsw->ale, mdb->addr, del_mask,
29486e8b070SVignesh Raghavendra 			   ALE_VLAN, mdb->vid);
29586e8b070SVignesh Raghavendra 	netdev_dbg(port->ndev, "MDB del: %s: vid %u:%pM  ports: %X\n",
29686e8b070SVignesh Raghavendra 		   port->ndev->name, mdb->vid, mdb->addr, del_mask);
29786e8b070SVignesh Raghavendra 
29886e8b070SVignesh Raghavendra 	return 0;
29986e8b070SVignesh Raghavendra }
30086e8b070SVignesh Raghavendra 
am65_cpsw_port_obj_add(struct net_device * ndev,const void * ctx,const struct switchdev_obj * obj,struct netlink_ext_ack * extack)30169bfac96SVladimir Oltean static int am65_cpsw_port_obj_add(struct net_device *ndev, const void *ctx,
30286e8b070SVignesh Raghavendra 				  const struct switchdev_obj *obj,
30386e8b070SVignesh Raghavendra 				  struct netlink_ext_ack *extack)
30486e8b070SVignesh Raghavendra {
30586e8b070SVignesh Raghavendra 	struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
30686e8b070SVignesh Raghavendra 	struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
30786e8b070SVignesh Raghavendra 	struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
30886e8b070SVignesh Raghavendra 	int err = 0;
30986e8b070SVignesh Raghavendra 
31086e8b070SVignesh Raghavendra 	netdev_dbg(ndev, "obj_add: id %u port: %u\n", obj->id, port->port_id);
31186e8b070SVignesh Raghavendra 
31286e8b070SVignesh Raghavendra 	switch (obj->id) {
31386e8b070SVignesh Raghavendra 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
31486e8b070SVignesh Raghavendra 		err = am65_cpsw_port_vlans_add(port, vlan);
31586e8b070SVignesh Raghavendra 		break;
31686e8b070SVignesh Raghavendra 	case SWITCHDEV_OBJ_ID_PORT_MDB:
31786e8b070SVignesh Raghavendra 	case SWITCHDEV_OBJ_ID_HOST_MDB:
31886e8b070SVignesh Raghavendra 		err = am65_cpsw_port_mdb_add(port, mdb);
31986e8b070SVignesh Raghavendra 		break;
32086e8b070SVignesh Raghavendra 	default:
32186e8b070SVignesh Raghavendra 		err = -EOPNOTSUPP;
32286e8b070SVignesh Raghavendra 		break;
32386e8b070SVignesh Raghavendra 	}
32486e8b070SVignesh Raghavendra 
32586e8b070SVignesh Raghavendra 	return err;
32686e8b070SVignesh Raghavendra }
32786e8b070SVignesh Raghavendra 
am65_cpsw_port_obj_del(struct net_device * ndev,const void * ctx,const struct switchdev_obj * obj)32869bfac96SVladimir Oltean static int am65_cpsw_port_obj_del(struct net_device *ndev, const void *ctx,
32986e8b070SVignesh Raghavendra 				  const struct switchdev_obj *obj)
33086e8b070SVignesh Raghavendra {
33186e8b070SVignesh Raghavendra 	struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
33286e8b070SVignesh Raghavendra 	struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
33386e8b070SVignesh Raghavendra 	struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
33486e8b070SVignesh Raghavendra 	int err = 0;
33586e8b070SVignesh Raghavendra 
33686e8b070SVignesh Raghavendra 	netdev_dbg(ndev, "obj_del: id %u port: %u\n", obj->id, port->port_id);
33786e8b070SVignesh Raghavendra 
33886e8b070SVignesh Raghavendra 	switch (obj->id) {
33986e8b070SVignesh Raghavendra 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
34086e8b070SVignesh Raghavendra 		err = am65_cpsw_port_vlans_del(port, vlan);
34186e8b070SVignesh Raghavendra 		break;
34286e8b070SVignesh Raghavendra 	case SWITCHDEV_OBJ_ID_PORT_MDB:
34386e8b070SVignesh Raghavendra 	case SWITCHDEV_OBJ_ID_HOST_MDB:
34486e8b070SVignesh Raghavendra 		err = am65_cpsw_port_mdb_del(port, mdb);
34586e8b070SVignesh Raghavendra 		break;
34686e8b070SVignesh Raghavendra 	default:
34786e8b070SVignesh Raghavendra 		err = -EOPNOTSUPP;
34886e8b070SVignesh Raghavendra 		break;
34986e8b070SVignesh Raghavendra 	}
35086e8b070SVignesh Raghavendra 
35186e8b070SVignesh Raghavendra 	return err;
35286e8b070SVignesh Raghavendra }
35386e8b070SVignesh Raghavendra 
am65_cpsw_fdb_offload_notify(struct net_device * ndev,struct switchdev_notifier_fdb_info * rcv)35486e8b070SVignesh Raghavendra static void am65_cpsw_fdb_offload_notify(struct net_device *ndev,
35586e8b070SVignesh Raghavendra 					 struct switchdev_notifier_fdb_info *rcv)
35686e8b070SVignesh Raghavendra {
357*c35b57ceSVladimir Oltean 	struct switchdev_notifier_fdb_info info = {};
35886e8b070SVignesh Raghavendra 
35986e8b070SVignesh Raghavendra 	info.addr = rcv->addr;
36086e8b070SVignesh Raghavendra 	info.vid = rcv->vid;
36186e8b070SVignesh Raghavendra 	info.offloaded = true;
36286e8b070SVignesh Raghavendra 	call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
36386e8b070SVignesh Raghavendra 				 ndev, &info.info, NULL);
36486e8b070SVignesh Raghavendra }
36586e8b070SVignesh Raghavendra 
am65_cpsw_switchdev_event_work(struct work_struct * work)36686e8b070SVignesh Raghavendra static void am65_cpsw_switchdev_event_work(struct work_struct *work)
36786e8b070SVignesh Raghavendra {
36886e8b070SVignesh Raghavendra 	struct am65_cpsw_switchdev_event_work *switchdev_work =
36986e8b070SVignesh Raghavendra 		container_of(work, struct am65_cpsw_switchdev_event_work, work);
37086e8b070SVignesh Raghavendra 	struct am65_cpsw_port *port = switchdev_work->port;
37186e8b070SVignesh Raghavendra 	struct switchdev_notifier_fdb_info *fdb;
37286e8b070SVignesh Raghavendra 	struct am65_cpsw_common *cpsw = port->common;
37386e8b070SVignesh Raghavendra 	int port_id = port->port_id;
37486e8b070SVignesh Raghavendra 
37586e8b070SVignesh Raghavendra 	rtnl_lock();
37686e8b070SVignesh Raghavendra 	switch (switchdev_work->event) {
37786e8b070SVignesh Raghavendra 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
37886e8b070SVignesh Raghavendra 		fdb = &switchdev_work->fdb_info;
37986e8b070SVignesh Raghavendra 
38086e8b070SVignesh Raghavendra 		netdev_dbg(port->ndev, "cpsw_fdb_add: MACID = %pM vid = %u flags = %u %u -- port %d\n",
38186e8b070SVignesh Raghavendra 			   fdb->addr, fdb->vid, fdb->added_by_user,
38286e8b070SVignesh Raghavendra 			   fdb->offloaded, port_id);
38386e8b070SVignesh Raghavendra 
3842c4eca3eSVladimir Oltean 		if (!fdb->added_by_user || fdb->is_local)
38586e8b070SVignesh Raghavendra 			break;
38686e8b070SVignesh Raghavendra 		if (memcmp(port->slave.mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0)
38786e8b070SVignesh Raghavendra 			port_id = HOST_PORT_NUM;
38886e8b070SVignesh Raghavendra 
38986e8b070SVignesh Raghavendra 		cpsw_ale_add_ucast(cpsw->ale, (u8 *)fdb->addr, port_id,
39086e8b070SVignesh Raghavendra 				   fdb->vid ? ALE_VLAN : 0, fdb->vid);
39186e8b070SVignesh Raghavendra 		am65_cpsw_fdb_offload_notify(port->ndev, fdb);
39286e8b070SVignesh Raghavendra 		break;
39386e8b070SVignesh Raghavendra 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
39486e8b070SVignesh Raghavendra 		fdb = &switchdev_work->fdb_info;
39586e8b070SVignesh Raghavendra 
39686e8b070SVignesh Raghavendra 		netdev_dbg(port->ndev, "cpsw_fdb_del: MACID = %pM vid = %u flags = %u %u -- port %d\n",
39786e8b070SVignesh Raghavendra 			   fdb->addr, fdb->vid, fdb->added_by_user,
39886e8b070SVignesh Raghavendra 			   fdb->offloaded, port_id);
39986e8b070SVignesh Raghavendra 
4002c4eca3eSVladimir Oltean 		if (!fdb->added_by_user || fdb->is_local)
40186e8b070SVignesh Raghavendra 			break;
40286e8b070SVignesh Raghavendra 		if (memcmp(port->slave.mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0)
40386e8b070SVignesh Raghavendra 			port_id = HOST_PORT_NUM;
40486e8b070SVignesh Raghavendra 
40586e8b070SVignesh Raghavendra 		cpsw_ale_del_ucast(cpsw->ale, (u8 *)fdb->addr, port_id,
40686e8b070SVignesh Raghavendra 				   fdb->vid ? ALE_VLAN : 0, fdb->vid);
40786e8b070SVignesh Raghavendra 		break;
40886e8b070SVignesh Raghavendra 	default:
40986e8b070SVignesh Raghavendra 		break;
41086e8b070SVignesh Raghavendra 	}
41186e8b070SVignesh Raghavendra 	rtnl_unlock();
41286e8b070SVignesh Raghavendra 
41386e8b070SVignesh Raghavendra 	kfree(switchdev_work->fdb_info.addr);
41486e8b070SVignesh Raghavendra 	kfree(switchdev_work);
41586e8b070SVignesh Raghavendra 	dev_put(port->ndev);
41686e8b070SVignesh Raghavendra }
41786e8b070SVignesh Raghavendra 
41886e8b070SVignesh Raghavendra /* called under rcu_read_lock() */
am65_cpsw_switchdev_event(struct notifier_block * unused,unsigned long event,void * ptr)41986e8b070SVignesh Raghavendra static int am65_cpsw_switchdev_event(struct notifier_block *unused,
42086e8b070SVignesh Raghavendra 				     unsigned long event, void *ptr)
42186e8b070SVignesh Raghavendra {
42286e8b070SVignesh Raghavendra 	struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
42386e8b070SVignesh Raghavendra 	struct am65_cpsw_switchdev_event_work *switchdev_work;
42486e8b070SVignesh Raghavendra 	struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
42586e8b070SVignesh Raghavendra 	struct switchdev_notifier_fdb_info *fdb_info = ptr;
42686e8b070SVignesh Raghavendra 	int err;
42786e8b070SVignesh Raghavendra 
42886e8b070SVignesh Raghavendra 	if (event == SWITCHDEV_PORT_ATTR_SET) {
42986e8b070SVignesh Raghavendra 		err = switchdev_handle_port_attr_set(ndev, ptr,
43086e8b070SVignesh Raghavendra 						     am65_cpsw_port_dev_check,
43186e8b070SVignesh Raghavendra 						     am65_cpsw_port_attr_set);
43286e8b070SVignesh Raghavendra 		return notifier_from_errno(err);
43386e8b070SVignesh Raghavendra 	}
43486e8b070SVignesh Raghavendra 
43586e8b070SVignesh Raghavendra 	if (!am65_cpsw_port_dev_check(ndev))
43686e8b070SVignesh Raghavendra 		return NOTIFY_DONE;
43786e8b070SVignesh Raghavendra 
43886e8b070SVignesh Raghavendra 	switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
43986e8b070SVignesh Raghavendra 	if (WARN_ON(!switchdev_work))
44086e8b070SVignesh Raghavendra 		return NOTIFY_BAD;
44186e8b070SVignesh Raghavendra 
44286e8b070SVignesh Raghavendra 	INIT_WORK(&switchdev_work->work, am65_cpsw_switchdev_event_work);
44386e8b070SVignesh Raghavendra 	switchdev_work->port = port;
44486e8b070SVignesh Raghavendra 	switchdev_work->event = event;
44586e8b070SVignesh Raghavendra 
44686e8b070SVignesh Raghavendra 	switch (event) {
44786e8b070SVignesh Raghavendra 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
44886e8b070SVignesh Raghavendra 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
44986e8b070SVignesh Raghavendra 		memcpy(&switchdev_work->fdb_info, ptr,
45086e8b070SVignesh Raghavendra 		       sizeof(switchdev_work->fdb_info));
45186e8b070SVignesh Raghavendra 		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
45286e8b070SVignesh Raghavendra 		if (!switchdev_work->fdb_info.addr)
45386e8b070SVignesh Raghavendra 			goto err_addr_alloc;
45486e8b070SVignesh Raghavendra 		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
45586e8b070SVignesh Raghavendra 				fdb_info->addr);
45686e8b070SVignesh Raghavendra 		dev_hold(ndev);
45786e8b070SVignesh Raghavendra 		break;
45886e8b070SVignesh Raghavendra 	default:
45986e8b070SVignesh Raghavendra 		kfree(switchdev_work);
46086e8b070SVignesh Raghavendra 		return NOTIFY_DONE;
46186e8b070SVignesh Raghavendra 	}
46286e8b070SVignesh Raghavendra 
46386e8b070SVignesh Raghavendra 	queue_work(system_long_wq, &switchdev_work->work);
46486e8b070SVignesh Raghavendra 
46586e8b070SVignesh Raghavendra 	return NOTIFY_DONE;
46686e8b070SVignesh Raghavendra 
46786e8b070SVignesh Raghavendra err_addr_alloc:
46886e8b070SVignesh Raghavendra 	kfree(switchdev_work);
46986e8b070SVignesh Raghavendra 	return NOTIFY_BAD;
47086e8b070SVignesh Raghavendra }
47186e8b070SVignesh Raghavendra 
47286e8b070SVignesh Raghavendra static struct notifier_block cpsw_switchdev_notifier = {
47386e8b070SVignesh Raghavendra 	.notifier_call = am65_cpsw_switchdev_event,
47486e8b070SVignesh Raghavendra };
47586e8b070SVignesh Raghavendra 
am65_cpsw_switchdev_blocking_event(struct notifier_block * unused,unsigned long event,void * ptr)47686e8b070SVignesh Raghavendra static int am65_cpsw_switchdev_blocking_event(struct notifier_block *unused,
47786e8b070SVignesh Raghavendra 					      unsigned long event, void *ptr)
47886e8b070SVignesh Raghavendra {
47986e8b070SVignesh Raghavendra 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
48086e8b070SVignesh Raghavendra 	int err;
48186e8b070SVignesh Raghavendra 
48286e8b070SVignesh Raghavendra 	switch (event) {
48386e8b070SVignesh Raghavendra 	case SWITCHDEV_PORT_OBJ_ADD:
48486e8b070SVignesh Raghavendra 		err = switchdev_handle_port_obj_add(dev, ptr,
48586e8b070SVignesh Raghavendra 						    am65_cpsw_port_dev_check,
48686e8b070SVignesh Raghavendra 						    am65_cpsw_port_obj_add);
48786e8b070SVignesh Raghavendra 		return notifier_from_errno(err);
48886e8b070SVignesh Raghavendra 	case SWITCHDEV_PORT_OBJ_DEL:
48986e8b070SVignesh Raghavendra 		err = switchdev_handle_port_obj_del(dev, ptr,
49086e8b070SVignesh Raghavendra 						    am65_cpsw_port_dev_check,
49186e8b070SVignesh Raghavendra 						    am65_cpsw_port_obj_del);
49286e8b070SVignesh Raghavendra 		return notifier_from_errno(err);
49386e8b070SVignesh Raghavendra 	case SWITCHDEV_PORT_ATTR_SET:
49486e8b070SVignesh Raghavendra 		err = switchdev_handle_port_attr_set(dev, ptr,
49586e8b070SVignesh Raghavendra 						     am65_cpsw_port_dev_check,
49686e8b070SVignesh Raghavendra 						     am65_cpsw_port_attr_set);
49786e8b070SVignesh Raghavendra 		return notifier_from_errno(err);
49886e8b070SVignesh Raghavendra 	default:
49986e8b070SVignesh Raghavendra 		break;
50086e8b070SVignesh Raghavendra 	}
50186e8b070SVignesh Raghavendra 
50286e8b070SVignesh Raghavendra 	return NOTIFY_DONE;
50386e8b070SVignesh Raghavendra }
50486e8b070SVignesh Raghavendra 
50586e8b070SVignesh Raghavendra static struct notifier_block cpsw_switchdev_bl_notifier = {
50686e8b070SVignesh Raghavendra 	.notifier_call = am65_cpsw_switchdev_blocking_event,
50786e8b070SVignesh Raghavendra };
50886e8b070SVignesh Raghavendra 
am65_cpsw_switchdev_register_notifiers(struct am65_cpsw_common * cpsw)50986e8b070SVignesh Raghavendra int am65_cpsw_switchdev_register_notifiers(struct am65_cpsw_common *cpsw)
51086e8b070SVignesh Raghavendra {
51186e8b070SVignesh Raghavendra 	int ret = 0;
51286e8b070SVignesh Raghavendra 
51386e8b070SVignesh Raghavendra 	ret = register_switchdev_notifier(&cpsw_switchdev_notifier);
51486e8b070SVignesh Raghavendra 	if (ret) {
51586e8b070SVignesh Raghavendra 		dev_err(cpsw->dev, "register switchdev notifier fail ret:%d\n",
51686e8b070SVignesh Raghavendra 			ret);
51786e8b070SVignesh Raghavendra 		return ret;
51886e8b070SVignesh Raghavendra 	}
51986e8b070SVignesh Raghavendra 
52086e8b070SVignesh Raghavendra 	ret = register_switchdev_blocking_notifier(&cpsw_switchdev_bl_notifier);
52186e8b070SVignesh Raghavendra 	if (ret) {
52286e8b070SVignesh Raghavendra 		dev_err(cpsw->dev, "register switchdev blocking notifier ret:%d\n",
52386e8b070SVignesh Raghavendra 			ret);
52486e8b070SVignesh Raghavendra 		unregister_switchdev_notifier(&cpsw_switchdev_notifier);
52586e8b070SVignesh Raghavendra 	}
52686e8b070SVignesh Raghavendra 
52786e8b070SVignesh Raghavendra 	return ret;
52886e8b070SVignesh Raghavendra }
52986e8b070SVignesh Raghavendra 
am65_cpsw_switchdev_unregister_notifiers(struct am65_cpsw_common * cpsw)53086e8b070SVignesh Raghavendra void am65_cpsw_switchdev_unregister_notifiers(struct am65_cpsw_common *cpsw)
53186e8b070SVignesh Raghavendra {
53286e8b070SVignesh Raghavendra 	unregister_switchdev_blocking_notifier(&cpsw_switchdev_bl_notifier);
53386e8b070SVignesh Raghavendra 	unregister_switchdev_notifier(&cpsw_switchdev_notifier);
53486e8b070SVignesh Raghavendra }
535