* [PATCH iwl-next v5 0/4] igc: add support for forcing link speed without autonegotiation
@ 2026-05-07 21:47 KhaiWenTan
2026-05-07 21:47 ` [PATCH iwl-next v5 1/4] igc: remove unused autoneg_failed field KhaiWenTan
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: KhaiWenTan @ 2026-05-07 21:47 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim
From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
This series adds support for forcing 10/100 Mb/s link speed via ethtool
when autonegotiation is disabled on the igc driver.
Changes in v5:
- add removal justification to include copper context in commit
description for igc: remove unused autoneg_failed field (Paul)
- check that cmd->base.duplex is either DUPLEX_HALF or DUPLEX_FULL
in igc_ethtool_set_link_ksettings() (Simon)
- dynamically override hw->fc.current_mode to igc_fc_none during
link configuration instead of mutating requested_mode (Simon)
Changes in v4:
- Validate that autoneg is AUTONEG_ENABLE or AUTONEG_DISABLE early
in igc_ethtool_set_link_ksettings() to avoid passing unexpected
values to igc_handle_autoneg_disabled(). (Simon Horman)
Changes in v3:
- Modify condition from "if (duplex == DUPLEX_HALF)" to
"if (duplex != DUPLEX_FULL)". (Simon Horman)
Changes in v2:
- When forcing half-duplex, set hw->fc.requested_mode = igc_fc_none,
since half-duplex cannot support flow control per IEEE 802.3.
(Simon Horman)
- Split the original single patch into three patches for clarity:
patches 1 and 2 are preparatory cleanups; patch 3 carries the
functional change.
v4 at:
https://patchwork.ozlabs.org/project/intel-wired-lan/cover/20260428060009.311393-1-khai.wen.tan@linux.intel.com/
v3 at:
https://patchwork.ozlabs.org/project/intel-wired-lan/cover/20260422155701.7420-1-khai.wen.tan@linux.intel.com/
v2 at:
https://patchwork.kernel.org/project/netdevbpf/patch/20260416015520.6090-4-khai.wen.tan@linux.intel.com/
v1 at:
https://patchwork.ozlabs.org/project/intel-wired-lan/patch/20260409072747.217836-1-khai.wen.tan@linux.intel.com/
Faizal Rahim (4):
igc: remove unused autoneg_failed field
igc: move autoneg-enabled settings into igc_handle_autoneg_enabled()
igc: replace goto out with direct returns in
igc_config_fc_after_link_up()
igc: add support for forcing link speed without autonegotiation
drivers/net/ethernet/intel/igc/igc_base.c | 35 +++-
drivers/net/ethernet/intel/igc/igc_defines.h | 9 +-
drivers/net/ethernet/intel/igc/igc_ethtool.c | 210 +++++++++++++------
drivers/net/ethernet/intel/igc/igc_hw.h | 10 +-
drivers/net/ethernet/intel/igc/igc_mac.c | 35 ++--
drivers/net/ethernet/intel/igc/igc_main.c | 2 +-
drivers/net/ethernet/intel/igc/igc_phy.c | 65 +++++-
drivers/net/ethernet/intel/igc/igc_phy.h | 1 +
8 files changed, 268 insertions(+), 99 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH iwl-next v5 1/4] igc: remove unused autoneg_failed field
2026-05-07 21:47 [PATCH iwl-next v5 0/4] igc: add support for forcing link speed without autonegotiation KhaiWenTan
@ 2026-05-07 21:47 ` KhaiWenTan
2026-06-14 7:16 ` [Intel-wired-lan] " Ruinskiy, Dima
2026-05-07 21:47 ` [PATCH iwl-next v5 2/4] igc: move autoneg-enabled settings into igc_handle_autoneg_enabled() KhaiWenTan
` (3 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: KhaiWenTan @ 2026-05-07 21:47 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim,
Aleksandr Loktionov, Khai Wen Tan
From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
autoneg_failed in struct igc_mac_info is never set in the igc driver.
Remove the field and the dead code checking it in
igc_config_fc_after_link_up().
The field originates from the e1000/e1000e fiber/serdes forced-link
path, where MAC-level autoneg timeout sets it to signal the flow-control
code to force pause. igc supports only copper, so it never needs to set
this field.
Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
---
drivers/net/ethernet/intel/igc/igc_hw.h | 1 -
drivers/net/ethernet/intel/igc/igc_mac.c | 16 +---------------
2 files changed, 1 insertion(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/intel/igc/igc_hw.h b/drivers/net/ethernet/intel/igc/igc_hw.h
index be8a49a86d09..86ab8f566f44 100644
--- a/drivers/net/ethernet/intel/igc/igc_hw.h
+++ b/drivers/net/ethernet/intel/igc/igc_hw.h
@@ -92,7 +92,6 @@ struct igc_mac_info {
bool asf_firmware_present;
bool arc_subsystem_valid;
- bool autoneg_failed;
bool get_link_status;
};
diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
index 7ac6637f8db7..142beb9ae557 100644
--- a/drivers/net/ethernet/intel/igc/igc_mac.c
+++ b/drivers/net/ethernet/intel/igc/igc_mac.c
@@ -438,28 +438,14 @@ void igc_config_collision_dist(struct igc_hw *hw)
* Checks the status of auto-negotiation after link up to ensure that the
* speed and duplex were not forced. If the link needed to be forced, then
* flow control needs to be forced also. If auto-negotiation is enabled
- * and did not fail, then we configure flow control based on our link
- * partner.
+ * then we configure flow control based on our link partner.
*/
s32 igc_config_fc_after_link_up(struct igc_hw *hw)
{
u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;
- struct igc_mac_info *mac = &hw->mac;
u16 speed, duplex;
s32 ret_val = 0;
- /* Check for the case where we have fiber media and auto-neg failed
- * so we had to force link. In this case, we need to force the
- * configuration of the MAC to match the "fc" parameter.
- */
- if (mac->autoneg_failed)
- ret_val = igc_force_mac_fc(hw);
-
- if (ret_val) {
- hw_dbg("Error forcing flow control settings\n");
- goto out;
- }
-
/* In auto-neg, we need to check and see if Auto-Neg has completed,
* and if so, how the PHY and link partner has flow control
* configured.
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH iwl-next v5 2/4] igc: move autoneg-enabled settings into igc_handle_autoneg_enabled()
2026-05-07 21:47 [PATCH iwl-next v5 0/4] igc: add support for forcing link speed without autonegotiation KhaiWenTan
2026-05-07 21:47 ` [PATCH iwl-next v5 1/4] igc: remove unused autoneg_failed field KhaiWenTan
@ 2026-05-07 21:47 ` KhaiWenTan
2026-06-14 7:17 ` [Intel-wired-lan] " Ruinskiy, Dima
2026-05-07 21:47 ` [PATCH iwl-next v5 3/4] igc: replace goto out with direct returns in igc_config_fc_after_link_up() KhaiWenTan
` (2 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: KhaiWenTan @ 2026-05-07 21:47 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim,
Aleksandr Loktionov, Khai Wen Tan
From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Move the advertised link modes and flow control configuration from
igc_ethtool_set_link_ksettings() into igc_handle_autoneg_enabled().
No functional change.
Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
---
drivers/net/ethernet/intel/igc/igc_ethtool.c | 72 ++++++++++++--------
1 file changed, 44 insertions(+), 28 deletions(-)
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
index 0122009bedd0..cfcbf2fdad6e 100644
--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
+++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
@@ -2000,6 +2000,49 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
return 0;
}
+/**
+ * igc_handle_autoneg_enabled - Configure autonegotiation advertisement
+ * @adapter: private driver structure
+ * @cmd: ethtool link ksettings from user
+ *
+ * Records advertised speeds and flow control settings when autoneg
+ * is enabled.
+ */
+static void igc_handle_autoneg_enabled(struct igc_adapter *adapter,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct igc_hw *hw = &adapter->hw;
+ u16 advertised = 0;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 2500baseT_Full))
+ advertised |= ADVERTISE_2500_FULL;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 1000baseT_Full))
+ advertised |= ADVERTISE_1000_FULL;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 100baseT_Full))
+ advertised |= ADVERTISE_100_FULL;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 100baseT_Half))
+ advertised |= ADVERTISE_100_HALF;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 10baseT_Full))
+ advertised |= ADVERTISE_10_FULL;
+
+ if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
+ 10baseT_Half))
+ advertised |= ADVERTISE_10_HALF;
+
+ hw->phy.autoneg_advertised = advertised;
+ if (adapter->fc_autoneg)
+ hw->fc.requested_mode = igc_fc_default;
+}
+
static int
igc_ethtool_set_link_ksettings(struct net_device *netdev,
const struct ethtool_link_ksettings *cmd)
@@ -2007,7 +2050,6 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
struct igc_adapter *adapter = netdev_priv(netdev);
struct net_device *dev = adapter->netdev;
struct igc_hw *hw = &adapter->hw;
- u16 advertised = 0;
/* When adapter in resetting mode, autoneg/speed/duplex
* cannot be changed
@@ -2032,34 +2074,8 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
usleep_range(1000, 2000);
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 2500baseT_Full))
- advertised |= ADVERTISE_2500_FULL;
-
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 1000baseT_Full))
- advertised |= ADVERTISE_1000_FULL;
-
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 100baseT_Full))
- advertised |= ADVERTISE_100_FULL;
-
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 100baseT_Half))
- advertised |= ADVERTISE_100_HALF;
-
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 10baseT_Full))
- advertised |= ADVERTISE_10_FULL;
-
- if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
- 10baseT_Half))
- advertised |= ADVERTISE_10_HALF;
-
if (cmd->base.autoneg == AUTONEG_ENABLE) {
- hw->phy.autoneg_advertised = advertised;
- if (adapter->fc_autoneg)
- hw->fc.requested_mode = igc_fc_default;
+ igc_handle_autoneg_enabled(adapter, cmd);
} else {
netdev_info(dev, "Force mode currently not supported\n");
}
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH iwl-next v5 3/4] igc: replace goto out with direct returns in igc_config_fc_after_link_up()
2026-05-07 21:47 [PATCH iwl-next v5 0/4] igc: add support for forcing link speed without autonegotiation KhaiWenTan
2026-05-07 21:47 ` [PATCH iwl-next v5 1/4] igc: remove unused autoneg_failed field KhaiWenTan
2026-05-07 21:47 ` [PATCH iwl-next v5 2/4] igc: move autoneg-enabled settings into igc_handle_autoneg_enabled() KhaiWenTan
@ 2026-05-07 21:47 ` KhaiWenTan
2026-06-14 7:17 ` [Intel-wired-lan] " Ruinskiy, Dima
2026-05-07 21:47 ` [PATCH iwl-next v5 4/4] igc: add support for forcing link speed without autonegotiation KhaiWenTan
2026-05-11 16:28 ` [PATCH iwl-next v5 0/4] " Simon Horman
4 siblings, 1 reply; 10+ messages in thread
From: KhaiWenTan @ 2026-05-07 21:47 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim,
Khai Wen Tan
From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
The out: label only returns ret_val with no cleanup. The kernel coding
style guide states: "If there is no cleanup needed then just return
directly." (Documentation/process/coding-style.rst, section 7).
This improves readability ahead of a subsequent patch that introduces a
new goto label in this function.
No functional change.
Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
---
drivers/net/ethernet/intel/igc/igc_mac.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
index 142beb9ae557..0a3d3f357505 100644
--- a/drivers/net/ethernet/intel/igc/igc_mac.c
+++ b/drivers/net/ethernet/intel/igc/igc_mac.c
@@ -458,15 +458,15 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
&mii_status_reg);
if (ret_val)
- goto out;
+ return ret_val;
ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
&mii_status_reg);
if (ret_val)
- goto out;
+ return ret_val;
if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {
hw_dbg("Copper PHY and Auto Neg has not completed.\n");
- goto out;
+ return ret_val;
}
/* The AutoNeg process has completed, so we now need to
@@ -478,11 +478,11 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV,
&mii_nway_adv_reg);
if (ret_val)
- goto out;
+ return ret_val;
ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY,
&mii_nway_lp_ability_reg);
if (ret_val)
- goto out;
+ return ret_val;
/* Two bits in the Auto Negotiation Advertisement Register
* (Address 4) and two bits in the Auto Negotiation Base
* Page Ability Register (Address 5) determine flow control
@@ -598,7 +598,7 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex);
if (ret_val) {
hw_dbg("Error getting link speed and duplex\n");
- goto out;
+ return ret_val;
}
if (duplex == HALF_DUPLEX)
@@ -610,10 +610,9 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
ret_val = igc_force_mac_fc(hw);
if (ret_val) {
hw_dbg("Error forcing flow control settings\n");
- goto out;
+ return ret_val;
}
-out:
return ret_val;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH iwl-next v5 4/4] igc: add support for forcing link speed without autonegotiation
2026-05-07 21:47 [PATCH iwl-next v5 0/4] igc: add support for forcing link speed without autonegotiation KhaiWenTan
` (2 preceding siblings ...)
2026-05-07 21:47 ` [PATCH iwl-next v5 3/4] igc: replace goto out with direct returns in igc_config_fc_after_link_up() KhaiWenTan
@ 2026-05-07 21:47 ` KhaiWenTan
2026-06-14 7:17 ` [Intel-wired-lan] " Ruinskiy, Dima
2026-05-11 16:28 ` [PATCH iwl-next v5 0/4] " Simon Horman
4 siblings, 1 reply; 10+ messages in thread
From: KhaiWenTan @ 2026-05-07 21:47 UTC (permalink / raw)
To: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim,
Khai Wen Tan
From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Allow users to force 10/100 Mb/s link speed and duplex via ethtool
when autonegotiation is disabled. Previously, the driver rejected
these requests with "Force mode currently not supported.".
Forcing at 1000 Mb/s and 2500 Mb/s is not supported.
Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
---
drivers/net/ethernet/intel/igc/igc_base.c | 35 ++++-
drivers/net/ethernet/intel/igc/igc_defines.h | 9 +-
drivers/net/ethernet/intel/igc/igc_ethtool.c | 138 ++++++++++++++-----
drivers/net/ethernet/intel/igc/igc_hw.h | 9 ++
drivers/net/ethernet/intel/igc/igc_mac.c | 12 ++
drivers/net/ethernet/intel/igc/igc_main.c | 2 +-
drivers/net/ethernet/intel/igc/igc_phy.c | 65 ++++++++-
drivers/net/ethernet/intel/igc/igc_phy.h | 1 +
8 files changed, 220 insertions(+), 51 deletions(-)
diff --git a/drivers/net/ethernet/intel/igc/igc_base.c b/drivers/net/ethernet/intel/igc/igc_base.c
index 1613b562d17c..ab9120a3127f 100644
--- a/drivers/net/ethernet/intel/igc/igc_base.c
+++ b/drivers/net/ethernet/intel/igc/igc_base.c
@@ -114,11 +114,35 @@ static s32 igc_setup_copper_link_base(struct igc_hw *hw)
u32 ctrl;
ctrl = rd32(IGC_CTRL);
- ctrl |= IGC_CTRL_SLU;
- ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX);
- wr32(IGC_CTRL, ctrl);
-
- ret_val = igc_setup_copper_link(hw);
+ ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX |
+ IGC_CTRL_SPEED_MASK | IGC_CTRL_FD);
+
+ if (hw->mac.autoneg_enabled) {
+ ctrl |= IGC_CTRL_SLU;
+ wr32(IGC_CTRL, ctrl);
+ ret_val = igc_setup_copper_link(hw);
+ } else {
+ ctrl |= IGC_CTRL_SLU | IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX;
+
+ switch (hw->mac.forced_speed_duplex) {
+ case IGC_FORCED_10H:
+ ctrl |= IGC_CTRL_SPEED_10;
+ break;
+ case IGC_FORCED_10F:
+ ctrl |= IGC_CTRL_SPEED_10 | IGC_CTRL_FD;
+ break;
+ case IGC_FORCED_100H:
+ ctrl |= IGC_CTRL_SPEED_100;
+ break;
+ case IGC_FORCED_100F:
+ ctrl |= IGC_CTRL_SPEED_100 | IGC_CTRL_FD;
+ break;
+ default:
+ return -IGC_ERR_CONFIG;
+ }
+ wr32(IGC_CTRL, ctrl);
+ ret_val = igc_setup_copper_link(hw);
+ }
return ret_val;
}
@@ -443,6 +467,7 @@ static const struct igc_phy_operations igc_phy_ops_base = {
.reset = igc_phy_hw_reset,
.read_reg = igc_read_phy_reg_gpy,
.write_reg = igc_write_phy_reg_gpy,
+ .force_speed_duplex = igc_force_speed_duplex,
};
const struct igc_info igc_base_info = {
diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h
index 9482ab11f050..3f504751c2d9 100644
--- a/drivers/net/ethernet/intel/igc/igc_defines.h
+++ b/drivers/net/ethernet/intel/igc/igc_defines.h
@@ -129,10 +129,13 @@
#define IGC_ERR_SWFW_SYNC 13
/* Device Control */
+#define IGC_CTRL_FD BIT(0) /* Full Duplex */
#define IGC_CTRL_RST 0x04000000 /* Global reset */
-
#define IGC_CTRL_PHY_RST 0x80000000 /* PHY Reset */
#define IGC_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
+#define IGC_CTRL_SPEED_MASK GENMASK(10, 8)
+#define IGC_CTRL_SPEED_10 FIELD_PREP(IGC_CTRL_SPEED_MASK, 0)
+#define IGC_CTRL_SPEED_100 FIELD_PREP(IGC_CTRL_SPEED_MASK, 1)
#define IGC_CTRL_FRCSPD 0x00000800 /* Force Speed */
#define IGC_CTRL_FRCDPX 0x00001000 /* Force Duplex */
#define IGC_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */
@@ -673,6 +676,10 @@
#define IGC_GEN_POLL_TIMEOUT 1920
/* PHY Control Register */
+#define MII_CR_SPEED_MASK (BIT(6) | BIT(13))
+#define MII_CR_SPEED_10 0x0000 /* SSM=0, SSL=0: 10 Mb/s */
+#define MII_CR_SPEED_100 BIT(13) /* SSM=0, SSL=1: 100 Mb/s */
+#define MII_CR_DUPLEX_EN BIT(8) /* 0 = Half Duplex, 1 = Full Duplex */
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
#define MII_CR_POWER_DOWN 0x0800 /* Power down */
#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
index cfcbf2fdad6e..b103836a895f 100644
--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
+++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
@@ -1914,44 +1914,58 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
- /* advertising link modes */
- if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half);
- if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full);
- if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Half);
- if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full);
- if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full);
- if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL)
- ethtool_link_ksettings_add_link_mode(cmd, advertising, 2500baseT_Full);
-
/* set autoneg settings */
ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
- ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
+ if (hw->mac.autoneg_enabled) {
+ ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
+ cmd->base.autoneg = AUTONEG_ENABLE;
+
+ /* advertising link modes only apply when autoneg is on */
+ if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 10baseT_Half);
+ if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 10baseT_Full);
+ if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 100baseT_Half);
+ if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 100baseT_Full);
+ if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 1000baseT_Full);
+ if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL)
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ 2500baseT_Full);
+
+ /* Set pause flow control advertising */
+ switch (hw->fc.requested_mode) {
+ case igc_fc_full:
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ Pause);
+ break;
+ case igc_fc_rx_pause:
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ Pause);
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ Asym_Pause);
+ break;
+ case igc_fc_tx_pause:
+ ethtool_link_ksettings_add_link_mode(cmd, advertising,
+ Asym_Pause);
+ break;
+ default:
+ break;
+ }
+ } else {
+ cmd->base.autoneg = AUTONEG_DISABLE;
+ }
- /* Set pause flow control settings */
+ /* Pause is always supported */
ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
- switch (hw->fc.requested_mode) {
- case igc_fc_full:
- ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
- break;
- case igc_fc_rx_pause:
- ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
- ethtool_link_ksettings_add_link_mode(cmd, advertising,
- Asym_Pause);
- break;
- case igc_fc_tx_pause:
- ethtool_link_ksettings_add_link_mode(cmd, advertising,
- Asym_Pause);
- break;
- default:
- break;
- }
-
status = pm_runtime_suspended(&adapter->pdev->dev) ?
0 : rd32(IGC_STATUS);
@@ -1983,7 +1997,6 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
cmd->base.duplex = DUPLEX_UNKNOWN;
}
cmd->base.speed = speed;
- cmd->base.autoneg = AUTONEG_ENABLE;
/* MDI-X => 2; MDI =>1; Invalid =>0 */
if (hw->phy.media_type == igc_media_type_copper)
@@ -2000,6 +2013,37 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
return 0;
}
+/**
+ * igc_handle_autoneg_disabled - Configure forced speed/duplex settings
+ * @adapter: private driver structure
+ * @speed: requested speed (must be SPEED_10 or SPEED_100)
+ * @duplex: requested duplex
+ *
+ * Records forced speed/duplex when autoneg is disabled.
+ * Caller must validate speed before calling this function.
+ */
+static void igc_handle_autoneg_disabled(struct igc_adapter *adapter, u32 speed,
+ u8 duplex)
+{
+ struct igc_mac_info *mac = &adapter->hw.mac;
+
+ switch (speed) {
+ case SPEED_10:
+ mac->forced_speed_duplex = (duplex == DUPLEX_FULL) ?
+ IGC_FORCED_10F : IGC_FORCED_10H;
+ break;
+ case SPEED_100:
+ mac->forced_speed_duplex = (duplex == DUPLEX_FULL) ?
+ IGC_FORCED_100F : IGC_FORCED_100H;
+ break;
+ default:
+ WARN_ONCE(1, "Unsupported speed %u\n", speed);
+ return;
+ }
+
+ mac->autoneg_enabled = false;
+}
+
/**
* igc_handle_autoneg_enabled - Configure autonegotiation advertisement
* @adapter: private driver structure
@@ -2038,6 +2082,7 @@ static void igc_handle_autoneg_enabled(struct igc_adapter *adapter,
10baseT_Half))
advertised |= ADVERTISE_10_HALF;
+ hw->mac.autoneg_enabled = true;
hw->phy.autoneg_advertised = advertised;
if (adapter->fc_autoneg)
hw->fc.requested_mode = igc_fc_default;
@@ -2059,6 +2104,12 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
return -EINVAL;
}
+ if (cmd->base.autoneg != AUTONEG_ENABLE &&
+ cmd->base.autoneg != AUTONEG_DISABLE) {
+ netdev_info(dev, "Unsupported autoneg setting\n");
+ return -EINVAL;
+ }
+
/* MDI setting is only allowed when autoneg enabled because
* some hardware doesn't allow MDI setting when speed or
* duplex is forced.
@@ -2071,14 +2122,25 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
}
}
+ if (cmd->base.autoneg == AUTONEG_DISABLE) {
+ if (cmd->base.speed != SPEED_10 && cmd->base.speed != SPEED_100) {
+ netdev_info(dev, "Unsupported speed for forced link\n");
+ return -EINVAL;
+ }
+ if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL) {
+ netdev_info(dev, "Duplex must be half or full for forced link\n");
+ return -EINVAL;
+ }
+ }
+
while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
usleep_range(1000, 2000);
- if (cmd->base.autoneg == AUTONEG_ENABLE) {
+ if (cmd->base.autoneg == AUTONEG_ENABLE)
igc_handle_autoneg_enabled(adapter, cmd);
- } else {
- netdev_info(dev, "Force mode currently not supported\n");
- }
+ else
+ igc_handle_autoneg_disabled(adapter, cmd->base.speed,
+ cmd->base.duplex);
/* MDI-X => 2; MDI => 1; Auto => 3 */
if (cmd->base.eth_tp_mdix_ctrl) {
diff --git a/drivers/net/ethernet/intel/igc/igc_hw.h b/drivers/net/ethernet/intel/igc/igc_hw.h
index 86ab8f566f44..62aaee55668a 100644
--- a/drivers/net/ethernet/intel/igc/igc_hw.h
+++ b/drivers/net/ethernet/intel/igc/igc_hw.h
@@ -73,6 +73,13 @@ struct igc_info {
extern const struct igc_info igc_base_info;
+enum igc_forced_speed_duplex {
+ IGC_FORCED_10H,
+ IGC_FORCED_10F,
+ IGC_FORCED_100H,
+ IGC_FORCED_100F,
+};
+
struct igc_mac_info {
struct igc_mac_operations ops;
@@ -93,6 +100,8 @@ struct igc_mac_info {
bool arc_subsystem_valid;
bool get_link_status;
+ bool autoneg_enabled;
+ enum igc_forced_speed_duplex forced_speed_duplex;
};
struct igc_nvm_operations {
diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
index 0a3d3f357505..d6f3f6618469 100644
--- a/drivers/net/ethernet/intel/igc/igc_mac.c
+++ b/drivers/net/ethernet/intel/igc/igc_mac.c
@@ -446,6 +446,17 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
u16 speed, duplex;
s32 ret_val = 0;
+ /* Without autoneg, flow control capability is not exchanged with the
+ * link partner. IEEE 802.3 prohibits flow control in half-duplex mode.
+ */
+ if (!hw->mac.autoneg_enabled) {
+ if (hw->mac.forced_speed_duplex == IGC_FORCED_10H ||
+ hw->mac.forced_speed_duplex == IGC_FORCED_100H)
+ hw->fc.current_mode = igc_fc_none;
+
+ goto force_fc;
+ }
+
/* In auto-neg, we need to check and see if Auto-Neg has completed,
* and if so, how the PHY and link partner has flow control
* configured.
@@ -607,6 +618,7 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
/* Now we call a subroutine to actually force the MAC
* controller to use the correct flow control settings.
*/
+force_fc:
ret_val = igc_force_mac_fc(hw);
if (ret_val) {
hw_dbg("Error forcing flow control settings\n");
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index 72bc5128d8b8..437e1d1ef1e4 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -7298,7 +7298,7 @@ static int igc_probe(struct pci_dev *pdev,
/* Initialize link properties that are user-changeable */
adapter->fc_autoneg = true;
hw->phy.autoneg_advertised = 0xaf;
-
+ hw->mac.autoneg_enabled = true;
hw->fc.requested_mode = igc_fc_default;
hw->fc.current_mode = igc_fc_default;
diff --git a/drivers/net/ethernet/intel/igc/igc_phy.c b/drivers/net/ethernet/intel/igc/igc_phy.c
index 6c4d204aecfa..4cf737fb3b21 100644
--- a/drivers/net/ethernet/intel/igc/igc_phy.c
+++ b/drivers/net/ethernet/intel/igc/igc_phy.c
@@ -494,12 +494,20 @@ s32 igc_setup_copper_link(struct igc_hw *hw)
s32 ret_val = 0;
bool link;
- /* Setup autoneg and flow control advertisement and perform
- * autonegotiation.
- */
- ret_val = igc_copper_link_autoneg(hw);
- if (ret_val)
- goto out;
+ if (hw->mac.autoneg_enabled) {
+ /* Setup autoneg and flow control advertisement and perform
+ * autonegotiation.
+ */
+ ret_val = igc_copper_link_autoneg(hw);
+ if (ret_val)
+ goto out;
+ } else {
+ ret_val = hw->phy.ops.force_speed_duplex(hw);
+ if (ret_val) {
+ hw_dbg("Error Forcing Speed/Duplex\n");
+ goto out;
+ }
+ }
/* Check link status. Wait up to 100 microseconds for link to become
* valid.
@@ -778,3 +786,48 @@ u16 igc_read_phy_fw_version(struct igc_hw *hw)
return gphy_version;
}
+
+/**
+ * igc_force_speed_duplex - Force PHY speed and duplex settings
+ * @hw: pointer to the HW structure
+ *
+ * Programs the GPY PHY control register to disable autonegotiation
+ * and force the speed/duplex indicated by hw->mac.forced_speed_duplex.
+ */
+s32 igc_force_speed_duplex(struct igc_hw *hw)
+{
+ struct igc_phy_info *phy = &hw->phy;
+ u16 phy_ctrl;
+ s32 ret_val;
+
+ ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
+ if (ret_val)
+ return ret_val;
+
+ phy_ctrl &= ~(MII_CR_SPEED_MASK | MII_CR_DUPLEX_EN |
+ MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
+
+ switch (hw->mac.forced_speed_duplex) {
+ case IGC_FORCED_10H:
+ phy_ctrl |= MII_CR_SPEED_10;
+ break;
+ case IGC_FORCED_10F:
+ phy_ctrl |= MII_CR_SPEED_10 | MII_CR_DUPLEX_EN;
+ break;
+ case IGC_FORCED_100H:
+ phy_ctrl |= MII_CR_SPEED_100;
+ break;
+ case IGC_FORCED_100F:
+ phy_ctrl |= MII_CR_SPEED_100 | MII_CR_DUPLEX_EN;
+ break;
+ default:
+ return -IGC_ERR_CONFIG;
+ }
+
+ ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
+ if (ret_val)
+ return ret_val;
+
+ hw->mac.get_link_status = true;
+ return 0;
+}
diff --git a/drivers/net/ethernet/intel/igc/igc_phy.h b/drivers/net/ethernet/intel/igc/igc_phy.h
index 832a7e359f18..d37a89174826 100644
--- a/drivers/net/ethernet/intel/igc/igc_phy.h
+++ b/drivers/net/ethernet/intel/igc/igc_phy.h
@@ -18,5 +18,6 @@ void igc_power_down_phy_copper(struct igc_hw *hw);
s32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data);
s32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data);
u16 igc_read_phy_fw_version(struct igc_hw *hw);
+s32 igc_force_speed_duplex(struct igc_hw *hw);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH iwl-next v5 0/4] igc: add support for forcing link speed without autonegotiation
2026-05-07 21:47 [PATCH iwl-next v5 0/4] igc: add support for forcing link speed without autonegotiation KhaiWenTan
` (3 preceding siblings ...)
2026-05-07 21:47 ` [PATCH iwl-next v5 4/4] igc: add support for forcing link speed without autonegotiation KhaiWenTan
@ 2026-05-11 16:28 ` Simon Horman
4 siblings, 0 replies; 10+ messages in thread
From: Simon Horman @ 2026-05-11 16:28 UTC (permalink / raw)
To: KhaiWenTan
Cc: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni, intel-wired-lan, netdev, linux-kernel,
faizal.abdul.rahim, hong.aun.looi, hector.blanco.alcaine,
khai.wen.tan, Faizal Rahim
On Fri, May 08, 2026 at 05:47:02AM +0800, KhaiWenTan wrote:
> From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
>
> This series adds support for forcing 10/100 Mb/s link speed via ethtool
> when autonegotiation is disabled on the igc driver.
>
> Changes in v5:
> - add removal justification to include copper context in commit
> description for igc: remove unused autoneg_failed field (Paul)
> - check that cmd->base.duplex is either DUPLEX_HALF or DUPLEX_FULL
> in igc_ethtool_set_link_ksettings() (Simon)
> - dynamically override hw->fc.current_mode to igc_fc_none during
> link configuration instead of mutating requested_mode (Simon)
>
> Changes in v4:
> - Validate that autoneg is AUTONEG_ENABLE or AUTONEG_DISABLE early
> in igc_ethtool_set_link_ksettings() to avoid passing unexpected
> values to igc_handle_autoneg_disabled(). (Simon Horman)
>
> Changes in v3:
> - Modify condition from "if (duplex == DUPLEX_HALF)" to
> "if (duplex != DUPLEX_FULL)". (Simon Horman)
>
> Changes in v2:
> - When forcing half-duplex, set hw->fc.requested_mode = igc_fc_none,
> since half-duplex cannot support flow control per IEEE 802.3.
> (Simon Horman)
> - Split the original single patch into three patches for clarity:
> patches 1 and 2 are preparatory cleanups; patch 3 carries the
> functional change.
Thanks for your persistence.
For the series:
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Intel-wired-lan] [PATCH iwl-next v5 1/4] igc: remove unused autoneg_failed field
2026-05-07 21:47 ` [PATCH iwl-next v5 1/4] igc: remove unused autoneg_failed field KhaiWenTan
@ 2026-06-14 7:16 ` Ruinskiy, Dima
0 siblings, 0 replies; 10+ messages in thread
From: Ruinskiy, Dima @ 2026-06-14 7:16 UTC (permalink / raw)
To: KhaiWenTan, anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim,
Aleksandr Loktionov
On 08/05/2026 0:47, KhaiWenTan wrote:
> From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
>
> autoneg_failed in struct igc_mac_info is never set in the igc driver.
> Remove the field and the dead code checking it in
> igc_config_fc_after_link_up().
>
> The field originates from the e1000/e1000e fiber/serdes forced-link
> path, where MAC-level autoneg timeout sets it to signal the flow-control
> code to force pause. igc supports only copper, so it never needs to set
> this field.
>
> Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
> Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
> ---
> drivers/net/ethernet/intel/igc/igc_hw.h | 1 -
> drivers/net/ethernet/intel/igc/igc_mac.c | 16 +---------------
> 2 files changed, 1 insertion(+), 16 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/igc/igc_hw.h b/drivers/net/ethernet/intel/igc/igc_hw.h
> index be8a49a86d09..86ab8f566f44 100644
> --- a/drivers/net/ethernet/intel/igc/igc_hw.h
> +++ b/drivers/net/ethernet/intel/igc/igc_hw.h
> @@ -92,7 +92,6 @@ struct igc_mac_info {
> bool asf_firmware_present;
> bool arc_subsystem_valid;
>
> - bool autoneg_failed;
> bool get_link_status;
> };
>
> diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
> index 7ac6637f8db7..142beb9ae557 100644
> --- a/drivers/net/ethernet/intel/igc/igc_mac.c
> +++ b/drivers/net/ethernet/intel/igc/igc_mac.c
> @@ -438,28 +438,14 @@ void igc_config_collision_dist(struct igc_hw *hw)
> * Checks the status of auto-negotiation after link up to ensure that the
> * speed and duplex were not forced. If the link needed to be forced, then
> * flow control needs to be forced also. If auto-negotiation is enabled
> - * and did not fail, then we configure flow control based on our link
> - * partner.
> + * then we configure flow control based on our link partner.
> */
> s32 igc_config_fc_after_link_up(struct igc_hw *hw)
> {
> u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;
> - struct igc_mac_info *mac = &hw->mac;
> u16 speed, duplex;
> s32 ret_val = 0;
>
> - /* Check for the case where we have fiber media and auto-neg failed
> - * so we had to force link. In this case, we need to force the
> - * configuration of the MAC to match the "fc" parameter.
> - */
> - if (mac->autoneg_failed)
> - ret_val = igc_force_mac_fc(hw);
> -
> - if (ret_val) {
> - hw_dbg("Error forcing flow control settings\n");
> - goto out;
> - }
> -
> /* In auto-neg, we need to check and see if Auto-Neg has completed,
> * and if so, how the PHY and link partner has flow control
> * configured.
Reviewed-by: Dima Ruinskiy <dima.ruinskiy@intel.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Intel-wired-lan] [PATCH iwl-next v5 2/4] igc: move autoneg-enabled settings into igc_handle_autoneg_enabled()
2026-05-07 21:47 ` [PATCH iwl-next v5 2/4] igc: move autoneg-enabled settings into igc_handle_autoneg_enabled() KhaiWenTan
@ 2026-06-14 7:17 ` Ruinskiy, Dima
0 siblings, 0 replies; 10+ messages in thread
From: Ruinskiy, Dima @ 2026-06-14 7:17 UTC (permalink / raw)
To: KhaiWenTan, anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim,
Aleksandr Loktionov
On 08/05/2026 0:47, KhaiWenTan wrote:
> From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
>
> Move the advertised link modes and flow control configuration from
> igc_ethtool_set_link_ksettings() into igc_handle_autoneg_enabled().
>
> No functional change.
>
> Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
> Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
> ---
> drivers/net/ethernet/intel/igc/igc_ethtool.c | 72 ++++++++++++--------
> 1 file changed, 44 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
> index 0122009bedd0..cfcbf2fdad6e 100644
> --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
> +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
> @@ -2000,6 +2000,49 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
> return 0;
> }
>
> +/**
> + * igc_handle_autoneg_enabled - Configure autonegotiation advertisement
> + * @adapter: private driver structure
> + * @cmd: ethtool link ksettings from user
> + *
> + * Records advertised speeds and flow control settings when autoneg
> + * is enabled.
> + */
> +static void igc_handle_autoneg_enabled(struct igc_adapter *adapter,
> + const struct ethtool_link_ksettings *cmd)
> +{
> + struct igc_hw *hw = &adapter->hw;
> + u16 advertised = 0;
> +
> + if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> + 2500baseT_Full))
> + advertised |= ADVERTISE_2500_FULL;
> +
> + if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> + 1000baseT_Full))
> + advertised |= ADVERTISE_1000_FULL;
> +
> + if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> + 100baseT_Full))
> + advertised |= ADVERTISE_100_FULL;
> +
> + if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> + 100baseT_Half))
> + advertised |= ADVERTISE_100_HALF;
> +
> + if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> + 10baseT_Full))
> + advertised |= ADVERTISE_10_FULL;
> +
> + if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> + 10baseT_Half))
> + advertised |= ADVERTISE_10_HALF;
> +
> + hw->phy.autoneg_advertised = advertised;
> + if (adapter->fc_autoneg)
> + hw->fc.requested_mode = igc_fc_default;
> +}
> +
> static int
> igc_ethtool_set_link_ksettings(struct net_device *netdev,
> const struct ethtool_link_ksettings *cmd)
> @@ -2007,7 +2050,6 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
> struct igc_adapter *adapter = netdev_priv(netdev);
> struct net_device *dev = adapter->netdev;
> struct igc_hw *hw = &adapter->hw;
> - u16 advertised = 0;
>
> /* When adapter in resetting mode, autoneg/speed/duplex
> * cannot be changed
> @@ -2032,34 +2074,8 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
> while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
> usleep_range(1000, 2000);
>
> - if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> - 2500baseT_Full))
> - advertised |= ADVERTISE_2500_FULL;
> -
> - if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> - 1000baseT_Full))
> - advertised |= ADVERTISE_1000_FULL;
> -
> - if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> - 100baseT_Full))
> - advertised |= ADVERTISE_100_FULL;
> -
> - if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> - 100baseT_Half))
> - advertised |= ADVERTISE_100_HALF;
> -
> - if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> - 10baseT_Full))
> - advertised |= ADVERTISE_10_FULL;
> -
> - if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
> - 10baseT_Half))
> - advertised |= ADVERTISE_10_HALF;
> -
> if (cmd->base.autoneg == AUTONEG_ENABLE) {
> - hw->phy.autoneg_advertised = advertised;
> - if (adapter->fc_autoneg)
> - hw->fc.requested_mode = igc_fc_default;
> + igc_handle_autoneg_enabled(adapter, cmd);
> } else {
> netdev_info(dev, "Force mode currently not supported\n");
> }
Reviewed-by: Dima Ruinskiy <dima.ruinskiy@intel.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Intel-wired-lan] [PATCH iwl-next v5 3/4] igc: replace goto out with direct returns in igc_config_fc_after_link_up()
2026-05-07 21:47 ` [PATCH iwl-next v5 3/4] igc: replace goto out with direct returns in igc_config_fc_after_link_up() KhaiWenTan
@ 2026-06-14 7:17 ` Ruinskiy, Dima
0 siblings, 0 replies; 10+ messages in thread
From: Ruinskiy, Dima @ 2026-06-14 7:17 UTC (permalink / raw)
To: KhaiWenTan, anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim
On 08/05/2026 0:47, KhaiWenTan wrote:
> From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
>
> The out: label only returns ret_val with no cleanup. The kernel coding
> style guide states: "If there is no cleanup needed then just return
> directly." (Documentation/process/coding-style.rst, section 7).
>
> This improves readability ahead of a subsequent patch that introduces a
> new goto label in this function.
>
> No functional change.
>
> Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
> Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
> Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
> ---
> drivers/net/ethernet/intel/igc/igc_mac.c | 15 +++++++--------
> 1 file changed, 7 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
> index 142beb9ae557..0a3d3f357505 100644
> --- a/drivers/net/ethernet/intel/igc/igc_mac.c
> +++ b/drivers/net/ethernet/intel/igc/igc_mac.c
> @@ -458,15 +458,15 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
> ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
> &mii_status_reg);
> if (ret_val)
> - goto out;
> + return ret_val;
> ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
> &mii_status_reg);
> if (ret_val)
> - goto out;
> + return ret_val;
>
> if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {
> hw_dbg("Copper PHY and Auto Neg has not completed.\n");
> - goto out;
> + return ret_val;
> }
>
> /* The AutoNeg process has completed, so we now need to
> @@ -478,11 +478,11 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
> ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV,
> &mii_nway_adv_reg);
> if (ret_val)
> - goto out;
> + return ret_val;
> ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY,
> &mii_nway_lp_ability_reg);
> if (ret_val)
> - goto out;
> + return ret_val;
> /* Two bits in the Auto Negotiation Advertisement Register
> * (Address 4) and two bits in the Auto Negotiation Base
> * Page Ability Register (Address 5) determine flow control
> @@ -598,7 +598,7 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
> ret_val = hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex);
> if (ret_val) {
> hw_dbg("Error getting link speed and duplex\n");
> - goto out;
> + return ret_val;
> }
>
> if (duplex == HALF_DUPLEX)
> @@ -610,10 +610,9 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
> ret_val = igc_force_mac_fc(hw);
> if (ret_val) {
> hw_dbg("Error forcing flow control settings\n");
> - goto out;
> + return ret_val;
> }
>
> -out:
> return ret_val;
> }
>
Reviewed-by: Dima Ruinskiy <dima.ruinskiy@intel.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Intel-wired-lan] [PATCH iwl-next v5 4/4] igc: add support for forcing link speed without autonegotiation
2026-05-07 21:47 ` [PATCH iwl-next v5 4/4] igc: add support for forcing link speed without autonegotiation KhaiWenTan
@ 2026-06-14 7:17 ` Ruinskiy, Dima
0 siblings, 0 replies; 10+ messages in thread
From: Ruinskiy, Dima @ 2026-06-14 7:17 UTC (permalink / raw)
To: KhaiWenTan, anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: intel-wired-lan, netdev, linux-kernel, faizal.abdul.rahim,
hong.aun.looi, hector.blanco.alcaine, khai.wen.tan, Faizal Rahim
On 08/05/2026 0:47, KhaiWenTan wrote:
> From: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
>
> Allow users to force 10/100 Mb/s link speed and duplex via ethtool
> when autonegotiation is disabled. Previously, the driver rejected
> these requests with "Force mode currently not supported.".
>
> Forcing at 1000 Mb/s and 2500 Mb/s is not supported.
>
> Reviewed-by: Looi Hong Aun <hong.aun.looi@intel.com>
> Signed-off-by: Faizal Rahim <faizal.abdul.rahim@linux.intel.com>
> Signed-off-by: Khai Wen Tan <khai.wen.tan@linux.intel.com>
> ---
> drivers/net/ethernet/intel/igc/igc_base.c | 35 ++++-
> drivers/net/ethernet/intel/igc/igc_defines.h | 9 +-
> drivers/net/ethernet/intel/igc/igc_ethtool.c | 138 ++++++++++++++-----
> drivers/net/ethernet/intel/igc/igc_hw.h | 9 ++
> drivers/net/ethernet/intel/igc/igc_mac.c | 12 ++
> drivers/net/ethernet/intel/igc/igc_main.c | 2 +-
> drivers/net/ethernet/intel/igc/igc_phy.c | 65 ++++++++-
> drivers/net/ethernet/intel/igc/igc_phy.h | 1 +
> 8 files changed, 220 insertions(+), 51 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/igc/igc_base.c b/drivers/net/ethernet/intel/igc/igc_base.c
> index 1613b562d17c..ab9120a3127f 100644
> --- a/drivers/net/ethernet/intel/igc/igc_base.c
> +++ b/drivers/net/ethernet/intel/igc/igc_base.c
> @@ -114,11 +114,35 @@ static s32 igc_setup_copper_link_base(struct igc_hw *hw)
> u32 ctrl;
>
> ctrl = rd32(IGC_CTRL);
> - ctrl |= IGC_CTRL_SLU;
> - ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX);
> - wr32(IGC_CTRL, ctrl);
> -
> - ret_val = igc_setup_copper_link(hw);
> + ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX |
> + IGC_CTRL_SPEED_MASK | IGC_CTRL_FD);
> +
> + if (hw->mac.autoneg_enabled) {
> + ctrl |= IGC_CTRL_SLU;
> + wr32(IGC_CTRL, ctrl);
> + ret_val = igc_setup_copper_link(hw);
> + } else {
> + ctrl |= IGC_CTRL_SLU | IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX;
> +
> + switch (hw->mac.forced_speed_duplex) {
> + case IGC_FORCED_10H:
> + ctrl |= IGC_CTRL_SPEED_10;
> + break;
> + case IGC_FORCED_10F:
> + ctrl |= IGC_CTRL_SPEED_10 | IGC_CTRL_FD;
> + break;
> + case IGC_FORCED_100H:
> + ctrl |= IGC_CTRL_SPEED_100;
> + break;
> + case IGC_FORCED_100F:
> + ctrl |= IGC_CTRL_SPEED_100 | IGC_CTRL_FD;
> + break;
> + default:
> + return -IGC_ERR_CONFIG;
> + }
> + wr32(IGC_CTRL, ctrl);
> + ret_val = igc_setup_copper_link(hw);
> + }
>
> return ret_val;
> }
> @@ -443,6 +467,7 @@ static const struct igc_phy_operations igc_phy_ops_base = {
> .reset = igc_phy_hw_reset,
> .read_reg = igc_read_phy_reg_gpy,
> .write_reg = igc_write_phy_reg_gpy,
> + .force_speed_duplex = igc_force_speed_duplex,
> };
>
> const struct igc_info igc_base_info = {
> diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h
> index 9482ab11f050..3f504751c2d9 100644
> --- a/drivers/net/ethernet/intel/igc/igc_defines.h
> +++ b/drivers/net/ethernet/intel/igc/igc_defines.h
> @@ -129,10 +129,13 @@
> #define IGC_ERR_SWFW_SYNC 13
>
> /* Device Control */
> +#define IGC_CTRL_FD BIT(0) /* Full Duplex */
> #define IGC_CTRL_RST 0x04000000 /* Global reset */
> -
> #define IGC_CTRL_PHY_RST 0x80000000 /* PHY Reset */
> #define IGC_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
> +#define IGC_CTRL_SPEED_MASK GENMASK(10, 8)
> +#define IGC_CTRL_SPEED_10 FIELD_PREP(IGC_CTRL_SPEED_MASK, 0)
> +#define IGC_CTRL_SPEED_100 FIELD_PREP(IGC_CTRL_SPEED_MASK, 1)
> #define IGC_CTRL_FRCSPD 0x00000800 /* Force Speed */
> #define IGC_CTRL_FRCDPX 0x00001000 /* Force Duplex */
> #define IGC_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */
> @@ -673,6 +676,10 @@
> #define IGC_GEN_POLL_TIMEOUT 1920
>
> /* PHY Control Register */
> +#define MII_CR_SPEED_MASK (BIT(6) | BIT(13))
> +#define MII_CR_SPEED_10 0x0000 /* SSM=0, SSL=0: 10 Mb/s */
> +#define MII_CR_SPEED_100 BIT(13) /* SSM=0, SSL=1: 100 Mb/s */
> +#define MII_CR_DUPLEX_EN BIT(8) /* 0 = Half Duplex, 1 = Full Duplex */
> #define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
> #define MII_CR_POWER_DOWN 0x0800 /* Power down */
> #define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
> diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
> index cfcbf2fdad6e..b103836a895f 100644
> --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
> +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
> @@ -1914,44 +1914,58 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
> ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
> ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
>
> - /* advertising link modes */
> - if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF)
> - ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half);
> - if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL)
> - ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full);
> - if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF)
> - ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Half);
> - if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)
> - ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full);
> - if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL)
> - ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full);
> - if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL)
> - ethtool_link_ksettings_add_link_mode(cmd, advertising, 2500baseT_Full);
> -
> /* set autoneg settings */
> ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
> - ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
> + if (hw->mac.autoneg_enabled) {
> + ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
> + cmd->base.autoneg = AUTONEG_ENABLE;
> +
> + /* advertising link modes only apply when autoneg is on */
> + if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF)
> + ethtool_link_ksettings_add_link_mode(cmd, advertising,
> + 10baseT_Half);
> + if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL)
> + ethtool_link_ksettings_add_link_mode(cmd, advertising,
> + 10baseT_Full);
> + if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF)
> + ethtool_link_ksettings_add_link_mode(cmd, advertising,
> + 100baseT_Half);
> + if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)
> + ethtool_link_ksettings_add_link_mode(cmd, advertising,
> + 100baseT_Full);
> + if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL)
> + ethtool_link_ksettings_add_link_mode(cmd, advertising,
> + 1000baseT_Full);
> + if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL)
> + ethtool_link_ksettings_add_link_mode(cmd, advertising,
> + 2500baseT_Full);
> +
> + /* Set pause flow control advertising */
> + switch (hw->fc.requested_mode) {
> + case igc_fc_full:
> + ethtool_link_ksettings_add_link_mode(cmd, advertising,
> + Pause);
> + break;
> + case igc_fc_rx_pause:
> + ethtool_link_ksettings_add_link_mode(cmd, advertising,
> + Pause);
> + ethtool_link_ksettings_add_link_mode(cmd, advertising,
> + Asym_Pause);
> + break;
> + case igc_fc_tx_pause:
> + ethtool_link_ksettings_add_link_mode(cmd, advertising,
> + Asym_Pause);
> + break;
> + default:
> + break;
> + }
> + } else {
> + cmd->base.autoneg = AUTONEG_DISABLE;
> + }
>
> - /* Set pause flow control settings */
> + /* Pause is always supported */
> ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
>
> - switch (hw->fc.requested_mode) {
> - case igc_fc_full:
> - ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
> - break;
> - case igc_fc_rx_pause:
> - ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
> - ethtool_link_ksettings_add_link_mode(cmd, advertising,
> - Asym_Pause);
> - break;
> - case igc_fc_tx_pause:
> - ethtool_link_ksettings_add_link_mode(cmd, advertising,
> - Asym_Pause);
> - break;
> - default:
> - break;
> - }
> -
> status = pm_runtime_suspended(&adapter->pdev->dev) ?
> 0 : rd32(IGC_STATUS);
>
> @@ -1983,7 +1997,6 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
> cmd->base.duplex = DUPLEX_UNKNOWN;
> }
> cmd->base.speed = speed;
> - cmd->base.autoneg = AUTONEG_ENABLE;
>
> /* MDI-X => 2; MDI =>1; Invalid =>0 */
> if (hw->phy.media_type == igc_media_type_copper)
> @@ -2000,6 +2013,37 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
> return 0;
> }
>
> +/**
> + * igc_handle_autoneg_disabled - Configure forced speed/duplex settings
> + * @adapter: private driver structure
> + * @speed: requested speed (must be SPEED_10 or SPEED_100)
> + * @duplex: requested duplex
> + *
> + * Records forced speed/duplex when autoneg is disabled.
> + * Caller must validate speed before calling this function.
> + */
> +static void igc_handle_autoneg_disabled(struct igc_adapter *adapter, u32 speed,
> + u8 duplex)
> +{
> + struct igc_mac_info *mac = &adapter->hw.mac;
> +
> + switch (speed) {
> + case SPEED_10:
> + mac->forced_speed_duplex = (duplex == DUPLEX_FULL) ?
> + IGC_FORCED_10F : IGC_FORCED_10H;
> + break;
> + case SPEED_100:
> + mac->forced_speed_duplex = (duplex == DUPLEX_FULL) ?
> + IGC_FORCED_100F : IGC_FORCED_100H;
> + break;
> + default:
> + WARN_ONCE(1, "Unsupported speed %u\n", speed);
> + return;
> + }
> +
> + mac->autoneg_enabled = false;
> +}
> +
> /**
> * igc_handle_autoneg_enabled - Configure autonegotiation advertisement
> * @adapter: private driver structure
> @@ -2038,6 +2082,7 @@ static void igc_handle_autoneg_enabled(struct igc_adapter *adapter,
> 10baseT_Half))
> advertised |= ADVERTISE_10_HALF;
>
> + hw->mac.autoneg_enabled = true;
> hw->phy.autoneg_advertised = advertised;
> if (adapter->fc_autoneg)
> hw->fc.requested_mode = igc_fc_default;
> @@ -2059,6 +2104,12 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
> return -EINVAL;
> }
>
> + if (cmd->base.autoneg != AUTONEG_ENABLE &&
> + cmd->base.autoneg != AUTONEG_DISABLE) {
> + netdev_info(dev, "Unsupported autoneg setting\n");
> + return -EINVAL;
> + }
> +
> /* MDI setting is only allowed when autoneg enabled because
> * some hardware doesn't allow MDI setting when speed or
> * duplex is forced.
> @@ -2071,14 +2122,25 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
> }
> }
>
> + if (cmd->base.autoneg == AUTONEG_DISABLE) {
> + if (cmd->base.speed != SPEED_10 && cmd->base.speed != SPEED_100) {
> + netdev_info(dev, "Unsupported speed for forced link\n");
> + return -EINVAL;
> + }
> + if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL) {
> + netdev_info(dev, "Duplex must be half or full for forced link\n");
> + return -EINVAL;
> + }
> + }
> +
> while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
> usleep_range(1000, 2000);
>
> - if (cmd->base.autoneg == AUTONEG_ENABLE) {
> + if (cmd->base.autoneg == AUTONEG_ENABLE)
> igc_handle_autoneg_enabled(adapter, cmd);
> - } else {
> - netdev_info(dev, "Force mode currently not supported\n");
> - }
> + else
> + igc_handle_autoneg_disabled(adapter, cmd->base.speed,
> + cmd->base.duplex);
>
> /* MDI-X => 2; MDI => 1; Auto => 3 */
> if (cmd->base.eth_tp_mdix_ctrl) {
> diff --git a/drivers/net/ethernet/intel/igc/igc_hw.h b/drivers/net/ethernet/intel/igc/igc_hw.h
> index 86ab8f566f44..62aaee55668a 100644
> --- a/drivers/net/ethernet/intel/igc/igc_hw.h
> +++ b/drivers/net/ethernet/intel/igc/igc_hw.h
> @@ -73,6 +73,13 @@ struct igc_info {
>
> extern const struct igc_info igc_base_info;
>
> +enum igc_forced_speed_duplex {
> + IGC_FORCED_10H,
> + IGC_FORCED_10F,
> + IGC_FORCED_100H,
> + IGC_FORCED_100F,
> +};
> +
> struct igc_mac_info {
> struct igc_mac_operations ops;
>
> @@ -93,6 +100,8 @@ struct igc_mac_info {
> bool arc_subsystem_valid;
>
> bool get_link_status;
> + bool autoneg_enabled;
> + enum igc_forced_speed_duplex forced_speed_duplex;
> };
>
> struct igc_nvm_operations {
> diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
> index 0a3d3f357505..d6f3f6618469 100644
> --- a/drivers/net/ethernet/intel/igc/igc_mac.c
> +++ b/drivers/net/ethernet/intel/igc/igc_mac.c
> @@ -446,6 +446,17 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
> u16 speed, duplex;
> s32 ret_val = 0;
>
> + /* Without autoneg, flow control capability is not exchanged with the
> + * link partner. IEEE 802.3 prohibits flow control in half-duplex mode.
> + */
> + if (!hw->mac.autoneg_enabled) {
> + if (hw->mac.forced_speed_duplex == IGC_FORCED_10H ||
> + hw->mac.forced_speed_duplex == IGC_FORCED_100H)
> + hw->fc.current_mode = igc_fc_none;
> +
> + goto force_fc;
> + }
> +
> /* In auto-neg, we need to check and see if Auto-Neg has completed,
> * and if so, how the PHY and link partner has flow control
> * configured.
> @@ -607,6 +618,7 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
> /* Now we call a subroutine to actually force the MAC
> * controller to use the correct flow control settings.
> */
> +force_fc:
> ret_val = igc_force_mac_fc(hw);
> if (ret_val) {
> hw_dbg("Error forcing flow control settings\n");
> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
> index 72bc5128d8b8..437e1d1ef1e4 100644
> --- a/drivers/net/ethernet/intel/igc/igc_main.c
> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
> @@ -7298,7 +7298,7 @@ static int igc_probe(struct pci_dev *pdev,
> /* Initialize link properties that are user-changeable */
> adapter->fc_autoneg = true;
> hw->phy.autoneg_advertised = 0xaf;
> -
> + hw->mac.autoneg_enabled = true;
> hw->fc.requested_mode = igc_fc_default;
> hw->fc.current_mode = igc_fc_default;
>
> diff --git a/drivers/net/ethernet/intel/igc/igc_phy.c b/drivers/net/ethernet/intel/igc/igc_phy.c
> index 6c4d204aecfa..4cf737fb3b21 100644
> --- a/drivers/net/ethernet/intel/igc/igc_phy.c
> +++ b/drivers/net/ethernet/intel/igc/igc_phy.c
> @@ -494,12 +494,20 @@ s32 igc_setup_copper_link(struct igc_hw *hw)
> s32 ret_val = 0;
> bool link;
>
> - /* Setup autoneg and flow control advertisement and perform
> - * autonegotiation.
> - */
> - ret_val = igc_copper_link_autoneg(hw);
> - if (ret_val)
> - goto out;
> + if (hw->mac.autoneg_enabled) {
> + /* Setup autoneg and flow control advertisement and perform
> + * autonegotiation.
> + */
> + ret_val = igc_copper_link_autoneg(hw);
> + if (ret_val)
> + goto out;
> + } else {
> + ret_val = hw->phy.ops.force_speed_duplex(hw);
> + if (ret_val) {
> + hw_dbg("Error Forcing Speed/Duplex\n");
> + goto out;
> + }
> + }
>
> /* Check link status. Wait up to 100 microseconds for link to become
> * valid.
> @@ -778,3 +786,48 @@ u16 igc_read_phy_fw_version(struct igc_hw *hw)
>
> return gphy_version;
> }
> +
> +/**
> + * igc_force_speed_duplex - Force PHY speed and duplex settings
> + * @hw: pointer to the HW structure
> + *
> + * Programs the GPY PHY control register to disable autonegotiation
> + * and force the speed/duplex indicated by hw->mac.forced_speed_duplex.
> + */
> +s32 igc_force_speed_duplex(struct igc_hw *hw)
> +{
> + struct igc_phy_info *phy = &hw->phy;
> + u16 phy_ctrl;
> + s32 ret_val;
> +
> + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
> + if (ret_val)
> + return ret_val;
> +
> + phy_ctrl &= ~(MII_CR_SPEED_MASK | MII_CR_DUPLEX_EN |
> + MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
> +
> + switch (hw->mac.forced_speed_duplex) {
> + case IGC_FORCED_10H:
> + phy_ctrl |= MII_CR_SPEED_10;
> + break;
> + case IGC_FORCED_10F:
> + phy_ctrl |= MII_CR_SPEED_10 | MII_CR_DUPLEX_EN;
> + break;
> + case IGC_FORCED_100H:
> + phy_ctrl |= MII_CR_SPEED_100;
> + break;
> + case IGC_FORCED_100F:
> + phy_ctrl |= MII_CR_SPEED_100 | MII_CR_DUPLEX_EN;
> + break;
> + default:
> + return -IGC_ERR_CONFIG;
> + }
> +
> + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
> + if (ret_val)
> + return ret_val;
> +
> + hw->mac.get_link_status = true;
> + return 0;
> +}
> diff --git a/drivers/net/ethernet/intel/igc/igc_phy.h b/drivers/net/ethernet/intel/igc/igc_phy.h
> index 832a7e359f18..d37a89174826 100644
> --- a/drivers/net/ethernet/intel/igc/igc_phy.h
> +++ b/drivers/net/ethernet/intel/igc/igc_phy.h
> @@ -18,5 +18,6 @@ void igc_power_down_phy_copper(struct igc_hw *hw);
> s32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data);
> s32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data);
> u16 igc_read_phy_fw_version(struct igc_hw *hw);
> +s32 igc_force_speed_duplex(struct igc_hw *hw);
>
> #endif
Reviewed-by: Dima Ruinskiy <dima.ruinskiy@intel.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-06-14 7:17 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 21:47 [PATCH iwl-next v5 0/4] igc: add support for forcing link speed without autonegotiation KhaiWenTan
2026-05-07 21:47 ` [PATCH iwl-next v5 1/4] igc: remove unused autoneg_failed field KhaiWenTan
2026-06-14 7:16 ` [Intel-wired-lan] " Ruinskiy, Dima
2026-05-07 21:47 ` [PATCH iwl-next v5 2/4] igc: move autoneg-enabled settings into igc_handle_autoneg_enabled() KhaiWenTan
2026-06-14 7:17 ` [Intel-wired-lan] " Ruinskiy, Dima
2026-05-07 21:47 ` [PATCH iwl-next v5 3/4] igc: replace goto out with direct returns in igc_config_fc_after_link_up() KhaiWenTan
2026-06-14 7:17 ` [Intel-wired-lan] " Ruinskiy, Dima
2026-05-07 21:47 ` [PATCH iwl-next v5 4/4] igc: add support for forcing link speed without autonegotiation KhaiWenTan
2026-06-14 7:17 ` [Intel-wired-lan] " Ruinskiy, Dima
2026-05-11 16:28 ` [PATCH iwl-next v5 0/4] " Simon Horman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox