Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / drivers / net / ethernet / intel / e1000e / ich8lan.c
index 174507ccd7d77c2d3e1fd8bcd80413956bf4e86b..ad9d8f2dd868778d1b4f8a1c2e3b767add06acd8 100644 (file)
@@ -838,6 +838,94 @@ release:
        return ret_val;
 }
 
+/**
+ *  e1000_platform_pm_pch_lpt - Set platform power management values
+ *  @hw: pointer to the HW structure
+ *  @link: bool indicating link status
+ *
+ *  Set the Latency Tolerance Reporting (LTR) values for the "PCIe-like"
+ *  GbE MAC in the Lynx Point PCH based on Rx buffer size and link speed
+ *  when link is up (which must not exceed the maximum latency supported
+ *  by the platform), otherwise specify there is no LTR requirement.
+ *  Unlike true-PCIe devices which set the LTR maximum snoop/no-snoop
+ *  latencies in the LTR Extended Capability Structure in the PCIe Extended
+ *  Capability register set, on this device LTR is set by writing the
+ *  equivalent snoop/no-snoop latencies in the LTRV register in the MAC and
+ *  set the SEND bit to send an Intel On-chip System Fabric sideband (IOSF-SB)
+ *  message to the PMC.
+ **/
+static s32 e1000_platform_pm_pch_lpt(struct e1000_hw *hw, bool link)
+{
+       u32 reg = link << (E1000_LTRV_REQ_SHIFT + E1000_LTRV_NOSNOOP_SHIFT) |
+           link << E1000_LTRV_REQ_SHIFT | E1000_LTRV_SEND;
+       u16 lat_enc = 0;        /* latency encoded */
+
+       if (link) {
+               u16 speed, duplex, scale = 0;
+               u16 max_snoop, max_nosnoop;
+               u16 max_ltr_enc;        /* max LTR latency encoded */
+               s64 lat_ns;     /* latency (ns) */
+               s64 value;
+               u32 rxa;
+
+               if (!hw->adapter->max_frame_size) {
+                       e_dbg("max_frame_size not set.\n");
+                       return -E1000_ERR_CONFIG;
+               }
+
+               hw->mac.ops.get_link_up_info(hw, &speed, &duplex);
+               if (!speed) {
+                       e_dbg("Speed not set.\n");
+                       return -E1000_ERR_CONFIG;
+               }
+
+               /* Rx Packet Buffer Allocation size (KB) */
+               rxa = er32(PBA) & E1000_PBA_RXA_MASK;
+
+               /* Determine the maximum latency tolerated by the device.
+                *
+                * Per the PCIe spec, the tolerated latencies are encoded as
+                * a 3-bit encoded scale (only 0-5 are valid) multiplied by
+                * a 10-bit value (0-1023) to provide a range from 1 ns to
+                * 2^25*(2^10-1) ns.  The scale is encoded as 0=2^0ns,
+                * 1=2^5ns, 2=2^10ns,...5=2^25ns.
+                */
+               lat_ns = ((s64)rxa * 1024 -
+                         (2 * (s64)hw->adapter->max_frame_size)) * 8 * 1000;
+               if (lat_ns < 0)
+                       lat_ns = 0;
+               else
+                       do_div(lat_ns, speed);
+
+               value = lat_ns;
+               while (value > PCI_LTR_VALUE_MASK) {
+                       scale++;
+                       value = DIV_ROUND_UP(value, (1 << 5));
+               }
+               if (scale > E1000_LTRV_SCALE_MAX) {
+                       e_dbg("Invalid LTR latency scale %d\n", scale);
+                       return -E1000_ERR_CONFIG;
+               }
+               lat_enc = (u16)((scale << PCI_LTR_SCALE_SHIFT) | value);
+
+               /* Determine the maximum latency tolerated by the platform */
+               pci_read_config_word(hw->adapter->pdev, E1000_PCI_LTR_CAP_LPT,
+                                    &max_snoop);
+               pci_read_config_word(hw->adapter->pdev,
+                                    E1000_PCI_LTR_CAP_LPT + 2, &max_nosnoop);
+               max_ltr_enc = max_t(u16, max_snoop, max_nosnoop);
+
+               if (lat_enc > max_ltr_enc)
+                       lat_enc = max_ltr_enc;
+       }
+
+       /* Set Snoop and No-Snoop latencies the same */
+       reg |= lat_enc | (lat_enc << E1000_LTRV_NOSNOOP_SHIFT);
+       ew32(LTRV, reg);
+
+       return 0;
+}
+
 /**
  *  e1000_check_for_copper_link_ich8lan - Check for link (Copper)
  *  @hw: pointer to the HW structure
@@ -911,6 +999,15 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
                        return ret_val;
        }
 
+       if (hw->mac.type == e1000_pch_lpt) {
+               /* Set platform power management values for
+                * Latency Tolerance Reporting (LTR)
+                */
+               ret_val = e1000_platform_pm_pch_lpt(hw, link);
+               if (ret_val)
+                       return ret_val;
+       }
+
        /* Clear link partner's EEE ability */
        hw->dev_spec.ich8lan.eee_lp_ability = 0;
 
@@ -1034,10 +1131,6 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter)
            (er32(FWSM) & E1000_ICH_FWSM_FW_VALID))
                adapter->flags2 |= FLAG2_PCIM2PCI_ARBITER_WA;
 
-       /* Disable EEE by default until IEEE802.3az spec is finalized */
-       if (adapter->flags2 & FLAG2_HAS_EEE)
-               adapter->hw.dev_spec.ich8lan.eee_disable = true;
-
        return 0;
 }