From 4f09bc55ac3d801e4bf16260548824b1ebab1b98 Mon Sep 17 00:00:00 2001 From: Alvin Zhang Date: Wed, 15 Apr 2020 16:48:02 +0800 Subject: [PATCH] net/igc: implement device base operations Bellow ops are implemented: dev_configure dev_start dev_stop dev_close dev_reset dev_set_link_up dev_set_link_down link_update fw_version_get dev_led_on dev_led_off Signed-off-by: Alvin Zhang Reviewed-by: Ferruh Yigit --- doc/guides/nics/features/igc.ini | 5 + drivers/net/igc/igc_ethdev.c | 644 ++++++++++++++++++++++++++++++- drivers/net/igc/igc_ethdev.h | 35 ++ 3 files changed, 674 insertions(+), 10 deletions(-) diff --git a/doc/guides/nics/features/igc.ini b/doc/guides/nics/features/igc.ini index ad75cc4c13..0fbdf7c390 100644 --- a/doc/guides/nics/features/igc.ini +++ b/doc/guides/nics/features/igc.ini @@ -3,6 +3,11 @@ ; Refer to default.ini for the full list of available PMD features. ; [Features] +Speed capabilities = Y +Link status = Y +Link status event = Y +FW version = Y +LED = Y Linux UIO = Y Linux VFIO = Y x86-64 = Y diff --git a/drivers/net/igc/igc_ethdev.c b/drivers/net/igc/igc_ethdev.c index f23543edd2..423c817b8d 100644 --- a/drivers/net/igc/igc_ethdev.c +++ b/drivers/net/igc/igc_ethdev.c @@ -3,6 +3,7 @@ */ #include +#include #include #include @@ -15,7 +16,34 @@ #define IGC_INTEL_VENDOR_ID 0x8086 +/* + * The overhead from MTU to max frame size. + * Considering VLAN so tag needs to be counted. + */ +#define IGC_ETH_OVERHEAD (RTE_ETHER_HDR_LEN + \ + RTE_ETHER_CRC_LEN + VLAN_TAG_SIZE) + #define IGC_FC_PAUSE_TIME 0x0680 +#define IGC_LINK_UPDATE_CHECK_TIMEOUT 90 /* 9s */ +#define IGC_LINK_UPDATE_CHECK_INTERVAL 100 /* ms */ + +#define IGC_MISC_VEC_ID RTE_INTR_VEC_ZERO_OFFSET +#define IGC_RX_VEC_START RTE_INTR_VEC_RXTX_OFFSET +#define IGC_MSIX_OTHER_INTR_VEC 0 /* MSI-X other interrupt vector */ +#define IGC_FLAG_NEED_LINK_UPDATE (1u << 0) /* need update link */ + +#define IGC_DEFAULT_RX_FREE_THRESH 32 + +#define IGC_DEFAULT_RX_PTHRESH 8 +#define IGC_DEFAULT_RX_HTHRESH 8 +#define IGC_DEFAULT_RX_WTHRESH 4 + +#define IGC_DEFAULT_TX_PTHRESH 8 +#define IGC_DEFAULT_TX_HTHRESH 1 +#define IGC_DEFAULT_TX_WTHRESH 16 + +/* MSI-X other interrupt vector */ +#define IGC_MSIX_OTHER_INTR_VEC 0 static const struct rte_pci_id pci_id_igc_map[] = { { RTE_PCI_DEVICE(IGC_INTEL_VENDOR_ID, IGC_DEV_ID_I225_LM) }, @@ -29,12 +57,20 @@ static int eth_igc_configure(struct rte_eth_dev *dev); static int eth_igc_link_update(struct rte_eth_dev *dev, int wait_to_complete); static void eth_igc_stop(struct rte_eth_dev *dev); static int eth_igc_start(struct rte_eth_dev *dev); +static int eth_igc_set_link_up(struct rte_eth_dev *dev); +static int eth_igc_set_link_down(struct rte_eth_dev *dev); static void eth_igc_close(struct rte_eth_dev *dev); static int eth_igc_reset(struct rte_eth_dev *dev); static int eth_igc_promiscuous_enable(struct rte_eth_dev *dev); static int eth_igc_promiscuous_disable(struct rte_eth_dev *dev); +static int eth_igc_fw_version_get(struct rte_eth_dev *dev, + char *fw_version, size_t fw_size); static int eth_igc_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info); +static int eth_igc_led_on(struct rte_eth_dev *dev); +static int eth_igc_led_off(struct rte_eth_dev *dev); +static void eth_igc_tx_queue_release(void *txq); +static void eth_igc_rx_queue_release(void *rxq); static int eth_igc_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, uint16_t nb_rx_desc, unsigned int socket_id, @@ -52,35 +88,394 @@ static const struct eth_dev_ops eth_igc_ops = { .dev_start = eth_igc_start, .dev_close = eth_igc_close, .dev_reset = eth_igc_reset, + .dev_set_link_up = eth_igc_set_link_up, + .dev_set_link_down = eth_igc_set_link_down, .promiscuous_enable = eth_igc_promiscuous_enable, .promiscuous_disable = eth_igc_promiscuous_disable, + + .fw_version_get = eth_igc_fw_version_get, .dev_infos_get = eth_igc_infos_get, + .dev_led_on = eth_igc_led_on, + .dev_led_off = eth_igc_led_off, + .rx_queue_setup = eth_igc_rx_queue_setup, + .rx_queue_release = eth_igc_rx_queue_release, .tx_queue_setup = eth_igc_tx_queue_setup, + .tx_queue_release = eth_igc_tx_queue_release, }; +/* + * multiple queue mode checking + */ +static int +igc_check_mq_mode(struct rte_eth_dev *dev) +{ + enum rte_eth_rx_mq_mode rx_mq_mode = dev->data->dev_conf.rxmode.mq_mode; + enum rte_eth_tx_mq_mode tx_mq_mode = dev->data->dev_conf.txmode.mq_mode; + + if (RTE_ETH_DEV_SRIOV(dev).active != 0) { + PMD_INIT_LOG(ERR, "SRIOV is not supported."); + return -EINVAL; + } + + if (rx_mq_mode != ETH_MQ_RX_NONE && + rx_mq_mode != ETH_MQ_RX_RSS) { + /* RSS together with VMDq not supported*/ + PMD_INIT_LOG(ERR, "RX mode %d is not supported.", + rx_mq_mode); + return -EINVAL; + } + + /* To no break software that set invalid mode, only display + * warning if invalid mode is used. + */ + if (tx_mq_mode != ETH_MQ_TX_NONE) + PMD_INIT_LOG(WARNING, + "TX mode %d is not supported. Due to meaningless in this driver, just ignore", + tx_mq_mode); + + return 0; +} + static int eth_igc_configure(struct rte_eth_dev *dev) { + struct igc_interrupt *intr = IGC_DEV_PRIVATE_INTR(dev); + int ret; + PMD_INIT_FUNC_TRACE(); - RTE_SET_USED(dev); + + ret = igc_check_mq_mode(dev); + if (ret != 0) + return ret; + + intr->flags |= IGC_FLAG_NEED_LINK_UPDATE; return 0; } static int -eth_igc_link_update(struct rte_eth_dev *dev, int wait_to_complete) +eth_igc_set_link_up(struct rte_eth_dev *dev) { - PMD_INIT_FUNC_TRACE(); - RTE_SET_USED(dev); - RTE_SET_USED(wait_to_complete); + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + + if (hw->phy.media_type == igc_media_type_copper) + igc_power_up_phy(hw); + else + igc_power_up_fiber_serdes_link(hw); + return 0; +} + +static int +eth_igc_set_link_down(struct rte_eth_dev *dev) +{ + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + + if (hw->phy.media_type == igc_media_type_copper) + igc_power_down_phy(hw); + else + igc_shutdown_fiber_serdes_link(hw); return 0; } +/* + * disable other interrupt + */ +static void +igc_intr_other_disable(struct rte_eth_dev *dev) +{ + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + + if (rte_intr_allow_others(intr_handle) && + dev->data->dev_conf.intr_conf.lsc) { + IGC_WRITE_REG(hw, IGC_EIMC, 1u << IGC_MSIX_OTHER_INTR_VEC); + } + + IGC_WRITE_REG(hw, IGC_IMC, ~0); + IGC_WRITE_FLUSH(hw); +} + +/* + * enable other interrupt + */ +static inline void +igc_intr_other_enable(struct rte_eth_dev *dev) +{ + struct igc_interrupt *intr = IGC_DEV_PRIVATE_INTR(dev); + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + + if (rte_intr_allow_others(intr_handle) && + dev->data->dev_conf.intr_conf.lsc) { + IGC_WRITE_REG(hw, IGC_EIMS, 1u << IGC_MSIX_OTHER_INTR_VEC); + } + + IGC_WRITE_REG(hw, IGC_IMS, intr->mask); + IGC_WRITE_FLUSH(hw); +} + +/* + * It reads ICR and gets interrupt causes, check it and set a bit flag + * to update link status. + */ +static void +eth_igc_interrupt_get_status(struct rte_eth_dev *dev) +{ + uint32_t icr; + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + struct igc_interrupt *intr = IGC_DEV_PRIVATE_INTR(dev); + + /* read-on-clear nic registers here */ + icr = IGC_READ_REG(hw, IGC_ICR); + + intr->flags = 0; + if (icr & IGC_ICR_LSC) + intr->flags |= IGC_FLAG_NEED_LINK_UPDATE; +} + +/* return 0 means link status changed, -1 means not changed */ +static int +eth_igc_link_update(struct rte_eth_dev *dev, int wait_to_complete) +{ + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + struct rte_eth_link link; + int link_check, count; + + link_check = 0; + hw->mac.get_link_status = 1; + + /* possible wait-to-complete in up to 9 seconds */ + for (count = 0; count < IGC_LINK_UPDATE_CHECK_TIMEOUT; count++) { + /* Read the real link status */ + switch (hw->phy.media_type) { + case igc_media_type_copper: + /* Do the work to read phy */ + igc_check_for_link(hw); + link_check = !hw->mac.get_link_status; + break; + + case igc_media_type_fiber: + igc_check_for_link(hw); + link_check = (IGC_READ_REG(hw, IGC_STATUS) & + IGC_STATUS_LU); + break; + + case igc_media_type_internal_serdes: + igc_check_for_link(hw); + link_check = hw->mac.serdes_has_link; + break; + + default: + break; + } + if (link_check || wait_to_complete == 0) + break; + rte_delay_ms(IGC_LINK_UPDATE_CHECK_INTERVAL); + } + memset(&link, 0, sizeof(link)); + + /* Now we check if a transition has happened */ + if (link_check) { + uint16_t duplex, speed; + hw->mac.ops.get_link_up_info(hw, &speed, &duplex); + link.link_duplex = (duplex == FULL_DUPLEX) ? + ETH_LINK_FULL_DUPLEX : + ETH_LINK_HALF_DUPLEX; + link.link_speed = speed; + link.link_status = ETH_LINK_UP; + link.link_autoneg = !(dev->data->dev_conf.link_speeds & + ETH_LINK_SPEED_FIXED); + + if (speed == SPEED_2500) { + uint32_t tipg = IGC_READ_REG(hw, IGC_TIPG); + if ((tipg & IGC_TIPG_IPGT_MASK) != 0x0b) { + tipg &= ~IGC_TIPG_IPGT_MASK; + tipg |= 0x0b; + IGC_WRITE_REG(hw, IGC_TIPG, tipg); + } + } + } else { + link.link_speed = 0; + link.link_duplex = ETH_LINK_HALF_DUPLEX; + link.link_status = ETH_LINK_DOWN; + link.link_autoneg = ETH_LINK_FIXED; + } + + return rte_eth_linkstatus_set(dev, &link); +} + +/* + * It executes link_update after knowing an interrupt is present. + */ +static void +eth_igc_interrupt_action(struct rte_eth_dev *dev) +{ + struct igc_interrupt *intr = IGC_DEV_PRIVATE_INTR(dev); + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_eth_link link; + int ret; + + if (intr->flags & IGC_FLAG_NEED_LINK_UPDATE) { + intr->flags &= ~IGC_FLAG_NEED_LINK_UPDATE; + + /* set get_link_status to check register later */ + ret = eth_igc_link_update(dev, 0); + + /* check if link has changed */ + if (ret < 0) + return; + + rte_eth_linkstatus_get(dev, &link); + if (link.link_status) + PMD_DRV_LOG(INFO, + " Port %d: Link Up - speed %u Mbps - %s", + dev->data->port_id, + (unsigned int)link.link_speed, + link.link_duplex == ETH_LINK_FULL_DUPLEX ? + "full-duplex" : "half-duplex"); + else + PMD_DRV_LOG(INFO, " Port %d: Link Down", + dev->data->port_id); + + PMD_DRV_LOG(DEBUG, "PCI Address: " PCI_PRI_FMT, + pci_dev->addr.domain, + pci_dev->addr.bus, + pci_dev->addr.devid, + pci_dev->addr.function); + _rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_LSC, + NULL); + } +} + +/* + * Interrupt handler which shall be registered at first. + * + * @handle + * Pointer to interrupt handle. + * @param + * The address of parameter (struct rte_eth_dev *) registered before. + */ +static void +eth_igc_interrupt_handler(void *param) +{ + struct rte_eth_dev *dev = (struct rte_eth_dev *)param; + + eth_igc_interrupt_get_status(dev); + eth_igc_interrupt_action(dev); +} + +/* + * This routine disables all traffic on the adapter by issuing a + * global reset on the MAC. + */ static void eth_igc_stop(struct rte_eth_dev *dev) { - PMD_INIT_FUNC_TRACE(); - RTE_SET_USED(dev); + struct igc_adapter *adapter = IGC_DEV_PRIVATE(dev); + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + struct rte_eth_link link; + + adapter->stopped = 1; + + /* disable all MSI-X interrupts */ + IGC_WRITE_REG(hw, IGC_EIMC, 0x1f); + IGC_WRITE_FLUSH(hw); + + /* clear all MSI-X interrupts */ + IGC_WRITE_REG(hw, IGC_EICR, 0x1f); + + igc_intr_other_disable(dev); + + /* disable intr eventfd mapping */ + rte_intr_disable(intr_handle); + + igc_reset_hw(hw); + + /* disable all wake up */ + IGC_WRITE_REG(hw, IGC_WUC, 0); + + /* Set bit for Go Link disconnect */ + igc_read_reg_check_set_bits(hw, IGC_82580_PHY_POWER_MGMT, + IGC_82580_PM_GO_LINKD); + + /* Power down the phy. Needed to make the link go Down */ + eth_igc_set_link_down(dev); + + /* clear the recorded link status */ + memset(&link, 0, sizeof(link)); + rte_eth_linkstatus_set(dev, &link); + + if (!rte_intr_allow_others(intr_handle)) + /* resume to the default handler */ + rte_intr_callback_register(intr_handle, + eth_igc_interrupt_handler, + (void *)dev); + + /* Clean datapath event and queue/vec mapping */ + rte_intr_efd_disable(intr_handle); +} + +/* Sets up the hardware to generate MSI-X interrupts properly + * @hw + * board private structure + */ +static void +igc_configure_msix_intr(struct rte_eth_dev *dev) +{ + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + + uint32_t intr_mask; + + /* won't configure msix register if no mapping is done + * between intr vector and event fd + */ + if (!rte_intr_dp_is_en(intr_handle) || + !dev->data->dev_conf.intr_conf.lsc) + return; + + /* turn on MSI-X capability first */ + IGC_WRITE_REG(hw, IGC_GPIE, IGC_GPIE_MSIX_MODE | + IGC_GPIE_PBA | IGC_GPIE_EIAME | + IGC_GPIE_NSICR); + + intr_mask = (1u << IGC_MSIX_OTHER_INTR_VEC); + + /* enable msix auto-clear */ + igc_read_reg_check_set_bits(hw, IGC_EIAC, intr_mask); + + /* set other cause interrupt vector */ + igc_read_reg_check_set_bits(hw, IGC_IVAR_MISC, + (uint32_t)(IGC_MSIX_OTHER_INTR_VEC | IGC_IVAR_VALID) << 8); + + /* enable auto-mask */ + igc_read_reg_check_set_bits(hw, IGC_EIAM, intr_mask); + + IGC_WRITE_FLUSH(hw); +} + +/** + * It enables the interrupt mask and then enable the interrupt. + * + * @dev + * Pointer to struct rte_eth_dev. + * @on + * Enable or Disable + */ +static void +igc_lsc_interrupt_setup(struct rte_eth_dev *dev, uint8_t on) +{ + struct igc_interrupt *intr = IGC_DEV_PRIVATE_INTR(dev); + + if (on) + intr->mask |= IGC_ICR_LSC; + else + intr->mask &= ~IGC_ICR_LSC; } /* @@ -170,9 +565,134 @@ igc_hardware_init(struct igc_hw *hw) static int eth_igc_start(struct rte_eth_dev *dev) { + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + struct igc_adapter *adapter = IGC_DEV_PRIVATE(dev); + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + uint32_t *speeds; + int num_speeds; + bool autoneg; + PMD_INIT_FUNC_TRACE(); - RTE_SET_USED(dev); + + /* disable all MSI-X interrupts */ + IGC_WRITE_REG(hw, IGC_EIMC, 0x1f); + IGC_WRITE_FLUSH(hw); + + /* clear all MSI-X interrupts */ + IGC_WRITE_REG(hw, IGC_EICR, 0x1f); + + /* disable uio/vfio intr/eventfd mapping */ + if (!adapter->stopped) + rte_intr_disable(intr_handle); + + /* Power up the phy. Needed to make the link go Up */ + eth_igc_set_link_up(dev); + + /* Put the address into the Receive Address Array */ + igc_rar_set(hw, hw->mac.addr, 0); + + /* Initialize the hardware */ + if (igc_hardware_init(hw)) { + PMD_DRV_LOG(ERR, "Unable to initialize the hardware"); + return -EIO; + } + adapter->stopped = 0; + + /* confiugre msix for rx interrupt */ + igc_configure_msix_intr(dev); + + igc_clear_hw_cntrs_base_generic(hw); + + /* Setup link speed and duplex */ + speeds = &dev->data->dev_conf.link_speeds; + if (*speeds == ETH_LINK_SPEED_AUTONEG) { + hw->phy.autoneg_advertised = IGC_ALL_SPEED_DUPLEX_2500; + hw->mac.autoneg = 1; + } else { + num_speeds = 0; + autoneg = (*speeds & ETH_LINK_SPEED_FIXED) == 0; + + /* Reset */ + hw->phy.autoneg_advertised = 0; + + if (*speeds & ~(ETH_LINK_SPEED_10M_HD | ETH_LINK_SPEED_10M | + ETH_LINK_SPEED_100M_HD | ETH_LINK_SPEED_100M | + ETH_LINK_SPEED_1G | ETH_LINK_SPEED_2_5G | + ETH_LINK_SPEED_FIXED)) { + num_speeds = -1; + goto error_invalid_config; + } + if (*speeds & ETH_LINK_SPEED_10M_HD) { + hw->phy.autoneg_advertised |= ADVERTISE_10_HALF; + num_speeds++; + } + if (*speeds & ETH_LINK_SPEED_10M) { + hw->phy.autoneg_advertised |= ADVERTISE_10_FULL; + num_speeds++; + } + if (*speeds & ETH_LINK_SPEED_100M_HD) { + hw->phy.autoneg_advertised |= ADVERTISE_100_HALF; + num_speeds++; + } + if (*speeds & ETH_LINK_SPEED_100M) { + hw->phy.autoneg_advertised |= ADVERTISE_100_FULL; + num_speeds++; + } + if (*speeds & ETH_LINK_SPEED_1G) { + hw->phy.autoneg_advertised |= ADVERTISE_1000_FULL; + num_speeds++; + } + if (*speeds & ETH_LINK_SPEED_2_5G) { + hw->phy.autoneg_advertised |= ADVERTISE_2500_FULL; + num_speeds++; + } + if (num_speeds == 0 || (!autoneg && num_speeds > 1)) + goto error_invalid_config; + + /* Set/reset the mac.autoneg based on the link speed, + * fixed or not + */ + if (!autoneg) { + hw->mac.autoneg = 0; + hw->mac.forced_speed_duplex = + hw->phy.autoneg_advertised; + } else { + hw->mac.autoneg = 1; + } + } + + igc_setup_link(hw); + + if (rte_intr_allow_others(intr_handle)) { + /* check if lsc interrupt is enabled */ + if (dev->data->dev_conf.intr_conf.lsc) + igc_lsc_interrupt_setup(dev, 1); + else + igc_lsc_interrupt_setup(dev, 0); + } else { + rte_intr_callback_unregister(intr_handle, + eth_igc_interrupt_handler, + (void *)dev); + if (dev->data->dev_conf.intr_conf.lsc) + PMD_DRV_LOG(INFO, + "LSC won't enable because of no intr multiplex"); + } + + /* enable uio/vfio intr/eventfd mapping */ + rte_intr_enable(intr_handle); + + /* resume enabled intr since hw reset */ + igc_intr_other_enable(dev); + + eth_igc_link_update(dev, 0); + return 0; + +error_invalid_config: + PMD_DRV_LOG(ERR, "Invalid advertised speeds (%u) for port %u", + dev->data->dev_conf.link_speeds, dev->data->port_id); + return -EINVAL; } static int @@ -232,10 +752,28 @@ igc_reset_swfw_lock(struct igc_hw *hw) static void eth_igc_close(struct rte_eth_dev *dev) { + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + struct igc_adapter *adapter = IGC_DEV_PRIVATE(dev); + int retry = 0; PMD_INIT_FUNC_TRACE(); + if (!adapter->stopped) + eth_igc_stop(dev); + + igc_intr_other_disable(dev); + do { + int ret = rte_intr_callback_unregister(intr_handle, + eth_igc_interrupt_handler, dev); + if (ret >= 0 || ret == -ENOENT || ret == -EINVAL) + break; + + PMD_DRV_LOG(ERR, "intr callback unregister failed: %d", ret); + DELAY(200 * 1000); /* delay 200ms */ + } while (retry++ < 5); + igc_phy_hw_reset(hw); igc_hw_control_release(hw); @@ -258,6 +796,7 @@ static int eth_igc_dev_init(struct rte_eth_dev *dev) { struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct igc_adapter *igc = IGC_DEV_PRIVATE(dev); struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); int error = 0; @@ -364,6 +903,7 @@ eth_igc_dev_init(struct rte_eth_dev *dev) dev->data->dev_flags |= RTE_ETH_DEV_CLOSE_REMOVE; hw->mac.get_link_status = 1; + igc->stopped = 0; /* Indicate SOL/IDER usage */ if (igc_check_reset_block(hw) < 0) @@ -374,6 +914,15 @@ eth_igc_dev_init(struct rte_eth_dev *dev) dev->data->port_id, pci_dev->id.vendor_id, pci_dev->id.device_id); + rte_intr_callback_register(&pci_dev->intr_handle, + eth_igc_interrupt_handler, (void *)dev); + + /* enable uio/vfio intr/eventfd mapping */ + rte_intr_enable(&pci_dev->intr_handle); + + /* enable support intr */ + igc_intr_other_enable(dev); + return 0; err_late: @@ -423,16 +972,81 @@ eth_igc_promiscuous_disable(struct rte_eth_dev *dev) return 0; } +static int +eth_igc_fw_version_get(struct rte_eth_dev *dev, char *fw_version, + size_t fw_size) +{ + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + struct igc_fw_version fw; + int ret; + + igc_get_fw_version(hw, &fw); + + /* if option rom is valid, display its version too */ + if (fw.or_valid) { + ret = snprintf(fw_version, fw_size, + "%d.%d, 0x%08x, %d.%d.%d", + fw.eep_major, fw.eep_minor, fw.etrack_id, + fw.or_major, fw.or_build, fw.or_patch); + /* no option rom */ + } else { + if (fw.etrack_id != 0X0000) { + ret = snprintf(fw_version, fw_size, + "%d.%d, 0x%08x", + fw.eep_major, fw.eep_minor, + fw.etrack_id); + } else { + ret = snprintf(fw_version, fw_size, + "%d.%d.%d", + fw.eep_major, fw.eep_minor, + fw.eep_build); + } + } + + ret += 1; /* add the size of '\0' */ + if (fw_size < (u32)ret) + return ret; + else + return 0; +} + static int eth_igc_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) { - PMD_INIT_FUNC_TRACE(); - RTE_SET_USED(dev); + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + + dev_info->min_rx_bufsize = 256; /* See BSIZE field of RCTL register. */ + dev_info->max_rx_pktlen = MAX_RX_JUMBO_FRAME_SIZE; + dev_info->max_mac_addrs = hw->mac.rar_entry_count; dev_info->max_rx_queues = IGC_QUEUE_PAIRS_NUM; dev_info->max_tx_queues = IGC_QUEUE_PAIRS_NUM; + dev_info->max_vmdq_pools = 0; + + dev_info->speed_capa = ETH_LINK_SPEED_10M_HD | ETH_LINK_SPEED_10M | + ETH_LINK_SPEED_100M_HD | ETH_LINK_SPEED_100M | + ETH_LINK_SPEED_1G | ETH_LINK_SPEED_2_5G; + + dev_info->max_mtu = dev_info->max_rx_pktlen - IGC_ETH_OVERHEAD; + dev_info->min_mtu = RTE_ETHER_MIN_MTU; return 0; } +static int +eth_igc_led_on(struct rte_eth_dev *dev) +{ + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + + return igc_led_on(hw) == IGC_SUCCESS ? 0 : -ENOTSUP; +} + +static int +eth_igc_led_off(struct rte_eth_dev *dev) +{ + struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev); + + return igc_led_off(hw) == IGC_SUCCESS ? 0 : -ENOTSUP; +} + static int eth_igc_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, uint16_t nb_rx_desc, unsigned int socket_id, @@ -463,6 +1077,16 @@ eth_igc_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx, return 0; } +static void eth_igc_tx_queue_release(void *txq) +{ + RTE_SET_USED(txq); +} + +static void eth_igc_rx_queue_release(void *rxq) +{ + RTE_SET_USED(rxq); +} + static int eth_igc_pci_probe(struct rte_pci_driver *pci_drv __rte_unused, struct rte_pci_device *pci_dev) diff --git a/drivers/net/igc/igc_ethdev.h b/drivers/net/igc/igc_ethdev.h index 4cff0e3e42..c1e32177d0 100644 --- a/drivers/net/igc/igc_ethdev.h +++ b/drivers/net/igc/igc_ethdev.h @@ -18,11 +18,19 @@ extern "C" { #define IGC_QUEUE_PAIRS_NUM 4 +/* structure for interrupt relative data */ +struct igc_interrupt { + uint32_t flags; + uint32_t mask; +}; + /* * Structure to store private data for each driver instance (for each port). */ struct igc_adapter { struct igc_hw hw; + struct igc_interrupt intr; + bool stopped; }; #define IGC_DEV_PRIVATE(_dev) ((_dev)->data->dev_private) @@ -30,6 +38,33 @@ struct igc_adapter { #define IGC_DEV_PRIVATE_HW(_dev) \ (&((struct igc_adapter *)(_dev)->data->dev_private)->hw) +#define IGC_DEV_PRIVATE_INTR(_dev) \ + (&((struct igc_adapter *)(_dev)->data->dev_private)->intr) + +static inline void +igc_read_reg_check_set_bits(struct igc_hw *hw, uint32_t reg, uint32_t bits) +{ + uint32_t reg_val = IGC_READ_REG(hw, reg); + + bits |= reg_val; + if (bits == reg_val) + return; /* no need to write back */ + + IGC_WRITE_REG(hw, reg, bits); +} + +static inline void +igc_read_reg_check_clear_bits(struct igc_hw *hw, uint32_t reg, uint32_t bits) +{ + uint32_t reg_val = IGC_READ_REG(hw, reg); + + bits = reg_val & ~bits; + if (bits == reg_val) + return; /* no need to write back */ + + IGC_WRITE_REG(hw, reg, bits); +} + #ifdef __cplusplus } #endif -- 2.20.1