@@ -249,6 +249,29 @@ Debug
This parameter adjusts the level of debug messages displayed in the system logs.
+EnableS0ix
+----------
+:Valid Range: 0, 1, 2
+:Default Value: 1 (Use Heuristics)
+
+ +-------+----------------+
+ | Value | EnableS0ix |
+ +=======+================+
+ | 0 | Disabled |
+ +-------+----------------+
+ | 1 | Use Heuristics |
+ +-------+----------------+
+ | 2 | Enabled |
+ +-------+----------------+
+
+Controls whether the e1000e driver will attempt s0ix flows. S0ix flows require
+correct platform configuration. By default the e1000e driver will use some heuristics
+to decide whether to enable s0ix. This parameter can be used to override heuristics.
+
+Additionally a sysfs file "enable_s0ix" will be present that can change the value at
+runtime.
+
+Note: This option is only effective on Cannon Point or later hardware.
Additional Features and Configurations
======================================
@@ -436,6 +436,7 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca);
#define FLAG2_DFLT_CRC_STRIPPING BIT(12)
#define FLAG2_CHECK_RX_HWTSTAMP BIT(13)
#define FLAG2_CHECK_SYSTIM_OVERFLOW BIT(14)
+#define FLAG2_ENABLE_S0IX_FLOWS BIT(15)
#define E1000_RX_DESC_PS(R, i) \
(&(((union e1000_rx_desc_packet_split *)((R).desc))[i]))
@@ -462,6 +463,12 @@ enum latency_range {
extern char e1000e_driver_name[];
void e1000e_check_options(struct e1000_adapter *adapter);
+ssize_t enable_s0ix_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+ssize_t enable_s0ix_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
void e1000e_set_ethtool_ops(struct net_device *netdev);
int e1000e_open(struct net_device *netdev);
@@ -103,44 +103,16 @@ static const struct e1000_reg_info e1000_reg_info_tbl[] = {
{0, NULL}
};
-struct e1000e_me_supported {
- u16 device_id; /* supported device ID */
-};
+static DEVICE_ATTR_RW(enable_s0ix);
-static const struct e1000e_me_supported me_supported[] = {
- {E1000_DEV_ID_PCH_LPT_I217_LM},
- {E1000_DEV_ID_PCH_LPTLP_I218_LM},
- {E1000_DEV_ID_PCH_I218_LM2},
- {E1000_DEV_ID_PCH_I218_LM3},
- {E1000_DEV_ID_PCH_SPT_I219_LM},
- {E1000_DEV_ID_PCH_SPT_I219_LM2},
- {E1000_DEV_ID_PCH_LBG_I219_LM3},
- {E1000_DEV_ID_PCH_SPT_I219_LM4},
- {E1000_DEV_ID_PCH_SPT_I219_LM5},
- {E1000_DEV_ID_PCH_CNP_I219_LM6},
- {E1000_DEV_ID_PCH_CNP_I219_LM7},
- {E1000_DEV_ID_PCH_ICP_I219_LM8},
- {E1000_DEV_ID_PCH_ICP_I219_LM9},
- {E1000_DEV_ID_PCH_CMP_I219_LM10},
- {E1000_DEV_ID_PCH_CMP_I219_LM11},
- {E1000_DEV_ID_PCH_CMP_I219_LM12},
- {E1000_DEV_ID_PCH_TGP_I219_LM13},
- {E1000_DEV_ID_PCH_TGP_I219_LM14},
- {E1000_DEV_ID_PCH_TGP_I219_LM15},
- {0}
+static struct attribute *e1000e_attrs[] = {
+ &dev_attr_enable_s0ix.attr,
+ NULL,
};
-static bool e1000e_check_me(u16 device_id)
-{
- struct e1000e_me_supported *id;
-
- for (id = (struct e1000e_me_supported *)me_supported;
- id->device_id; id++)
- if (device_id == id->device_id)
- return true;
-
- return false;
-}
+static struct attribute_group e1000e_group = {
+ .attrs = e1000e_attrs,
+};
/**
* __ew32_prepare - prepare to write to MAC CSR register on certain parts
@@ -4243,7 +4215,7 @@ void e1000e_reset(struct e1000_adapter *adapter)
/**
* e1000e_trigger_lsc - trigger an LSC interrupt
- * @adapter:
+ * @adapter:
*
* Fire a link status change interrupt to start the watchdog.
**/
@@ -6975,7 +6947,7 @@ static __maybe_unused int e1000e_pm_suspend(struct device *dev)
/* Introduce S0ix implementation */
if (hw->mac.type >= e1000_pch_cnp &&
- !e1000e_check_me(hw->adapter->pdev->device))
+ adapter->flags2 & FLAG2_ENABLE_S0IX_FLOWS)
e1000e_s0ix_entry_flow(adapter);
return rc;
@@ -6991,7 +6963,7 @@ static __maybe_unused int e1000e_pm_resume(struct device *dev)
/* Introduce S0ix implementation */
if (hw->mac.type >= e1000_pch_cnp &&
- !e1000e_check_me(hw->adapter->pdev->device))
+ adapter->flags2 & FLAG2_ENABLE_S0IX_FLOWS)
e1000e_s0ix_exit_flow(adapter);
rc = __e1000_resume(pdev);
@@ -7655,6 +7627,13 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!(adapter->flags & FLAG_HAS_AMT))
e1000e_get_hw_control(adapter);
+ /* offer to turn on/off s0ix flows */
+ if (hw->mac.type >= e1000_pch_cnp) {
+ ret_val = sysfs_create_group(&pdev->dev.kobj, &e1000e_group);
+ if (ret_val)
+ goto err_sysfs;
+ }
+
strlcpy(netdev->name, "eth%d", sizeof(netdev->name));
err = register_netdev(netdev);
if (err)
@@ -7673,6 +7652,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
err_register:
+ if (hw->mac.type >= e1000_pch_cnp)
+ sysfs_remove_group(&pdev->dev.kobj, &e1000e_group);
+
+err_sysfs:
if (!(adapter->flags & FLAG_HAS_AMT))
e1000e_release_hw_control(adapter);
err_eeprom:
@@ -7710,6 +7693,7 @@ static void e1000_remove(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct e1000_adapter *adapter = netdev_priv(netdev);
+ struct e1000_hw *hw = &adapter->hw;
e1000e_ptp_remove(adapter);
@@ -7734,6 +7718,10 @@ static void e1000_remove(struct pci_dev *pdev)
}
}
+ /* only allow turning on/off s0ix for cannon point and later */
+ if (hw->mac.type >= e1000_pch_cnp)
+ sysfs_remove_group(&pdev->dev.kobj, &e1000e_group);
+
unregister_netdev(netdev);
if (pci_dev_run_wake(pdev))
@@ -138,6 +138,20 @@ E1000_PARAM(WriteProtectNVM,
E1000_PARAM(CrcStripping,
"Enable CRC Stripping, disable if your BMC needs the CRC");
+/* Enable s0ix flows
+ *
+ * Valid Range: varies depending on hardware support
+ *
+ * disabled=0, heuristics=1, enabled=2
+ *
+ * Default Value: 1 (heuristics)
+ */
+E1000_PARAM(EnableS0ix,
+ "Enable S0ix flows for the system");
+#define S0IX_FORCE_ON 2
+#define S0IX_HEURISTICS 1
+#define S0IX_FORCE_OFF 0
+
struct e1000_option {
enum { enable_option, range_option, list_option } type;
const char *name;
@@ -160,6 +174,45 @@ struct e1000_option {
} arg;
};
+struct e1000e_me_supported {
+ u16 device_id; /* supported device ID */
+};
+
+static const struct e1000e_me_supported me_supported[] = {
+ {E1000_DEV_ID_PCH_LPT_I217_LM},
+ {E1000_DEV_ID_PCH_LPTLP_I218_LM},
+ {E1000_DEV_ID_PCH_I218_LM2},
+ {E1000_DEV_ID_PCH_I218_LM3},
+ {E1000_DEV_ID_PCH_SPT_I219_LM},
+ {E1000_DEV_ID_PCH_SPT_I219_LM2},
+ {E1000_DEV_ID_PCH_LBG_I219_LM3},
+ {E1000_DEV_ID_PCH_SPT_I219_LM4},
+ {E1000_DEV_ID_PCH_SPT_I219_LM5},
+ {E1000_DEV_ID_PCH_CNP_I219_LM6},
+ {E1000_DEV_ID_PCH_CNP_I219_LM7},
+ {E1000_DEV_ID_PCH_ICP_I219_LM8},
+ {E1000_DEV_ID_PCH_ICP_I219_LM9},
+ {E1000_DEV_ID_PCH_CMP_I219_LM10},
+ {E1000_DEV_ID_PCH_CMP_I219_LM11},
+ {E1000_DEV_ID_PCH_CMP_I219_LM12},
+ {E1000_DEV_ID_PCH_TGP_I219_LM13},
+ {E1000_DEV_ID_PCH_TGP_I219_LM14},
+ {E1000_DEV_ID_PCH_TGP_I219_LM15},
+ {0}
+};
+
+static bool e1000e_check_me(u16 device_id)
+{
+ struct e1000e_me_supported *id;
+
+ for (id = (struct e1000e_me_supported *)me_supported;
+ id->device_id; id++)
+ if (device_id == id->device_id)
+ return true;
+
+ return false;
+}
+
static int e1000_validate_option(unsigned int *value,
const struct e1000_option *opt,
struct e1000_adapter *adapter)
@@ -526,4 +579,61 @@ void e1000e_check_options(struct e1000_adapter *adapter)
}
}
}
+ /* Enable S0ix flows */
+ {
+ static const struct e1000_option opt = {
+ .type = range_option,
+ .name = "S0ix flows (Cannon point or later)",
+ .err = "defaulting to heuristics",
+ .def = S0IX_HEURISTICS,
+ .arg = { .r = { .min = S0IX_FORCE_OFF,
+ .max = S0IX_FORCE_ON } }
+ };
+ unsigned int enabled = opt.def;
+
+ if (num_EnableS0ix > bd) {
+ unsigned int s0ix = EnableS0ix[bd];
+
+ e1000_validate_option(&s0ix, &opt, adapter);
+ enabled = s0ix;
+ }
+
+ if (enabled == S0IX_HEURISTICS) {
+ /* default to off for ME configurations */
+ if (e1000e_check_me(hw->adapter->pdev->device))
+ enabled = S0IX_FORCE_OFF;
+ }
+
+ if (enabled > S0IX_FORCE_OFF)
+ adapter->flags2 |= FLAG2_ENABLE_S0IX_FLOWS;
+ }
+}
+
+ssize_t enable_s0ix_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct net_device *netdev = pci_get_drvdata(to_pci_dev(dev));
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+ ssize_t ret;
+ bool enable_s0ix;
+
+ ret = kstrtobool(buf, &enable_s0ix);
+ e_info("s0ix flow set to %d\n", enable_s0ix);
+ if (enable_s0ix)
+ adapter->flags2 |= FLAG2_ENABLE_S0IX_FLOWS;
+ else
+ adapter->flags2 &= ~FLAG2_ENABLE_S0IX_FLOWS;
+
+ return ret ? ret : count;
+}
+
+ssize_t enable_s0ix_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_device *netdev = pci_get_drvdata(to_pci_dev(dev));
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+
+ return sprintf(buf, "%d\n", (adapter->flags2 & FLAG2_ENABLE_S0IX_FLOWS) > 0);
}