// SPDX-License-Identifier: GPL-2.0-or-later /* A network driver using virtio. * * Copyright 2007 Rusty Russell IBM Corporation */ //#define DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "crete_nic.h" #include "crete_nic_io.h" #include #include "../crete-core/crete_fw_update.h" #define CRETE_VNIC_DRIVER_VERSION "crete-vnic-1.0" /******************net tools****************************/ static void crete_vnic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct crete_vnic_priv *vnic_priv = netdev_priv(dev); struct crete_core_dev *core_dev = vnic_priv->coredev; struct pci_dev *pdev = core_dev->pdev; strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d.%d", fw_ver_maj(core_dev), fw_ver_min(core_dev), fw_ver_sub(core_dev), fw_ver_rev(core_dev)); strlcpy(info->version, CRETE_VNIC_DRIVER_VERSION, sizeof(info->version)); strlcpy(info->bus_info, pci_name(pdev), sizeof(info->bus_info)); } static u32 crete_vnic_get_link(struct net_device *netdev) { //struct crete_vnic_priv *vnic_priv = netdev_priv(netdev); //struct crete_core_dev *core_dev =vnic_priv->coredev; /* If the link is not reported up to netdev, interrupts are disabled, * and so the physical link state may have changed since we last * looked. Set get_link_status to make sure that the true link * state is interrogated, rather than pulling a cached and possibly * stale link state from the driver. */ if (!netif_carrier_ok(netdev)) { /*todo to vdev get link status */ return 0; } return 1; } /* * about the anolis 5.10.134-14 kernel version * auxliary define with return int value */ #if defined(SNIC_ANOLIS_VERSION14) || !defined(CONFIG_ETHTOOL_EXTEND_RINGPARAM) static void crete_vnic_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring) { struct crete_vnic_priv *vnic_priv = netdev_priv(dev); ring->rx_max_pending = virtqueue_get_vring_size(vnic_priv->rq[0].vq); ring->tx_max_pending = virtqueue_get_vring_size(vnic_priv->sq[0].vq); ring->rx_pending = ring->rx_max_pending; ring->tx_pending = ring->tx_max_pending; } #else static void crete_vnic_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring, struct netlink_ext_ack *extack) { struct crete_vnic_priv *vnic_priv = netdev_priv(dev); ring->rx_max_pending = virtqueue_get_vring_size(vnic_priv->rq[0].vq); ring->tx_max_pending = virtqueue_get_vring_size(vnic_priv->sq[0].vq); ring->rx_pending = ring->rx_max_pending; ring->tx_pending = ring->tx_max_pending; } #endif static void crete_vnic_get_channels(struct net_device *dev, struct ethtool_channels *channels) { struct crete_vnic_priv *vnic_priv = netdev_priv(dev); channels->combined_count = vnic_priv->curr_queue_pairs; channels->max_combined = vnic_priv->max_queue_pairs; channels->max_other = 0; channels->rx_count = 0; channels->tx_count = 0; channels->other_count = 0; } /* TODO: Eliminate OOO packets during switching */ static int crete_vnic_set_channels(struct net_device *dev, struct ethtool_channels *channels) { struct crete_vnic_priv *vnic_priv = netdev_priv(dev); u16 queue_pairs = channels->combined_count; int err; /* We don't support separate rx/tx channels. * We don't allow setting 'other' channels. */ if (channels->rx_count || channels->tx_count || channels->other_count) return -EINVAL; if (queue_pairs > vnic_priv->max_queue_pairs || queue_pairs == 0) return -EINVAL; err = __crete_vnic_set_queues(dev, queue_pairs); if (err) goto err; netif_set_real_num_tx_queues(dev, queue_pairs); netif_set_real_num_rx_queues(dev, queue_pairs); err: return err; } int crete_vnic_ethtool_flash_device(struct crete_vnic_priv *vnic_priv, struct ethtool_flash *flash) { struct crete_core_dev *cdev = vnic_priv->coredev; struct net_device *dev = vnic_priv->netdev; const struct firmware *fw; int err; if (flash->region != ETHTOOL_FLASH_ALL_REGIONS) return -EOPNOTSUPP; err = request_firmware_direct(&fw, flash->data, &dev->dev); if (err) return err; dev_hold(dev); rtnl_unlock(); err = crete_firmware_flash(cdev, fw, NULL, CRETE_FWU_ETHTOOL); release_firmware(fw); rtnl_lock(); dev_put(dev); return err; } static int crete_vnic_flash_device(struct net_device *dev, struct ethtool_flash *flash) { struct crete_vnic_priv *vnic_priv = netdev_priv(dev); return crete_vnic_ethtool_flash_device(vnic_priv, flash); } struct ptys2ethtool_config { __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); __ETHTOOL_DECLARE_LINK_MODE_MASK(advertised); }; static struct ptys2ethtool_config ptys2ethtool_table[CRETE_LINK_MODES_CAP_BIT]; #define CRETE_BUILD_PTYS2ETHTOOL_CONFIG(reg_, table, ...) \ ({ \ struct ptys2ethtool_config *cfg; \ const unsigned int modes[] = { __VA_ARGS__ }; \ unsigned int i, bit, idx; \ cfg = &table[reg_]; \ bitmap_zero(cfg->supported, \ __ETHTOOL_LINK_MODE_MASK_NBITS); \ bitmap_zero(cfg->advertised, \ __ETHTOOL_LINK_MODE_MASK_NBITS); \ for (i = 0 ; i < ARRAY_SIZE(modes) ; ++i) { \ bit = modes[i] % 64; \ idx = modes[i] / 64; \ __set_bit(bit, &cfg->supported[idx]); \ __set_bit(bit, &cfg->advertised[idx]); \ } \ }) void crete_build_ptys2ethtool_map(void) { memset(ptys2ethtool_table, 0, sizeof(ptys2ethtool_table)); CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_10GBASE_BIT, ptys2ethtool_table, ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, ETHTOOL_LINK_MODE_10000baseER_Full_BIT); CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_25GBASE_BIT, ptys2ethtool_table, ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, ETHTOOL_LINK_MODE_25000baseSR_Full_BIT); CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_40GBASE_BIT, ptys2ethtool_table, ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT); CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_50GBASE_BIT, ptys2ethtool_table, ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT); CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_100GBASE_BIT, ptys2ethtool_table, ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT, ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT); CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_200GBASE_BIT, ptys2ethtool_table, ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT, ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT, ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT, ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT, ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT); CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_400GBASE_BIT, ptys2ethtool_table, ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT, ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT, ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT); } u16 crete_port_speed2linkmode(u32 speed) { int i; for (i = 0; i < ARRAY_SIZE(crete_link_speed); ++i) { if (crete_link_speed[i] == speed) return i; } return 0; } int crete_ethtool_set_link_ksettings(struct crete_vnic_priv *vnic_priv, const struct ethtool_link_ksettings *link_ksettings) { struct crete_core_dev *cdev = vnic_priv->coredev; const struct ethtool_link_settings *base = &link_ksettings->base; struct net_device *dev = vnic_priv->netdev; struct crete_speed_duplex sd_current = {0}; u16 change_bit = 0; u16 link_mode; int ret; if (base->autoneg == AUTONEG_ENABLE) { netdev_err(dev, "Autonegotiation is not supported!\n"); return -EINVAL; } if (base->duplex != DUPLEX_FULL) { netdev_err(dev, "Only full duplex mode is supported!\n"); return -EINVAL; } link_mode = crete_port_speed2linkmode(base->speed); if (link_mode >= CRETE_LINK_MODES_NUMBER || link_mode < CRETE_10GBASE) { netdev_err(dev, "speed parameter is illeagl.\n"); return -EINVAL; } ret = crete_get_port_speed_duplex(cdev, &sd_current); if (ret) { netdev_err(dev, "Failed to get current speed and duplex mode!\n"); return ret; } if (sd_current.speed == link_mode && sd_current.duplex == base->duplex) { netdev_info(dev, "Speed and duplex mode are already set to the requested values.\n"); return 0; } change_bit |= (sd_current.speed != link_mode) ? LINK_SPEED_OP_BIT : 0; change_bit |= (sd_current.duplex != base->duplex) ? DUPLEX_MODE_OP_BIT : 0; if (!netif_running(dev)) { netdev_err(dev, "Network interface is not running. Cannot change settings.\n"); return -EINVAL; } ret = crete_set_port_speed_duplex(cdev, link_mode, base->duplex, change_bit); if (ret) { netdev_err(dev, "Failed to set speed and duplex mode!\n"); return ret; } netdev_info(dev, "Speed set to %d Mbps, duplex mode set to full.\n", base->speed); return 0; } static int crete_vnic_set_link_ksettings(struct net_device *dev, const struct ethtool_link_ksettings *link_ksettings) { struct crete_vnic_priv *vnic_priv = netdev_priv(dev); return crete_ethtool_set_link_ksettings(vnic_priv, link_ksettings); } static void ptys2ethtool_adver_link(u32 eth_speed_cap, u8 tx_pause, u8 rx_pause, struct ethtool_link_ksettings *link_ksettings) { unsigned long speed_cap = eth_speed_cap; unsigned long *advertising_modes = link_ksettings->link_modes.advertising; int speed; for_each_set_bit(speed, &speed_cap, CRETE_LINK_MODES_CAP_BIT) bitmap_or(advertising_modes, advertising_modes, ptys2ethtool_table[speed].advertised, __ETHTOOL_LINK_MODE_MASK_NBITS); if (rx_pause) ethtool_link_ksettings_add_link_mode(link_ksettings, advertising, Pause); if (tx_pause ^ rx_pause) ethtool_link_ksettings_add_link_mode(link_ksettings, advertising, Asym_Pause); } static void ptys2ethtool_supported_link(u32 eth_speed_cap, struct ethtool_link_ksettings *link_ksettings) { unsigned long speed_cap = eth_speed_cap; unsigned long *supported_modes = link_ksettings->link_modes.supported; int speed; for_each_set_bit(speed, &speed_cap, CRETE_LINK_MODES_CAP_BIT) bitmap_or(supported_modes, supported_modes, ptys2ethtool_table[speed].supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ethtool_link_ksettings_add_link_mode(link_ksettings, supported, Pause); } static int get_fec_supported_advertised(struct crete_core_dev *cdev, struct ethtool_link_ksettings *link_ksettings) { u16 active_fec; u16 eth_fec_cap; u8 fec; int err; unsigned long fec_cap; err = crete_get_fec_mode(cdev, &active_fec, ð_fec_cap); if (err) return err; fec_cap = eth_fec_cap; for_each_set_bit(fec, &fec_cap, ARRAY_SIZE(crete_fec_2_ethtool_bit)) __set_bit(crete_fec_2_ethtool_bit[fec], link_ksettings->link_modes.supported); if (active_fec <= CRETE_FEC_LLRS) __set_bit(crete_fec_2_ethtool_bit[active_fec], link_ksettings->link_modes.advertising); return 0; } static int crete_vnic_get_link_ksettings(struct net_device *dev, struct ethtool_link_ksettings *link_ksettings) { struct crete_speed_duplex sd = {0}; struct crete_vnic_priv *vnic_priv = netdev_priv(dev); struct crete_core_dev *cdev = vnic_priv->coredev; u8 rx_pause = 0; u8 tx_pause = 0; int ret = 0; link_ksettings->base.speed = SPEED_UNKNOWN; link_ksettings->base.duplex = DUPLEX_UNKNOWN; ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); crete_get_pfc_enable(cdev, &rx_pause, &tx_pause); ret = crete_get_port_speed_duplex(cdev, &sd); if (ret) { netdev_err(dev, "Failed to get speed and duplex mode!\n"); ret = 0; } if (sd.speed >= CRETE_10GBASE && sd.speed < CRETE_LINK_MODES_NUMBER) link_ksettings->base.speed = crete_link_speed[sd.speed]; if (sd.duplex >= CRETE_HALF_DUPLEX && sd.duplex < CRETE_DUPLEX_MAX) link_ksettings->base.duplex = crete_link_duplex[sd.duplex]; netdev_info(dev, "Speed Get speed[%d] speed_cap[0x%x].\n", sd.speed, sd.speed_cap); ptys2ethtool_supported_link(sd.speed_cap, link_ksettings); ptys2ethtool_adver_link(sd.speed_cap, tx_pause, rx_pause, link_ksettings); link_ksettings->base.port = PORT_FIBRE; ret = get_fec_supported_advertised(cdev, link_ksettings); if (ret) { netdev_err(dev, "%s: FEC caps query failed: %d\n", __func__, ret); ret = 0; /* don't fail caps query because of FEC error */ } ethtool_link_ksettings_add_link_mode(link_ksettings, supported, FIBRE); ethtool_link_ksettings_add_link_mode(link_ksettings, advertising, FIBRE); return ret; } static int crete_vnic_set_fecparam(struct net_device *dev, struct ethtool_fecparam *fecparam) { struct crete_vnic_priv *vnic_priv = netdev_priv(dev); struct crete_core_dev *cdev = vnic_priv->coredev; unsigned long fec_bitmap; u16 fec_policy = 0; bitmap_from_arr32(&fec_bitmap, &fecparam->fec, sizeof(fecparam->fec) * BITS_PER_BYTE); if (bitmap_weight(&fec_bitmap, ETHTOOL_FEC_LLRS_BIT + 1) > 1) return -EOPNOTSUPP; switch (fecparam->fec) { case ETHTOOL_FEC_OFF: fec_policy = CRETE_FEC_OFF; break; case ETHTOOL_FEC_RS: fec_policy = CRETE_FEC_RS; break; case ETHTOOL_FEC_BASER: fec_policy = CRETE_FEC_BASER; break; default: netdev_err(dev, "Unsupported FEC mode: %d\n", fecparam->fec); return -EINVAL; } return crete_set_fec_mode(cdev, fec_policy); } static u32 crete_fec_cap_to_ethtool_fec(u16 fec_cap) { u32 ethtool_fec = 0; if (fec_cap & BIT(CRETE_FEC_OFF)) ethtool_fec |= ETHTOOL_FEC_OFF; if (fec_cap & BIT(CRETE_FEC_AUTO)) ethtool_fec |= ETHTOOL_FEC_AUTO; if (fec_cap & BIT(CRETE_FEC_RS)) ethtool_fec |= ETHTOOL_FEC_RS; if (fec_cap & BIT(CRETE_FEC_BASER)) ethtool_fec |= ETHTOOL_FEC_BASER; if (fec_cap & BIT(CRETE_FEC_LLRS)) ethtool_fec |= ETHTOOL_FEC_LLRS; return ethtool_fec; } static int crete_vnic_get_fecparam(struct net_device *dev, struct ethtool_fecparam *fecparam) { struct crete_vnic_priv *vnic_priv = netdev_priv(dev); struct crete_core_dev *cdev = vnic_priv->coredev; u16 fec_active; u16 fec_cap; int err; err = crete_get_fec_mode(cdev, &fec_active, &fec_cap); if (err) return err; fecparam->active_fec = crete_fec_2_ethtool[fec_active]; fecparam->fec = crete_fec_cap_to_ethtool_fec(fec_cap); return 0; } static const struct ethtool_ops crete_vnic_ethtool_ops = { .get_drvinfo = crete_vnic_get_drvinfo, .get_link = crete_vnic_get_link, .get_ringparam = crete_vnic_get_ringparam, .get_strings = crete_vnic_get_strings, .get_sset_count = crete_vnic_get_sset_count, .get_ethtool_stats = crete_vnic_get_ethtool_stats, .get_channels = crete_vnic_get_channels, .set_channels = crete_vnic_set_channels, .flash_device = crete_vnic_flash_device, .get_link_ksettings = crete_vnic_get_link_ksettings, .set_link_ksettings = crete_vnic_set_link_ksettings, .get_fecparam = crete_vnic_get_fecparam, .set_fecparam = crete_vnic_set_fecparam, }; void crete_set_ethtool_ops(struct net_device *netdev) { netdev->ethtool_ops = &crete_vnic_ethtool_ops; }