Skip to content

Commit

Permalink
net: ethernet: ti: am65-cpsw-qos: Add Frame Preemption MAC Merge support
Browse files Browse the repository at this point in the history
Add driver support for viewing / changing the MAC Merge sublayer
parameters and seeing the verification state machine's current state
via ethtool.

As hardware does not support interrupt notification for verification
events we resort to polling on link up. On link up we try a couple of
times for verification success and if unsuccessful then give up.

The Frame Preemption feature is described in the Technical Reference
Manual [1] in section:
	12.3.1.4.6.7 Intersperced Express Traffic (IET – P802.3br/D2.0)

Due to Silicon Errata i2208 [2] we set limit min IET fragment size to
124 (excluding 4 bytes mCRC).

[1] AM62x TRM - https://www.ti.com/lit/ug/spruiv7a/spruiv7a.pdf
[2] AM62x Silicon Errata - https://www.ti.com/lit/er/sprz487c/sprz487c.pdf

Signed-off-by: Roger Quadros <rogerq@kernel.org>
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Roger Quadros authored and David S. Miller committed Dec 23, 2023
1 parent bc8d62e commit 49a2eb9
Show file tree
Hide file tree
Showing 6 changed files with 456 additions and 2 deletions.
5 changes: 3 additions & 2 deletions drivers/net/ethernet/ti/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,9 @@ config TI_AM65_CPSW_QOS
depends on TI_K3_AM65_CPSW_NUSS && NET_SCH_TAPRIO && TI_K3_AM65_CPTS
help
This option enables QoS offload features in AM65 CPSW like
Time Aware Shaper (TAS) / Enhanced Scheduled Traffic (EST)
and MQPRIO qdisc offload.
Time Aware Shaper (TAS) / Enhanced Scheduled Traffic (EST),
MQPRIO qdisc offload and Frame-Preemption MAC Merge / Interspersing
Express Traffic (IET).
The EST scheduler runs on CPTS and the TAS/EST schedule is
updated in the Fetch RAM memory of the CPSW.

Expand Down
167 changes: 167 additions & 0 deletions drivers/net/ethernet/ti/am65-cpsw-ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <linux/pm_runtime.h>

#include "am65-cpsw-nuss.h"
#include "am65-cpsw-qos.h"
#include "cpsw_ale.h"
#include "am65-cpts.h"

Expand Down Expand Up @@ -670,6 +671,9 @@ static void am65_cpsw_get_eth_mac_stats(struct net_device *ndev,

stats = port->stat_base;

if (s->src != ETHTOOL_MAC_STATS_SRC_AGGREGATE)
return;

s->FramesTransmittedOK = readl_relaxed(&stats->tx_good_frames);
s->SingleCollisionFrames = readl_relaxed(&stats->tx_single_coll_frames);
s->MultipleCollisionFrames = readl_relaxed(&stats->tx_mult_coll_frames);
Expand Down Expand Up @@ -740,6 +744,166 @@ static int am65_cpsw_set_ethtool_priv_flags(struct net_device *ndev, u32 flags)
return 0;
}

static void am65_cpsw_port_iet_rx_enable(struct am65_cpsw_port *port, bool enable)
{
u32 val;

val = readl(port->port_base + AM65_CPSW_PN_REG_CTL);
if (enable)
val |= AM65_CPSW_PN_CTL_IET_PORT_EN;
else
val &= ~AM65_CPSW_PN_CTL_IET_PORT_EN;

writel(val, port->port_base + AM65_CPSW_PN_REG_CTL);
am65_cpsw_iet_common_enable(port->common);
}

static void am65_cpsw_port_iet_tx_enable(struct am65_cpsw_port *port, bool enable)
{
u32 val;

val = readl(port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
if (enable)
val |= AM65_CPSW_PN_IET_MAC_PENABLE;
else
val &= ~AM65_CPSW_PN_IET_MAC_PENABLE;

writel(val, port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
}

static int am65_cpsw_get_mm(struct net_device *ndev, struct ethtool_mm_state *state)
{
struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
struct am65_cpsw_ndev_priv *priv = netdev_priv(ndev);
u32 port_ctrl, iet_ctrl, iet_status;
u32 add_frag_size;

if (!IS_ENABLED(CONFIG_TI_AM65_CPSW_QOS))
return -EOPNOTSUPP;

mutex_lock(&priv->mm_lock);

iet_ctrl = readl(port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
port_ctrl = readl(port->port_base + AM65_CPSW_PN_REG_CTL);

state->tx_enabled = !!(iet_ctrl & AM65_CPSW_PN_IET_MAC_PENABLE);
state->pmac_enabled = !!(port_ctrl & AM65_CPSW_PN_CTL_IET_PORT_EN);

iet_status = readl(port->port_base + AM65_CPSW_PN_REG_IET_STATUS);

if (iet_ctrl & AM65_CPSW_PN_IET_MAC_DISABLEVERIFY)
state->verify_status = ETHTOOL_MM_VERIFY_STATUS_DISABLED;
else if (iet_status & AM65_CPSW_PN_MAC_VERIFIED)
state->verify_status = ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
else if (iet_status & AM65_CPSW_PN_MAC_VERIFY_FAIL)
state->verify_status = ETHTOOL_MM_VERIFY_STATUS_FAILED;
else
state->verify_status = ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;

add_frag_size = AM65_CPSW_PN_IET_MAC_GET_ADDFRAGSIZE(iet_ctrl);
state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size);

/* Errata i2208: RX min fragment size cannot be less than 124 */
state->rx_min_frag_size = 124;

/* FPE active if common tx_enabled and verification success or disabled (forced) */
state->tx_active = state->tx_enabled &&
(state->verify_status == ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED ||
state->verify_status == ETHTOOL_MM_VERIFY_STATUS_DISABLED);
state->verify_enabled = !(iet_ctrl & AM65_CPSW_PN_IET_MAC_DISABLEVERIFY);

state->verify_time = port->qos.iet.verify_time_ms;

/* 802.3-2018 clause 30.14.1.6, says that the aMACMergeVerifyTime
* variable has a range between 1 and 128 ms inclusive. Limit to that.
*/
state->max_verify_time = 128;

mutex_unlock(&priv->mm_lock);

return 0;
}

static int am65_cpsw_set_mm(struct net_device *ndev, struct ethtool_mm_cfg *cfg,
struct netlink_ext_ack *extack)
{
struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
struct am65_cpsw_ndev_priv *priv = netdev_priv(ndev);
struct am65_cpsw_iet *iet = &port->qos.iet;
u32 val, add_frag_size;
int err;

if (!IS_ENABLED(CONFIG_TI_AM65_CPSW_QOS))
return -EOPNOTSUPP;

err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size, &add_frag_size, extack);
if (err)
return err;

mutex_lock(&priv->mm_lock);

if (cfg->pmac_enabled) {
/* change TX & RX FIFO MAX_BLKS as per TRM recommendation */
if (!iet->original_max_blks)
iet->original_max_blks = readl(port->port_base + AM65_CPSW_PN_REG_MAX_BLKS);

writel(AM65_CPSW_PN_TX_RX_MAX_BLKS_IET,
port->port_base + AM65_CPSW_PN_REG_MAX_BLKS);
} else if (iet->original_max_blks) {
/* restore RX & TX FIFO MAX_BLKS */
writel(iet->original_max_blks,
port->port_base + AM65_CPSW_PN_REG_MAX_BLKS);
}

am65_cpsw_port_iet_rx_enable(port, cfg->pmac_enabled);
am65_cpsw_port_iet_tx_enable(port, cfg->tx_enabled);

val = readl(port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
if (cfg->verify_enabled) {
val &= ~AM65_CPSW_PN_IET_MAC_DISABLEVERIFY;
/* Reset Verify state machine. Verification won't start here.
* Verification will be done once link-up.
*/
val |= AM65_CPSW_PN_IET_MAC_LINKFAIL;
} else {
val |= AM65_CPSW_PN_IET_MAC_DISABLEVERIFY;
/* Clear LINKFAIL to allow verify/response packets */
val &= ~AM65_CPSW_PN_IET_MAC_LINKFAIL;
}

val &= ~AM65_CPSW_PN_IET_MAC_MAC_ADDFRAGSIZE_MASK;
val |= AM65_CPSW_PN_IET_MAC_SET_ADDFRAGSIZE(add_frag_size);
writel(val, port->port_base + AM65_CPSW_PN_REG_IET_CTRL);

/* verify_timeout_count can only be set at valid link */
port->qos.iet.verify_time_ms = cfg->verify_time;

/* enable/disable preemption based on link status */
am65_cpsw_iet_commit_preemptible_tcs(port);

mutex_unlock(&priv->mm_lock);

return 0;
}

static void am65_cpsw_get_mm_stats(struct net_device *ndev,
struct ethtool_mm_stats *s)
{
struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
void __iomem *base = port->stat_base;

s->MACMergeFrameAssOkCount = readl(base + AM65_CPSW_STATN_IET_RX_ASSEMBLY_OK);
s->MACMergeFrameAssErrorCount = readl(base + AM65_CPSW_STATN_IET_RX_ASSEMBLY_ERROR);
s->MACMergeFrameSmdErrorCount = readl(base + AM65_CPSW_STATN_IET_RX_SMD_ERROR);
/* CPSW Functional Spec states:
* "The IET stat aMACMergeFragCountRx is derived by adding the
* Receive Assembly Error count to this value. i.e. AM65_CPSW_STATN_IET_RX_FRAG"
*/
s->MACMergeFragCountRx = readl(base + AM65_CPSW_STATN_IET_RX_FRAG) + s->MACMergeFrameAssErrorCount;
s->MACMergeFragCountTx = readl(base + AM65_CPSW_STATN_IET_TX_FRAG);
s->MACMergeHoldCount = readl(base + AM65_CPSW_STATN_IET_TX_HOLD);
}

const struct ethtool_ops am65_cpsw_ethtool_ops_slave = {
.begin = am65_cpsw_ethtool_op_begin,
.complete = am65_cpsw_ethtool_op_complete,
Expand Down Expand Up @@ -769,4 +933,7 @@ const struct ethtool_ops am65_cpsw_ethtool_ops_slave = {
.get_eee = am65_cpsw_get_eee,
.set_eee = am65_cpsw_set_eee,
.nway_reset = am65_cpsw_nway_reset,
.get_mm = am65_cpsw_get_mm,
.set_mm = am65_cpsw_set_mm,
.get_mm_stats = am65_cpsw_get_mm_stats,
};
2 changes: 2 additions & 0 deletions drivers/net/ethernet/ti/am65-cpsw-nuss.c
Original file line number Diff line number Diff line change
Expand Up @@ -2194,6 +2194,8 @@ am65_cpsw_nuss_init_port_ndev(struct am65_cpsw_common *common, u32 port_idx)
ndev_priv = netdev_priv(port->ndev);
ndev_priv->port = port;
ndev_priv->msg_enable = AM65_CPSW_DEBUG;
mutex_init(&ndev_priv->mm_lock);
port->qos.link_speed = SPEED_UNKNOWN;
SET_NETDEV_DEV(port->ndev, dev);

eth_hw_addr_set(port->ndev, port->slave.mac_addr);
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/ethernet/ti/am65-cpsw-nuss.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ struct am65_cpsw_common {
bool pf_p0_rx_ptype_rrobin;
struct am65_cpts *cpts;
int est_enabled;
bool iet_enabled;

bool is_emac_mode;
u16 br_members;
Expand All @@ -170,6 +171,10 @@ struct am65_cpsw_ndev_priv {
struct am65_cpsw_port *port;
struct am65_cpsw_ndev_stats __percpu *stats;
bool offload_fwd_mark;
/* Serialize access to MAC Merge state between ethtool requests
* and link state updates
*/
struct mutex mm_lock;
};

#define am65_ndev_to_priv(ndev) \
Expand Down
Loading

0 comments on commit 49a2eb9

Please sign in to comment.