]> git.baikalelectronics.ru Git - kernel.git/commitdiff
net: ti: am65-cpsw-nuss: Add netdevice notifiers
authorVignesh Raghavendra <vigneshr@ti.com>
Thu, 11 Feb 2021 10:56:42 +0000 (16:26 +0530)
committerDavid S. Miller <davem@davemloft.net>
Fri, 12 Feb 2021 01:52:13 +0000 (17:52 -0800)
Register netdevice notifiers in order to receive notification when
individual MAC ports are added to the HW bridge.

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ti/am65-cpsw-nuss.c
drivers/net/ethernet/ti/am65-cpsw-nuss.h

index 4d97c4b90aa8ddd22015c112bfd78c2186d894b7..87556f4debe91f88b36cd4e87a83e67702619365 100644 (file)
@@ -2036,6 +2036,126 @@ static void am65_cpsw_nuss_cleanup_ndev(struct am65_cpsw_common *common)
        }
 }
 
+static void am65_cpsw_port_offload_fwd_mark_update(struct am65_cpsw_common *common)
+{
+       int set_val = 0;
+       int i;
+
+       if (common->br_members == (GENMASK(common->port_num, 1) & ~common->disabled_ports_mask))
+               set_val = 1;
+
+       dev_dbg(common->dev, "set offload_fwd_mark %d\n", set_val);
+
+       for (i = 1; i <= common->port_num; i++) {
+               struct am65_cpsw_port *port = am65_common_get_port(common, i);
+               struct am65_cpsw_ndev_priv *priv = am65_ndev_to_priv(port->ndev);
+
+               priv->offload_fwd_mark = set_val;
+       }
+}
+
+bool am65_cpsw_port_dev_check(const struct net_device *ndev)
+{
+       if (ndev->netdev_ops == &am65_cpsw_nuss_netdev_ops) {
+               struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
+
+               return !common->is_emac_mode;
+       }
+
+       return false;
+}
+
+static int am65_cpsw_netdevice_port_link(struct net_device *ndev, struct net_device *br_ndev)
+{
+       struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
+       struct am65_cpsw_ndev_priv *priv = am65_ndev_to_priv(ndev);
+
+       if (!common->br_members) {
+               common->hw_bridge_dev = br_ndev;
+       } else {
+               /* This is adding the port to a second bridge, this is
+                * unsupported
+                */
+               if (common->hw_bridge_dev != br_ndev)
+                       return -EOPNOTSUPP;
+       }
+
+       common->br_members |= BIT(priv->port->port_id);
+
+       am65_cpsw_port_offload_fwd_mark_update(common);
+
+       return NOTIFY_DONE;
+}
+
+static void am65_cpsw_netdevice_port_unlink(struct net_device *ndev)
+{
+       struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
+       struct am65_cpsw_ndev_priv *priv = am65_ndev_to_priv(ndev);
+
+       common->br_members &= ~BIT(priv->port->port_id);
+
+       am65_cpsw_port_offload_fwd_mark_update(common);
+
+       if (!common->br_members)
+               common->hw_bridge_dev = NULL;
+}
+
+/* netdev notifier */
+static int am65_cpsw_netdevice_event(struct notifier_block *unused,
+                                    unsigned long event, void *ptr)
+{
+       struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
+       struct netdev_notifier_changeupper_info *info;
+       int ret = NOTIFY_DONE;
+
+       if (!am65_cpsw_port_dev_check(ndev))
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case NETDEV_CHANGEUPPER:
+               info = ptr;
+
+               if (netif_is_bridge_master(info->upper_dev)) {
+                       if (info->linking)
+                               ret = am65_cpsw_netdevice_port_link(ndev, info->upper_dev);
+                       else
+                               am65_cpsw_netdevice_port_unlink(ndev);
+               }
+               break;
+       default:
+               return NOTIFY_DONE;
+       }
+
+       return notifier_from_errno(ret);
+}
+
+static int am65_cpsw_register_notifiers(struct am65_cpsw_common *cpsw)
+{
+       int ret = 0;
+
+       if (AM65_CPSW_IS_CPSW2G(cpsw) ||
+           !IS_REACHABLE(CONFIG_TI_K3_AM65_CPSW_SWITCHDEV))
+               return 0;
+
+       cpsw->am65_cpsw_netdevice_nb.notifier_call = &am65_cpsw_netdevice_event;
+       ret = register_netdevice_notifier(&cpsw->am65_cpsw_netdevice_nb);
+       if (ret) {
+               dev_err(cpsw->dev, "can't register netdevice notifier\n");
+               return ret;
+       }
+
+       return ret;
+}
+
+static void am65_cpsw_unregister_notifiers(struct am65_cpsw_common *cpsw)
+{
+       if (AM65_CPSW_IS_CPSW2G(cpsw) ||
+           !IS_REACHABLE(CONFIG_TI_K3_AM65_CPSW_SWITCHDEV))
+               return;
+
+       unregister_netdevice_notifier(&cpsw->am65_cpsw_netdevice_nb);
+}
+
 static const struct devlink_ops am65_cpsw_devlink_ops = {};
 
 static void am65_cpsw_init_stp_ale_entry(struct am65_cpsw_common *cpsw)
@@ -2379,17 +2499,24 @@ static int am65_cpsw_nuss_register_ndevs(struct am65_cpsw_common *common)
                }
        }
 
-       ret = am65_cpsw_nuss_register_devlink(common);
+       ret = am65_cpsw_register_notifiers(common);
        if (ret)
                goto err_cleanup_ndev;
 
+       ret = am65_cpsw_nuss_register_devlink(common);
+       if (ret)
+               goto clean_unregister_notifiers;
+
        /* can't auto unregister ndev using devm_add_action() due to
         * devres release sequence in DD core for DMA
         */
 
        return 0;
+clean_unregister_notifiers:
+       am65_cpsw_unregister_notifiers(common);
 err_cleanup_ndev:
        am65_cpsw_nuss_cleanup_ndev(common);
+
        return ret;
 }
 
@@ -2621,6 +2748,7 @@ static int am65_cpsw_nuss_remove(struct platform_device *pdev)
        }
 
        am65_cpsw_unregister_devlink(common);
+       am65_cpsw_unregister_notifiers(common);
 
        /* must unregister ndevs here because DD release_driver routine calls
         * dma_deconfigure(dev) before devres_release_all(dev)
index c1b175762fd42701531b19c642554f2e2f529e89..5d93e346f05eb61ce6207c97c669725182798677 100644 (file)
@@ -137,6 +137,8 @@ struct am65_cpsw_common {
        u16                     br_members;
        int                     default_vlan;
        struct devlink *devlink;
+       struct net_device *hw_bridge_dev;
+       struct notifier_block am65_cpsw_netdevice_nb;
        unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];
 };
 
@@ -180,4 +182,6 @@ void am65_cpsw_nuss_set_p0_ptype(struct am65_cpsw_common *common);
 void am65_cpsw_nuss_remove_tx_chns(struct am65_cpsw_common *common);
 int am65_cpsw_nuss_update_tx_chns(struct am65_cpsw_common *common, int num_tx);
 
+bool am65_cpsw_port_dev_check(const struct net_device *dev);
+
 #endif /* AM65_CPSW_NUSS_H_ */