return netif_is_l3_master(info->upper_dev);
 }
 
-static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
+static int mlxsw_sp_netdevice_event(struct notifier_block *nb,
                                    unsigned long event, void *ptr)
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct mlxsw_sp *mlxsw_sp;
        int err = 0;
 
-       if (event == NETDEV_CHANGEADDR || event == NETDEV_CHANGEMTU)
+       mlxsw_sp = container_of(nb, struct mlxsw_sp, netdevice_nb);
+       if (mlxsw_sp_netdev_is_ipip(mlxsw_sp, dev))
+               err = mlxsw_sp_netdevice_ipip_event(mlxsw_sp, dev, event);
+       else if (event == NETDEV_CHANGEADDR || event == NETDEV_CHANGEMTU)
                err = mlxsw_sp_netdevice_router_port_event(dev);
        else if (mlxsw_sp_is_vrf_event(event, ptr))
                err = mlxsw_sp_netdevice_vrf_event(dev, event, ptr);
 
 {
        u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev);
        struct mlxsw_sp_router *router = mlxsw_sp->router;
-       struct mlxsw_sp_fib_entry *decap_fib_entry;
        struct mlxsw_sp_ipip_entry *ipip_entry;
        enum mlxsw_sp_l3proto ul_proto;
        union mlxsw_sp_l3addr saddr;
        if (IS_ERR(ipip_entry))
                return ipip_entry;
 
-       decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry);
-       if (decap_fib_entry)
-               mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry,
-                                                 decap_fib_entry);
-
        list_add_tail(&ipip_entry->ipip_list_node,
                      &mlxsw_sp->router->ipip_list);
 
 {
        if (--ipip_entry->ref_count == 0) {
                list_del(&ipip_entry->ipip_list_node);
-               if (ipip_entry->decap_fib_entry)
-                       mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
                mlxsw_sp_ipip_entry_destroy(ipip_entry);
        }
 }
        return false;
 }
 
+bool mlxsw_sp_netdev_is_ipip(const struct mlxsw_sp *mlxsw_sp,
+                            const struct net_device *dev)
+{
+       return mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL);
+}
+
+static struct mlxsw_sp_ipip_entry *
+mlxsw_sp_ipip_entry_find_by_ol_dev(struct mlxsw_sp *mlxsw_sp,
+                                  const struct net_device *ol_dev)
+{
+       struct mlxsw_sp_ipip_entry *ipip_entry;
+
+       list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
+                           ipip_list_node)
+               if (ipip_entry->ol_dev == ol_dev)
+                       return ipip_entry;
+
+       return NULL;
+}
+
+static int mlxsw_sp_netdevice_ipip_reg_event(struct mlxsw_sp *mlxsw_sp,
+                                            struct net_device *ol_dev)
+{
+       struct mlxsw_sp_router *router = mlxsw_sp->router;
+       struct mlxsw_sp_ipip_entry *ipip_entry;
+       enum mlxsw_sp_ipip_type ipipt;
+
+       mlxsw_sp_netdev_ipip_type(mlxsw_sp, ol_dev, &ipipt);
+       if (router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, ol_dev,
+                                                    MLXSW_SP_L3_PROTO_IPV4) ||
+           router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, ol_dev,
+                                                    MLXSW_SP_L3_PROTO_IPV6)) {
+               ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev);
+               if (IS_ERR(ipip_entry))
+                       return PTR_ERR(ipip_entry);
+       }
+
+       return 0;
+}
+
+static void mlxsw_sp_netdevice_ipip_unreg_event(struct mlxsw_sp *mlxsw_sp,
+                                               struct net_device *ol_dev)
+{
+       struct mlxsw_sp_ipip_entry *ipip_entry;
+
+       ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
+       if (ipip_entry)
+               mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry);
+}
+
+static int mlxsw_sp_netdevice_ipip_up_event(struct mlxsw_sp *mlxsw_sp,
+                                           struct net_device *ol_dev)
+{
+       struct mlxsw_sp_fib_entry *decap_fib_entry;
+       struct mlxsw_sp_ipip_entry *ipip_entry;
+
+       ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
+       if (ipip_entry) {
+               decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp,
+                                                                ipip_entry);
+               if (decap_fib_entry)
+                       mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry,
+                                                         decap_fib_entry);
+       }
+
+       return 0;
+}
+
+static void mlxsw_sp_netdevice_ipip_down_event(struct mlxsw_sp *mlxsw_sp,
+                                              struct net_device *ol_dev)
+{
+       struct mlxsw_sp_ipip_entry *ipip_entry;
+
+       ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
+       if (ipip_entry && ipip_entry->decap_fib_entry)
+               mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
+}
+
+int mlxsw_sp_netdevice_ipip_event(struct mlxsw_sp *mlxsw_sp,
+                                 struct net_device *ol_dev,
+                                 unsigned long event)
+{
+       switch (event) {
+       case NETDEV_REGISTER:
+               return mlxsw_sp_netdevice_ipip_reg_event(mlxsw_sp, ol_dev);
+       case NETDEV_UNREGISTER:
+               mlxsw_sp_netdevice_ipip_unreg_event(mlxsw_sp, ol_dev);
+               return 0;
+       case NETDEV_UP:
+               return mlxsw_sp_netdevice_ipip_up_event(mlxsw_sp, ol_dev);
+       case NETDEV_DOWN:
+               mlxsw_sp_netdevice_ipip_down_event(mlxsw_sp, ol_dev);
+               return 0;
+       }
+       return 0;
+}
+
 struct mlxsw_sp_neigh_key {
        struct neighbour *n;
 };