Skip to content

Commit

Permalink
net: mscc: ocelot: add support for preemptible traffic classes
Browse files Browse the repository at this point in the history
In order to not transmit (preemptible) frames which will be received by
the link partner as corrupted (because it doesn't support FP), the
hardware requires the driver to program the QSYS_PREEMPTION_CFG_P_QUEUES
register only after the MAC Merge layer becomes active (verification
succeeds, or was disabled).

There are some cases when FP is known (through experimentation) to be
broken. Give priority to FP over cut-through switching, and disable FP
for known broken link modes.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Vladimir Oltean authored and Jakub Kicinski committed Apr 18, 2023
1 parent a1ca9f8 commit 403ffc2
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 3 deletions.
7 changes: 5 additions & 2 deletions drivers/net/dsa/ocelot/felix_vsc9959.c
Original file line number Diff line number Diff line change
Expand Up @@ -2519,6 +2519,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;
Expand Down Expand Up @@ -2559,10 +2560,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->active_preemptible_tcs;

for (tc = 0; tc < OCELOT_NUM_TC; tc++)
if (vsc9959_port_qmaxsdu_get(ocelot, port, tc))
Expand Down
10 changes: 9 additions & 1 deletion drivers/net/ethernet/mscc/ocelot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1006,7 +1006,12 @@ void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port,
*/
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);
}

Expand Down Expand Up @@ -2705,6 +2710,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_change_fp(ocelot, port, 0);
}

int ocelot_port_mqprio(struct ocelot *ocelot, int port,
Expand Down Expand Up @@ -2741,6 +2747,8 @@ 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:
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/ethernet/mscc/ocelot.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ int ocelot_stats_init(struct ocelot *ocelot);
void ocelot_stats_deinit(struct ocelot *ocelot);

int ocelot_mm_init(struct ocelot *ocelot);
void ocelot_port_change_fp(struct ocelot *ocelot, int port,
unsigned long preemptible_tcs);
void ocelot_port_update_active_preemptible_tcs(struct ocelot *ocelot, int port);

extern struct notifier_block ocelot_netdevice_nb;
extern struct notifier_block ocelot_switchdev_nb;
Expand Down
54 changes: 54 additions & 0 deletions drivers/net/ethernet/mscc/ocelot_mm.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,59 @@ static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val)
}
}

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];
Expand All @@ -74,6 +127,7 @@ static void ocelot_mm_update_port_status(struct ocelot *ocelot, int 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;
}
Expand Down
3 changes: 3 additions & 0 deletions include/soc/mscc/ocelot.h
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,8 @@ struct ocelot_mm_state {
enum ethtool_mm_verify_status verify_status;
bool tx_enabled;
bool tx_active;
u8 preemptible_tcs;
u8 active_preemptible_tcs;
};

struct ocelot_port;
Expand Down Expand Up @@ -1158,6 +1160,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,
Expand Down

0 comments on commit 403ffc2

Please sign in to comment.