for (port = 0; port < ocelot->num_phys_ports; port++) {
                struct ocelot_port *ocelot_port = ocelot->ports[port];
+               struct ocelot_mm_state *mm = &ocelot->mm[port];
                int min_speed = ocelot_port->speed;
                unsigned long mask = 0;
                u32 tmp, val = 0;
 
                /* Enable cut-through forwarding for all traffic classes that
                 * don't have oversized dropping enabled, since this check is
-                * bypassed in cut-through mode.
+                * bypassed in cut-through mode. Also exclude preemptible
+                * traffic classes, since these would hang the port for some
+                * reason, if sent as cut-through.
                 */
                if (ocelot_port->speed == min_speed) {
-                       val = GENMASK(7, 0);
+                       val = GENMASK(7, 0) & ~mm->active_preemptible_tcs;
 
                        for (tc = 0; tc < OCELOT_NUM_TC; tc++)
                                if (vsc9959_port_qmaxsdu_get(ocelot, port, tc))
 
         */
        if (ocelot->ops->cut_through_fwd) {
                mutex_lock(&ocelot->fwd_domain_lock);
-               ocelot->ops->cut_through_fwd(ocelot);
+               /* Workaround for hardware bug - FP doesn't work
+                * at all link speeds for all PHY modes. The function
+                * below also calls ocelot->ops->cut_through_fwd(),
+                * so we don't need to do it twice.
+                */
+               ocelot_port_update_active_preemptible_tcs(ocelot, port);
                mutex_unlock(&ocelot->fwd_domain_lock);
        }
 
        struct net_device *dev = ocelot->ops->port_to_netdev(ocelot, port);
 
        netdev_reset_tc(dev);
+       ocelot_port_change_fp(ocelot, port, 0);
 }
 
 int ocelot_port_mqprio(struct ocelot *ocelot, int port,
        if (err)
                goto err_reset_tc;
 
+       ocelot_port_change_fp(ocelot, port, mqprio->preemptible_tcs);
+
        return 0;
 
 err_reset_tc:
 
        }
 }
 
+void ocelot_port_update_active_preemptible_tcs(struct ocelot *ocelot, int port)
+{
+       struct ocelot_port *ocelot_port = ocelot->ports[port];
+       struct ocelot_mm_state *mm = &ocelot->mm[port];
+       u32 val = 0;
+
+       lockdep_assert_held(&ocelot->fwd_domain_lock);
+
+       /* Only commit preemptible TCs when MAC Merge is active.
+        * On NXP LS1028A, when using QSGMII, the port hangs if transmitting
+        * preemptible frames at any other link speed than gigabit, so avoid
+        * preemption at lower speeds in this PHY mode.
+        */
+       if ((ocelot_port->phy_mode != PHY_INTERFACE_MODE_QSGMII ||
+            ocelot_port->speed == SPEED_1000) && mm->tx_active)
+               val = mm->preemptible_tcs;
+
+       /* Cut through switching doesn't work for preemptible priorities,
+        * so first make sure it is disabled.
+        */
+       mm->active_preemptible_tcs = val;
+       ocelot->ops->cut_through_fwd(ocelot);
+
+       dev_dbg(ocelot->dev,
+               "port %d %s/%s, MM TX %s, preemptible TCs 0x%x, active 0x%x\n",
+               port, phy_modes(ocelot_port->phy_mode),
+               phy_speed_to_str(ocelot_port->speed),
+               mm->tx_active ? "active" : "inactive", mm->preemptible_tcs,
+               mm->active_preemptible_tcs);
+
+       ocelot_rmw_rix(ocelot, QSYS_PREEMPTION_CFG_P_QUEUES(val),
+                      QSYS_PREEMPTION_CFG_P_QUEUES_M,
+                      QSYS_PREEMPTION_CFG, port);
+}
+
+void ocelot_port_change_fp(struct ocelot *ocelot, int port,
+                          unsigned long preemptible_tcs)
+{
+       struct ocelot_mm_state *mm = &ocelot->mm[port];
+
+       mutex_lock(&ocelot->fwd_domain_lock);
+
+       if (mm->preemptible_tcs == preemptible_tcs)
+               goto out_unlock;
+
+       mm->preemptible_tcs = preemptible_tcs;
+
+       ocelot_port_update_active_preemptible_tcs(ocelot, port);
+
+out_unlock:
+       mutex_unlock(&ocelot->fwd_domain_lock);
+}
+
 static void ocelot_mm_update_port_status(struct ocelot *ocelot, int port)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
 
                dev_dbg(ocelot->dev, "Port %d TX preemption %s\n",
                        port, mm->tx_active ? "active" : "inactive");
+               ocelot_port_update_active_preemptible_tcs(ocelot, port);
 
                ack |= DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY;
        }
 
        enum ethtool_mm_verify_status verify_status;
        bool tx_enabled;
        bool tx_active;
+       u8 preemptible_tcs;
+       u8 active_preemptible_tcs;
 };
 
 struct ocelot_port;
                       struct ethtool_mm_state *state);
 int ocelot_port_mqprio(struct ocelot *ocelot, int port,
                       struct tc_mqprio_qopt_offload *mqprio);
+void ocelot_port_update_preemptible_tcs(struct ocelot *ocelot, int port);
 
 #if IS_ENABLED(CONFIG_BRIDGE_MRP)
 int ocelot_mrp_add(struct ocelot *ocelot, int port,