linux/drivers/net/ethernet/mscc/ocelot_mm.c
Vladimir Oltean 28113cfada net: mscc: ocelot: fix incorrect verify_enabled reporting in ethtool get_mm()
We don't read the verify_enabled variable from hardware in the MAC Merge
layer state GET operation, instead we always leave it set to "false".
The user may think something is wrong if they set verify_enabled to
true, then read it back and see it's still false, even though the
configuration took place.

Fixes: 6505b6805655 ("net: mscc: ocelot: add MAC Merge layer support for VSC9959")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://lore.kernel.org/r/20230123184538.3420098-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-01-24 18:34:20 -08:00

216 lines
5.7 KiB
C

// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable
* switches (VSC9959)
*
* Copyright 2022-2023 NXP
*/
#include <linux/ethtool.h>
#include <soc/mscc/ocelot.h>
#include <soc/mscc/ocelot_dev.h>
#include <soc/mscc/ocelot_qsys.h>
#include "ocelot.h"
static const char *
mm_verify_state_to_string(enum ethtool_mm_verify_status state)
{
switch (state) {
case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
return "INITIAL";
case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
return "VERIFYING";
case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
return "SUCCEEDED";
case ETHTOOL_MM_VERIFY_STATUS_FAILED:
return "FAILED";
case ETHTOOL_MM_VERIFY_STATUS_DISABLED:
return "DISABLED";
default:
return "UNKNOWN";
}
}
static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val)
{
switch (DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(val)) {
case 0:
return ETHTOOL_MM_VERIFY_STATUS_INITIAL;
case 1:
return ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
case 2:
return ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
case 3:
return ETHTOOL_MM_VERIFY_STATUS_FAILED;
case 4:
return ETHTOOL_MM_VERIFY_STATUS_DISABLED;
default:
return ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;
}
}
void ocelot_port_mm_irq(struct ocelot *ocelot, int port)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_mm_state *mm = &ocelot->mm[port];
enum ethtool_mm_verify_status verify_status;
u32 val;
mutex_lock(&mm->lock);
val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
verify_status = ocelot_mm_verify_status(val);
if (mm->verify_status != verify_status) {
dev_dbg(ocelot->dev,
"Port %d MAC Merge verification state %s\n",
port, mm_verify_state_to_string(verify_status));
mm->verify_status = verify_status;
}
if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) {
mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS);
dev_dbg(ocelot->dev, "Port %d TX preemption %s\n",
port, mm->tx_active ? "active" : "inactive");
}
if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) {
dev_err(ocelot->dev,
"Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n",
port);
}
if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) {
dev_err(ocelot->dev,
"Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n",
port);
}
ocelot_port_writel(ocelot_port, val, DEV_MM_STATUS);
mutex_unlock(&mm->lock);
}
EXPORT_SYMBOL_GPL(ocelot_port_mm_irq);
int ocelot_port_set_mm(struct ocelot *ocelot, int port,
struct ethtool_mm_cfg *cfg,
struct netlink_ext_ack *extack)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
u32 mm_enable = 0, verify_disable = 0, add_frag_size;
struct ocelot_mm_state *mm;
int err;
if (!ocelot->mm_supported)
return -EOPNOTSUPP;
mm = &ocelot->mm[port];
err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size,
&add_frag_size, extack);
if (err)
return err;
if (cfg->pmac_enabled)
mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA;
if (cfg->tx_enabled)
mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA;
if (!cfg->verify_enabled)
verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS;
mutex_lock(&mm->lock);
ocelot_port_rmwl(ocelot_port, mm_enable,
DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA |
DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA,
DEV_MM_ENABLE_CONFIG);
ocelot_port_rmwl(ocelot_port, verify_disable |
DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time),
DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS |
DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M,
DEV_MM_VERIF_CONFIG);
ocelot_rmw_rix(ocelot,
QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size),
QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M,
QSYS_PREEMPTION_CFG,
port);
mutex_unlock(&mm->lock);
return 0;
}
EXPORT_SYMBOL_GPL(ocelot_port_set_mm);
int ocelot_port_get_mm(struct ocelot *ocelot, int port,
struct ethtool_mm_state *state)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_mm_state *mm;
u32 val, add_frag_size;
if (!ocelot->mm_supported)
return -EOPNOTSUPP;
mm = &ocelot->mm[port];
mutex_lock(&mm->lock);
val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG);
state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA);
state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA);
val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG);
state->verify_enabled = !(val & DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS);
state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val);
state->max_verify_time = 128;
val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port);
add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val);
state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size);
state->rx_min_frag_size = ETH_ZLEN;
state->verify_status = mm->verify_status;
state->tx_active = mm->tx_active;
mutex_unlock(&mm->lock);
return 0;
}
EXPORT_SYMBOL_GPL(ocelot_port_get_mm);
int ocelot_mm_init(struct ocelot *ocelot)
{
struct ocelot_port *ocelot_port;
struct ocelot_mm_state *mm;
int port;
if (!ocelot->mm_supported)
return 0;
ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
sizeof(*ocelot->mm), GFP_KERNEL);
if (!ocelot->mm)
return -ENOMEM;
for (port = 0; port < ocelot->num_phys_ports; port++) {
u32 val;
mm = &ocelot->mm[port];
mutex_init(&mm->lock);
ocelot_port = ocelot->ports[port];
/* Update initial status variable for the
* verification state machine
*/
val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
mm->verify_status = ocelot_mm_verify_status(val);
}
return 0;
}