| 1 | |
| 2 |
|
| 3 |
|
| 4 | #include <linux/module.h>
|
| 5 | #include <linux/netdevice.h>
|
| 6 | #include <linux/sfp.h>
|
| 7 |
|
| 8 | #include "ionic.h"
|
| 9 | #include "ionic_bus.h"
|
| 10 | #include "ionic_lif.h"
|
| 11 | #include "ionic_ethtool.h"
|
| 12 | #include "ionic_stats.h"
|
| 13 |
|
| 14 | #define IONIC_MAX_RX_COPYBREAK min(U16_MAX, IONIC_MAX_BUF_LEN)
|
| 15 |
|
| 16 | static void ionic_get_stats_strings(struct ionic_lif *lif, u8 *buf)
|
| 17 | {
|
| 18 | u32 i;
|
| 19 |
|
| 20 | for (i = 0; i < ionic_num_stats_grps; i++)
|
| 21 | ionic_stats_groups[i].get_strings(lif, &buf);
|
| 22 | }
|
| 23 |
|
| 24 | static void ionic_get_stats(struct net_device *netdev,
|
| 25 | struct ethtool_stats *stats, u64 *buf)
|
| 26 | {
|
| 27 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 28 | u32 i;
|
| 29 |
|
| 30 | if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
| 31 | return;
|
| 32 |
|
| 33 | memset(buf, 0, stats->n_stats * sizeof(*buf));
|
| 34 | for (i = 0; i < ionic_num_stats_grps; i++)
|
| 35 | ionic_stats_groups[i].get_values(lif, &buf);
|
| 36 | }
|
| 37 |
|
| 38 | static int ionic_get_stats_count(struct ionic_lif *lif)
|
| 39 | {
|
| 40 | int i, num_stats = 0;
|
| 41 |
|
| 42 | for (i = 0; i < ionic_num_stats_grps; i++)
|
| 43 | num_stats += ionic_stats_groups[i].get_count(lif);
|
| 44 |
|
| 45 | return num_stats;
|
| 46 | }
|
| 47 |
|
| 48 | static int ionic_get_sset_count(struct net_device *netdev, int sset)
|
| 49 | {
|
| 50 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 51 | int count = 0;
|
| 52 |
|
| 53 | switch (sset) {
|
| 54 | case ETH_SS_STATS:
|
| 55 | count = ionic_get_stats_count(lif);
|
| 56 | break;
|
| 57 | }
|
| 58 | return count;
|
| 59 | }
|
| 60 |
|
| 61 | static void ionic_get_strings(struct net_device *netdev,
|
| 62 | u32 sset, u8 *buf)
|
| 63 | {
|
| 64 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 65 |
|
| 66 | switch (sset) {
|
| 67 | case ETH_SS_STATS:
|
| 68 | ionic_get_stats_strings(lif, buf);
|
| 69 | break;
|
| 70 | }
|
| 71 | }
|
| 72 |
|
| 73 | static void ionic_get_drvinfo(struct net_device *netdev,
|
| 74 | struct ethtool_drvinfo *drvinfo)
|
| 75 | {
|
| 76 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 77 | struct ionic *ionic = lif->ionic;
|
| 78 |
|
| 79 | strscpy(drvinfo->driver, IONIC_DRV_NAME, sizeof(drvinfo->driver));
|
| 80 | strscpy(drvinfo->fw_version, ionic->idev.dev_info.fw_version,
|
| 81 | sizeof(drvinfo->fw_version));
|
| 82 | strscpy(drvinfo->bus_info, ionic_bus_info(ionic),
|
| 83 | sizeof(drvinfo->bus_info));
|
| 84 | }
|
| 85 |
|
| 86 | static int ionic_get_regs_len(struct net_device *netdev)
|
| 87 | {
|
| 88 | return (IONIC_DEV_INFO_REG_COUNT + IONIC_DEV_CMD_REG_COUNT) * sizeof(u32);
|
| 89 | }
|
| 90 |
|
| 91 | static void ionic_get_regs(struct net_device *netdev, struct ethtool_regs *regs,
|
| 92 | void *p)
|
| 93 | {
|
| 94 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 95 | struct ionic_dev *idev;
|
| 96 | unsigned int offset;
|
| 97 | unsigned int size;
|
| 98 |
|
| 99 | regs->version = IONIC_DEV_CMD_REG_VERSION;
|
| 100 |
|
| 101 | idev = &lif->ionic->idev;
|
| 102 | if (!idev->dev_info_regs)
|
| 103 | return;
|
| 104 |
|
| 105 | offset = 0;
|
| 106 | size = IONIC_DEV_INFO_REG_COUNT * sizeof(u32);
|
| 107 | memcpy_fromio(p + offset, lif->ionic->idev.dev_info_regs->words, size);
|
| 108 |
|
| 109 | offset += size;
|
| 110 | size = IONIC_DEV_CMD_REG_COUNT * sizeof(u32);
|
| 111 | memcpy_fromio(p + offset, idev->dev_cmd_regs->words, size);
|
| 112 | }
|
| 113 |
|
| 114 | static void ionic_get_link_ext_stats(struct net_device *netdev,
|
| 115 | struct ethtool_link_ext_stats *stats)
|
| 116 | {
|
| 117 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 118 | struct ionic *ionic = lif->ionic;
|
| 119 | u64 link_down_count_total;
|
| 120 | u16 link_down_count_fw;
|
| 121 |
|
| 122 | if (ionic->pdev->is_virtfn)
|
| 123 | return;
|
| 124 |
|
| 125 | if (!ionic->idev.port_info) {
|
| 126 | netdev_err_once(netdev, "port_info not initialized\n");
|
| 127 | return;
|
| 128 | }
|
| 129 |
|
| 130 | link_down_count_fw =
|
| 131 | le16_to_cpu(ionic->idev.port_info->status.link_down_count);
|
| 132 | link_down_count_total = ionic->idev.link_down_count_total +
|
| 133 | link_down_count_fw -
|
| 134 | ionic->idev.link_down_count_last;
|
| 135 |
|
| 136 |
|
| 137 | if (link_down_count_fw < ionic->idev.link_down_count_last)
|
| 138 | link_down_count_total += BIT(16);
|
| 139 |
|
| 140 | ionic->idev.link_down_count_last = link_down_count_fw;
|
| 141 | ionic->idev.link_down_count_total = link_down_count_total;
|
| 142 |
|
| 143 | stats->link_down_events = link_down_count_total;
|
| 144 | }
|
| 145 |
|
| 146 | static int ionic_get_link_ksettings(struct net_device *netdev,
|
| 147 | struct ethtool_link_ksettings *ks)
|
| 148 | {
|
| 149 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 150 | struct ionic_dev *idev = &lif->ionic->idev;
|
| 151 | int copper_seen = 0;
|
| 152 |
|
| 153 | ethtool_link_ksettings_zero_link_mode(ks, supported);
|
| 154 |
|
| 155 | if (!idev->port_info) {
|
| 156 | netdev_err(netdev, "port_info not initialized\n");
|
| 157 | return -EOPNOTSUPP;
|
| 158 | }
|
| 159 |
|
| 160 | |
| 161 | * up-to-date, so there's no need to request the data from the
|
| 162 | * NIC, we already have it in our memory space.
|
| 163 |
|
| 164 |
|
| 165 | switch (le16_to_cpu(idev->port_info->status.xcvr.pid)) {
|
| 166 |
|
| 167 | case IONIC_XCVR_PID_QSFP_100G_CR4:
|
| 168 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 169 | 100000baseCR4_Full);
|
| 170 | copper_seen++;
|
| 171 | break;
|
| 172 | case IONIC_XCVR_PID_QSFP_40GBASE_CR4:
|
| 173 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 174 | 40000baseCR4_Full);
|
| 175 | copper_seen++;
|
| 176 | break;
|
| 177 | case IONIC_XCVR_PID_SFP_25GBASE_CR_S:
|
| 178 | case IONIC_XCVR_PID_SFP_25GBASE_CR_L:
|
| 179 | case IONIC_XCVR_PID_SFP_25GBASE_CR_N:
|
| 180 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 181 | 25000baseCR_Full);
|
| 182 | copper_seen++;
|
| 183 | break;
|
| 184 | case IONIC_XCVR_PID_QSFP_50G_CR2_FC:
|
| 185 | case IONIC_XCVR_PID_QSFP_50G_CR2:
|
| 186 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 187 | 50000baseCR2_Full);
|
| 188 | copper_seen++;
|
| 189 | break;
|
| 190 | case IONIC_XCVR_PID_QSFP_200G_CR4:
|
| 191 | ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseCR4_Full);
|
| 192 | copper_seen++;
|
| 193 | break;
|
| 194 | case IONIC_XCVR_PID_QSFP_400G_CR4:
|
| 195 | ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseCR4_Full);
|
| 196 | copper_seen++;
|
| 197 | break;
|
| 198 | case IONIC_XCVR_PID_SFP_10GBASE_AOC:
|
| 199 | case IONIC_XCVR_PID_SFP_10GBASE_CU:
|
| 200 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 201 | 10000baseCR_Full);
|
| 202 | copper_seen++;
|
| 203 | break;
|
| 204 |
|
| 205 |
|
| 206 | case IONIC_XCVR_PID_QSFP_100G_SR4:
|
| 207 | case IONIC_XCVR_PID_QSFP_100G_AOC:
|
| 208 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 209 | 100000baseSR4_Full);
|
| 210 | break;
|
| 211 | case IONIC_XCVR_PID_QSFP_100G_CWDM4:
|
| 212 | case IONIC_XCVR_PID_QSFP_100G_PSM4:
|
| 213 | case IONIC_XCVR_PID_QSFP_100G_LR4:
|
| 214 | case IONIC_XCVR_PID_QSFP_100G_ER4:
|
| 215 | case IONIC_XCVR_PID_QSFP_100G_FR4:
|
| 216 | case IONIC_XCVR_PID_QSFP_100G_DR4:
|
| 217 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 218 | 100000baseLR4_ER4_Full);
|
| 219 | break;
|
| 220 | case IONIC_XCVR_PID_QSFP_40GBASE_SR4:
|
| 221 | case IONIC_XCVR_PID_QSFP_40GBASE_AOC:
|
| 222 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 223 | 40000baseSR4_Full);
|
| 224 | break;
|
| 225 | case IONIC_XCVR_PID_QSFP_40GBASE_LR4:
|
| 226 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 227 | 40000baseLR4_Full);
|
| 228 | break;
|
| 229 | case IONIC_XCVR_PID_SFP_25GBASE_SR:
|
| 230 | case IONIC_XCVR_PID_SFP_25GBASE_AOC:
|
| 231 | case IONIC_XCVR_PID_SFP_25GBASE_ACC:
|
| 232 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 233 | 25000baseSR_Full);
|
| 234 | break;
|
| 235 | case IONIC_XCVR_PID_QSFP_200G_AOC:
|
| 236 | case IONIC_XCVR_PID_QSFP_200G_SR4:
|
| 237 | case IONIC_XCVR_PID_QSFP_200G_AEC:
|
| 238 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 239 | 200000baseSR4_Full);
|
| 240 | break;
|
| 241 | case IONIC_XCVR_PID_QSFP_200G_FR4:
|
| 242 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 243 | 200000baseLR4_ER4_FR4_Full);
|
| 244 | break;
|
| 245 | case IONIC_XCVR_PID_QSFP_200G_DR4:
|
| 246 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 247 | 200000baseDR4_Full);
|
| 248 | break;
|
| 249 | case IONIC_XCVR_PID_QSFP_400G_FR4:
|
| 250 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 251 | 400000baseLR4_ER4_FR4_Full);
|
| 252 | break;
|
| 253 | case IONIC_XCVR_PID_QSFP_400G_DR4:
|
| 254 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 255 | 400000baseDR4_Full);
|
| 256 | break;
|
| 257 | case IONIC_XCVR_PID_QSFP_400G_SR4:
|
| 258 | case IONIC_XCVR_PID_QSFP_400G_AOC:
|
| 259 | case IONIC_XCVR_PID_QSFP_400G_AEC:
|
| 260 | case IONIC_XCVR_PID_QSFP_400G_LPO:
|
| 261 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 262 | 400000baseSR4_Full);
|
| 263 | break;
|
| 264 | case IONIC_XCVR_PID_SFP_10GBASE_SR:
|
| 265 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 266 | 10000baseSR_Full);
|
| 267 | break;
|
| 268 | case IONIC_XCVR_PID_SFP_10GBASE_LR:
|
| 269 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 270 | 10000baseLR_Full);
|
| 271 | break;
|
| 272 | case IONIC_XCVR_PID_SFP_10GBASE_LRM:
|
| 273 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 274 | 10000baseLRM_Full);
|
| 275 | break;
|
| 276 | case IONIC_XCVR_PID_SFP_10GBASE_ER:
|
| 277 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 278 | 10000baseER_Full);
|
| 279 | break;
|
| 280 | case IONIC_XCVR_PID_SFP_10GBASE_T:
|
| 281 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 282 | 10000baseT_Full);
|
| 283 | break;
|
| 284 | case IONIC_XCVR_PID_SFP_1000BASE_T:
|
| 285 | ethtool_link_ksettings_add_link_mode(ks, supported,
|
| 286 | 1000baseT_Full);
|
| 287 | break;
|
| 288 | case IONIC_XCVR_PID_UNKNOWN:
|
| 289 |
|
| 290 | break;
|
| 291 | default:
|
| 292 | dev_dbg_ratelimited(lif->ionic->dev,
|
| 293 | "unknown xcvr type pid=%d / 0x%x\n",
|
| 294 | idev->port_info->status.xcvr.pid,
|
| 295 | idev->port_info->status.xcvr.pid);
|
| 296 | break;
|
| 297 | }
|
| 298 |
|
| 299 | linkmode_copy(ks->link_modes.advertising, ks->link_modes.supported);
|
| 300 |
|
| 301 | ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER);
|
| 302 | ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS);
|
| 303 | if (idev->port_info->config.fec_type == IONIC_PORT_FEC_TYPE_FC)
|
| 304 | ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_BASER);
|
| 305 | else if (idev->port_info->config.fec_type == IONIC_PORT_FEC_TYPE_RS)
|
| 306 | ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_RS);
|
| 307 |
|
| 308 | ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);
|
| 309 | ethtool_link_ksettings_add_link_mode(ks, supported, Pause);
|
| 310 |
|
| 311 | if (idev->port_info->status.xcvr.phy == IONIC_PHY_TYPE_COPPER ||
|
| 312 | copper_seen)
|
| 313 | ks->base.port = PORT_DA;
|
| 314 | else if (idev->port_info->status.xcvr.phy == IONIC_PHY_TYPE_FIBER)
|
| 315 | ks->base.port = PORT_FIBRE;
|
| 316 | else
|
| 317 | ks->base.port = PORT_NONE;
|
| 318 |
|
| 319 | if (ks->base.port != PORT_NONE) {
|
| 320 | ks->base.speed = le32_to_cpu(lif->info->status.link_speed);
|
| 321 |
|
| 322 | if (le16_to_cpu(lif->info->status.link_status))
|
| 323 | ks->base.duplex = DUPLEX_FULL;
|
| 324 | else
|
| 325 | ks->base.duplex = DUPLEX_UNKNOWN;
|
| 326 |
|
| 327 | ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
|
| 328 |
|
| 329 | if (idev->port_info->config.an_enable) {
|
| 330 | ethtool_link_ksettings_add_link_mode(ks, advertising,
|
| 331 | Autoneg);
|
| 332 | ks->base.autoneg = AUTONEG_ENABLE;
|
| 333 | }
|
| 334 | }
|
| 335 |
|
| 336 | return 0;
|
| 337 | }
|
| 338 |
|
| 339 | static int ionic_set_link_ksettings(struct net_device *netdev,
|
| 340 | const struct ethtool_link_ksettings *ks)
|
| 341 | {
|
| 342 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 343 | struct ionic_dev *idev = &lif->ionic->idev;
|
| 344 | struct ionic *ionic = lif->ionic;
|
| 345 | int err = 0;
|
| 346 |
|
| 347 | if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
| 348 | return -EBUSY;
|
| 349 |
|
| 350 |
|
| 351 | if (ks->base.autoneg != idev->port_info->config.an_enable) {
|
| 352 | mutex_lock(&ionic->dev_cmd_lock);
|
| 353 | ionic_dev_cmd_port_autoneg(idev, ks->base.autoneg);
|
| 354 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
|
| 355 | mutex_unlock(&ionic->dev_cmd_lock);
|
| 356 | if (err)
|
| 357 | return err;
|
| 358 | }
|
| 359 |
|
| 360 |
|
| 361 | if (ks->base.speed != le32_to_cpu(idev->port_info->config.speed)) {
|
| 362 | mutex_lock(&ionic->dev_cmd_lock);
|
| 363 | ionic_dev_cmd_port_speed(idev, ks->base.speed);
|
| 364 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
|
| 365 | mutex_unlock(&ionic->dev_cmd_lock);
|
| 366 | if (err)
|
| 367 | return err;
|
| 368 | }
|
| 369 |
|
| 370 | return 0;
|
| 371 | }
|
| 372 |
|
| 373 | static void ionic_get_pauseparam(struct net_device *netdev,
|
| 374 | struct ethtool_pauseparam *pause)
|
| 375 | {
|
| 376 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 377 | u8 pause_type;
|
| 378 |
|
| 379 | pause->autoneg = 0;
|
| 380 |
|
| 381 | pause_type = lif->ionic->idev.port_info->config.pause_type;
|
| 382 | if (pause_type) {
|
| 383 | pause->rx_pause = (pause_type & IONIC_PAUSE_F_RX) ? 1 : 0;
|
| 384 | pause->tx_pause = (pause_type & IONIC_PAUSE_F_TX) ? 1 : 0;
|
| 385 | }
|
| 386 | }
|
| 387 |
|
| 388 | static int ionic_set_pauseparam(struct net_device *netdev,
|
| 389 | struct ethtool_pauseparam *pause)
|
| 390 | {
|
| 391 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 392 | struct ionic *ionic = lif->ionic;
|
| 393 | u32 requested_pause;
|
| 394 | int err;
|
| 395 |
|
| 396 | if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
| 397 | return -EBUSY;
|
| 398 |
|
| 399 | if (pause->autoneg)
|
| 400 | return -EOPNOTSUPP;
|
| 401 |
|
| 402 |
|
| 403 | requested_pause = IONIC_PORT_PAUSE_TYPE_LINK;
|
| 404 | if (pause->rx_pause)
|
| 405 | requested_pause |= IONIC_PAUSE_F_RX;
|
| 406 | if (pause->tx_pause)
|
| 407 | requested_pause |= IONIC_PAUSE_F_TX;
|
| 408 |
|
| 409 | if (requested_pause == lif->ionic->idev.port_info->config.pause_type)
|
| 410 | return 0;
|
| 411 |
|
| 412 | mutex_lock(&ionic->dev_cmd_lock);
|
| 413 | ionic_dev_cmd_port_pause(&lif->ionic->idev, requested_pause);
|
| 414 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
|
| 415 | mutex_unlock(&ionic->dev_cmd_lock);
|
| 416 | if (err)
|
| 417 | return err;
|
| 418 |
|
| 419 | return 0;
|
| 420 | }
|
| 421 |
|
| 422 | static int ionic_get_fecparam(struct net_device *netdev,
|
| 423 | struct ethtool_fecparam *fec)
|
| 424 | {
|
| 425 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 426 |
|
| 427 | switch (lif->ionic->idev.port_info->config.fec_type) {
|
| 428 | case IONIC_PORT_FEC_TYPE_NONE:
|
| 429 | fec->active_fec = ETHTOOL_FEC_OFF;
|
| 430 | break;
|
| 431 | case IONIC_PORT_FEC_TYPE_RS:
|
| 432 | fec->active_fec = ETHTOOL_FEC_RS;
|
| 433 | break;
|
| 434 | case IONIC_PORT_FEC_TYPE_FC:
|
| 435 | fec->active_fec = ETHTOOL_FEC_BASER;
|
| 436 | break;
|
| 437 | }
|
| 438 |
|
| 439 | fec->fec = ETHTOOL_FEC_OFF | ETHTOOL_FEC_RS | ETHTOOL_FEC_BASER;
|
| 440 |
|
| 441 | return 0;
|
| 442 | }
|
| 443 |
|
| 444 | static int ionic_set_fecparam(struct net_device *netdev,
|
| 445 | struct ethtool_fecparam *fec)
|
| 446 | {
|
| 447 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 448 | u8 fec_type;
|
| 449 | int ret = 0;
|
| 450 |
|
| 451 | if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
| 452 | return -EBUSY;
|
| 453 |
|
| 454 | if (lif->ionic->idev.port_info->config.an_enable) {
|
| 455 | netdev_err(netdev, "FEC request not allowed while autoneg is enabled\n");
|
| 456 | return -EINVAL;
|
| 457 | }
|
| 458 |
|
| 459 | switch (fec->fec) {
|
| 460 | case ETHTOOL_FEC_NONE:
|
| 461 | fec_type = IONIC_PORT_FEC_TYPE_NONE;
|
| 462 | break;
|
| 463 | case ETHTOOL_FEC_OFF:
|
| 464 | fec_type = IONIC_PORT_FEC_TYPE_NONE;
|
| 465 | break;
|
| 466 | case ETHTOOL_FEC_RS:
|
| 467 | fec_type = IONIC_PORT_FEC_TYPE_RS;
|
| 468 | break;
|
| 469 | case ETHTOOL_FEC_BASER:
|
| 470 | fec_type = IONIC_PORT_FEC_TYPE_FC;
|
| 471 | break;
|
| 472 | case ETHTOOL_FEC_AUTO:
|
| 473 | default:
|
| 474 | netdev_err(netdev, "FEC request 0x%04x not supported\n",
|
| 475 | fec->fec);
|
| 476 | return -EINVAL;
|
| 477 | }
|
| 478 |
|
| 479 | if (fec_type != lif->ionic->idev.port_info->config.fec_type) {
|
| 480 | mutex_lock(&lif->ionic->dev_cmd_lock);
|
| 481 | ionic_dev_cmd_port_fec(&lif->ionic->idev, fec_type);
|
| 482 | ret = ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT);
|
| 483 | mutex_unlock(&lif->ionic->dev_cmd_lock);
|
| 484 | }
|
| 485 |
|
| 486 | return ret;
|
| 487 | }
|
| 488 |
|
| 489 | static int ionic_get_coalesce(struct net_device *netdev,
|
| 490 | struct ethtool_coalesce *coalesce,
|
| 491 | struct kernel_ethtool_coalesce *kernel_coal,
|
| 492 | struct netlink_ext_ack *extack)
|
| 493 | {
|
| 494 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 495 |
|
| 496 | coalesce->tx_coalesce_usecs = lif->tx_coalesce_usecs;
|
| 497 | coalesce->rx_coalesce_usecs = lif->rx_coalesce_usecs;
|
| 498 |
|
| 499 | if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
|
| 500 | coalesce->use_adaptive_tx_coalesce = test_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state);
|
| 501 | else
|
| 502 | coalesce->use_adaptive_tx_coalesce = 0;
|
| 503 |
|
| 504 | coalesce->use_adaptive_rx_coalesce = test_bit(IONIC_LIF_F_RX_DIM_INTR, lif->state);
|
| 505 |
|
| 506 | return 0;
|
| 507 | }
|
| 508 |
|
| 509 | static int ionic_set_coalesce(struct net_device *netdev,
|
| 510 | struct ethtool_coalesce *coalesce,
|
| 511 | struct kernel_ethtool_coalesce *kernel_coal,
|
| 512 | struct netlink_ext_ack *extack)
|
| 513 | {
|
| 514 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 515 | struct ionic_identity *ident;
|
| 516 | u32 rx_coal, rx_dim;
|
| 517 | u32 tx_coal, tx_dim;
|
| 518 | unsigned int i;
|
| 519 |
|
| 520 | ident = &lif->ionic->ident;
|
| 521 | if (ident->dev.intr_coal_div == 0) {
|
| 522 | netdev_warn(netdev, "bad HW value in dev.intr_coal_div = %d\n",
|
| 523 | ident->dev.intr_coal_div);
|
| 524 | return -EIO;
|
| 525 | }
|
| 526 |
|
| 527 |
|
| 528 | if (!test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state) &&
|
| 529 | (coalesce->tx_coalesce_usecs != lif->rx_coalesce_usecs ||
|
| 530 | coalesce->use_adaptive_tx_coalesce)) {
|
| 531 | netdev_warn(netdev, "only rx parameters can be changed\n");
|
| 532 | return -EINVAL;
|
| 533 | }
|
| 534 |
|
| 535 | |
| 536 | * for non-zero and it resolved to zero, bump it up
|
| 537 |
|
| 538 | rx_coal = ionic_coal_usec_to_hw(lif->ionic, coalesce->rx_coalesce_usecs);
|
| 539 | if (!rx_coal && coalesce->rx_coalesce_usecs)
|
| 540 | rx_coal = 1;
|
| 541 | tx_coal = ionic_coal_usec_to_hw(lif->ionic, coalesce->tx_coalesce_usecs);
|
| 542 | if (!tx_coal && coalesce->tx_coalesce_usecs)
|
| 543 | tx_coal = 1;
|
| 544 |
|
| 545 | if (rx_coal > IONIC_INTR_CTRL_COAL_MAX ||
|
| 546 | tx_coal > IONIC_INTR_CTRL_COAL_MAX)
|
| 547 | return -ERANGE;
|
| 548 |
|
| 549 |
|
| 550 | lif->rx_coalesce_usecs = coalesce->rx_coalesce_usecs;
|
| 551 | lif->rx_coalesce_hw = rx_coal;
|
| 552 |
|
| 553 | if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
|
| 554 | lif->tx_coalesce_usecs = coalesce->tx_coalesce_usecs;
|
| 555 | else
|
| 556 | lif->tx_coalesce_usecs = coalesce->rx_coalesce_usecs;
|
| 557 | lif->tx_coalesce_hw = tx_coal;
|
| 558 |
|
| 559 | if (coalesce->use_adaptive_rx_coalesce) {
|
| 560 | set_bit(IONIC_LIF_F_RX_DIM_INTR, lif->state);
|
| 561 | rx_dim = rx_coal;
|
| 562 | } else {
|
| 563 | clear_bit(IONIC_LIF_F_RX_DIM_INTR, lif->state);
|
| 564 | rx_dim = 0;
|
| 565 | }
|
| 566 |
|
| 567 | if (coalesce->use_adaptive_tx_coalesce) {
|
| 568 | set_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state);
|
| 569 | tx_dim = tx_coal;
|
| 570 | } else {
|
| 571 | clear_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state);
|
| 572 | tx_dim = 0;
|
| 573 | }
|
| 574 |
|
| 575 | if (test_bit(IONIC_LIF_F_UP, lif->state)) {
|
| 576 | for (i = 0; i < lif->nxqs; i++) {
|
| 577 | if (lif->rxqcqs[i]->flags & IONIC_QCQ_F_INTR) {
|
| 578 | ionic_intr_coal_init(lif->ionic->idev.intr_ctrl,
|
| 579 | lif->rxqcqs[i]->intr.index,
|
| 580 | lif->rx_coalesce_hw);
|
| 581 | lif->rxqcqs[i]->intr.dim_coal_hw = rx_dim;
|
| 582 | }
|
| 583 |
|
| 584 | if (lif->txqcqs[i]->flags & IONIC_QCQ_F_INTR) {
|
| 585 | ionic_intr_coal_init(lif->ionic->idev.intr_ctrl,
|
| 586 | lif->txqcqs[i]->intr.index,
|
| 587 | lif->tx_coalesce_hw);
|
| 588 | lif->txqcqs[i]->intr.dim_coal_hw = tx_dim;
|
| 589 | }
|
| 590 | }
|
| 591 | }
|
| 592 |
|
| 593 | return 0;
|
| 594 | }
|
| 595 |
|
| 596 | static int ionic_validate_cmb_config(struct ionic_lif *lif,
|
| 597 | struct ionic_queue_params *qparam)
|
| 598 | {
|
| 599 | int pages_have, pages_required = 0;
|
| 600 | unsigned long sz;
|
| 601 |
|
| 602 | if (!lif->ionic->idev.cmb_inuse &&
|
| 603 | (qparam->cmb_tx || qparam->cmb_rx)) {
|
| 604 | netdev_info(lif->netdev, "CMB rings are not supported on this device\n");
|
| 605 | return -EOPNOTSUPP;
|
| 606 | }
|
| 607 |
|
| 608 | if (qparam->cmb_tx) {
|
| 609 | if (!(lif->qtype_info[IONIC_QTYPE_TXQ].features & IONIC_QIDENT_F_CMB)) {
|
| 610 | netdev_info(lif->netdev,
|
| 611 | "CMB rings for tx-push are not supported on this device\n");
|
| 612 | return -EOPNOTSUPP;
|
| 613 | }
|
| 614 |
|
| 615 | sz = sizeof(struct ionic_txq_desc) * qparam->ntxq_descs * qparam->nxqs;
|
| 616 | pages_required += ALIGN(sz, PAGE_SIZE) / PAGE_SIZE;
|
| 617 | }
|
| 618 |
|
| 619 | if (qparam->cmb_rx) {
|
| 620 | if (!(lif->qtype_info[IONIC_QTYPE_RXQ].features & IONIC_QIDENT_F_CMB)) {
|
| 621 | netdev_info(lif->netdev,
|
| 622 | "CMB rings for rx-push are not supported on this device\n");
|
| 623 | return -EOPNOTSUPP;
|
| 624 | }
|
| 625 |
|
| 626 | sz = sizeof(struct ionic_rxq_desc) * qparam->nrxq_descs * qparam->nxqs;
|
| 627 | pages_required += ALIGN(sz, PAGE_SIZE) / PAGE_SIZE;
|
| 628 | }
|
| 629 |
|
| 630 | pages_have = lif->ionic->bars[IONIC_PCI_BAR_CMB].len / PAGE_SIZE;
|
| 631 | if (pages_required > pages_have) {
|
| 632 | netdev_info(lif->netdev,
|
| 633 | "Not enough CMB pages for number of queues and size of descriptor rings, need %d have %d",
|
| 634 | pages_required, pages_have);
|
| 635 | return -ENOMEM;
|
| 636 | }
|
| 637 |
|
| 638 | return pages_required;
|
| 639 | }
|
| 640 |
|
| 641 | static int ionic_cmb_rings_toggle(struct ionic_lif *lif, bool cmb_tx, bool cmb_rx)
|
| 642 | {
|
| 643 | struct ionic_queue_params qparam;
|
| 644 | int pages_used;
|
| 645 |
|
| 646 | if (netif_running(lif->netdev)) {
|
| 647 | netdev_info(lif->netdev, "Please stop device to toggle CMB for tx/rx-push\n");
|
| 648 | return -EBUSY;
|
| 649 | }
|
| 650 |
|
| 651 | ionic_init_queue_params(lif, &qparam);
|
| 652 | qparam.cmb_tx = cmb_tx;
|
| 653 | qparam.cmb_rx = cmb_rx;
|
| 654 | pages_used = ionic_validate_cmb_config(lif, &qparam);
|
| 655 | if (pages_used < 0)
|
| 656 | return pages_used;
|
| 657 |
|
| 658 | if (cmb_tx)
|
| 659 | set_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state);
|
| 660 | else
|
| 661 | clear_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state);
|
| 662 |
|
| 663 | if (cmb_rx)
|
| 664 | set_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state);
|
| 665 | else
|
| 666 | clear_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state);
|
| 667 |
|
| 668 | if (cmb_tx || cmb_rx)
|
| 669 | netdev_info(lif->netdev, "Enabling CMB %s %s rings - %d pages\n",
|
| 670 | cmb_tx ? "TX" : "", cmb_rx ? "RX" : "", pages_used);
|
| 671 | else
|
| 672 | netdev_info(lif->netdev, "Disabling CMB rings\n");
|
| 673 |
|
| 674 | return 0;
|
| 675 | }
|
| 676 |
|
| 677 | static void ionic_get_ringparam(struct net_device *netdev,
|
| 678 | struct ethtool_ringparam *ring,
|
| 679 | struct kernel_ethtool_ringparam *kernel_ring,
|
| 680 | struct netlink_ext_ack *extack)
|
| 681 | {
|
| 682 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 683 |
|
| 684 | ring->tx_max_pending = IONIC_MAX_TX_DESC;
|
| 685 | ring->tx_pending = lif->ntxq_descs;
|
| 686 | ring->rx_max_pending = IONIC_MAX_RX_DESC;
|
| 687 | ring->rx_pending = lif->nrxq_descs;
|
| 688 | kernel_ring->tx_push = test_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state);
|
| 689 | kernel_ring->rx_push = test_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state);
|
| 690 | }
|
| 691 |
|
| 692 | static int ionic_set_ringparam(struct net_device *netdev,
|
| 693 | struct ethtool_ringparam *ring,
|
| 694 | struct kernel_ethtool_ringparam *kernel_ring,
|
| 695 | struct netlink_ext_ack *extack)
|
| 696 | {
|
| 697 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 698 | struct ionic_queue_params qparam;
|
| 699 | int err;
|
| 700 |
|
| 701 | if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
| 702 | return -EBUSY;
|
| 703 |
|
| 704 | ionic_init_queue_params(lif, &qparam);
|
| 705 |
|
| 706 | if (ring->rx_mini_pending || ring->rx_jumbo_pending) {
|
| 707 | netdev_info(netdev, "Changing jumbo or mini descriptors not supported\n");
|
| 708 | return -EINVAL;
|
| 709 | }
|
| 710 |
|
| 711 | if (!is_power_of_2(ring->tx_pending) ||
|
| 712 | !is_power_of_2(ring->rx_pending)) {
|
| 713 | netdev_info(netdev, "Descriptor count must be a power of 2\n");
|
| 714 | return -EINVAL;
|
| 715 | }
|
| 716 |
|
| 717 |
|
| 718 | if (ring->tx_pending == lif->ntxq_descs &&
|
| 719 | ring->rx_pending == lif->nrxq_descs &&
|
| 720 | kernel_ring->tx_push == test_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state) &&
|
| 721 | kernel_ring->rx_push == test_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state))
|
| 722 | return 0;
|
| 723 |
|
| 724 | qparam.ntxq_descs = ring->tx_pending;
|
| 725 | qparam.nrxq_descs = ring->rx_pending;
|
| 726 | qparam.cmb_tx = kernel_ring->tx_push;
|
| 727 | qparam.cmb_rx = kernel_ring->rx_push;
|
| 728 |
|
| 729 | err = ionic_validate_cmb_config(lif, &qparam);
|
| 730 | if (err < 0)
|
| 731 | return err;
|
| 732 |
|
| 733 | if (kernel_ring->tx_push != test_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state) ||
|
| 734 | kernel_ring->rx_push != test_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state)) {
|
| 735 | err = ionic_cmb_rings_toggle(lif, kernel_ring->tx_push,
|
| 736 | kernel_ring->rx_push);
|
| 737 | if (err < 0)
|
| 738 | return err;
|
| 739 | }
|
| 740 |
|
| 741 | if (ring->tx_pending != lif->ntxq_descs)
|
| 742 | netdev_info(netdev, "Changing Tx ring size from %d to %d\n",
|
| 743 | lif->ntxq_descs, ring->tx_pending);
|
| 744 |
|
| 745 | if (ring->rx_pending != lif->nrxq_descs)
|
| 746 | netdev_info(netdev, "Changing Rx ring size from %d to %d\n",
|
| 747 | lif->nrxq_descs, ring->rx_pending);
|
| 748 |
|
| 749 |
|
| 750 | if (!netif_running(lif->netdev)) {
|
| 751 | lif->ntxq_descs = ring->tx_pending;
|
| 752 | lif->nrxq_descs = ring->rx_pending;
|
| 753 | return 0;
|
| 754 | }
|
| 755 |
|
| 756 | mutex_lock(&lif->queue_lock);
|
| 757 | err = ionic_reconfigure_queues(lif, &qparam);
|
| 758 | mutex_unlock(&lif->queue_lock);
|
| 759 | if (err)
|
| 760 | netdev_info(netdev, "Ring reconfiguration failed, changes canceled: %d\n", err);
|
| 761 |
|
| 762 | return err;
|
| 763 | }
|
| 764 |
|
| 765 | static void ionic_get_channels(struct net_device *netdev,
|
| 766 | struct ethtool_channels *ch)
|
| 767 | {
|
| 768 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 769 |
|
| 770 |
|
| 771 | ch->max_combined = lif->ionic->ntxqs_per_lif;
|
| 772 | ch->max_rx = lif->ionic->ntxqs_per_lif / 2;
|
| 773 | ch->max_tx = lif->ionic->ntxqs_per_lif / 2;
|
| 774 |
|
| 775 |
|
| 776 | if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) {
|
| 777 | ch->rx_count = lif->nxqs;
|
| 778 | ch->tx_count = lif->nxqs;
|
| 779 | } else {
|
| 780 | ch->combined_count = lif->nxqs;
|
| 781 | }
|
| 782 | }
|
| 783 |
|
| 784 | static int ionic_set_channels(struct net_device *netdev,
|
| 785 | struct ethtool_channels *ch)
|
| 786 | {
|
| 787 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 788 | struct ionic_queue_params qparam;
|
| 789 | int max_cnt;
|
| 790 | int err;
|
| 791 |
|
| 792 | if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
| 793 | return -EBUSY;
|
| 794 |
|
| 795 | ionic_init_queue_params(lif, &qparam);
|
| 796 |
|
| 797 | if ((ch->rx_count || ch->tx_count) && lif->xdp_prog) {
|
| 798 | netdev_info(lif->netdev, "Split Tx/Rx interrupts not available when using XDP\n");
|
| 799 | return -EOPNOTSUPP;
|
| 800 | }
|
| 801 |
|
| 802 | if (ch->rx_count != ch->tx_count) {
|
| 803 | netdev_info(netdev, "The rx and tx count must be equal\n");
|
| 804 | return -EINVAL;
|
| 805 | }
|
| 806 |
|
| 807 | if (ch->combined_count && ch->rx_count) {
|
| 808 | netdev_info(netdev, "Use either combined or rx and tx, not both\n");
|
| 809 | return -EINVAL;
|
| 810 | }
|
| 811 |
|
| 812 | max_cnt = lif->ionic->ntxqs_per_lif;
|
| 813 | if (ch->combined_count) {
|
| 814 | if (ch->combined_count > max_cnt)
|
| 815 | return -EINVAL;
|
| 816 |
|
| 817 | if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
|
| 818 | netdev_info(lif->netdev, "Sharing queue interrupts\n");
|
| 819 | else if (ch->combined_count == lif->nxqs)
|
| 820 | return 0;
|
| 821 |
|
| 822 | if (lif->nxqs != ch->combined_count)
|
| 823 | netdev_info(netdev, "Changing queue count from %d to %d\n",
|
| 824 | lif->nxqs, ch->combined_count);
|
| 825 |
|
| 826 | qparam.nxqs = ch->combined_count;
|
| 827 | qparam.intr_split = false;
|
| 828 | } else {
|
| 829 | max_cnt /= 2;
|
| 830 | if (ch->rx_count > max_cnt)
|
| 831 | return -EINVAL;
|
| 832 |
|
| 833 | if (!test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
|
| 834 | netdev_info(lif->netdev, "Splitting queue interrupts\n");
|
| 835 | else if (ch->rx_count == lif->nxqs)
|
| 836 | return 0;
|
| 837 |
|
| 838 | if (lif->nxqs != ch->rx_count)
|
| 839 | netdev_info(netdev, "Changing queue count from %d to %d\n",
|
| 840 | lif->nxqs, ch->rx_count);
|
| 841 |
|
| 842 | qparam.nxqs = ch->rx_count;
|
| 843 | qparam.intr_split = true;
|
| 844 | }
|
| 845 |
|
| 846 | err = ionic_validate_cmb_config(lif, &qparam);
|
| 847 | if (err < 0)
|
| 848 | return err;
|
| 849 |
|
| 850 |
|
| 851 | if (!netif_running(lif->netdev)) {
|
| 852 | lif->nxqs = qparam.nxqs;
|
| 853 |
|
| 854 | if (qparam.intr_split) {
|
| 855 | set_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
|
| 856 | } else {
|
| 857 | clear_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
|
| 858 | lif->tx_coalesce_usecs = lif->rx_coalesce_usecs;
|
| 859 | lif->tx_coalesce_hw = lif->rx_coalesce_hw;
|
| 860 | }
|
| 861 | return 0;
|
| 862 | }
|
| 863 |
|
| 864 | mutex_lock(&lif->queue_lock);
|
| 865 | err = ionic_reconfigure_queues(lif, &qparam);
|
| 866 | mutex_unlock(&lif->queue_lock);
|
| 867 | if (err)
|
| 868 | netdev_info(netdev, "Queue reconfiguration failed, changes canceled: %d\n", err);
|
| 869 |
|
| 870 | return err;
|
| 871 | }
|
| 872 |
|
| 873 | static u32 ionic_get_rx_ring_count(struct net_device *netdev)
|
| 874 | {
|
| 875 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 876 |
|
| 877 | return lif->nxqs;
|
| 878 | }
|
| 879 |
|
| 880 | static u32 ionic_get_rxfh_indir_size(struct net_device *netdev)
|
| 881 | {
|
| 882 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 883 |
|
| 884 | return le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
|
| 885 | }
|
| 886 |
|
| 887 | static u32 ionic_get_rxfh_key_size(struct net_device *netdev)
|
| 888 | {
|
| 889 | return IONIC_RSS_HASH_KEY_SIZE;
|
| 890 | }
|
| 891 |
|
| 892 | static int ionic_get_rxfh(struct net_device *netdev,
|
| 893 | struct ethtool_rxfh_param *rxfh)
|
| 894 | {
|
| 895 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 896 | unsigned int i, tbl_sz;
|
| 897 |
|
| 898 | if (rxfh->indir) {
|
| 899 | tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
|
| 900 | for (i = 0; i < tbl_sz; i++)
|
| 901 | rxfh->indir[i] = lif->rss_ind_tbl[i];
|
| 902 | }
|
| 903 |
|
| 904 | if (rxfh->key)
|
| 905 | memcpy(rxfh->key, lif->rss_hash_key, IONIC_RSS_HASH_KEY_SIZE);
|
| 906 |
|
| 907 | rxfh->hfunc = ETH_RSS_HASH_TOP;
|
| 908 |
|
| 909 | return 0;
|
| 910 | }
|
| 911 |
|
| 912 | static int ionic_set_rxfh(struct net_device *netdev,
|
| 913 | struct ethtool_rxfh_param *rxfh,
|
| 914 | struct netlink_ext_ack *extack)
|
| 915 | {
|
| 916 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 917 |
|
| 918 | if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE &&
|
| 919 | rxfh->hfunc != ETH_RSS_HASH_TOP)
|
| 920 | return -EOPNOTSUPP;
|
| 921 |
|
| 922 | return ionic_lif_rss_config(lif, lif->rss_types,
|
| 923 | rxfh->key, rxfh->indir);
|
| 924 | }
|
| 925 |
|
| 926 | static int ionic_set_tunable(struct net_device *dev,
|
| 927 | const struct ethtool_tunable *tuna,
|
| 928 | const void *data)
|
| 929 | {
|
| 930 | struct ionic_lif *lif = netdev_priv(dev);
|
| 931 | u32 rx_copybreak;
|
| 932 |
|
| 933 | switch (tuna->id) {
|
| 934 | case ETHTOOL_RX_COPYBREAK:
|
| 935 | rx_copybreak = *(u32 *)data;
|
| 936 | if (rx_copybreak > IONIC_MAX_RX_COPYBREAK) {
|
| 937 | netdev_err(dev, "Max supported rx_copybreak size: %u\n",
|
| 938 | IONIC_MAX_RX_COPYBREAK);
|
| 939 | return -EINVAL;
|
| 940 | }
|
| 941 | lif->rx_copybreak = (u16)rx_copybreak;
|
| 942 | break;
|
| 943 | default:
|
| 944 | return -EOPNOTSUPP;
|
| 945 | }
|
| 946 |
|
| 947 | return 0;
|
| 948 | }
|
| 949 |
|
| 950 | static int ionic_get_tunable(struct net_device *netdev,
|
| 951 | const struct ethtool_tunable *tuna, void *data)
|
| 952 | {
|
| 953 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 954 |
|
| 955 | switch (tuna->id) {
|
| 956 | case ETHTOOL_RX_COPYBREAK:
|
| 957 | *(u32 *)data = lif->rx_copybreak;
|
| 958 | break;
|
| 959 | default:
|
| 960 | return -EOPNOTSUPP;
|
| 961 | }
|
| 962 |
|
| 963 | return 0;
|
| 964 | }
|
| 965 |
|
| 966 | static int ionic_do_module_copy(u8 *dst, u8 *src, u32 len)
|
| 967 | {
|
| 968 | char tbuf[sizeof_field(struct ionic_xcvr_status, sprom)];
|
| 969 | int count = 10;
|
| 970 |
|
| 971 | |
| 972 | * so we can simply copy the module bytes into the data buffer.
|
| 973 |
|
| 974 | do {
|
| 975 | memcpy(dst, src, len);
|
| 976 | memcpy(tbuf, src, len);
|
| 977 |
|
| 978 |
|
| 979 | if (!memcmp(dst, tbuf, len))
|
| 980 | break;
|
| 981 |
|
| 982 | } while (--count);
|
| 983 |
|
| 984 | if (!count)
|
| 985 | return -ETIMEDOUT;
|
| 986 |
|
| 987 | return 0;
|
| 988 | }
|
| 989 |
|
| 990 | static int ionic_get_module_eeprom_by_page(struct net_device *netdev,
|
| 991 | const struct ethtool_module_eeprom *page_data,
|
| 992 | struct netlink_ext_ack *extack)
|
| 993 | {
|
| 994 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 995 | struct ionic_dev *idev = &lif->ionic->idev;
|
| 996 | int err;
|
| 997 | u8 *src;
|
| 998 |
|
| 999 | if (!page_data->length)
|
| 1000 | return -EINVAL;
|
| 1001 |
|
| 1002 | if (page_data->bank != 0) {
|
| 1003 | NL_SET_ERR_MSG_MOD(extack, "Only bank 0 is supported");
|
| 1004 | return -EINVAL;
|
| 1005 | }
|
| 1006 |
|
| 1007 | switch (page_data->page) {
|
| 1008 | case 0:
|
| 1009 | src = &idev->port_info->status.xcvr.sprom[page_data->offset];
|
| 1010 | break;
|
| 1011 | case 1:
|
| 1012 | src = &idev->port_info->sprom_page1[page_data->offset - 128];
|
| 1013 | break;
|
| 1014 | case 2:
|
| 1015 | src = &idev->port_info->sprom_page2[page_data->offset - 128];
|
| 1016 | break;
|
| 1017 | case 17:
|
| 1018 | src = &idev->port_info->sprom_page17[page_data->offset - 128];
|
| 1019 | break;
|
| 1020 | default:
|
| 1021 | return -EOPNOTSUPP;
|
| 1022 | }
|
| 1023 |
|
| 1024 | memset(page_data->data, 0, page_data->length);
|
| 1025 | err = ionic_do_module_copy(page_data->data, src, page_data->length);
|
| 1026 | if (err)
|
| 1027 | return err;
|
| 1028 |
|
| 1029 | return page_data->length;
|
| 1030 | }
|
| 1031 |
|
| 1032 | static int ionic_get_ts_info(struct net_device *netdev,
|
| 1033 | struct kernel_ethtool_ts_info *info)
|
| 1034 | {
|
| 1035 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 1036 | struct ionic *ionic = lif->ionic;
|
| 1037 | __le64 mask;
|
| 1038 |
|
| 1039 | if (!lif->phc || !lif->phc->ptp)
|
| 1040 | return ethtool_op_get_ts_info(netdev, info);
|
| 1041 |
|
| 1042 | info->phc_index = ptp_clock_index(lif->phc->ptp);
|
| 1043 |
|
| 1044 | info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
|
| 1045 |
|
| 1046 | if (!(lif->hw_features & IONIC_ETH_HW_TIMESTAMP))
|
| 1047 | return 0;
|
| 1048 |
|
| 1049 | info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE |
|
| 1050 | SOF_TIMESTAMPING_RX_HARDWARE |
|
| 1051 | SOF_TIMESTAMPING_RAW_HARDWARE;
|
| 1052 |
|
| 1053 |
|
| 1054 |
|
| 1055 | info->tx_types = BIT(HWTSTAMP_TX_OFF) |
|
| 1056 | BIT(HWTSTAMP_TX_ON);
|
| 1057 |
|
| 1058 | mask = cpu_to_le64(BIT_ULL(IONIC_TXSTAMP_ONESTEP_SYNC));
|
| 1059 | if (ionic->ident.lif.eth.hwstamp_tx_modes & mask)
|
| 1060 | info->tx_types |= BIT(HWTSTAMP_TX_ONESTEP_SYNC);
|
| 1061 |
|
| 1062 | mask = cpu_to_le64(BIT_ULL(IONIC_TXSTAMP_ONESTEP_P2P));
|
| 1063 | if (ionic->ident.lif.eth.hwstamp_tx_modes & mask)
|
| 1064 | info->tx_types |= BIT(HWTSTAMP_TX_ONESTEP_P2P);
|
| 1065 |
|
| 1066 |
|
| 1067 |
|
| 1068 | info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
|
| 1069 | BIT(HWTSTAMP_FILTER_ALL);
|
| 1070 |
|
| 1071 | mask = cpu_to_le64(IONIC_PKT_CLS_NTP_ALL);
|
| 1072 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1073 | info->rx_filters |= BIT(HWTSTAMP_FILTER_NTP_ALL);
|
| 1074 |
|
| 1075 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP1_SYNC);
|
| 1076 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1077 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC);
|
| 1078 |
|
| 1079 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP1_DREQ);
|
| 1080 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1081 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ);
|
| 1082 |
|
| 1083 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP1_ALL);
|
| 1084 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1085 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V1_L4_EVENT);
|
| 1086 |
|
| 1087 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L4_SYNC);
|
| 1088 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1089 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC);
|
| 1090 |
|
| 1091 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L4_DREQ);
|
| 1092 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1093 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ);
|
| 1094 |
|
| 1095 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L4_ALL);
|
| 1096 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1097 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT);
|
| 1098 |
|
| 1099 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L2_SYNC);
|
| 1100 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1101 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC);
|
| 1102 |
|
| 1103 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L2_DREQ);
|
| 1104 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1105 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ);
|
| 1106 |
|
| 1107 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_L2_ALL);
|
| 1108 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1109 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT);
|
| 1110 |
|
| 1111 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_SYNC);
|
| 1112 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1113 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V2_SYNC);
|
| 1114 |
|
| 1115 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_DREQ);
|
| 1116 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1117 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ);
|
| 1118 |
|
| 1119 | mask = cpu_to_le64(IONIC_PKT_CLS_PTP2_ALL);
|
| 1120 | if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) == mask)
|
| 1121 | info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
|
| 1122 |
|
| 1123 | return 0;
|
| 1124 | }
|
| 1125 |
|
| 1126 | static int ionic_nway_reset(struct net_device *netdev)
|
| 1127 | {
|
| 1128 | struct ionic_lif *lif = netdev_priv(netdev);
|
| 1129 | struct ionic *ionic = lif->ionic;
|
| 1130 | int err = 0;
|
| 1131 |
|
| 1132 | if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
| 1133 | return -EBUSY;
|
| 1134 |
|
| 1135 |
|
| 1136 |
|
| 1137 | mutex_lock(&ionic->dev_cmd_lock);
|
| 1138 |
|
| 1139 | ionic_dev_cmd_port_state(&ionic->idev, IONIC_PORT_ADMIN_STATE_DOWN);
|
| 1140 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
|
| 1141 |
|
| 1142 | if (!err) {
|
| 1143 | ionic_dev_cmd_port_state(&ionic->idev, IONIC_PORT_ADMIN_STATE_UP);
|
| 1144 | err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
|
| 1145 | }
|
| 1146 |
|
| 1147 | mutex_unlock(&ionic->dev_cmd_lock);
|
| 1148 |
|
| 1149 | return err;
|
| 1150 | }
|
| 1151 |
|
| 1152 | static const struct ethtool_ops ionic_ethtool_ops = {
|
| 1153 | .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
|
| 1154 | ETHTOOL_COALESCE_USE_ADAPTIVE_RX |
|
| 1155 | ETHTOOL_COALESCE_USE_ADAPTIVE_TX,
|
| 1156 | .supported_ring_params = ETHTOOL_RING_USE_TX_PUSH |
|
| 1157 | ETHTOOL_RING_USE_RX_PUSH,
|
| 1158 | .get_drvinfo = ionic_get_drvinfo,
|
| 1159 | .get_regs_len = ionic_get_regs_len,
|
| 1160 | .get_regs = ionic_get_regs,
|
| 1161 | .get_link = ethtool_op_get_link,
|
| 1162 | .get_link_ext_stats = ionic_get_link_ext_stats,
|
| 1163 | .get_link_ksettings = ionic_get_link_ksettings,
|
| 1164 | .set_link_ksettings = ionic_set_link_ksettings,
|
| 1165 | .get_coalesce = ionic_get_coalesce,
|
| 1166 | .set_coalesce = ionic_set_coalesce,
|
| 1167 | .get_ringparam = ionic_get_ringparam,
|
| 1168 | .set_ringparam = ionic_set_ringparam,
|
| 1169 | .get_channels = ionic_get_channels,
|
| 1170 | .set_channels = ionic_set_channels,
|
| 1171 | .get_strings = ionic_get_strings,
|
| 1172 | .get_ethtool_stats = ionic_get_stats,
|
| 1173 | .get_sset_count = ionic_get_sset_count,
|
| 1174 | .get_rx_ring_count = ionic_get_rx_ring_count,
|
| 1175 | .get_rxfh_indir_size = ionic_get_rxfh_indir_size,
|
| 1176 | .get_rxfh_key_size = ionic_get_rxfh_key_size,
|
| 1177 | .get_rxfh = ionic_get_rxfh,
|
| 1178 | .set_rxfh = ionic_set_rxfh,
|
| 1179 | .get_tunable = ionic_get_tunable,
|
| 1180 | .set_tunable = ionic_set_tunable,
|
| 1181 | .get_module_eeprom_by_page = ionic_get_module_eeprom_by_page,
|
| 1182 | .get_pauseparam = ionic_get_pauseparam,
|
| 1183 | .set_pauseparam = ionic_set_pauseparam,
|
| 1184 | .get_fecparam = ionic_get_fecparam,
|
| 1185 | .set_fecparam = ionic_set_fecparam,
|
| 1186 | .get_ts_info = ionic_get_ts_info,
|
| 1187 | .nway_reset = ionic_nway_reset,
|
| 1188 | };
|
| 1189 |
|
| 1190 | void ionic_ethtool_set_ops(struct net_device *netdev)
|
| 1191 | {
|
| 1192 | netdev->ethtool_ops = &ionic_ethtool_ops;
|
| 1193 | }
|