|
@@ -599,6 +599,20 @@ static void i40e_stat_update32(struct i40e_hw *hw, u32 reg,
|
|
|
*stat = (u32)((new_data + BIT_ULL(32)) - *offset);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * i40e_stat_update_and_clear32 - read and clear hw reg, update a 32 bit stat
|
|
|
+ * @hw: ptr to the hardware info
|
|
|
+ * @reg: the hw reg to read and clear
|
|
|
+ * @stat: ptr to the stat
|
|
|
+ **/
|
|
|
+static void i40e_stat_update_and_clear32(struct i40e_hw *hw, u32 reg, u64 *stat)
|
|
|
+{
|
|
|
+ u32 new_data = rd32(hw, reg);
|
|
|
+
|
|
|
+ wr32(hw, reg, 1); /* must write a nonzero value to clear register */
|
|
|
+ *stat += new_data;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* i40e_update_eth_stats - Update VSI-specific ethernet statistics counters.
|
|
|
* @vsi: the VSI to be updated
|
|
@@ -1040,18 +1054,15 @@ static void i40e_update_pf_stats(struct i40e_pf *pf)
|
|
|
&osd->rx_jabber, &nsd->rx_jabber);
|
|
|
|
|
|
/* FDIR stats */
|
|
|
- i40e_stat_update32(hw,
|
|
|
- I40E_GLQF_PCNT(I40E_FD_ATR_STAT_IDX(pf->hw.pf_id)),
|
|
|
- pf->stat_offsets_loaded,
|
|
|
- &osd->fd_atr_match, &nsd->fd_atr_match);
|
|
|
- i40e_stat_update32(hw,
|
|
|
- I40E_GLQF_PCNT(I40E_FD_SB_STAT_IDX(pf->hw.pf_id)),
|
|
|
- pf->stat_offsets_loaded,
|
|
|
- &osd->fd_sb_match, &nsd->fd_sb_match);
|
|
|
- i40e_stat_update32(hw,
|
|
|
- I40E_GLQF_PCNT(I40E_FD_ATR_TUNNEL_STAT_IDX(pf->hw.pf_id)),
|
|
|
- pf->stat_offsets_loaded,
|
|
|
- &osd->fd_atr_tunnel_match, &nsd->fd_atr_tunnel_match);
|
|
|
+ i40e_stat_update_and_clear32(hw,
|
|
|
+ I40E_GLQF_PCNT(I40E_FD_ATR_STAT_IDX(hw->pf_id)),
|
|
|
+ &nsd->fd_atr_match);
|
|
|
+ i40e_stat_update_and_clear32(hw,
|
|
|
+ I40E_GLQF_PCNT(I40E_FD_SB_STAT_IDX(hw->pf_id)),
|
|
|
+ &nsd->fd_sb_match);
|
|
|
+ i40e_stat_update_and_clear32(hw,
|
|
|
+ I40E_GLQF_PCNT(I40E_FD_ATR_TUNNEL_STAT_IDX(hw->pf_id)),
|
|
|
+ &nsd->fd_atr_tunnel_match);
|
|
|
|
|
|
val = rd32(hw, I40E_PRTPM_EEE_STAT);
|
|
|
nsd->tx_lpi_status =
|
|
@@ -3593,14 +3604,20 @@ static int i40e_vsi_enable_irq(struct i40e_vsi *vsi)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * i40e_stop_misc_vector - Stop the vector that handles non-queue events
|
|
|
+ * i40e_free_misc_vector - Free the vector that handles non-queue events
|
|
|
* @pf: board private structure
|
|
|
**/
|
|
|
-static void i40e_stop_misc_vector(struct i40e_pf *pf)
|
|
|
+static void i40e_free_misc_vector(struct i40e_pf *pf)
|
|
|
{
|
|
|
/* Disable ICR 0 */
|
|
|
wr32(&pf->hw, I40E_PFINT_ICR0_ENA, 0);
|
|
|
i40e_flush(&pf->hw);
|
|
|
+
|
|
|
+ if (pf->flags & I40E_FLAG_MSIX_ENABLED && pf->msix_entries) {
|
|
|
+ synchronize_irq(pf->msix_entries[0].vector);
|
|
|
+ free_irq(pf->msix_entries[0].vector, pf);
|
|
|
+ clear_bit(__I40E_MISC_IRQ_REQUESTED, pf->state);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -4455,11 +4472,7 @@ static void i40e_clear_interrupt_scheme(struct i40e_pf *pf)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
- i40e_stop_misc_vector(pf);
|
|
|
- if (pf->flags & I40E_FLAG_MSIX_ENABLED && pf->msix_entries) {
|
|
|
- synchronize_irq(pf->msix_entries[0].vector);
|
|
|
- free_irq(pf->msix_entries[0].vector, pf);
|
|
|
- }
|
|
|
+ i40e_free_misc_vector(pf);
|
|
|
|
|
|
i40e_put_lump(pf->irq_pile, pf->iwarp_base_vector,
|
|
|
I40E_IWARP_IRQ_PILE_ID);
|
|
@@ -5346,13 +5359,14 @@ out:
|
|
|
void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
|
|
|
{
|
|
|
enum i40e_aq_link_speed new_speed;
|
|
|
+ struct i40e_pf *pf = vsi->back;
|
|
|
char *speed = "Unknown";
|
|
|
char *fc = "Unknown";
|
|
|
char *fec = "";
|
|
|
char *req_fec = "";
|
|
|
char *an = "";
|
|
|
|
|
|
- new_speed = vsi->back->hw.phy.link_info.link_speed;
|
|
|
+ new_speed = pf->hw.phy.link_info.link_speed;
|
|
|
|
|
|
if ((vsi->current_isup == isup) && (vsi->current_speed == new_speed))
|
|
|
return;
|
|
@@ -5366,13 +5380,13 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
|
|
|
/* Warn user if link speed on NPAR enabled partition is not at
|
|
|
* least 10GB
|
|
|
*/
|
|
|
- if (vsi->back->hw.func_caps.npar_enable &&
|
|
|
- (vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_1GB ||
|
|
|
- vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_100MB))
|
|
|
+ if (pf->hw.func_caps.npar_enable &&
|
|
|
+ (pf->hw.phy.link_info.link_speed == I40E_LINK_SPEED_1GB ||
|
|
|
+ pf->hw.phy.link_info.link_speed == I40E_LINK_SPEED_100MB))
|
|
|
netdev_warn(vsi->netdev,
|
|
|
"The partition detected link speed that is less than 10Gbps\n");
|
|
|
|
|
|
- switch (vsi->back->hw.phy.link_info.link_speed) {
|
|
|
+ switch (pf->hw.phy.link_info.link_speed) {
|
|
|
case I40E_LINK_SPEED_40GB:
|
|
|
speed = "40 G";
|
|
|
break;
|
|
@@ -5395,7 +5409,7 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- switch (vsi->back->hw.fc.current_mode) {
|
|
|
+ switch (pf->hw.fc.current_mode) {
|
|
|
case I40E_FC_FULL:
|
|
|
fc = "RX/TX";
|
|
|
break;
|
|
@@ -5410,18 +5424,18 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- if (vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_25GB) {
|
|
|
+ if (pf->hw.phy.link_info.link_speed == I40E_LINK_SPEED_25GB) {
|
|
|
req_fec = ", Requested FEC: None";
|
|
|
fec = ", FEC: None";
|
|
|
an = ", Autoneg: False";
|
|
|
|
|
|
- if (vsi->back->hw.phy.link_info.an_info & I40E_AQ_AN_COMPLETED)
|
|
|
+ if (pf->hw.phy.link_info.an_info & I40E_AQ_AN_COMPLETED)
|
|
|
an = ", Autoneg: True";
|
|
|
|
|
|
- if (vsi->back->hw.phy.link_info.fec_info &
|
|
|
+ if (pf->hw.phy.link_info.fec_info &
|
|
|
I40E_AQ_CONFIG_FEC_KR_ENA)
|
|
|
fec = ", FEC: CL74 FC-FEC/BASE-R";
|
|
|
- else if (vsi->back->hw.phy.link_info.fec_info &
|
|
|
+ else if (pf->hw.phy.link_info.fec_info &
|
|
|
I40E_AQ_CONFIG_FEC_RS_ENA)
|
|
|
fec = ", FEC: CL108 RS-FEC";
|
|
|
|
|
@@ -5470,15 +5484,6 @@ static int i40e_up_complete(struct i40e_vsi *vsi)
|
|
|
i40e_print_link_message(vsi, true);
|
|
|
netif_tx_start_all_queues(vsi->netdev);
|
|
|
netif_carrier_on(vsi->netdev);
|
|
|
- } else if (vsi->netdev) {
|
|
|
- i40e_print_link_message(vsi, false);
|
|
|
- /* need to check for qualified module here*/
|
|
|
- if ((pf->hw.phy.link_info.link_info &
|
|
|
- I40E_AQ_MEDIA_AVAILABLE) &&
|
|
|
- (!(pf->hw.phy.link_info.an_info &
|
|
|
- I40E_AQ_QUALIFIED_MODULE)))
|
|
|
- netdev_err(vsi->netdev,
|
|
|
- "the driver failed to link because an unqualified module was detected.");
|
|
|
}
|
|
|
|
|
|
/* replay FDIR SB filters */
|
|
@@ -6429,8 +6434,7 @@ static void i40e_link_event(struct i40e_pf *pf)
|
|
|
new_link == netif_carrier_ok(vsi->netdev)))
|
|
|
return;
|
|
|
|
|
|
- if (!test_bit(__I40E_VSI_DOWN, vsi->state))
|
|
|
- i40e_print_link_message(vsi, new_link);
|
|
|
+ i40e_print_link_message(vsi, new_link);
|
|
|
|
|
|
/* Notify the base of the switch tree connected to
|
|
|
* the link. Floating VEBs are not notified.
|
|
@@ -8350,6 +8354,57 @@ static int i40e_init_interrupt_scheme(struct i40e_pf *pf)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_PM
|
|
|
+/**
|
|
|
+ * i40e_restore_interrupt_scheme - Restore the interrupt scheme
|
|
|
+ * @pf: private board data structure
|
|
|
+ *
|
|
|
+ * Restore the interrupt scheme that was cleared when we suspended the
|
|
|
+ * device. This should be called during resume to re-allocate the q_vectors
|
|
|
+ * and reacquire IRQs.
|
|
|
+ */
|
|
|
+static int i40e_restore_interrupt_scheme(struct i40e_pf *pf)
|
|
|
+{
|
|
|
+ int err, i;
|
|
|
+
|
|
|
+ /* We cleared the MSI and MSI-X flags when disabling the old interrupt
|
|
|
+ * scheme. We need to re-enabled them here in order to attempt to
|
|
|
+ * re-acquire the MSI or MSI-X vectors
|
|
|
+ */
|
|
|
+ pf->flags |= (I40E_FLAG_MSIX_ENABLED | I40E_FLAG_MSI_ENABLED);
|
|
|
+
|
|
|
+ err = i40e_init_interrupt_scheme(pf);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ /* Now that we've re-acquired IRQs, we need to remap the vectors and
|
|
|
+ * rings together again.
|
|
|
+ */
|
|
|
+ for (i = 0; i < pf->num_alloc_vsi; i++) {
|
|
|
+ if (pf->vsi[i]) {
|
|
|
+ err = i40e_vsi_alloc_q_vectors(pf->vsi[i]);
|
|
|
+ if (err)
|
|
|
+ goto err_unwind;
|
|
|
+ i40e_vsi_map_rings_to_vectors(pf->vsi[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ err = i40e_setup_misc_vector(pf);
|
|
|
+ if (err)
|
|
|
+ goto err_unwind;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_unwind:
|
|
|
+ while (i--) {
|
|
|
+ if (pf->vsi[i])
|
|
|
+ i40e_vsi_free_q_vectors(pf->vsi[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+#endif /* CONFIG_PM */
|
|
|
+
|
|
|
/**
|
|
|
* i40e_setup_misc_vector - Setup the misc vector to handle non queue events
|
|
|
* @pf: board private structure
|
|
@@ -8363,13 +8418,12 @@ static int i40e_setup_misc_vector(struct i40e_pf *pf)
|
|
|
struct i40e_hw *hw = &pf->hw;
|
|
|
int err = 0;
|
|
|
|
|
|
- /* Only request the irq if this is the first time through, and
|
|
|
- * not when we're rebuilding after a Reset
|
|
|
- */
|
|
|
- if (!test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) {
|
|
|
+ /* Only request the IRQ once, the first time through. */
|
|
|
+ if (!test_and_set_bit(__I40E_MISC_IRQ_REQUESTED, pf->state)) {
|
|
|
err = request_irq(pf->msix_entries[0].vector,
|
|
|
i40e_intr, 0, pf->int_name, pf);
|
|
|
if (err) {
|
|
|
+ clear_bit(__I40E_MISC_IRQ_REQUESTED, pf->state);
|
|
|
dev_info(&pf->pdev->dev,
|
|
|
"request_irq for %s failed: %d\n",
|
|
|
pf->int_name, err);
|
|
@@ -11380,11 +11434,10 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
|
i40e_nvm_version_str(hw));
|
|
|
|
|
|
if (hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
|
|
|
- hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR)
|
|
|
+ hw->aq.api_min_ver > I40E_FW_MINOR_VERSION(hw))
|
|
|
dev_info(&pdev->dev,
|
|
|
"The driver for the device detected a newer version of the NVM image than expected. Please install the most recent version of the network driver.\n");
|
|
|
- else if (hw->aq.api_maj_ver < I40E_FW_API_VERSION_MAJOR ||
|
|
|
- hw->aq.api_min_ver < (I40E_FW_API_VERSION_MINOR - 1))
|
|
|
+ else if (hw->aq.api_maj_ver == 1 && hw->aq.api_min_ver < 4)
|
|
|
dev_info(&pdev->dev,
|
|
|
"The driver for the device detected an older version of the NVM image than expected. Please update the NVM image.\n");
|
|
|
|
|
@@ -12048,18 +12101,25 @@ static void i40e_shutdown(struct pci_dev *pdev)
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
/**
|
|
|
- * i40e_suspend - PCI callback for moving to D3
|
|
|
- * @pdev: PCI device information struct
|
|
|
+ * i40e_suspend - PM callback for moving to D3
|
|
|
+ * @dev: generic device information structure
|
|
|
**/
|
|
|
-static int i40e_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
+static int i40e_suspend(struct device *dev)
|
|
|
{
|
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
struct i40e_pf *pf = pci_get_drvdata(pdev);
|
|
|
struct i40e_hw *hw = &pf->hw;
|
|
|
- int retval = 0;
|
|
|
|
|
|
- set_bit(__I40E_SUSPENDED, pf->state);
|
|
|
+ /* If we're already suspended, then there is nothing to do */
|
|
|
+ if (test_and_set_bit(__I40E_SUSPENDED, pf->state))
|
|
|
+ return 0;
|
|
|
+
|
|
|
set_bit(__I40E_DOWN, pf->state);
|
|
|
|
|
|
+ /* Ensure service task will not be running */
|
|
|
+ del_timer_sync(&pf->service_timer);
|
|
|
+ cancel_work_sync(&pf->service_task);
|
|
|
+
|
|
|
if (pf->wol_en && (pf->hw_features & I40E_HW_WOL_MC_MAGIC_PKT_WAKE))
|
|
|
i40e_enable_mc_magic_wake(pf);
|
|
|
|
|
@@ -12068,81 +12128,72 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
wr32(hw, I40E_PFPM_APM, (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0));
|
|
|
wr32(hw, I40E_PFPM_WUFC, (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0));
|
|
|
|
|
|
- i40e_stop_misc_vector(pf);
|
|
|
- if (pf->msix_entries) {
|
|
|
- synchronize_irq(pf->msix_entries[0].vector);
|
|
|
- free_irq(pf->msix_entries[0].vector, pf);
|
|
|
- }
|
|
|
- retval = pci_save_state(pdev);
|
|
|
- if (retval)
|
|
|
- return retval;
|
|
|
-
|
|
|
- pci_wake_from_d3(pdev, pf->wol_en);
|
|
|
- pci_set_power_state(pdev, PCI_D3hot);
|
|
|
+ /* Clear the interrupt scheme and release our IRQs so that the system
|
|
|
+ * can safely hibernate even when there are a large number of CPUs.
|
|
|
+ * Otherwise hibernation might fail when mapping all the vectors back
|
|
|
+ * to CPU0.
|
|
|
+ */
|
|
|
+ i40e_clear_interrupt_scheme(pf);
|
|
|
|
|
|
- return retval;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * i40e_resume - PCI callback for waking up from D3
|
|
|
- * @pdev: PCI device information struct
|
|
|
+ * i40e_resume - PM callback for waking up from D3
|
|
|
+ * @dev: generic device information structure
|
|
|
**/
|
|
|
-static int i40e_resume(struct pci_dev *pdev)
|
|
|
+static int i40e_resume(struct device *dev)
|
|
|
{
|
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
struct i40e_pf *pf = pci_get_drvdata(pdev);
|
|
|
- u32 err;
|
|
|
+ int err;
|
|
|
|
|
|
- pci_set_power_state(pdev, PCI_D0);
|
|
|
- pci_restore_state(pdev);
|
|
|
- /* pci_restore_state() clears dev->state_saves, so
|
|
|
- * call pci_save_state() again to restore it.
|
|
|
- */
|
|
|
- pci_save_state(pdev);
|
|
|
+ /* If we're not suspended, then there is nothing to do */
|
|
|
+ if (!test_bit(__I40E_SUSPENDED, pf->state))
|
|
|
+ return 0;
|
|
|
|
|
|
- err = pci_enable_device_mem(pdev);
|
|
|
+ /* We cleared the interrupt scheme when we suspended, so we need to
|
|
|
+ * restore it now to resume device functionality.
|
|
|
+ */
|
|
|
+ err = i40e_restore_interrupt_scheme(pf);
|
|
|
if (err) {
|
|
|
- dev_err(&pdev->dev, "Cannot enable PCI device from suspend\n");
|
|
|
- return err;
|
|
|
+ dev_err(&pdev->dev, "Cannot restore interrupt scheme: %d\n",
|
|
|
+ err);
|
|
|
}
|
|
|
- pci_set_master(pdev);
|
|
|
|
|
|
- /* no wakeup events while running */
|
|
|
- pci_wake_from_d3(pdev, false);
|
|
|
-
|
|
|
- /* handling the reset will rebuild the device state */
|
|
|
- if (test_and_clear_bit(__I40E_SUSPENDED, pf->state)) {
|
|
|
- clear_bit(__I40E_DOWN, pf->state);
|
|
|
- if (pf->msix_entries) {
|
|
|
- err = request_irq(pf->msix_entries[0].vector,
|
|
|
- i40e_intr, 0, pf->int_name, pf);
|
|
|
- if (err) {
|
|
|
- dev_err(&pf->pdev->dev,
|
|
|
- "request_irq for %s failed: %d\n",
|
|
|
- pf->int_name, err);
|
|
|
- }
|
|
|
- }
|
|
|
- i40e_reset_and_rebuild(pf, false, false);
|
|
|
- }
|
|
|
+ clear_bit(__I40E_DOWN, pf->state);
|
|
|
+ i40e_reset_and_rebuild(pf, false, false);
|
|
|
+
|
|
|
+ /* Clear suspended state last after everything is recovered */
|
|
|
+ clear_bit(__I40E_SUSPENDED, pf->state);
|
|
|
+
|
|
|
+ /* Restart the service task */
|
|
|
+ mod_timer(&pf->service_timer,
|
|
|
+ round_jiffies(jiffies + pf->service_timer_period));
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#endif
|
|
|
+#endif /* CONFIG_PM */
|
|
|
+
|
|
|
static const struct pci_error_handlers i40e_err_handler = {
|
|
|
.error_detected = i40e_pci_error_detected,
|
|
|
.slot_reset = i40e_pci_error_slot_reset,
|
|
|
.resume = i40e_pci_error_resume,
|
|
|
};
|
|
|
|
|
|
+static SIMPLE_DEV_PM_OPS(i40e_pm_ops, i40e_suspend, i40e_resume);
|
|
|
+
|
|
|
static struct pci_driver i40e_driver = {
|
|
|
.name = i40e_driver_name,
|
|
|
.id_table = i40e_pci_tbl,
|
|
|
.probe = i40e_probe,
|
|
|
.remove = i40e_remove,
|
|
|
#ifdef CONFIG_PM
|
|
|
- .suspend = i40e_suspend,
|
|
|
- .resume = i40e_resume,
|
|
|
-#endif
|
|
|
+ .driver = {
|
|
|
+ .pm = &i40e_pm_ops,
|
|
|
+ },
|
|
|
+#endif /* CONFIG_PM */
|
|
|
.shutdown = i40e_shutdown,
|
|
|
.err_handler = &i40e_err_handler,
|
|
|
.sriov_configure = i40e_pci_sriov_configure,
|