net/igc: support device initialization
[dpdk.git] / drivers / net / igc / igc_ethdev.c
index c590ab2..f23543e 100644 (file)
 #include "igc_ethdev.h"
 
 #define IGC_INTEL_VENDOR_ID            0x8086
-#define IGC_DEV_ID_I225_LM             0x15F2
-#define IGC_DEV_ID_I225_V              0x15F3
-#define IGC_DEV_ID_I225_K              0x3100
-#define IGC_DEV_ID_I225_I              0x15F8
-#define IGC_DEV_ID_I220_V              0x15F7
+
+#define IGC_FC_PAUSE_TIME              0x0680
 
 static const struct rte_pci_id pci_id_igc_map[] = {
        { RTE_PCI_DEVICE(IGC_INTEL_VENDOR_ID, IGC_DEV_ID_I225_LM) },
@@ -86,6 +83,90 @@ eth_igc_stop(struct rte_eth_dev *dev)
        RTE_SET_USED(dev);
 }
 
+/*
+ *  Get hardware rx-buffer size.
+ */
+static inline int
+igc_get_rx_buffer_size(struct igc_hw *hw)
+{
+       return (IGC_READ_REG(hw, IGC_RXPBS) & 0x3f) << 10;
+}
+
+/*
+ * igc_hw_control_acquire sets CTRL_EXT:DRV_LOAD bit.
+ * For ASF and Pass Through versions of f/w this means
+ * that the driver is loaded.
+ */
+static void
+igc_hw_control_acquire(struct igc_hw *hw)
+{
+       uint32_t ctrl_ext;
+
+       /* Let firmware know the driver has taken over */
+       ctrl_ext = IGC_READ_REG(hw, IGC_CTRL_EXT);
+       IGC_WRITE_REG(hw, IGC_CTRL_EXT, ctrl_ext | IGC_CTRL_EXT_DRV_LOAD);
+}
+
+/*
+ * igc_hw_control_release resets CTRL_EXT:DRV_LOAD bit.
+ * For ASF and Pass Through versions of f/w this means that the
+ * driver is no longer loaded.
+ */
+static void
+igc_hw_control_release(struct igc_hw *hw)
+{
+       uint32_t ctrl_ext;
+
+       /* Let firmware taken over control of h/w */
+       ctrl_ext = IGC_READ_REG(hw, IGC_CTRL_EXT);
+       IGC_WRITE_REG(hw, IGC_CTRL_EXT,
+                       ctrl_ext & ~IGC_CTRL_EXT_DRV_LOAD);
+}
+
+static int
+igc_hardware_init(struct igc_hw *hw)
+{
+       uint32_t rx_buf_size;
+       int diag;
+
+       /* Let the firmware know the OS is in control */
+       igc_hw_control_acquire(hw);
+
+       /* Issue a global reset */
+       igc_reset_hw(hw);
+
+       /* disable all wake up */
+       IGC_WRITE_REG(hw, IGC_WUC, 0);
+
+       /*
+        * Hardware flow control
+        * - High water mark should allow for at least two standard size (1518)
+        *   frames to be received after sending an XOFF.
+        * - Low water mark works best when it is very near the high water mark.
+        *   This allows the receiver to restart by sending XON when it has
+        *   drained a bit. Here we use an arbitrary value of 1500 which will
+        *   restart after one full frame is pulled from the buffer. There
+        *   could be several smaller frames in the buffer and if so they will
+        *   not trigger the XON until their total number reduces the buffer
+        *   by 1500.
+        */
+       rx_buf_size = igc_get_rx_buffer_size(hw);
+       hw->fc.high_water = rx_buf_size - (RTE_ETHER_MAX_LEN * 2);
+       hw->fc.low_water = hw->fc.high_water - 1500;
+       hw->fc.pause_time = IGC_FC_PAUSE_TIME;
+       hw->fc.send_xon = 1;
+       hw->fc.requested_mode = igc_fc_full;
+
+       diag = igc_init_hw(hw);
+       if (diag < 0)
+               return diag;
+
+       igc_get_phy_info(hw);
+       igc_check_for_link(hw);
+
+       return 0;
+}
+
 static int
 eth_igc_start(struct rte_eth_dev *dev)
 {
@@ -94,17 +175,91 @@ eth_igc_start(struct rte_eth_dev *dev)
        return 0;
 }
 
+static int
+igc_reset_swfw_lock(struct igc_hw *hw)
+{
+       int ret_val;
+
+       /*
+        * Do mac ops initialization manually here, since we will need
+        * some function pointers set by this call.
+        */
+       ret_val = igc_init_mac_params(hw);
+       if (ret_val)
+               return ret_val;
+
+       /*
+        * SMBI lock should not fail in this early stage. If this is the case,
+        * it is due to an improper exit of the application.
+        * So force the release of the faulty lock.
+        */
+       if (igc_get_hw_semaphore_generic(hw) < 0)
+               PMD_DRV_LOG(DEBUG, "SMBI lock released");
+
+       igc_put_hw_semaphore_generic(hw);
+
+       if (hw->mac.ops.acquire_swfw_sync != NULL) {
+               uint16_t mask;
+
+               /*
+                * Phy lock should not fail in this early stage.
+                * If this is the case, it is due to an improper exit of the
+                * application. So force the release of the faulty lock.
+                */
+               mask = IGC_SWFW_PHY0_SM;
+               if (hw->mac.ops.acquire_swfw_sync(hw, mask) < 0) {
+                       PMD_DRV_LOG(DEBUG, "SWFW phy%d lock released",
+                                   hw->bus.func);
+               }
+               hw->mac.ops.release_swfw_sync(hw, mask);
+
+               /*
+                * This one is more tricky since it is common to all ports; but
+                * swfw_sync retries last long enough (1s) to be almost sure
+                * that if lock can not be taken it is due to an improper lock
+                * of the semaphore.
+                */
+               mask = IGC_SWFW_EEP_SM;
+               if (hw->mac.ops.acquire_swfw_sync(hw, mask) < 0)
+                       PMD_DRV_LOG(DEBUG, "SWFW common locks released");
+
+               hw->mac.ops.release_swfw_sync(hw, mask);
+       }
+
+       return IGC_SUCCESS;
+}
+
 static void
 eth_igc_close(struct rte_eth_dev *dev)
 {
+       struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+
        PMD_INIT_FUNC_TRACE();
-        RTE_SET_USED(dev);
+
+       igc_phy_hw_reset(hw);
+       igc_hw_control_release(hw);
+
+       /* Reset any pending lock */
+       igc_reset_swfw_lock(hw);
+}
+
+static void
+igc_identify_hardware(struct rte_eth_dev *dev, struct rte_pci_device *pci_dev)
+{
+       struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+
+       hw->vendor_id = pci_dev->id.vendor_id;
+       hw->device_id = pci_dev->id.device_id;
+       hw->subsystem_vendor_id = pci_dev->id.subsystem_vendor_id;
+       hw->subsystem_device_id = pci_dev->id.subsystem_device_id;
 }
 
 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_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+       int error = 0;
 
        PMD_INIT_FUNC_TRACE();
        dev->dev_ops = &eth_igc_ops;
@@ -117,12 +272,90 @@ eth_igc_dev_init(struct rte_eth_dev *dev)
        if (rte_eal_process_type() != RTE_PROC_PRIMARY)
                return 0;
 
+       rte_eth_copy_pci_info(dev, pci_dev);
+
+       hw->back = pci_dev;
+       hw->hw_addr = (void *)pci_dev->mem_resource[0].addr;
+
+       igc_identify_hardware(dev, pci_dev);
+       if (igc_setup_init_funcs(hw, false) != IGC_SUCCESS) {
+               error = -EIO;
+               goto err_late;
+       }
+
+       igc_get_bus_info(hw);
+
+       /* Reset any pending lock */
+       if (igc_reset_swfw_lock(hw) != IGC_SUCCESS) {
+               error = -EIO;
+               goto err_late;
+       }
+
+       /* Finish initialization */
+       if (igc_setup_init_funcs(hw, true) != IGC_SUCCESS) {
+               error = -EIO;
+               goto err_late;
+       }
+
+       hw->mac.autoneg = 1;
+       hw->phy.autoneg_wait_to_complete = 0;
+       hw->phy.autoneg_advertised = IGC_ALL_SPEED_DUPLEX_2500;
+
+       /* Copper options */
+       if (hw->phy.media_type == igc_media_type_copper) {
+               hw->phy.mdix = 0; /* AUTO_ALL_MODES */
+               hw->phy.disable_polarity_correction = 0;
+               hw->phy.ms_type = igc_ms_hw_default;
+       }
+
+       /*
+        * Start from a known state, this is important in reading the nvm
+        * and mac from that.
+        */
+       igc_reset_hw(hw);
+
+       /* Make sure we have a good EEPROM before we read from it */
+       if (igc_validate_nvm_checksum(hw) < 0) {
+               /*
+                * Some PCI-E parts fail the first check due to
+                * the link being in sleep state, call it again,
+                * if it fails a second time its a real issue.
+                */
+               if (igc_validate_nvm_checksum(hw) < 0) {
+                       PMD_INIT_LOG(ERR, "EEPROM checksum invalid");
+                       error = -EIO;
+                       goto err_late;
+               }
+       }
+
+       /* Read the permanent MAC address out of the EEPROM */
+       if (igc_read_mac_addr(hw) != 0) {
+               PMD_INIT_LOG(ERR, "EEPROM error while reading MAC address");
+               error = -EIO;
+               goto err_late;
+       }
+
+       /* Allocate memory for storing MAC addresses */
        dev->data->mac_addrs = rte_zmalloc("igc",
-               RTE_ETHER_ADDR_LEN, 0);
+               RTE_ETHER_ADDR_LEN * hw->mac.rar_entry_count, 0);
        if (dev->data->mac_addrs == NULL) {
                PMD_INIT_LOG(ERR, "Failed to allocate %d bytes for storing MAC",
-                               RTE_ETHER_ADDR_LEN);
-               return -ENOMEM;
+                               RTE_ETHER_ADDR_LEN * hw->mac.rar_entry_count);
+               error = -ENOMEM;
+               goto err_late;
+       }
+
+       /* Copy the permanent MAC address */
+       rte_ether_addr_copy((struct rte_ether_addr *)hw->mac.addr,
+                       &dev->data->mac_addrs[0]);
+
+       /* Now initialize the hardware */
+       if (igc_hardware_init(hw) != 0) {
+               PMD_INIT_LOG(ERR, "Hardware initialization failed");
+               rte_free(dev->data->mac_addrs);
+               dev->data->mac_addrs = NULL;
+               error = -ENODEV;
+               goto err_late;
        }
 
        /* Pass the information to the rte_eth_dev_close() that it should also
@@ -130,11 +363,22 @@ eth_igc_dev_init(struct rte_eth_dev *dev)
         */
        dev->data->dev_flags |= RTE_ETH_DEV_CLOSE_REMOVE;
 
+       hw->mac.get_link_status = 1;
+
+       /* Indicate SOL/IDER usage */
+       if (igc_check_reset_block(hw) < 0)
+               PMD_INIT_LOG(ERR,
+                       "PHY reset is blocked due to SOL/IDER session.");
+
        PMD_INIT_LOG(DEBUG, "port_id %d vendorID=0x%x deviceID=0x%x",
                        dev->data->port_id, pci_dev->id.vendor_id,
                        pci_dev->id.device_id);
 
        return 0;
+
+err_late:
+       igc_hw_control_release(hw);
+       return error;
 }
 
 static int
@@ -224,7 +468,8 @@ eth_igc_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
        struct rte_pci_device *pci_dev)
 {
        PMD_INIT_FUNC_TRACE();
-       return rte_eth_dev_pci_generic_probe(pci_dev, 0, eth_igc_dev_init);
+       return rte_eth_dev_pci_generic_probe(pci_dev,
+               sizeof(struct igc_adapter), eth_igc_dev_init);
 }
 
 static int