1 // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 /* 3 * Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable 4 * switches (VSC9959) 5 * 6 * Copyright 2022-2023 NXP 7 */ 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 15 static const char * 16 mm_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 34 static 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 52 void 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 transmitting 62 * preemptible frames at any other link speed than gigabit, so avoid 63 * 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 preemptible 71 * TCs affects the oversized frame dropping logic, so that needs to be 72 * re-triggered. And since tas_guard_bands_update() also implicitly 73 * 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 90 void 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 105 static 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 155 void 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 } 166 EXPORT_SYMBOL_GPL(ocelot_mm_irq); 167 168 int 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 it 216 * has become inactive. We optimize ocelot_mm_update_port_status() to 217 * 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 } 232 EXPORT_SYMBOL_GPL(ocelot_port_set_mm); 233 234 int 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 } 270 EXPORT_SYMBOL_GPL(ocelot_port_get_mm); 271 272 int 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 the 293 * verification state machine 294 */ 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