1 // SPDX-License-Identifier: GPL-2.0 2 #define pr_fmt(fmt) "bcmasp_ethtool: " fmt 3 4 #include <linux/unaligned.h> 5 #include <linux/ethtool.h> 6 #include <linux/netdevice.h> 7 #include <linux/platform_device.h> 8 9 #include "bcmasp.h" 10 #include "bcmasp_intf_defs.h" 11 12 enum bcmasp_stat_type { 13 BCMASP_STAT_RX_CTRL, 14 BCMASP_STAT_RX_CTRL_PER_INTF, 15 BCMASP_STAT_SOFT, 16 }; 17 18 struct bcmasp_stats { 19 char stat_string[ETH_GSTRING_LEN]; 20 enum bcmasp_stat_type type; 21 u32 reg_offset; 22 }; 23 24 #define STAT_BCMASP_SOFT_MIB(str) { \ 25 .stat_string = str, \ 26 .type = BCMASP_STAT_SOFT, \ 27 } 28 29 #define STAT_BCMASP_OFFSET(str, _type, offset) { \ 30 .stat_string = str, \ 31 .type = _type, \ 32 .reg_offset = offset, \ 33 } 34 35 #define STAT_BCMASP_RX_CTRL(str, offset) \ 36 STAT_BCMASP_OFFSET(str, BCMASP_STAT_RX_CTRL, offset) 37 #define STAT_BCMASP_RX_CTRL_PER_INTF(str, offset) \ 38 STAT_BCMASP_OFFSET(str, BCMASP_STAT_RX_CTRL_PER_INTF, offset) 39 40 /* Must match the order of struct bcmasp_mib_counters */ 41 static const struct bcmasp_stats bcmasp_gstrings_stats[] = { 42 /* ASP RX control */ 43 STAT_BCMASP_RX_CTRL_PER_INTF("Frames From Unimac", 44 ASP_RX_CTRL_UMAC_0_FRAME_COUNT), 45 STAT_BCMASP_RX_CTRL_PER_INTF("Frames From Port", 46 ASP_RX_CTRL_FB_0_FRAME_COUNT), 47 STAT_BCMASP_RX_CTRL_PER_INTF("RX Buffer FIFO Depth", 48 ASP_RX_CTRL_FB_RX_FIFO_DEPTH), 49 STAT_BCMASP_RX_CTRL("Frames Out(Buffer)", 50 ASP_RX_CTRL_FB_OUT_FRAME_COUNT), 51 STAT_BCMASP_RX_CTRL("Frames Out(Filters)", 52 ASP_RX_CTRL_FB_FILT_OUT_FRAME_COUNT), 53 /* Software maintained statistics */ 54 STAT_BCMASP_SOFT_MIB("RX SKB Alloc Failed"), 55 STAT_BCMASP_SOFT_MIB("TX DMA Failed"), 56 STAT_BCMASP_SOFT_MIB("Multicast Filters Full"), 57 STAT_BCMASP_SOFT_MIB("Unicast Filters Full"), 58 STAT_BCMASP_SOFT_MIB("MDA Filters Combined"), 59 STAT_BCMASP_SOFT_MIB("Promisc Filter Set"), 60 STAT_BCMASP_SOFT_MIB("TX Realloc For Offload Failed"), 61 STAT_BCMASP_SOFT_MIB("Tx Timeout Count"), 62 }; 63 64 #define BCMASP_STATS_LEN ARRAY_SIZE(bcmasp_gstrings_stats) 65 66 static int bcmasp_get_sset_count(struct net_device *dev, int string_set) 67 { 68 switch (string_set) { 69 case ETH_SS_STATS: 70 return BCMASP_STATS_LEN; 71 default: 72 return -EOPNOTSUPP; 73 } 74 } 75 76 static void bcmasp_get_strings(struct net_device *dev, u32 stringset, 77 u8 *data) 78 { 79 const char *str; 80 unsigned int i; 81 82 switch (stringset) { 83 case ETH_SS_STATS: 84 for (i = 0; i < BCMASP_STATS_LEN; i++) { 85 str = bcmasp_gstrings_stats[i].stat_string; 86 ethtool_puts(&data, str); 87 } 88 break; 89 default: 90 return; 91 } 92 } 93 94 static void bcmasp_update_mib_counters(struct bcmasp_intf *intf) 95 { 96 unsigned int i; 97 98 for (i = 0; i < BCMASP_STATS_LEN; i++) { 99 const struct bcmasp_stats *s; 100 u32 offset, val; 101 char *p; 102 103 s = &bcmasp_gstrings_stats[i]; 104 offset = s->reg_offset; 105 switch (s->type) { 106 case BCMASP_STAT_SOFT: 107 continue; 108 case BCMASP_STAT_RX_CTRL: 109 val = rx_ctrl_core_rl(intf->parent, offset); 110 break; 111 case BCMASP_STAT_RX_CTRL_PER_INTF: 112 offset += sizeof(u32) * intf->port; 113 val = rx_ctrl_core_rl(intf->parent, offset); 114 break; 115 default: 116 continue; 117 } 118 p = (char *)(&intf->mib) + (i * sizeof(u32)); 119 put_unaligned(val, (u32 *)p); 120 } 121 } 122 123 static void bcmasp_get_ethtool_stats(struct net_device *dev, 124 struct ethtool_stats *stats, 125 u64 *data) 126 { 127 struct bcmasp_intf *intf = netdev_priv(dev); 128 unsigned int i; 129 char *p; 130 131 if (netif_running(dev)) 132 bcmasp_update_mib_counters(intf); 133 134 for (i = 0; i < BCMASP_STATS_LEN; i++) { 135 p = (char *)(&intf->mib) + (i * sizeof(u32)); 136 data[i] = *(u32 *)p; 137 } 138 } 139 140 static void bcmasp_get_drvinfo(struct net_device *dev, 141 struct ethtool_drvinfo *info) 142 { 143 strscpy(info->driver, "bcmasp", sizeof(info->driver)); 144 strscpy(info->bus_info, dev_name(dev->dev.parent), 145 sizeof(info->bus_info)); 146 } 147 148 static u32 bcmasp_get_msglevel(struct net_device *dev) 149 { 150 struct bcmasp_intf *intf = netdev_priv(dev); 151 152 return intf->msg_enable; 153 } 154 155 static void bcmasp_set_msglevel(struct net_device *dev, u32 level) 156 { 157 struct bcmasp_intf *intf = netdev_priv(dev); 158 159 intf->msg_enable = level; 160 } 161 162 #define BCMASP_SUPPORTED_WAKE (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER) 163 static void bcmasp_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 164 { 165 struct bcmasp_intf *intf = netdev_priv(dev); 166 167 wol->supported = BCMASP_SUPPORTED_WAKE; 168 wol->wolopts = intf->wolopts; 169 memset(wol->sopass, 0, sizeof(wol->sopass)); 170 171 if (wol->wolopts & WAKE_MAGICSECURE) 172 memcpy(wol->sopass, intf->sopass, sizeof(intf->sopass)); 173 } 174 175 static int bcmasp_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 176 { 177 struct bcmasp_intf *intf = netdev_priv(dev); 178 struct bcmasp_priv *priv = intf->parent; 179 struct device *kdev = &priv->pdev->dev; 180 181 if (!device_can_wakeup(kdev)) 182 return -EOPNOTSUPP; 183 184 /* Interface Specific */ 185 intf->wolopts = wol->wolopts; 186 if (intf->wolopts & WAKE_MAGICSECURE) 187 memcpy(intf->sopass, wol->sopass, sizeof(wol->sopass)); 188 189 mutex_lock(&priv->wol_lock); 190 bcmasp_enable_wol(intf, !!intf->wolopts); 191 mutex_unlock(&priv->wol_lock); 192 193 return 0; 194 } 195 196 static int bcmasp_flow_insert(struct net_device *dev, struct ethtool_rxnfc *cmd) 197 { 198 struct bcmasp_intf *intf = netdev_priv(dev); 199 struct bcmasp_net_filter *nfilter; 200 u32 loc = cmd->fs.location; 201 bool wake = false; 202 203 if (cmd->fs.ring_cookie == RX_CLS_FLOW_WAKE) 204 wake = true; 205 206 /* Currently only supports WAKE filters */ 207 if (!wake) 208 return -EOPNOTSUPP; 209 210 switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { 211 case ETHER_FLOW: 212 case IP_USER_FLOW: 213 case TCP_V4_FLOW: 214 case UDP_V4_FLOW: 215 case TCP_V6_FLOW: 216 case UDP_V6_FLOW: 217 break; 218 default: 219 return -EOPNOTSUPP; 220 } 221 222 /* Check if filter already exists */ 223 if (bcmasp_netfilt_check_dup(intf, &cmd->fs)) 224 return -EINVAL; 225 226 nfilter = bcmasp_netfilt_get_init(intf, loc, wake, true); 227 if (IS_ERR(nfilter)) 228 return PTR_ERR(nfilter); 229 230 /* Return the location where we did insert the filter */ 231 cmd->fs.location = nfilter->hw_index; 232 memcpy(&nfilter->fs, &cmd->fs, sizeof(struct ethtool_rx_flow_spec)); 233 234 /* Since we only support wake filters, defer register programming till 235 * suspend time. 236 */ 237 return 0; 238 } 239 240 static int bcmasp_flow_delete(struct net_device *dev, struct ethtool_rxnfc *cmd) 241 { 242 struct bcmasp_intf *intf = netdev_priv(dev); 243 struct bcmasp_net_filter *nfilter; 244 245 nfilter = bcmasp_netfilt_get_init(intf, cmd->fs.location, false, false); 246 if (IS_ERR(nfilter)) 247 return PTR_ERR(nfilter); 248 249 bcmasp_netfilt_release(intf, nfilter); 250 251 return 0; 252 } 253 254 static int bcmasp_flow_get(struct bcmasp_intf *intf, struct ethtool_rxnfc *cmd) 255 { 256 struct bcmasp_net_filter *nfilter; 257 258 nfilter = bcmasp_netfilt_get_init(intf, cmd->fs.location, false, false); 259 if (IS_ERR(nfilter)) 260 return PTR_ERR(nfilter); 261 262 memcpy(&cmd->fs, &nfilter->fs, sizeof(nfilter->fs)); 263 264 cmd->data = intf->parent->num_net_filters; 265 266 return 0; 267 } 268 269 static int bcmasp_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) 270 { 271 struct bcmasp_intf *intf = netdev_priv(dev); 272 int ret = -EOPNOTSUPP; 273 274 mutex_lock(&intf->parent->net_lock); 275 276 switch (cmd->cmd) { 277 case ETHTOOL_SRXCLSRLINS: 278 ret = bcmasp_flow_insert(dev, cmd); 279 break; 280 case ETHTOOL_SRXCLSRLDEL: 281 ret = bcmasp_flow_delete(dev, cmd); 282 break; 283 default: 284 break; 285 } 286 287 mutex_unlock(&intf->parent->net_lock); 288 289 return ret; 290 } 291 292 static int bcmasp_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 293 u32 *rule_locs) 294 { 295 struct bcmasp_intf *intf = netdev_priv(dev); 296 int err = 0; 297 298 mutex_lock(&intf->parent->net_lock); 299 300 switch (cmd->cmd) { 301 case ETHTOOL_GRXCLSRLCNT: 302 cmd->rule_cnt = bcmasp_netfilt_get_active(intf); 303 /* We support specifying rule locations */ 304 cmd->data |= RX_CLS_LOC_SPECIAL; 305 break; 306 case ETHTOOL_GRXCLSRULE: 307 err = bcmasp_flow_get(intf, cmd); 308 break; 309 case ETHTOOL_GRXCLSRLALL: 310 err = bcmasp_netfilt_get_all_active(intf, rule_locs, &cmd->rule_cnt); 311 cmd->data = intf->parent->num_net_filters; 312 break; 313 default: 314 err = -EOPNOTSUPP; 315 break; 316 } 317 318 mutex_unlock(&intf->parent->net_lock); 319 320 return err; 321 } 322 323 static int bcmasp_get_eee(struct net_device *dev, struct ethtool_keee *e) 324 { 325 if (!dev->phydev) 326 return -ENODEV; 327 328 return phy_ethtool_get_eee(dev->phydev, e); 329 } 330 331 static int bcmasp_set_eee(struct net_device *dev, struct ethtool_keee *e) 332 { 333 if (!dev->phydev) 334 return -ENODEV; 335 336 return phy_ethtool_set_eee(dev->phydev, e); 337 } 338 339 static void bcmasp_get_eth_mac_stats(struct net_device *dev, 340 struct ethtool_eth_mac_stats *mac_stats) 341 { 342 struct bcmasp_intf *intf = netdev_priv(dev); 343 344 mac_stats->FramesTransmittedOK = umac_rl(intf, UMC_GTPOK); 345 mac_stats->SingleCollisionFrames = umac_rl(intf, UMC_GTSCL); 346 mac_stats->MultipleCollisionFrames = umac_rl(intf, UMC_GTMCL); 347 mac_stats->FramesReceivedOK = umac_rl(intf, UMC_GRPOK); 348 mac_stats->FrameCheckSequenceErrors = umac_rl(intf, UMC_GRFCS); 349 mac_stats->AlignmentErrors = umac_rl(intf, UMC_GRALN); 350 mac_stats->OctetsTransmittedOK = umac_rl(intf, UMC_GTBYT); 351 mac_stats->FramesWithDeferredXmissions = umac_rl(intf, UMC_GTDRF); 352 mac_stats->LateCollisions = umac_rl(intf, UMC_GTLCL); 353 mac_stats->FramesAbortedDueToXSColls = umac_rl(intf, UMC_GTXCL); 354 mac_stats->OctetsReceivedOK = umac_rl(intf, UMC_GRBYT); 355 mac_stats->MulticastFramesXmittedOK = umac_rl(intf, UMC_GTMCA); 356 mac_stats->BroadcastFramesXmittedOK = umac_rl(intf, UMC_GTBCA); 357 mac_stats->FramesWithExcessiveDeferral = umac_rl(intf, UMC_GTEDF); 358 mac_stats->MulticastFramesReceivedOK = umac_rl(intf, UMC_GRMCA); 359 mac_stats->BroadcastFramesReceivedOK = umac_rl(intf, UMC_GRBCA); 360 } 361 362 static const struct ethtool_rmon_hist_range bcmasp_rmon_ranges[] = { 363 { 0, 64}, 364 { 65, 127}, 365 { 128, 255}, 366 { 256, 511}, 367 { 512, 1023}, 368 { 1024, 1518}, 369 { 1519, 1522}, 370 {} 371 }; 372 373 static void bcmasp_get_rmon_stats(struct net_device *dev, 374 struct ethtool_rmon_stats *rmon_stats, 375 const struct ethtool_rmon_hist_range **ranges) 376 { 377 struct bcmasp_intf *intf = netdev_priv(dev); 378 379 *ranges = bcmasp_rmon_ranges; 380 381 rmon_stats->undersize_pkts = umac_rl(intf, UMC_RRUND); 382 rmon_stats->oversize_pkts = umac_rl(intf, UMC_GROVR); 383 rmon_stats->fragments = umac_rl(intf, UMC_RRFRG); 384 rmon_stats->jabbers = umac_rl(intf, UMC_GRJBR); 385 386 rmon_stats->hist[0] = umac_rl(intf, UMC_GR64); 387 rmon_stats->hist[1] = umac_rl(intf, UMC_GR127); 388 rmon_stats->hist[2] = umac_rl(intf, UMC_GR255); 389 rmon_stats->hist[3] = umac_rl(intf, UMC_GR511); 390 rmon_stats->hist[4] = umac_rl(intf, UMC_GR1023); 391 rmon_stats->hist[5] = umac_rl(intf, UMC_GR1518); 392 rmon_stats->hist[6] = umac_rl(intf, UMC_GRMGV); 393 394 rmon_stats->hist_tx[0] = umac_rl(intf, UMC_TR64); 395 rmon_stats->hist_tx[1] = umac_rl(intf, UMC_TR127); 396 rmon_stats->hist_tx[2] = umac_rl(intf, UMC_TR255); 397 rmon_stats->hist_tx[3] = umac_rl(intf, UMC_TR511); 398 rmon_stats->hist_tx[4] = umac_rl(intf, UMC_TR1023); 399 rmon_stats->hist_tx[5] = umac_rl(intf, UMC_TR1518); 400 rmon_stats->hist_tx[6] = umac_rl(intf, UMC_TRMGV); 401 } 402 403 static void bcmasp_get_eth_ctrl_stats(struct net_device *dev, 404 struct ethtool_eth_ctrl_stats *ctrl_stats) 405 { 406 struct bcmasp_intf *intf = netdev_priv(dev); 407 408 ctrl_stats->MACControlFramesTransmitted = umac_rl(intf, UMC_GTXCF); 409 ctrl_stats->MACControlFramesReceived = umac_rl(intf, UMC_GRXCF); 410 ctrl_stats->UnsupportedOpcodesReceived = umac_rl(intf, UMC_GRXUO); 411 } 412 413 const struct ethtool_ops bcmasp_ethtool_ops = { 414 .get_drvinfo = bcmasp_get_drvinfo, 415 .get_link = ethtool_op_get_link, 416 .get_link_ksettings = phy_ethtool_get_link_ksettings, 417 .set_link_ksettings = phy_ethtool_set_link_ksettings, 418 .get_msglevel = bcmasp_get_msglevel, 419 .set_msglevel = bcmasp_set_msglevel, 420 .get_wol = bcmasp_get_wol, 421 .set_wol = bcmasp_set_wol, 422 .get_rxnfc = bcmasp_get_rxnfc, 423 .set_rxnfc = bcmasp_set_rxnfc, 424 .set_eee = bcmasp_set_eee, 425 .get_eee = bcmasp_get_eee, 426 .get_eth_mac_stats = bcmasp_get_eth_mac_stats, 427 .get_rmon_stats = bcmasp_get_rmon_stats, 428 .get_eth_ctrl_stats = bcmasp_get_eth_ctrl_stats, 429 .get_strings = bcmasp_get_strings, 430 .get_ethtool_stats = bcmasp_get_ethtool_stats, 431 .get_sset_count = bcmasp_get_sset_count, 432 .get_ts_info = ethtool_op_get_ts_info, 433 .nway_reset = phy_ethtool_nway_reset, 434 }; 435