xref: /openbmc/linux/drivers/net/ethernet/mscc/ocelot_mm.c (revision 15f93f46f31232da863316769182c699e364c45f)
16505b680SVladimir Oltean // SPDX-License-Identifier: (GPL-2.0 OR MIT)
26505b680SVladimir Oltean /*
36505b680SVladimir Oltean  * Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable
46505b680SVladimir Oltean  * switches (VSC9959)
56505b680SVladimir Oltean  *
66505b680SVladimir Oltean  * Copyright 2022-2023 NXP
76505b680SVladimir Oltean  */
86505b680SVladimir Oltean #include <linux/ethtool.h>
96505b680SVladimir Oltean #include <soc/mscc/ocelot.h>
106505b680SVladimir Oltean #include <soc/mscc/ocelot_dev.h>
116505b680SVladimir Oltean #include <soc/mscc/ocelot_qsys.h>
126505b680SVladimir Oltean 
136505b680SVladimir Oltean #include "ocelot.h"
146505b680SVladimir Oltean 
156505b680SVladimir Oltean static const char *
166505b680SVladimir Oltean mm_verify_state_to_string(enum ethtool_mm_verify_status state)
176505b680SVladimir Oltean {
186505b680SVladimir Oltean 	switch (state) {
196505b680SVladimir Oltean 	case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
206505b680SVladimir Oltean 		return "INITIAL";
216505b680SVladimir Oltean 	case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
226505b680SVladimir Oltean 		return "VERIFYING";
236505b680SVladimir Oltean 	case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
246505b680SVladimir Oltean 		return "SUCCEEDED";
256505b680SVladimir Oltean 	case ETHTOOL_MM_VERIFY_STATUS_FAILED:
266505b680SVladimir Oltean 		return "FAILED";
276505b680SVladimir Oltean 	case ETHTOOL_MM_VERIFY_STATUS_DISABLED:
286505b680SVladimir Oltean 		return "DISABLED";
296505b680SVladimir Oltean 	default:
306505b680SVladimir Oltean 		return "UNKNOWN";
316505b680SVladimir Oltean 	}
326505b680SVladimir Oltean }
336505b680SVladimir Oltean 
346505b680SVladimir Oltean static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val)
356505b680SVladimir Oltean {
366505b680SVladimir Oltean 	switch (DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(val)) {
376505b680SVladimir Oltean 	case 0:
386505b680SVladimir Oltean 		return ETHTOOL_MM_VERIFY_STATUS_INITIAL;
396505b680SVladimir Oltean 	case 1:
406505b680SVladimir Oltean 		return ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
416505b680SVladimir Oltean 	case 2:
426505b680SVladimir Oltean 		return ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
436505b680SVladimir Oltean 	case 3:
446505b680SVladimir Oltean 		return ETHTOOL_MM_VERIFY_STATUS_FAILED;
456505b680SVladimir Oltean 	case 4:
466505b680SVladimir Oltean 		return ETHTOOL_MM_VERIFY_STATUS_DISABLED;
476505b680SVladimir Oltean 	default:
486505b680SVladimir Oltean 		return ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;
496505b680SVladimir Oltean 	}
506505b680SVladimir Oltean }
516505b680SVladimir Oltean 
52*15f93f46SVladimir Oltean static void ocelot_mm_update_port_status(struct ocelot *ocelot, int port)
536505b680SVladimir Oltean {
546505b680SVladimir Oltean 	struct ocelot_port *ocelot_port = ocelot->ports[port];
556505b680SVladimir Oltean 	struct ocelot_mm_state *mm = &ocelot->mm[port];
566505b680SVladimir Oltean 	enum ethtool_mm_verify_status verify_status;
576505b680SVladimir Oltean 	u32 val;
586505b680SVladimir Oltean 
596505b680SVladimir Oltean 	mutex_lock(&mm->lock);
606505b680SVladimir Oltean 
616505b680SVladimir Oltean 	val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
626505b680SVladimir Oltean 
636505b680SVladimir Oltean 	verify_status = ocelot_mm_verify_status(val);
646505b680SVladimir Oltean 	if (mm->verify_status != verify_status) {
656505b680SVladimir Oltean 		dev_dbg(ocelot->dev,
666505b680SVladimir Oltean 			"Port %d MAC Merge verification state %s\n",
676505b680SVladimir Oltean 			port, mm_verify_state_to_string(verify_status));
686505b680SVladimir Oltean 		mm->verify_status = verify_status;
696505b680SVladimir Oltean 	}
706505b680SVladimir Oltean 
716505b680SVladimir Oltean 	if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) {
726505b680SVladimir Oltean 		mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS);
736505b680SVladimir Oltean 
746505b680SVladimir Oltean 		dev_dbg(ocelot->dev, "Port %d TX preemption %s\n",
756505b680SVladimir Oltean 			port, mm->tx_active ? "active" : "inactive");
766505b680SVladimir Oltean 	}
776505b680SVladimir Oltean 
786505b680SVladimir Oltean 	if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) {
796505b680SVladimir Oltean 		dev_err(ocelot->dev,
806505b680SVladimir Oltean 			"Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n",
816505b680SVladimir Oltean 			port);
826505b680SVladimir Oltean 	}
836505b680SVladimir Oltean 
846505b680SVladimir Oltean 	if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) {
856505b680SVladimir Oltean 		dev_err(ocelot->dev,
866505b680SVladimir Oltean 			"Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n",
876505b680SVladimir Oltean 			port);
886505b680SVladimir Oltean 	}
896505b680SVladimir Oltean 
906505b680SVladimir Oltean 	ocelot_port_writel(ocelot_port, val, DEV_MM_STATUS);
916505b680SVladimir Oltean 
926505b680SVladimir Oltean 	mutex_unlock(&mm->lock);
936505b680SVladimir Oltean }
94*15f93f46SVladimir Oltean 
95*15f93f46SVladimir Oltean void ocelot_mm_irq(struct ocelot *ocelot)
96*15f93f46SVladimir Oltean {
97*15f93f46SVladimir Oltean 	int port;
98*15f93f46SVladimir Oltean 
99*15f93f46SVladimir Oltean 	for (port = 0; port < ocelot->num_phys_ports; port++)
100*15f93f46SVladimir Oltean 		ocelot_mm_update_port_status(ocelot, port);
101*15f93f46SVladimir Oltean }
102*15f93f46SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_mm_irq);
1036505b680SVladimir Oltean 
1046505b680SVladimir Oltean int ocelot_port_set_mm(struct ocelot *ocelot, int port,
1056505b680SVladimir Oltean 		       struct ethtool_mm_cfg *cfg,
1066505b680SVladimir Oltean 		       struct netlink_ext_ack *extack)
1076505b680SVladimir Oltean {
1086505b680SVladimir Oltean 	struct ocelot_port *ocelot_port = ocelot->ports[port];
1096505b680SVladimir Oltean 	u32 mm_enable = 0, verify_disable = 0, add_frag_size;
1106505b680SVladimir Oltean 	struct ocelot_mm_state *mm;
1116505b680SVladimir Oltean 	int err;
1126505b680SVladimir Oltean 
1136505b680SVladimir Oltean 	if (!ocelot->mm_supported)
1146505b680SVladimir Oltean 		return -EOPNOTSUPP;
1156505b680SVladimir Oltean 
1166505b680SVladimir Oltean 	mm = &ocelot->mm[port];
1176505b680SVladimir Oltean 
1186505b680SVladimir Oltean 	err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size,
1196505b680SVladimir Oltean 					      &add_frag_size, extack);
1206505b680SVladimir Oltean 	if (err)
1216505b680SVladimir Oltean 		return err;
1226505b680SVladimir Oltean 
1236505b680SVladimir Oltean 	if (cfg->pmac_enabled)
1246505b680SVladimir Oltean 		mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA;
1256505b680SVladimir Oltean 
1266505b680SVladimir Oltean 	if (cfg->tx_enabled)
1276505b680SVladimir Oltean 		mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA;
1286505b680SVladimir Oltean 
1296505b680SVladimir Oltean 	if (!cfg->verify_enabled)
1306505b680SVladimir Oltean 		verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS;
1316505b680SVladimir Oltean 
1326505b680SVladimir Oltean 	mutex_lock(&mm->lock);
1336505b680SVladimir Oltean 
1346505b680SVladimir Oltean 	ocelot_port_rmwl(ocelot_port, mm_enable,
1356505b680SVladimir Oltean 			 DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA |
1366505b680SVladimir Oltean 			 DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA,
1376505b680SVladimir Oltean 			 DEV_MM_ENABLE_CONFIG);
1386505b680SVladimir Oltean 
1396505b680SVladimir Oltean 	ocelot_port_rmwl(ocelot_port, verify_disable |
1406505b680SVladimir Oltean 			 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time),
1416505b680SVladimir Oltean 			 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS |
1426505b680SVladimir Oltean 			 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M,
1436505b680SVladimir Oltean 			 DEV_MM_VERIF_CONFIG);
1446505b680SVladimir Oltean 
1456505b680SVladimir Oltean 	ocelot_rmw_rix(ocelot,
1466505b680SVladimir Oltean 		       QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size),
1476505b680SVladimir Oltean 		       QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M,
1486505b680SVladimir Oltean 		       QSYS_PREEMPTION_CFG,
1496505b680SVladimir Oltean 		       port);
1506505b680SVladimir Oltean 
1516505b680SVladimir Oltean 	mutex_unlock(&mm->lock);
1526505b680SVladimir Oltean 
1536505b680SVladimir Oltean 	return 0;
1546505b680SVladimir Oltean }
1556505b680SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_port_set_mm);
1566505b680SVladimir Oltean 
1576505b680SVladimir Oltean int ocelot_port_get_mm(struct ocelot *ocelot, int port,
1586505b680SVladimir Oltean 		       struct ethtool_mm_state *state)
1596505b680SVladimir Oltean {
1606505b680SVladimir Oltean 	struct ocelot_port *ocelot_port = ocelot->ports[port];
1616505b680SVladimir Oltean 	struct ocelot_mm_state *mm;
1626505b680SVladimir Oltean 	u32 val, add_frag_size;
1636505b680SVladimir Oltean 
1646505b680SVladimir Oltean 	if (!ocelot->mm_supported)
1656505b680SVladimir Oltean 		return -EOPNOTSUPP;
1666505b680SVladimir Oltean 
1676505b680SVladimir Oltean 	mm = &ocelot->mm[port];
1686505b680SVladimir Oltean 
1696505b680SVladimir Oltean 	mutex_lock(&mm->lock);
1706505b680SVladimir Oltean 
1716505b680SVladimir Oltean 	val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG);
1726505b680SVladimir Oltean 	state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA);
1736505b680SVladimir Oltean 	state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA);
1746505b680SVladimir Oltean 
1756505b680SVladimir Oltean 	val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG);
17628113cfaSVladimir Oltean 	state->verify_enabled = !(val & DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS);
1776505b680SVladimir Oltean 	state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val);
1786505b680SVladimir Oltean 	state->max_verify_time = 128;
1796505b680SVladimir Oltean 
1806505b680SVladimir Oltean 	val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port);
1816505b680SVladimir Oltean 	add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val);
1826505b680SVladimir Oltean 	state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size);
1836505b680SVladimir Oltean 	state->rx_min_frag_size = ETH_ZLEN;
1846505b680SVladimir Oltean 
1856505b680SVladimir Oltean 	state->verify_status = mm->verify_status;
1866505b680SVladimir Oltean 	state->tx_active = mm->tx_active;
1876505b680SVladimir Oltean 
1886505b680SVladimir Oltean 	mutex_unlock(&mm->lock);
1896505b680SVladimir Oltean 
1906505b680SVladimir Oltean 	return 0;
1916505b680SVladimir Oltean }
1926505b680SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_port_get_mm);
1936505b680SVladimir Oltean 
1946505b680SVladimir Oltean int ocelot_mm_init(struct ocelot *ocelot)
1956505b680SVladimir Oltean {
1966505b680SVladimir Oltean 	struct ocelot_port *ocelot_port;
1976505b680SVladimir Oltean 	struct ocelot_mm_state *mm;
1986505b680SVladimir Oltean 	int port;
1996505b680SVladimir Oltean 
2006505b680SVladimir Oltean 	if (!ocelot->mm_supported)
2016505b680SVladimir Oltean 		return 0;
2026505b680SVladimir Oltean 
2036505b680SVladimir Oltean 	ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
2046505b680SVladimir Oltean 				  sizeof(*ocelot->mm), GFP_KERNEL);
2056505b680SVladimir Oltean 	if (!ocelot->mm)
2066505b680SVladimir Oltean 		return -ENOMEM;
2076505b680SVladimir Oltean 
2086505b680SVladimir Oltean 	for (port = 0; port < ocelot->num_phys_ports; port++) {
2096505b680SVladimir Oltean 		u32 val;
2106505b680SVladimir Oltean 
2116505b680SVladimir Oltean 		mm = &ocelot->mm[port];
2126505b680SVladimir Oltean 		mutex_init(&mm->lock);
2136505b680SVladimir Oltean 		ocelot_port = ocelot->ports[port];
2146505b680SVladimir Oltean 
2156505b680SVladimir Oltean 		/* Update initial status variable for the
2166505b680SVladimir Oltean 		 * verification state machine
2176505b680SVladimir Oltean 		 */
2186505b680SVladimir Oltean 		val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
2196505b680SVladimir Oltean 		mm->verify_status = ocelot_mm_verify_status(val);
2206505b680SVladimir Oltean 	}
2216505b680SVladimir Oltean 
2226505b680SVladimir Oltean 	return 0;
2236505b680SVladimir Oltean }
224