struct mlxsw_sp_neigh_key key;
        u16 rif;
        struct neighbour *n;
+       bool offloaded;
+       struct delayed_work dw;
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       unsigned char ha[ETH_ALEN];
 };
 
 static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
                               mlxsw_sp_neigh_ht_params);
 }
 
+static void mlxsw_sp_router_neigh_update_hw(struct work_struct *work);
+
 static struct mlxsw_sp_neigh_entry *
 mlxsw_sp_neigh_entry_create(const void *addr, size_t addr_len,
                            struct net_device *dev, u16 rif,
        neigh_entry->key.dev = dev;
        neigh_entry->rif = rif;
        neigh_entry->n = n;
+       INIT_DELAYED_WORK(&neigh_entry->dw, mlxsw_sp_router_neigh_update_hw);
        return neigh_entry;
 }
 
        mlxsw_sp_router_neighs_update_work_schedule(mlxsw_sp);
 }
 
+static void mlxsw_sp_router_neigh_update_hw(struct work_struct *work)
+{
+       struct mlxsw_sp_neigh_entry *neigh_entry =
+               container_of(work, struct mlxsw_sp_neigh_entry, dw.work);
+       struct neighbour *n = neigh_entry->n;
+       struct mlxsw_sp_port *mlxsw_sp_port = neigh_entry->mlxsw_sp_port;
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char rauht_pl[MLXSW_REG_RAUHT_LEN];
+       struct net_device *dev;
+       bool entry_connected;
+       u8 nud_state;
+       bool updating;
+       bool removing;
+       bool adding;
+       u32 dip;
+       int err;
+
+       read_lock_bh(&n->lock);
+       dip = ntohl(*((__be32 *) n->primary_key));
+       memcpy(neigh_entry->ha, n->ha, sizeof(neigh_entry->ha));
+       nud_state = n->nud_state;
+       dev = n->dev;
+       read_unlock_bh(&n->lock);
+
+       entry_connected = nud_state & NUD_VALID;
+       adding = (!neigh_entry->offloaded) && entry_connected;
+       updating = neigh_entry->offloaded && entry_connected;
+       removing = neigh_entry->offloaded && !entry_connected;
+
+       if (adding || updating) {
+               mlxsw_reg_rauht_pack4(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_ADD,
+                                     neigh_entry->rif,
+                                     neigh_entry->ha, dip);
+               err = mlxsw_reg_write(mlxsw_sp->core,
+                                     MLXSW_REG(rauht), rauht_pl);
+               if (err) {
+                       netdev_err(dev, "Could not add neigh %pI4h\n", &dip);
+                       neigh_entry->offloaded = false;
+               } else {
+                       neigh_entry->offloaded = true;
+               }
+       } else if (removing) {
+               mlxsw_reg_rauht_pack4(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_DELETE,
+                                     neigh_entry->rif,
+                                     neigh_entry->ha, dip);
+               err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht),
+                                     rauht_pl);
+               if (err) {
+                       netdev_err(dev, "Could not delete neigh %pI4h\n", &dip);
+                       neigh_entry->offloaded = true;
+               } else {
+                       neigh_entry->offloaded = false;
+               }
+       }
+
+       neigh_release(n);
+       mlxsw_sp_port_dev_put(mlxsw_sp_port);
+}
+
 static int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
                                          unsigned long event, void *ptr)
 {
+       struct mlxsw_sp_neigh_entry *neigh_entry;
        struct mlxsw_sp_port *mlxsw_sp_port;
        struct mlxsw_sp *mlxsw_sp;
        unsigned long interval;
+       struct net_device *dev;
        struct neigh_parms *p;
+       struct neighbour *n;
+       u32 dip;
 
        switch (event) {
        case NETEVENT_DELAY_PROBE_TIME_UPDATE:
 
                mlxsw_sp_port_dev_put(mlxsw_sp_port);
                break;
+       case NETEVENT_NEIGH_UPDATE:
+               n = ptr;
+               dev = n->dev;
+
+               if (n->tbl != &arp_tbl)
+                       return NOTIFY_DONE;
+
+               mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(dev);
+               if (!mlxsw_sp_port)
+                       return NOTIFY_DONE;
+
+               mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+               dip = ntohl(*((__be32 *) n->primary_key));
+               neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp,
+                                                         &dip,
+                                                         sizeof(__be32),
+                                                         dev);
+               if (WARN_ON(!neigh_entry) || WARN_ON(neigh_entry->n != n)) {
+                       mlxsw_sp_port_dev_put(mlxsw_sp_port);
+                       return NOTIFY_DONE;
+               }
+               neigh_entry->mlxsw_sp_port = mlxsw_sp_port;
+
+               /* Take a reference to ensure the neighbour won't be
+                * destructed until we drop the reference in delayed
+                * work.
+                */
+               neigh_clone(n);
+               if (!mlxsw_core_schedule_dw(&neigh_entry->dw, 0)) {
+                       neigh_release(n);
+                       mlxsw_sp_port_dev_put(mlxsw_sp_port);
+               }
+               break;
        }
 
        return NOTIFY_DONE;