1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
3 * Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable
6 * Copyright 2022-2023 NXP
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>
16 mm_verify_state_to_string(enum ethtool_mm_verify_status state)
19 case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
21 case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
23 case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
25 case ETHTOOL_MM_VERIFY_STATUS_FAILED:
27 case ETHTOOL_MM_VERIFY_STATUS_DISABLED:
34 static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val)
36 switch (DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(val)) {
38 return ETHTOOL_MM_VERIFY_STATUS_INITIAL;
40 return ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
42 return ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
44 return ETHTOOL_MM_VERIFY_STATUS_FAILED;
46 return ETHTOOL_MM_VERIFY_STATUS_DISABLED;
48 return ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;
52 static void ocelot_mm_update_port_status(struct ocelot *ocelot, int port)
54 struct ocelot_port *ocelot_port = ocelot->ports[port];
55 struct ocelot_mm_state *mm = &ocelot->mm[port];
56 enum ethtool_mm_verify_status verify_status;
62 val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
64 verify_status = ocelot_mm_verify_status(val);
65 if (mm->verify_status != verify_status) {
67 "Port %d MAC Merge verification state %s\n",
68 port, mm_verify_state_to_string(verify_status));
69 mm->verify_status = verify_status;
72 if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) {
73 mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS);
75 dev_dbg(ocelot->dev, "Port %d TX preemption %s\n",
76 port, mm->tx_active ? "active" : "inactive");
78 ack |= DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY;
81 if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) {
83 "Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n",
86 ack |= DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY;
89 if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) {
91 "Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n",
94 ack |= DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY;
98 ocelot_port_writel(ocelot_port, ack, DEV_MM_STATUS);
101 void ocelot_mm_irq(struct ocelot *ocelot)
105 mutex_lock(&ocelot->fwd_domain_lock);
107 for (port = 0; port < ocelot->num_phys_ports; port++)
108 ocelot_mm_update_port_status(ocelot, port);
110 mutex_unlock(&ocelot->fwd_domain_lock);
112 EXPORT_SYMBOL_GPL(ocelot_mm_irq);
114 int ocelot_port_set_mm(struct ocelot *ocelot, int port,
115 struct ethtool_mm_cfg *cfg,
116 struct netlink_ext_ack *extack)
118 struct ocelot_port *ocelot_port = ocelot->ports[port];
119 u32 mm_enable = 0, verify_disable = 0, add_frag_size;
120 struct ocelot_mm_state *mm;
123 if (!ocelot->mm_supported)
126 mm = &ocelot->mm[port];
128 err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size,
129 &add_frag_size, extack);
133 if (cfg->pmac_enabled)
134 mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA;
137 mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA;
139 if (!cfg->verify_enabled)
140 verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS;
142 mutex_lock(&ocelot->fwd_domain_lock);
144 ocelot_port_rmwl(ocelot_port, mm_enable,
145 DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA |
146 DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA,
147 DEV_MM_ENABLE_CONFIG);
149 ocelot_port_rmwl(ocelot_port, verify_disable |
150 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time),
151 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS |
152 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M,
153 DEV_MM_VERIF_CONFIG);
155 ocelot_rmw_rix(ocelot,
156 QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size),
157 QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M,
161 /* The switch will emit an IRQ when TX is disabled, to notify that it
162 * has become inactive. We optimize ocelot_mm_update_port_status() to
163 * not bother processing MM IRQs at all for ports with TX disabled,
164 * but we need to ACK this IRQ now, while mm->tx_enabled is still set,
165 * otherwise we get an IRQ storm.
167 if (mm->tx_enabled && !cfg->tx_enabled) {
168 ocelot_mm_update_port_status(ocelot, port);
169 WARN_ON(mm->tx_active);
172 mm->tx_enabled = cfg->tx_enabled;
174 mutex_unlock(&ocelot->fwd_domain_lock);
178 EXPORT_SYMBOL_GPL(ocelot_port_set_mm);
180 int ocelot_port_get_mm(struct ocelot *ocelot, int port,
181 struct ethtool_mm_state *state)
183 struct ocelot_port *ocelot_port = ocelot->ports[port];
184 struct ocelot_mm_state *mm;
185 u32 val, add_frag_size;
187 if (!ocelot->mm_supported)
190 mm = &ocelot->mm[port];
192 mutex_lock(&ocelot->fwd_domain_lock);
194 val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG);
195 state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA);
196 state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA);
198 val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG);
199 state->verify_enabled = !(val & DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS);
200 state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val);
201 state->max_verify_time = 128;
203 val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port);
204 add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val);
205 state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size);
206 state->rx_min_frag_size = ETH_ZLEN;
208 ocelot_mm_update_port_status(ocelot, port);
209 state->verify_status = mm->verify_status;
210 state->tx_active = mm->tx_active;
212 mutex_unlock(&ocelot->fwd_domain_lock);
216 EXPORT_SYMBOL_GPL(ocelot_port_get_mm);
218 int ocelot_mm_init(struct ocelot *ocelot)
220 struct ocelot_port *ocelot_port;
221 struct ocelot_mm_state *mm;
224 if (!ocelot->mm_supported)
227 ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
228 sizeof(*ocelot->mm), GFP_KERNEL);
232 for (port = 0; port < ocelot->num_phys_ports; port++) {
235 mm = &ocelot->mm[port];
236 ocelot_port = ocelot->ports[port];
238 /* Update initial status variable for the
239 * verification state machine
241 val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
242 mm->verify_status = ocelot_mm_verify_status(val);