+/*
+ * 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.
+ */