@@ -1343,6 +1343,7 @@ static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port,
u32 speed)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
+ struct ocelot_mm_state *mm = &ocelot->mm[port];
u8 tas_speed;
switch (speed) {
@@ -1374,6 +1375,11 @@ static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port,
vsc9959_tas_guard_bands_update(ocelot, port);
mutex_unlock(&ocelot->tas_lock);
+
+ /* Workaround for hardware bug */
+ mutex_lock(&mm->lock);
+ ocelot_port_update_preemptible_tcs(ocelot, port);
+ mutex_unlock(&mm->lock);
}
static void vsc9959_new_base_time(struct ocelot *ocelot, ktime_t base_time,
@@ -2519,6 +2525,7 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot)
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;
@@ -2559,10 +2566,12 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot)
/* 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->preemptible_tcs;
for (tc = 0; tc < OCELOT_NUM_TC; tc++)
if (vsc9959_port_qmaxsdu_get(ocelot, port, tc))
@@ -2608,6 +2608,7 @@ static void ocelot_port_reset_mqprio(struct ocelot *ocelot, int port)
struct net_device *dev = ocelot->ops->port_to_netdev(ocelot, port);
netdev_reset_tc(dev);
+ ocelot_port_update_fp(ocelot, port, 0);
}
int ocelot_port_mqprio(struct ocelot *ocelot, int port,
@@ -2644,6 +2645,8 @@ int ocelot_port_mqprio(struct ocelot *ocelot, int port,
if (err)
goto err_reset_tc;
+ ocelot_port_update_fp(ocelot, port, mqprio->preemptible_tcs);
+
return 0;
err_reset_tc:
@@ -110,6 +110,8 @@ int ocelot_stats_init(struct ocelot *ocelot);
void ocelot_stats_deinit(struct ocelot *ocelot);
int ocelot_mm_init(struct ocelot *ocelot);
+void ocelot_port_update_fp(struct ocelot *ocelot, int port,
+ unsigned long preemptible_tcs);
extern struct notifier_block ocelot_netdevice_nb;
extern struct notifier_block ocelot_switchdev_nb;
@@ -49,6 +49,57 @@ static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val)
}
}
+void ocelot_port_update_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(&mm->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->verify_status == ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED ||
+ mm->verify_status == ETHTOOL_MM_VERIFY_STATUS_DISABLED))
+ val = mm->preemptible_tcs;
+
+ ocelot_rmw_rix(ocelot, QSYS_PREEMPTION_CFG_P_QUEUES(val),
+ QSYS_PREEMPTION_CFG_P_QUEUES_M,
+ QSYS_PREEMPTION_CFG, port);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_update_preemptible_tcs);
+
+void ocelot_port_update_fp(struct ocelot *ocelot, int port,
+ unsigned long preemptible_tcs)
+{
+ struct ocelot_mm_state *mm = &ocelot->mm[port];
+
+ mutex_lock(&mm->lock);
+
+ if (mm->preemptible_tcs == preemptible_tcs)
+ goto out_unlock;
+
+ mm->preemptible_tcs = preemptible_tcs;
+
+ /* Cut through switching doesn't work for preemptible priorities,
+ * so disable it.
+ */
+ mutex_lock(&ocelot->fwd_domain_lock);
+ ocelot->ops->cut_through_fwd(ocelot);
+ mutex_unlock(&ocelot->fwd_domain_lock);
+
+ ocelot_port_update_preemptible_tcs(ocelot, port);
+
+out_unlock:
+ mutex_unlock(&mm->lock);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_update_fp);
+
void ocelot_port_mm_irq(struct ocelot *ocelot, int port)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
@@ -66,6 +117,7 @@ void ocelot_port_mm_irq(struct ocelot *ocelot, int port)
"Port %d MAC Merge verification state %s\n",
port, mm_verify_state_to_string(verify_status));
mm->verify_status = verify_status;
+ ocelot_port_update_preemptible_tcs(ocelot, port);
}
if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) {
@@ -748,6 +748,7 @@ struct ocelot_mm_state {
struct mutex lock;
enum ethtool_mm_verify_status verify_status;
bool tx_active;
+ u8 preemptible_tcs;
};
struct ocelot_port;
@@ -1149,6 +1150,7 @@ int ocelot_port_get_mm(struct ocelot *ocelot, int 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,