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_mm_irq(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 enum ethtool_mm_verify_status verify_status; 57 u32 val; 58 59 mutex_lock(&mm->lock); 60 61 val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS); 62 63 verify_status = ocelot_mm_verify_status(val); 64 if (mm->verify_status != verify_status) { 65 dev_dbg(ocelot->dev, 66 "Port %d MAC Merge verification state %s\n", 67 port, mm_verify_state_to_string(verify_status)); 68 mm->verify_status = verify_status; 69 } 70 71 if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) { 72 mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS); 73 74 dev_dbg(ocelot->dev, "Port %d TX preemption %s\n", 75 port, mm->tx_active ? "active" : "inactive"); 76 } 77 78 if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) { 79 dev_err(ocelot->dev, 80 "Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n", 81 port); 82 } 83 84 if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) { 85 dev_err(ocelot->dev, 86 "Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n", 87 port); 88 } 89 90 ocelot_port_writel(ocelot_port, val, DEV_MM_STATUS); 91 92 mutex_unlock(&mm->lock); 93 } 94 EXPORT_SYMBOL_GPL(ocelot_port_mm_irq); 95 96 int ocelot_port_set_mm(struct ocelot *ocelot, int port, 97 struct ethtool_mm_cfg *cfg, 98 struct netlink_ext_ack *extack) 99 { 100 struct ocelot_port *ocelot_port = ocelot->ports[port]; 101 u32 mm_enable = 0, verify_disable = 0, add_frag_size; 102 struct ocelot_mm_state *mm; 103 int err; 104 105 if (!ocelot->mm_supported) 106 return -EOPNOTSUPP; 107 108 mm = &ocelot->mm[port]; 109 110 err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size, 111 &add_frag_size, extack); 112 if (err) 113 return err; 114 115 if (cfg->pmac_enabled) 116 mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA; 117 118 if (cfg->tx_enabled) 119 mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA; 120 121 if (!cfg->verify_enabled) 122 verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS; 123 124 mutex_lock(&mm->lock); 125 126 ocelot_port_rmwl(ocelot_port, mm_enable, 127 DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA | 128 DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA, 129 DEV_MM_ENABLE_CONFIG); 130 131 ocelot_port_rmwl(ocelot_port, verify_disable | 132 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time), 133 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS | 134 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M, 135 DEV_MM_VERIF_CONFIG); 136 137 ocelot_rmw_rix(ocelot, 138 QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size), 139 QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M, 140 QSYS_PREEMPTION_CFG, 141 port); 142 143 mutex_unlock(&mm->lock); 144 145 return 0; 146 } 147 EXPORT_SYMBOL_GPL(ocelot_port_set_mm); 148 149 int ocelot_port_get_mm(struct ocelot *ocelot, int port, 150 struct ethtool_mm_state *state) 151 { 152 struct ocelot_port *ocelot_port = ocelot->ports[port]; 153 struct ocelot_mm_state *mm; 154 u32 val, add_frag_size; 155 156 if (!ocelot->mm_supported) 157 return -EOPNOTSUPP; 158 159 mm = &ocelot->mm[port]; 160 161 mutex_lock(&mm->lock); 162 163 val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG); 164 state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA); 165 state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA); 166 167 val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG); 168 state->verify_enabled = !(val & DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS); 169 state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val); 170 state->max_verify_time = 128; 171 172 val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port); 173 add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val); 174 state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size); 175 state->rx_min_frag_size = ETH_ZLEN; 176 177 state->verify_status = mm->verify_status; 178 state->tx_active = mm->tx_active; 179 180 mutex_unlock(&mm->lock); 181 182 return 0; 183 } 184 EXPORT_SYMBOL_GPL(ocelot_port_get_mm); 185 186 int ocelot_mm_init(struct ocelot *ocelot) 187 { 188 struct ocelot_port *ocelot_port; 189 struct ocelot_mm_state *mm; 190 int port; 191 192 if (!ocelot->mm_supported) 193 return 0; 194 195 ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports, 196 sizeof(*ocelot->mm), GFP_KERNEL); 197 if (!ocelot->mm) 198 return -ENOMEM; 199 200 for (port = 0; port < ocelot->num_phys_ports; port++) { 201 u32 val; 202 203 mm = &ocelot->mm[port]; 204 mutex_init(&mm->lock); 205 ocelot_port = ocelot->ports[port]; 206 207 /* Update initial status variable for the 208 * verification state machine 209 */ 210 val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS); 211 mm->verify_status = ocelot_mm_verify_status(val); 212 } 213 214 return 0; 215 } 216