@@ -12,6 +12,7 @@ config FSL_DPAA2
config FSL_DPAA2_ETHSW
tristate "Freescale DPAA2 Ethernet Switch"
+ depends on BRIDGE || BRIDGE=n
depends on FSL_DPAA2
depends on NET_SWITCHDEV
help
@@ -814,6 +814,50 @@ static int dpaa2_switch_port_fdb_dump(struct sk_buff *skb, struct netlink_callba
return err;
}
+static int dpaa2_switch_port_vlan_add(struct net_device *netdev, __be16 proto,
+ u16 vid)
+{
+ struct switchdev_obj_port_vlan vlan = {
+ .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
+ .vid_begin = vid,
+ .vid_end = vid,
+ /* This API only allows programming tagged, non-PVID VIDs */
+ .flags = 0,
+ };
+ struct switchdev_trans trans;
+ int err;
+
+ trans.ph_prepare = true;
+ err = dpaa2_switch_port_vlans_add(netdev, &vlan, &trans);
+ if (err)
+ return err;
+
+ trans.ph_prepare = false;
+ err = dpaa2_switch_port_vlans_add(netdev, &vlan, &trans);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int dpaa2_switch_port_vlan_kill(struct net_device *netdev, __be16 proto,
+ u16 vid)
+{
+ struct switchdev_obj_port_vlan vlan = {
+ .vid_begin = vid,
+ .vid_end = vid,
+ /* This API only allows programming tagged, non-PVID VIDs */
+ .flags = 0,
+ };
+ int err;
+
+ err = dpaa2_switch_port_vlans_del(netdev, &vlan);
+ if (err)
+ return err;
+
+ return 0;
+}
+
static int dpaa2_switch_port_set_mac_addr(struct ethsw_port_priv *port_priv)
{
struct ethsw_core *ethsw = port_priv->ethsw_data;
@@ -996,6 +1040,8 @@ static const struct net_device_ops dpaa2_switch_port_ops = {
.ndo_fdb_add = dpaa2_switch_port_fdb_add,
.ndo_fdb_del = dpaa2_switch_port_fdb_del,
.ndo_fdb_dump = dpaa2_switch_port_fdb_dump,
+ .ndo_vlan_rx_add_vid = dpaa2_switch_port_vlan_add,
+ .ndo_vlan_rx_kill_vid = dpaa2_switch_port_vlan_kill,
.ndo_start_xmit = dpaa2_switch_port_tx,
.ndo_get_port_parent_id = dpaa2_switch_port_parent_id,
@@ -1195,7 +1241,8 @@ static int dpaa2_switch_port_attr_set(struct net_device *netdev,
attr->u.brport_flags);
break;
case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
- /* VLANs are supported by default */
+ if (!attr->u.vlan_filtering)
+ return -EOPNOTSUPP;
break;
default:
err = -EOPNOTSUPP;
@@ -1205,9 +1252,9 @@ static int dpaa2_switch_port_attr_set(struct net_device *netdev,
return err;
}
-static int dpaa2_switch_port_vlans_add(struct net_device *netdev,
- const struct switchdev_obj_port_vlan *vlan,
- struct switchdev_trans *trans)
+int dpaa2_switch_port_vlans_add(struct net_device *netdev,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
struct ethsw_core *ethsw = port_priv->ethsw_data;
@@ -1375,13 +1422,13 @@ static int dpaa2_switch_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid
return 0;
}
-static int dpaa2_switch_port_vlans_del(struct net_device *netdev,
- const struct switchdev_obj_port_vlan *vlan)
+int dpaa2_switch_port_vlans_del(struct net_device *netdev,
+ const struct switchdev_obj_port_vlan *vlan)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
int vid, err = 0;
- if (netif_is_bridge_master(vlan->obj.orig_dev))
+ if (vlan->obj.orig_dev && netif_is_bridge_master(vlan->obj.orig_dev))
return -EOPNOTSUPP;
for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
@@ -1575,14 +1622,23 @@ static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,
{
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
struct netdev_notifier_changeupper_info *info = ptr;
+ struct netlink_ext_ack *extack;
struct net_device *upper_dev;
int err = 0;
if (!dpaa2_switch_port_dev_check(netdev, nb))
return NOTIFY_DONE;
- /* Handle just upper dev link/unlink for the moment */
- if (event == NETDEV_CHANGEUPPER) {
+ extack = netdev_notifier_info_to_extack(&info->info);
+ switch (event) {
+ case NETDEV_PRECHANGEUPPER:
+ upper_dev = info->upper_dev;
+ if (netif_is_bridge_master(upper_dev) && !br_vlan_enabled(upper_dev)) {
+ NL_SET_ERR_MSG_MOD(extack, "Cannot join a vlan-unaware bridge");
+ err = -EOPNOTSUPP;
+ }
+ break;
+ case NETDEV_CHANGEUPPER:
upper_dev = info->upper_dev;
if (netif_is_bridge_master(upper_dev)) {
if (info->linking)
@@ -1590,6 +1646,7 @@ static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,
else
err = dpaa2_switch_port_bridge_leave(netdev);
}
+ break;
}
return notifier_from_errno(err);
@@ -2646,6 +2703,11 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw,
port_netdev->min_mtu = ETH_MIN_MTU;
port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH;
+ /* The DPAA2 Switch's ingress path depends on the VLAN table,
+ * thus we are not able to disable VLAN filtering.
+ */
+ port_netdev->features = NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER;
+
err = dpaa2_switch_port_init(port_priv, port_idx);
if (err)
goto err_port_probe;
@@ -143,4 +143,11 @@ static inline bool dpaa2_switch_has_ctrl_if(struct ethsw_core *ethsw)
bool dpaa2_switch_port_dev_check(const struct net_device *netdev,
struct notifier_block *nb);
+int dpaa2_switch_port_vlans_add(struct net_device *netdev,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans);
+
+int dpaa2_switch_port_vlans_del(struct net_device *netdev,
+ const struct switchdev_obj_port_vlan *vlan);
+
#endif /* __ETHSW_H */