brintos

brintos / linux-shallow public Read only

0
0
Text · 8.5 KiB · c815ae6 Raw
301 lines · c
1// SPDX-License-Identifier: (GPL-2.0 OR MIT)2/*3 * Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable4 * switches (VSC9959)5 *6 * Copyright 2022-2023 NXP7 */8#include <linux/ethtool.h>9#include <soc/mscc/ocelot.h>10#include <soc/mscc/ocelot_dev.h>11#include <soc/mscc/ocelot_qsys.h>12 13#include "ocelot.h"14 15static const char *16mm_verify_state_to_string(enum ethtool_mm_verify_status state)17{18	switch (state) {19	case ETHTOOL_MM_VERIFY_STATUS_INITIAL:20		return "INITIAL";21	case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:22		return "VERIFYING";23	case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:24		return "SUCCEEDED";25	case ETHTOOL_MM_VERIFY_STATUS_FAILED:26		return "FAILED";27	case ETHTOOL_MM_VERIFY_STATUS_DISABLED:28		return "DISABLED";29	default:30		return "UNKNOWN";31	}32}33 34static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val)35{36	switch (DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(val)) {37	case 0:38		return ETHTOOL_MM_VERIFY_STATUS_INITIAL;39	case 1:40		return ETHTOOL_MM_VERIFY_STATUS_VERIFYING;41	case 2:42		return ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;43	case 3:44		return ETHTOOL_MM_VERIFY_STATUS_FAILED;45	case 4:46		return ETHTOOL_MM_VERIFY_STATUS_DISABLED;47	default:48		return ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;49	}50}51 52void ocelot_port_update_active_preemptible_tcs(struct ocelot *ocelot, int port)53{54	struct ocelot_port *ocelot_port = ocelot->ports[port];55	struct ocelot_mm_state *mm = &ocelot->mm[port];56	u32 val = 0;57 58	lockdep_assert_held(&ocelot->fwd_domain_lock);59 60	/* Only commit preemptible TCs when MAC Merge is active.61	 * On NXP LS1028A, when using QSGMII, the port hangs if transmitting62	 * preemptible frames at any other link speed than gigabit, so avoid63	 * preemption at lower speeds in this PHY mode.64	 */65	if ((ocelot_port->phy_mode != PHY_INTERFACE_MODE_QSGMII ||66	     ocelot_port->speed == SPEED_1000) && mm->tx_active)67		val = mm->preemptible_tcs;68 69	/* Cut through switching doesn't work for preemptible priorities,70	 * so first make sure it is disabled. Also, changing the preemptible71	 * TCs affects the oversized frame dropping logic, so that needs to be72	 * re-triggered. And since tas_guard_bands_update() also implicitly73	 * calls cut_through_fwd(), we don't need to explicitly call it.74	 */75	mm->active_preemptible_tcs = val;76	ocelot->ops->tas_guard_bands_update(ocelot, port);77 78	dev_dbg(ocelot->dev,79		"port %d %s/%s, MM TX %s, preemptible TCs 0x%x, active 0x%x\n",80		port, phy_modes(ocelot_port->phy_mode),81		phy_speed_to_str(ocelot_port->speed),82		mm->tx_active ? "active" : "inactive", mm->preemptible_tcs,83		mm->active_preemptible_tcs);84 85	ocelot_rmw_rix(ocelot, QSYS_PREEMPTION_CFG_P_QUEUES(val),86		       QSYS_PREEMPTION_CFG_P_QUEUES_M,87		       QSYS_PREEMPTION_CFG, port);88}89 90void ocelot_port_change_fp(struct ocelot *ocelot, int port,91			   unsigned long preemptible_tcs)92{93	struct ocelot_mm_state *mm = &ocelot->mm[port];94 95	lockdep_assert_held(&ocelot->fwd_domain_lock);96 97	if (mm->preemptible_tcs == preemptible_tcs)98		return;99 100	mm->preemptible_tcs = preemptible_tcs;101 102	ocelot_port_update_active_preemptible_tcs(ocelot, port);103}104 105static void ocelot_mm_update_port_status(struct ocelot *ocelot, int port)106{107	struct ocelot_port *ocelot_port = ocelot->ports[port];108	struct ocelot_mm_state *mm = &ocelot->mm[port];109	enum ethtool_mm_verify_status verify_status;110	u32 val, ack = 0;111 112	if (!mm->tx_enabled)113		return;114 115	val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);116 117	verify_status = ocelot_mm_verify_status(val);118	if (mm->verify_status != verify_status) {119		dev_dbg(ocelot->dev,120			"Port %d MAC Merge verification state %s\n",121			port, mm_verify_state_to_string(verify_status));122		mm->verify_status = verify_status;123	}124 125	if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) {126		mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS);127 128		dev_dbg(ocelot->dev, "Port %d TX preemption %s\n",129			port, mm->tx_active ? "active" : "inactive");130		ocelot_port_update_active_preemptible_tcs(ocelot, port);131 132		ack |= DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY;133	}134 135	if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) {136		dev_err(ocelot->dev,137			"Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n",138			port);139 140		ack |= DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY;141	}142 143	if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) {144		dev_err(ocelot->dev,145			"Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n",146			port);147 148		ack |= DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY;149	}150 151	if (ack)152		ocelot_port_writel(ocelot_port, ack, DEV_MM_STATUS);153}154 155void ocelot_mm_irq(struct ocelot *ocelot)156{157	int port;158 159	mutex_lock(&ocelot->fwd_domain_lock);160 161	for (port = 0; port < ocelot->num_phys_ports; port++)162		ocelot_mm_update_port_status(ocelot, port);163 164	mutex_unlock(&ocelot->fwd_domain_lock);165}166EXPORT_SYMBOL_GPL(ocelot_mm_irq);167 168int ocelot_port_set_mm(struct ocelot *ocelot, int port,169		       struct ethtool_mm_cfg *cfg,170		       struct netlink_ext_ack *extack)171{172	struct ocelot_port *ocelot_port = ocelot->ports[port];173	u32 mm_enable = 0, verify_disable = 0, add_frag_size;174	struct ocelot_mm_state *mm;175	int err;176 177	if (!ocelot->mm_supported)178		return -EOPNOTSUPP;179 180	mm = &ocelot->mm[port];181 182	err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size,183					      &add_frag_size, extack);184	if (err)185		return err;186 187	if (cfg->pmac_enabled)188		mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA;189 190	if (cfg->tx_enabled)191		mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA;192 193	if (!cfg->verify_enabled)194		verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS;195 196	mutex_lock(&ocelot->fwd_domain_lock);197 198	ocelot_port_rmwl(ocelot_port, mm_enable,199			 DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA |200			 DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA,201			 DEV_MM_ENABLE_CONFIG);202 203	ocelot_port_rmwl(ocelot_port, verify_disable |204			 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time),205			 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS |206			 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M,207			 DEV_MM_VERIF_CONFIG);208 209	ocelot_rmw_rix(ocelot,210		       QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size),211		       QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M,212		       QSYS_PREEMPTION_CFG,213		       port);214 215	/* The switch will emit an IRQ when TX is disabled, to notify that it216	 * has become inactive. We optimize ocelot_mm_update_port_status() to217	 * not bother processing MM IRQs at all for ports with TX disabled,218	 * but we need to ACK this IRQ now, while mm->tx_enabled is still set,219	 * otherwise we get an IRQ storm.220	 */221	if (mm->tx_enabled && !cfg->tx_enabled) {222		ocelot_mm_update_port_status(ocelot, port);223		WARN_ON(mm->tx_active);224	}225 226	mm->tx_enabled = cfg->tx_enabled;227 228	mutex_unlock(&ocelot->fwd_domain_lock);229 230	return 0;231}232EXPORT_SYMBOL_GPL(ocelot_port_set_mm);233 234int ocelot_port_get_mm(struct ocelot *ocelot, int port,235		       struct ethtool_mm_state *state)236{237	struct ocelot_port *ocelot_port = ocelot->ports[port];238	struct ocelot_mm_state *mm;239	u32 val, add_frag_size;240 241	if (!ocelot->mm_supported)242		return -EOPNOTSUPP;243 244	mm = &ocelot->mm[port];245 246	mutex_lock(&ocelot->fwd_domain_lock);247 248	val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG);249	state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA);250	state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA);251 252	val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG);253	state->verify_enabled = !(val & DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS);254	state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val);255	state->max_verify_time = 128;256 257	val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port);258	add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val);259	state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size);260	state->rx_min_frag_size = ETH_ZLEN;261 262	ocelot_mm_update_port_status(ocelot, port);263	state->verify_status = mm->verify_status;264	state->tx_active = mm->tx_active;265 266	mutex_unlock(&ocelot->fwd_domain_lock);267 268	return 0;269}270EXPORT_SYMBOL_GPL(ocelot_port_get_mm);271 272int ocelot_mm_init(struct ocelot *ocelot)273{274	struct ocelot_port *ocelot_port;275	struct ocelot_mm_state *mm;276	int port;277 278	if (!ocelot->mm_supported)279		return 0;280 281	ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,282				  sizeof(*ocelot->mm), GFP_KERNEL);283	if (!ocelot->mm)284		return -ENOMEM;285 286	for (port = 0; port < ocelot->num_phys_ports; port++) {287		u32 val;288 289		mm = &ocelot->mm[port];290		ocelot_port = ocelot->ports[port];291 292		/* Update initial status variable for the293		 * verification state machine294		 */295		val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);296		mm->verify_status = ocelot_mm_verify_status(val);297	}298 299	return 0;300}301