[PATCH] ethtool: add userspace support for KSZ87xx PHY tunables
From: Fidelio Lawson <hidden>
Date: 2026-06-12 09:02:35
Subsystem:
the rest · Maintainer:
Linus Torvalds
Add userspace support for three new KSZ87xx PHY tunables introduced in the kernel: - ETHTOOL_PHY_SHORT_CABLE_PRESET - ETHTOOL_PHY_LPF_BW - ETHTOOL_PHY_DSP_EQ_INIT_VALUE Extend --get-phy-tunable and --set-phy-tunable to query/set these tunables through ETHTOOL_PHY_GTUNABLE/ETHTOOL_PHY_STUNABLE. Also update: - bundled uapi/linux/ethtool.h phy_tunable_id enum - CLI help text - man page documentation - bash completion for get/set phy tunables This enables full userspace control of KSZ87xx short-cable/low-loss tuning knobs. Kernel-side link: https://git.kernel.org/netdev/net-next/c/13655144ddca Signed-off-by: Fidelio Lawson <redacted> --- Add support for the recently introduced KSZ87xx PHY tunables: - phy-short-cable-preset - phy-lpf-bandwidth - phy-dsp-eq-init-value These tunables allow control of the KSZ87xx PHY equalizer and low-pass filter settings to handle short or low-loss cables. The UAPI definitions were introduced in the kernel through: net: ethtool: add KSZ87xx low-loss cable PHY tunables --- ethtool.8.in | 36 ++++++++++++ ethtool.c | 126 ++++++++++++++++++++++++++++++++++++++++++ shell-completion/bash/ethtool | 16 ++++-- test-cmdline.c | 25 +++++++++ uapi/linux/ethtool.h | 3 + 5 files changed, 202 insertions(+), 4 deletions(-)
diff --git a/ethtool.8.in b/ethtool.8.in
index 32b971a..09e4671 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in@@ -444,12 +444,27 @@ ethtool \- query or control network driver and hardware settings .A1 on off .BN msecs .RB ] +.RB [ +.B short\-cable\-preset +.A1 on off +.RB ] +.RB [ +.B lpf\-bandwidth +.BN value +.RB ] +.RB [ +.B dsp\-eq\-init\-value +.BN value +.RB ] .HP .B ethtool \-\-get\-phy\-tunable .I devname .RB [ downshift ] .RB [ fast-link-down ] .RB [ energy-detect-power-down ] +.RB [ short-cable-preset ] +.RB [ lpf-bandwidth ] +.RB [ dsp-eq-init-value ] .HP .B ethtool \-\-get\-tunable .I devname
@@ -1510,6 +1525,18 @@ lB l. value (in milliseconds) will be used by the PHY. .TE .TP +.A2 short-cable-preset on off +Specifies whether a PHY short-cable preset should be enabled (if supported). +On KSZ87xx this is a global switch-wide preset and not a per-port setting. +.TP +.BI lpf-bandwidth \ value +Sets PHY LPF bandwidth register value (if supported). +On KSZ87xx this is a global switch-wide setting and not a per-port setting. +.TP +.BI dsp-eq-init-value \ value +Sets PHY DSP EQ initial value register value (if supported). +On KSZ87xx this is a global switch-wide setting and not a per-port setting. +.TP .PD .RE .TP
@@ -1535,6 +1562,15 @@ Gets the PHY Fast Link Down status / period. .TP .B energy\-detect\-power\-down Gets the current configured setting for Energy Detect Power Down (if supported). +.TP +.B short\-cable\-preset +Gets PHY short-cable preset status (if supported). +.TP +.B lpf\-bandwidth +Gets PHY LPF bandwidth register value (if supported). +.TP +.B dsp\-eq\-init\-value +Gets PHY DSP EQ initial value register value (if supported). .RE .TP
diff --git a/ethtool.c b/ethtool.c
index 2444d85..b7c65c2 100644
--- a/ethtool.c
+++ b/ethtool.c@@ -5461,6 +5461,55 @@ static int do_get_phy_tunable(struct cmd_context *ctx) fprintf(stdout, "Energy Detect Power Down: enabled, TX %u msecs\n", cont.msecs); + } else if (!strcmp(argp[0], "short-cable-preset")) { + struct { + struct ethtool_tunable tuna; + u8 enabled; + } cont; + + cont.tuna.cmd = ETHTOOL_PHY_GTUNABLE; + cont.tuna.id = ETHTOOL_PHY_SHORT_CABLE_PRESET; + cont.tuna.type_id = ETHTOOL_TUNABLE_U8; + cont.tuna.len = 1; + if (send_ioctl(ctx, &cont.tuna) < 0) { + perror("Cannot Get PHY short-cable preset value"); + return 87; + } + + fprintf(stdout, "Short cable preset: %s\n", + cont.enabled ? "enabled" : "disabled"); + } else if (!strcmp(argp[0], "lpf-bandwidth")) { + struct { + struct ethtool_tunable tuna; + u32 bw; + } cont; + + cont.tuna.cmd = ETHTOOL_PHY_GTUNABLE; + cont.tuna.id = ETHTOOL_PHY_LPF_BW; + cont.tuna.type_id = ETHTOOL_TUNABLE_U32; + cont.tuna.len = 4; + if (send_ioctl(ctx, &cont.tuna) < 0) { + perror("Cannot Get PHY LPF bandwidth value"); + return 87; + } + + fprintf(stdout, "LPF bandwidth: %u\n", cont.bw); + } else if (!strcmp(argp[0], "dsp-eq-init-value")) { + struct { + struct ethtool_tunable tuna; + u32 eq; + } cont; + + cont.tuna.cmd = ETHTOOL_PHY_GTUNABLE; + cont.tuna.id = ETHTOOL_PHY_DSP_EQ_INIT_VALUE; + cont.tuna.type_id = ETHTOOL_TUNABLE_U32; + cont.tuna.len = 4; + if (send_ioctl(ctx, &cont.tuna) < 0) { + perror("Cannot Get PHY DSP EQ init value"); + return 87; + } + + fprintf(stdout, "DSP EQ init value: %u\n", cont.eq); } else { exit_bad_args(); }
@@ -5625,6 +5674,18 @@ static int parse_named_u16(struct cmd_context *ctx, const char *name, u16 *val) return ret; } +static int parse_named_u32(struct cmd_context *ctx, const char *name, u32 *val) +{ + unsigned long long val1; + int ret; + + ret = parse_named_uint(ctx, name, &val1, 0xffffffffULL); + if (ret) + *val = val1; + + return ret; +} + static int do_set_phy_tunable(struct cmd_context *ctx) { int err = 0;
@@ -5634,6 +5695,11 @@ static int do_set_phy_tunable(struct cmd_context *ctx) u8 fld_msecs = ETHTOOL_PHY_FAST_LINK_DOWN_ON; u8 edpd_changed = 0, edpd_enable = 0; u16 edpd_tx_interval = ETHTOOL_PHY_EDPD_DFLT_TX_MSECS; + u8 scp_changed = 0, scp_enable = 0; + u8 lpf_bw_changed = 0; + u32 lpf_bw = 0; + u8 dsp_eq_init_changed = 0; + u32 dsp_eq_init = 0; /* Parse arguments */ if (parse_named_bool(ctx, "downshift", &ds_enable)) {
@@ -5648,6 +5714,12 @@ static int do_set_phy_tunable(struct cmd_context *ctx) edpd_changed = 1; if (edpd_enable) parse_named_u16(ctx, "msecs", &edpd_tx_interval); + } else if (parse_named_bool(ctx, "short-cable-preset", &scp_enable)) { + scp_changed = 1; + } else if (parse_named_u32(ctx, "lpf-bandwidth", &lpf_bw)) { + lpf_bw_changed = 1; + } else if (parse_named_u32(ctx, "dsp-eq-init-value", &dsp_eq_init)) { + dsp_eq_init_changed = 1; } else { exit_bad_args(); }
@@ -5733,6 +5805,54 @@ static int do_set_phy_tunable(struct cmd_context *ctx) perror("Cannot Set PHY Energy Detect Power Down"); err = 87; } + } else if (scp_changed) { + struct { + struct ethtool_tunable tuna; + u8 enabled; + } cont; + + cont.tuna.cmd = ETHTOOL_PHY_STUNABLE; + cont.tuna.id = ETHTOOL_PHY_SHORT_CABLE_PRESET; + cont.tuna.type_id = ETHTOOL_TUNABLE_U8; + cont.tuna.len = 1; + cont.enabled = scp_enable; + err = send_ioctl(ctx, &cont.tuna); + if (err < 0) { + perror("Cannot Set PHY short-cable preset value"); + err = 87; + } + } else if (lpf_bw_changed) { + struct { + struct ethtool_tunable tuna; + u32 bw; + } cont; + + cont.tuna.cmd = ETHTOOL_PHY_STUNABLE; + cont.tuna.id = ETHTOOL_PHY_LPF_BW; + cont.tuna.type_id = ETHTOOL_TUNABLE_U32; + cont.tuna.len = 4; + cont.bw = lpf_bw; + err = send_ioctl(ctx, &cont.tuna); + if (err < 0) { + perror("Cannot Set PHY LPF bandwidth value"); + err = 87; + } + } else if (dsp_eq_init_changed) { + struct { + struct ethtool_tunable tuna; + u32 eq; + } cont; + + cont.tuna.cmd = ETHTOOL_PHY_STUNABLE; + cont.tuna.id = ETHTOOL_PHY_DSP_EQ_INIT_VALUE; + cont.tuna.type_id = ETHTOOL_TUNABLE_U32; + cont.tuna.len = 4; + cont.eq = dsp_eq_init; + err = send_ioctl(ctx, &cont.tuna); + if (err < 0) { + perror("Cannot Set PHY DSP EQ init value"); + err = 87; + } } return err;
@@ -6195,6 +6315,9 @@ static const struct option args[] = { .xhelp = " [ downshift on|off [count N] ]\n" " [ fast-link-down on|off [msecs N] ]\n" " [ energy-detect-power-down on|off [msecs N] ]\n" + " [ short-cable-preset on|off ]\n" + " [ lpf-bandwidth N ]\n" + " [ dsp-eq-init-value N ]\n" }, { .opts = "--get-phy-tunable",
@@ -6203,6 +6326,9 @@ static const struct option args[] = { .xhelp = " [ downshift ]\n" " [ fast-link-down ]\n" " [ energy-detect-power-down ]\n" + " [ short-cable-preset ]\n" + " [ lpf-bandwidth ]\n" + " [ dsp-eq-init-value ]\n" }, { .opts = "--get-tunable",
diff --git a/shell-completion/bash/ethtool b/shell-completion/bash/ethtool
index 57c39c4..b6a67c4 100644
--- a/shell-completion/bash/ethtool
+++ b/shell-completion/bash/ethtool@@ -659,7 +659,7 @@ _ethtool_get_dump() _ethtool_get_phy_tunable() { if [ "$cword" -eq 3 ]; then - COMPREPLY=( $( compgen -W downshift -- "$cur" ) ) + COMPREPLY=( $( compgen -W 'downshift fast-link-down energy-detect-power-down short-cable-preset lpf-bandwidth dsp-eq-init-value' -- "$cur" ) ) return fi }
@@ -1023,13 +1023,21 @@ _ethtool_set_phy_tunable() { case "$cword" in 3) - COMPREPLY=( $( compgen -W downshift -- "$cur" ) ) + COMPREPLY=( $( compgen -W 'downshift fast-link-down energy-detect-power-down short-cable-preset lpf-bandwidth dsp-eq-init-value' -- "$cur" ) ) return ;; 4) - COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) ) + case "${words[3]}" in + downshift|fast-link-down|energy-detect-power-down|short-cable-preset) + COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) ) ;; + esac return ;; 5) - COMPREPLY=( $( compgen -W count -- "$cur" ) ) + case "${words[3]}" in + downshift) + COMPREPLY=( $( compgen -W count -- "$cur" ) ) ;; + fast-link-down|energy-detect-power-down) + COMPREPLY=( $( compgen -W msecs -- "$cur" ) ) ;; + esac return ;; esac }
diff --git a/test-cmdline.c b/test-cmdline.c
index c48be87..b9f1a0f 100644
--- a/test-cmdline.c
+++ b/test-cmdline.c@@ -283,6 +283,31 @@ static struct test_case { { 0, "--set-eee devname tx-timer 42 advertise 0x4321" }, { 1, "--set-eee devname tx-timer foo" }, { 1, "--set-eee devname advertise foo" }, + { 1, "--get-phy-tunable" }, + { 1, "--get-phy-tunable devname" }, + { 0, "--get-phy-tunable devname downshift" }, + { 0, "--get-phy-tunable devname fast-link-down" }, + { 0, "--get-phy-tunable devname energy-detect-power-down" }, + { 0, "--get-phy-tunable devname short-cable-preset" }, + { 0, "--get-phy-tunable devname lpf-bandwidth" }, + { 0, "--get-phy-tunable devname dsp-eq-init-value" }, + { 1, "--get-phy-tunable devname foo" }, + { 1, "--set-phy-tunable" }, + { 1, "--set-phy-tunable devname" }, + { 1, "--set-phy-tunable devname downshift" }, + { 0, "--set-phy-tunable devname downshift on" }, + { 0, "--set-phy-tunable devname downshift off" }, + { 0, "--set-phy-tunable devname downshift on count 3" }, + { 1, "--set-phy-tunable devname short-cable-preset" }, + { 0, "--set-phy-tunable devname short-cable-preset on" }, + { 0, "--set-phy-tunable devname short-cable-preset off" }, + { 1, "--set-phy-tunable devname short-cable-preset foo" }, + { 1, "--set-phy-tunable devname lpf-bandwidth" }, + { 0, "--set-phy-tunable devname lpf-bandwidth 1024" }, + { 1, "--set-phy-tunable devname lpf-bandwidth foo" }, + { 1, "--set-phy-tunable devname dsp-eq-init-value" }, + { 0, "--set-phy-tunable devname dsp-eq-init-value 65535" }, + { 1, "--set-phy-tunable devname dsp-eq-init-value foo" }, { 1, "--set-fec devname" }, { 0, "--set-fec devname encoding auto" }, { 0, "--set-fec devname encoding off" },
diff --git a/uapi/linux/ethtool.h b/uapi/linux/ethtool.h
index a5d7d24..41f557a 100644
--- a/uapi/linux/ethtool.h
+++ b/uapi/linux/ethtool.h@@ -291,6 +291,9 @@ enum phy_tunable_id { ETHTOOL_PHY_DOWNSHIFT, ETHTOOL_PHY_FAST_LINK_DOWN, ETHTOOL_PHY_EDPD, + ETHTOOL_PHY_SHORT_CABLE_PRESET, + ETHTOOL_PHY_LPF_BW, + ETHTOOL_PHY_DSP_EQ_INIT_VALUE, /* * Add your fresh new phy tunable attribute above and remember to update * phy_tunable_strings[] in net/ethtool/common.c
--- base-commit: 75fd12445ffd6d697d53b7d296e345d524a9bedd change-id: 20260612-add-ksz87xx-tunables-support-577cc70b2180 Best regards, -- Fidelio Lawson [off-list ref]