fm10k: Add support for ITR scaling based on PCIe link speed
authorJacob Keller <jacob.e.keller@intel.com>
Fri, 16 Oct 2015 17:57:05 +0000 (10:57 -0700)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Sun, 6 Dec 2015 07:55:20 +0000 (23:55 -0800)
The Intel Ethernet Switch FM10000 Host Interface interrupt throttle
timers are based on the PCIe link speed. Because of this, the value
being programmed into the ITR registers must be scaled accordingly.

For the PF, this is as simple as reading the PCIe link speed and storing
the result. However, in the case of SR-IOV, the VF's interrupt throttle
timers are based on the link speed of the PF. However, the VF is unable
to get the link speed information from its configuration space, so the
PF must inform it of what scale to use.

Rather than pass this scale via mailbox message, take advantage of
unused bits in the TDLEN register to pass the scale. It is the
responsibility of the PF to program this for the VF while setting up the
VF queues and the responsibility of the VF to get the information
accordingly. This is preferable because it allows the VF to set up the
interrupts properly during initialization and matches how the MAC
address is passed in the TDBAL/TDBAH registers.

Since we're modifying fm10k_type.h, we may as well also update the
copyright year.

Reported-by: Matthew Vick <matthew.vick@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Reviewed-by: Bruce Allan <bruce.w.allan@intel.com>
Tested-by: Krishneil Singh <krishneil.k.singh@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/fm10k/fm10k_pf.c
drivers/net/ethernet/intel/fm10k/fm10k_type.h
drivers/net/ethernet/intel/fm10k/fm10k_vf.c

index 00f7a29e734fb6ef4841925a64b1d2c8fa983016..8b9b6ba5b92bfb76ff2efa95f2c07160af65611a 100644 (file)
@@ -150,19 +150,26 @@ static s32 fm10k_init_hw_pf(struct fm10k_hw *hw)
                                FM10K_TPH_RXCTRL_HDR_WROEN);
        }
 
-       /* set max hold interval to align with 1.024 usec in all modes */
+       /* set max hold interval to align with 1.024 usec in all modes and
+        * store ITR scale
+        */
        switch (hw->bus.speed) {
        case fm10k_bus_speed_2500:
                dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN1;
+               hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN1;
                break;
        case fm10k_bus_speed_5000:
                dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN2;
+               hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN2;
                break;
        case fm10k_bus_speed_8000:
                dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN3;
+               hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN3;
                break;
        default:
                dma_ctrl = 0;
+               /* just in case, assume Gen3 ITR scale */
+               hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN3;
                break;
        }
 
@@ -903,6 +910,13 @@ static s32 fm10k_iov_assign_default_mac_vlan_pf(struct fm10k_hw *hw,
        fm10k_write_reg(hw, FM10K_TDBAL(vf_q_idx), tdbal);
        fm10k_write_reg(hw, FM10K_TDBAH(vf_q_idx), tdbah);
 
+       /* Provide the VF the ITR scale, using software-defined fields in TDLEN
+        * to pass the information during VF initialization. See definition of
+        * FM10K_TDLEN_ITR_SCALE_SHIFT for more details.
+        */
+       fm10k_write_reg(hw, FM10K_TDLEN(vf_q_idx), hw->mac.itr_scale <<
+                                                  FM10K_TDLEN_ITR_SCALE_SHIFT);
+
 err_out:
        /* configure Queue control register */
        txqctl = ((u32)vf_vid << FM10K_TXQCTL_VID_SHIFT) &
@@ -1035,6 +1049,12 @@ static s32 fm10k_iov_reset_resources_pf(struct fm10k_hw *hw,
        for (i = queues_per_pool; i--;) {
                fm10k_write_reg(hw, FM10K_TDBAL(vf_q_idx + i), tdbal);
                fm10k_write_reg(hw, FM10K_TDBAH(vf_q_idx + i), tdbah);
+               /* See definition of FM10K_TDLEN_ITR_SCALE_SHIFT for an
+                * explanation of how TDLEN is used.
+                */
+               fm10k_write_reg(hw, FM10K_TDLEN(vf_q_idx + i),
+                               hw->mac.itr_scale <<
+                               FM10K_TDLEN_ITR_SCALE_SHIFT);
                fm10k_write_reg(hw, FM10K_TQMAP(qmap_idx + i), vf_q_idx + i);
                fm10k_write_reg(hw, FM10K_RQMAP(qmap_idx + i), vf_q_idx + i);
        }
index 35afd711d14460eb28c8a7c10e0120898b80a19c..02727250ce1f5919ef0d0a87953e4222719018b6 100644 (file)
@@ -1,5 +1,5 @@
 /* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * Copyright(c) 2013 - 2015 Intel Corporation.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -272,6 +272,20 @@ struct fm10k_hw;
 #define FM10K_TDBAL(_n)                ((0x40 * (_n)) + 0x8000)
 #define FM10K_TDBAH(_n)                ((0x40 * (_n)) + 0x8001)
 #define FM10K_TDLEN(_n)                ((0x40 * (_n)) + 0x8002)
+/* When fist initialized, VFs need to know the Interrupt Throttle Rate (ITR)
+ * scale which is based on the PCIe speed but the speed information in the PCI
+ * configuration space may not be accurate. The PF already knows the ITR scale
+ * but there is no defined method to pass that information from the PF to the
+ * VF. This is accomplished during VF initialization by temporarily co-opting
+ * the yet-to-be-used TDLEN register to have the PF store the ITR shift for
+ * the VF to retrieve before the VF needs to use the TDLEN register for its
+ * intended purpose, i.e. before the Tx resources are allocated.
+ */
+#define FM10K_TDLEN_ITR_SCALE_SHIFT            9
+#define FM10K_TDLEN_ITR_SCALE_MASK             0x00000E00
+#define FM10K_TDLEN_ITR_SCALE_GEN1             2
+#define FM10K_TDLEN_ITR_SCALE_GEN2             1
+#define FM10K_TDLEN_ITR_SCALE_GEN3             0
 #define FM10K_TPH_TXCTRL(_n)   ((0x40 * (_n)) + 0x8003)
 #define FM10K_TPH_TXCTRL_DESC_TPHEN            0x00000020
 #define FM10K_TPH_TXCTRL_DESC_RROEN            0x00000200
@@ -560,6 +574,7 @@ struct fm10k_mac_info {
        bool get_host_state;
        bool tx_ready;
        u32 dglort_map;
+       u8 itr_scale;
 };
 
 struct fm10k_swapi_table_info {
index d512575c33f3b1d35525bdb197ac7b0e4e46fa42..2af697df5abc1b58a716c2cf618ca3582508ad51 100644 (file)
@@ -28,7 +28,7 @@
 static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw)
 {
        u8 *perm_addr = hw->mac.perm_addr;
-       u32 bal = 0, bah = 0;
+       u32 bal = 0, bah = 0, tdlen;
        s32 err;
        u16 i;
 
@@ -48,6 +48,9 @@ static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw)
                       ((u32)perm_addr[2]);
        }
 
+       /* restore default itr_scale for next VF initialization */
+       tdlen = hw->mac.itr_scale << FM10K_TDLEN_ITR_SCALE_SHIFT;
+
        /* The queues have already been disabled so we just need to
         * update their base address registers
         */
@@ -56,6 +59,12 @@ static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw)
                fm10k_write_reg(hw, FM10K_TDBAH(i), bah);
                fm10k_write_reg(hw, FM10K_RDBAL(i), bal);
                fm10k_write_reg(hw, FM10K_RDBAH(i), bah);
+               /* Restore ITR scale in software-defined mechanism in TDLEN
+                * for next VF initialization. See definition of
+                * FM10K_TDLEN_ITR_SCALE_SHIFT for more details on the use of
+                * TDLEN here.
+                */
+               fm10k_write_reg(hw, FM10K_TDLEN(i), tdlen);
        }
 
        return 0;
@@ -131,9 +140,16 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw)
        /* record maximum queue count */
        hw->mac.max_queues = i;
 
-       /* fetch default VLAN */
+       /* fetch default VLAN and ITR scale */
        hw->mac.default_vid = (fm10k_read_reg(hw, FM10K_TXQCTL(0)) &
                               FM10K_TXQCTL_VID_MASK) >> FM10K_TXQCTL_VID_SHIFT;
+       /* Read the ITR scale from TDLEN. See the definition of
+        * FM10K_TDLEN_ITR_SCALE_SHIFT for more information about how TDLEN is
+        * used here.
+        */
+       hw->mac.itr_scale = (fm10k_read_reg(hw, FM10K_TDLEN(0)) &
+                            FM10K_TDLEN_ITR_SCALE_MASK) >>
+                           FM10K_TDLEN_ITR_SCALE_SHIFT;
 
        return 0;